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에서 보기