mcpField
A
analyze-gas-optimization

analyze-gas-optimization

github.com/aptos-labs開発2090 回インストール24 週間前に更新
リポジトリ

analyze-gas-optimization について

analyze-gas-optimization は、Aptos Move コントラクトのガスコストを分析し、最適化の余地を探すスキルです。ストレージ、計算、参照、コレクションの調査を案内し、プロファイリングとテストも考慮します。

主な機能

  1. 01高コストなストレージとベクター操作を特定
  2. 02ストレージと計算のパターンを確認
  3. 03バッチ処理やコレクションの代替案を提案
  4. 04シミュレーションまたはテストでガスを測定
  5. 05テストとセキュリティ確認を含む

活用シーン

  1. 01メインネット公開前にガスコストを評価
  2. 02高いトランザクションコストを調査
  3. 03高頻度操作を含むコントラクトを確認
  4. 04セキュリティとテストを踏まえて最適化を評価

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/aptos-labs/aptos-agent-skills --skill analyze-gas-optimization

インストール後、Claude Code で/analyze-gas-optimizationを入力して読み込まれたか確認します。

詳細

スキル:analyze-gas-optimization

Aptos Move コントラクトのガス効率を分析・最適化し、コストの高い処理を特定して最適化を提案します。

このスキルを使用するタイミング

トリガーフレーズ:

  • 「ガスを最適化」「ガスコストを削減」「ガス分析」
  • 「コントラクトを安くする」「ガス効率」
  • 「ガス使用量を分析」「ガス最適化」
  • 「トランザクションコストを削減」

ユースケース:

  • メインネットへのデプロイ前
  • トランザクションコストが高い場合
  • 高頻度の処理を最適化する場合
  • 多数のトランザクションを伴う DeFi プロトコルを構築する場合

ガス最適化の基本原則

1. ストレージの最適化
  • 保存するデータ量を最小限に抑える
  • 効率的なデータ構造を使用する
  • 構造体のフィールドを効率的にパッキングする
  • 不要なフィールドを削除する
2. 計算の最適化
  • 大きなコレクションに対するループを避ける
  • 繰り返し行う計算をキャッシュする
  • 可能な場合はビット演算を使用する
  • ベクター操作を最小限に抑える
3. 参照の最適化
  • 可能な場合は移動よりも借用を優先する
  • & と &mut を効率的に使用する
  • 不要なコピーを避ける

ガスコストの分析

コストの高い処理
1. グローバルストレージ操作
// 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)) { ... }
2. ベクター操作
// 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)
3. 文字列操作
// EXPENSIVE: String concatenation
string::append(&mut s1, s2);

// EXPENSIVE: UTF8 validation
string::utf8(bytes);
最適化パターン
1. バッチ処理
// 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;
    }
}
2. ストレージのパッキング
// 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)
}
3. 遅延評価
// 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
    }
}
4. イベントの最適化
// 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
}
5. コレクションの最適化
// 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()
    }
}

ガスの測定

1. トランザクションのシミュレーション
# 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
2. ガスプロファイリング
#[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);
}

最適化チェックリスト

ストレージのチェックリスト
  • サイズを最小限に抑えるため、構造体のフィールドをパッキングする
  • 適切な整数サイズ(u8、u16、u32、u64)を使用する
  • 不要なフィールドを削除する
  • 大きなデータにはオフチェーンストレージを検討する
  • ログにはストレージの代わりにイベントを使用する
計算のチェックリスト
  • 繰り返し行う計算をキャッシュする
  • コレクションに対するループを最小限に抑える
  • 不要な処理を省くため、早期リターンを使用する
  • 類似する処理をまとめて実行する
  • 重複するチェックを避ける
コレクションのチェックリスト
  • キーと値の検索には Table/TableWithLength を使用する
  • 大きなコレクションには SmartTable を使用する
  • ベクターのサイズを制限する
  • 大きな結果にはページネーションを検討する
  • 適切なデータ構造を使用する
ベストプラクティス
  • 最適化の前後にプロファイリングを行う
  • 単体テストでガス使用量をテストする
  • 公開関数のガスコストを文書化する
  • コントラクトの設計時にガスコストを考慮する
  • メインネットのガス使用量を監視する

よくあるガス最適化

1. ベクターをテーブルに置き換える
// 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
}
2. ストレージの読み取りを最小限に抑える
// 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
}
3. ビットフラグを使用する
// 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 と併用します
  • 最適化後も正しさが保たれることを検証するには、generate-tests と併用します
  • メインネットへのデプロイでは、deploy-contracts の前に適用します
  • 詳細なパターンについては STORAGE_OPTIMIZATION.md を参照してください

絶対に守るべきルール

  • ❌ セキュリティチェック(アクセス制御、入力検証)を最適化によって取り除かないでください
  • ❌ 再テストせずに最適化済みコードをデプロイしないでください
  • ❌ ガス分析中に .env や ~/.aptos/config.yaml を読み取らないでください(秘密鍵が含まれています)

参考資料

コメント