Skip to main content

gas-snapshot-testing

Use when tracking gas costs, detecting regressions in CI, or optimizing Solidity contracts. Covers forge snapshot, gas reports, CI integration, and optimization techniques.

ソース情報

リポジトリ
ccashwell/evm-cortex
ソースの最終更新活動
2026年4月10日 16:31
検出された SKILL.md の言語
英語
スター
131
フォーク
18

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
gas-snapshot-testing
description
Use when tracking gas costs, detecting regressions in CI, or optimizing Solidity contracts. Covers forge snapshot, gas reports, CI integration, and optimization techniques.
# Gas Snapshot Testing ## Overview Gas snapshots capture the gas cost of every test function and store them in `.gas-snapshot`. This file is committed to the repo and used in CI to detect gas regressions. ## Creating Snapshots ```bash # Generate gas snapshot forge snapshot # Output stored in .gas-snapshot: # MyContractTest:test_deposit() (gas: 45231) # MyContractTest:test_withdraw() (gas: 38912) # MyContractTest:test_transfer() (gas: 21453) ``` ## Checking for Regressions in CI ```bash # Compare current gas against committed snapshot # Fails if any test uses more gas than recorded forge snapshot --check # Allow up to 5% increase forge snapshot --tolerance 5 ``` ## Gas Report ```bash # Detailed per-function gas breakdown forge test --gas-report # Output: # | src/Vault.sol:Vault | | | | | | # |---------------------|-----------------|-------|--------|-------|---------| # | Function Name | min | avg | median | max | # calls | # | deposit | 45231 | 47892 | 45231 | 53214 | 12 | # | withdraw | 38912 | 40123 | 38912 | 42345 | 8 | # Filter to specific contracts forge test --gas-report --match-contract Vault ``` ## CI Integration ### GitHub Actions ```yaml name: Gas Check on: [pull_request] jobs: gas: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: foundry-rs/foundry-toolchain@v1 - name: Install dependencies run: forge install - name: Check gas snapshots run: forge snapshot --check --tolerance 3 - name: Gas report run: forge test --gas-report | tee gas-report.txt - name: Comment gas report on PR uses: actions/github-script@v7 if: github.event_name == 'pull_request' with: script: | const fs = require('fs'); const report = fs.readFileSync('gas-report.txt', 'utf8'); github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `## Gas Report\n\`\`\`\n${report}\n\`\`\`` }); ``` ## Snapshot-Driven Development ```bash # 1. Create baseline snapshot forge snapshot --snap .gas-snapshot-baseline # 2. Make optimizations # ... edit contracts ... # 3. Compare against baseline forge snapshot --diff .gas-snapshot-baseline # Output shows differences: # test_deposit() (gas: 45231 -> 42100) -3131 (-6.92%) # test_withdraw() (gas: 38912 -> 38900) -12 (-0.03%) ``` ## Gas-Specific Test Patterns ```solidity contract GasTest is Test { MyContract target; function setUp() public { target = new MyContract(); // Warm storage slots for realistic measurements target.initialize(); } // Naming convention: gas_ prefix for benchmark tests function test_gas_deposit_firstTime() public { // Cold storage write — most expensive target.deposit(1 ether); } function test_gas_deposit_subsequent() public { target.deposit(1 ether); // warm up target.deposit(1 ether); // measure this } function test_gas_batchDeposit_10() public { for (uint256 i = 0; i < 10; i++) { target.deposit(1 ether); } } // Measure specific operations with gasleft() function test_gas_specificOperation() public { // Setup (not measured) target.deposit(1 ether); uint256 gasBefore = gasleft(); target.withdraw(0.5 ether); uint256 gasUsed = gasBefore - gasleft(); // Log for manual review emit log_named_uint("withdraw gas", gasUsed); // Assert gas bound assertLt(gasUsed, 50_000, "withdraw too expensive"); } } ``` ## Common Gas Optimizations | Optimization | Gas Saved | Risk | |-------------|-----------|------| | `calldata` over `memory` for external args | 200-2000 | None | | Pack storage (multiple vars in one slot) | 2100 per cold SLOAD | Readability | | Use `uint256` over smaller uints | 0-30 per operation | None | | Unchecked math (where overflow impossible) | 40-100 per op | Must prove safety | | Cache storage reads in memory | 100 per SLOAD | None | | Use custom errors over require strings | 200-5000 per revert | Compatibility | | `immutable` for constructor-set values | 2100 per read | None | ```solidity // Before: 3 SLOADs function getInfo() external view returns (uint256, uint256, uint256) { return (value1, value2, value3); } // After: cache in memory function getInfo() external view returns (uint256 a, uint256 b, uint256 c) { a = value1; b = value2; c = value3; } // Unchecked math where overflow is impossible function distribute(uint256 total, uint256 share) internal pure returns (uint256) { unchecked { return total / share; // share is validated > 0 by caller } } ``` ## Checklist - [ ] `.gas-snapshot` committed to repo - [ ] CI runs `forge snapshot --check` on every PR - [ ] Tolerance threshold set appropriately (1-5%) - [ ] Separate gas benchmark tests from functional tests - [ ] Warm storage slots in setUp before measuring - [ ] Cold vs warm storage costs differentiated in benchmarks - [ ] Gas report reviewed for unexpected cost spikes after changes - [ ] Document accepted gas regression rationale in PR description
GitHubで見る