From aptos-agent-skills
Analyzes Aptos Move contracts for gas efficiency, identifying expensive global storage, vector, and string operations, and suggests optimizations like batching, efficient data structures, and reference borrowing. Useful before mainnet deployment or for high-volume DeFi.
npx claudepluginhub aptos-labs/aptos-agent-skills --plugin aptos-agent-skillsThis skill uses the workspace's default tool permissions.
Analyze and optimize Aptos Move contracts for gas efficiency, identifying expensive operations and suggesting
Optimizes gas usage in Solidity smart contracts on EVM chains with chain-specific analysis (full for Ethereum L1, calldata-focused for L2s). Scans, fixes, verifies.
Generates secure Aptos Move V2 smart contracts with Object model, Digital Asset NFT integration, security patterns, and storage type guidance for collections, marketplaces, DAOs, staking.
Analyzes EVM internals for gas optimization, Yul assembly, opcodes, storage layout, calldata, proxies, and bytecode. Useful for Solidity efficiency, debugging, MEV design. Activates on EVM/gas/yul mentions.
Share bugs, ideas, or general feedback.
Analyze and optimize Aptos Move contracts for gas efficiency, identifying expensive operations and suggesting optimizations.
Trigger phrases:
Use cases:
& and &mut efficiently// EXPENSIVE: Writing to global storage
move_to(account, large_struct);
// EXPENSIVE: Reading and writing
let data = borrow_global_mut<LargeData>(addr);
// EXPENSIVE: Checking existence
if (exists<Resource>(addr)) { ... }
// EXPENSIVE: Growing vectors dynamically
vector::push_back(&mut vec, item); // O(n) worst case
// EXPENSIVE: Searching vectors
vector::contains(&vec, &item); // O(n)
// EXPENSIVE: Removing from middle
vector::remove(&mut vec, index); // O(n)
// EXPENSIVE: String concatenation
string::append(&mut s1, s2);
// EXPENSIVE: UTF8 validation
string::utf8(bytes);
// BAD: Multiple storage accesses
public fun update_values(account: &signer, updates: vector<Update>) {
let i = 0;
while (i < vector::length(&updates)) {
let update = vector::borrow(&updates, i);
let data = borrow_global_mut<Data>(update.address);
data.value = update.value;
i = i + 1;
}
}
// GOOD: Single storage access with batch update
public fun batch_update(account: &signer, updates: vector<Update>) {
let data = borrow_global_mut<Data>(signer::address_of(account));
let i = 0;
while (i < vector::length(&updates)) {
let update = vector::borrow(&updates, i);
// Update in memory
update_memory_data(data, update);
i = i + 1;
}
}
// BAD: Wasteful storage
struct UserData has key {
active: bool, // 1 byte used, 7 wasted
level: u8, // 1 byte used, 7 wasted
score: u64, // 8 bytes
timestamp: u64, // 8 bytes
// Total: 32 bytes (50% wasted)
}
// GOOD: Packed storage
struct UserData has key {
// Pack small fields together
flags: u8, // Bits: [active, reserved...]
level: u8,
reserved: u16, // Future use
score: u64,
timestamp: u64,
// Total: 20 bytes (37.5% saved)
}
// BAD: Always compute expensive value
struct Pool has key {
total_shares: u64,
total_assets: u64,
// Computed on every update
share_price: u64,
}
// GOOD: Compute only when needed
struct Pool has key {
total_shares: u64,
total_assets: u64,
// Don't store computed values
}
public fun get_share_price(pool_addr: address): u64 {
let pool = borrow_global<Pool>(pool_addr);
if (pool.total_shares == 0) {
INITIAL_SHARE_PRICE
} else {
pool.total_assets * PRECISION / pool.total_shares
}
}
// BAD: Large event data
struct TradeEvent has drop, store {
pool: Object<Pool>,
trader: address,
token_in: Object<Token>,
token_out: Object<Token>,
amount_in: u64,
amount_out: u64,
fees: u64,
timestamp: u64,
metadata: vector<u8>, // Large metadata
}
// GOOD: Minimal event data
struct TradeEvent has drop, store {
pool_id: u64, // Use ID instead of Object
trader: address,
amounts: u128, // Pack amount_in and amount_out
fees: u64,
// Compute other data from state
}
// BAD: Linear search
public fun find_item(items: &vector<Item>, id: u64): Option<Item> {
let i = 0;
while (i < vector::length(items)) {
let item = vector::borrow(items, i);
if (item.id == id) {
return option::some(*item)
};
i = i + 1;
}
option::none()
}
// GOOD: Use Table for O(1) lookup
struct Storage has key {
items: Table<u64, Item>,
}
public fun find_item(storage: &Storage, id: u64): Option<Item> {
if (table::contains(&storage.items, id)) {
option::some(*table::borrow(&storage.items, id))
} else {
option::none()
}
}
# Simulate to get gas estimate
aptos move run-function \
--function-id 0x1::module::function \
--args ... \
--simulate
# Output includes:
# - gas_unit_price
# - max_gas_amount
# - gas_used
#[test]
public fun test_gas_usage() {
// Measure gas for operation
let gas_before = gas::remaining_gas();
expensive_operation();
let gas_used = gas_before - gas::remaining_gas();
// Assert reasonable gas usage
assert!(gas_used < MAX_ACCEPTABLE_GAS, E_TOO_EXPENSIVE);
}
// Before: O(n) search
struct Registry has key {
users: vector<User>,
}
// After: O(1) lookup
struct Registry has key {
users: Table<address, User>,
user_list: vector<address>, // If iteration needed
}
// Before: Multiple reads
public fun transfer(from: &signer, to: address, amount: u64) {
assert!(get_balance(signer::address_of(from)) >= amount, E_INSUFFICIENT);
let from_balance = borrow_global_mut<Balance>(signer::address_of(from));
let to_balance = borrow_global_mut<Balance>(to);
// ...
}
// After: Single read with validation
public fun transfer(from: &signer, to: address, amount: u64) {
let from_addr = signer::address_of(from);
let from_balance = borrow_global_mut<Balance>(from_addr);
assert!(from_balance.value >= amount, E_INSUFFICIENT);
// ... rest of logic
}
// Before: Multiple bool fields (8 bytes each)
struct Settings has copy, drop, store {
is_active: bool,
is_paused: bool,
is_initialized: bool,
allows_deposits: bool,
}
// After: Single u8 (1 byte)
struct Settings has copy, drop, store {
flags: u8, // Bit 0: active, 1: paused, 2: initialized, 3: deposits
}
const FLAG_ACTIVE: u8 = 1; // 0b00000001
const FLAG_PAUSED: u8 = 2; // 0b00000010
const FLAG_INITIALIZED: u8 = 4; // 0b00000100
const FLAG_DEPOSITS: u8 = 8; // 0b00001000
public fun is_active(settings: &Settings): bool {
(settings.flags & FLAG_ACTIVE) != 0
}
# Gas Optimization Report
## Summary
- Current average gas: X units
- Optimized average gas: Y units
- Savings: Z% reduction
## Optimizations Applied
### 1. Storage Optimization
- Packed struct fields (saved X bytes)
- Replaced vectors with tables (O(n) → O(1))
- Removed redundant fields
### 2. Computation Optimization
- Cached price calculations (saved X operations)
- Batched updates (N calls → 1 call)
- Early returns in validation
### 3. Event Optimization
- Reduced event size from X to Y bytes
- Removed redundant event fields
## Measurements
| Function | Before | After | Savings |
| -------- | ------ | ------ | ------- |
| mint | 50,000 | 35,000 | 30% |
| transfer | 30,000 | 25,000 | 17% |
| swap | 80,000 | 60,000 | 25% |
## Recommendations
1. Consider further optimizations for high-frequency functions
2. Monitor mainnet usage patterns
3. Set up gas usage alerts
security-audit to ensure optimizations don't compromise securitygenerate-tests to verify optimizations maintain correctnessdeploy-contracts for mainnet deploymentsSTORAGE_OPTIMIZATION.md for detailed patterns.env or ~/.aptos/config.yaml during gas analysis (contain private keys)