Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Advanced gas optimization techniques for EVM smart contracts. Covers storage packing, memory vs calldata optimization, assembly/Yul, efficient data structures, batch operations, and benchmark-driven optimization strategies.
.claude/skills/a5c-ai-gas-optimization/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 64% | 0% |
| case-14 | ✓→✓ | = Same ✓ | 84% | 0% |
| case-01 | ✗→✗ | = Same ✗ | 304% | 0% |
| case-02 | ✗→✗ | = Same ✗ | 71% | 0% |
Advanced gas optimization techniques for EVM smart contracts with benchmark-driven analysis.
| Tool | Purpose | Reference | |------|---------|-----------| | Foundry MCP | Gas reports, testing | foundry-mcp-server | | EVM MCP Tools | Opcode analysis | evm-mcp-tools |
solidity// BAD: 3 storage slots (96 bytes used, 96 bytes allocated) contract BadPacking { uint128 a; // slot 0 (16 bytes) uint256 b; // slot 1 (32 bytes) - can't pack with a uint128 c; // slot 2 (16 bytes) } // GOOD: 2 storage slots (80 bytes used, 64 bytes allocated) contract GoodPacking { uint128 a; // slot 0, bytes 0-15 uint128 c; // slot 0, bytes 16-31 uint256 b; // slot 1 } // Gas savings: ~20,000 gas per SSTORE avoided
solidity// BAD: Multiple storage writes function badUpdate(uint256 newA, uint256 newB) external { a = newA; // SSTORE: 20,000 gas (cold) or 2,900 gas (warm) b = newB; // SSTORE: 2,900 gas (warm slot in same tx) } // GOOD: Single storage write with packed struct struct Data { uint128 a; uint128 b; } Data public data; function goodUpdate(uint128 newA, uint128 newB) external { data = Data(newA, newB); // Single SSTORE: 20,000 gas }
solidity// BAD: O(n) lookup, expensive for large arrays uint256[] public values; function exists(uint256 value) public view returns (bool) { for (uint i = 0; i < values.length; i++) { if (values[i] == value) return true; // SLOAD per iteration } return false; } // GOOD: O(1) lookup mapping(uint256 => bool) public valueExists; function exists(uint256 value) public view returns (bool) { return valueExists[value]; // Single SLOAD }
solidity// BAD: Copies array to memory function processArray(uint256[] memory data) external { // Memory copy cost: 3 gas per word + expansion } // GOOD: Read directly from calldata function processArray(uint256[] calldata data) external { // No copy, just pointer to calldata // Savings: ~60 gas per 32 bytes } // Note: Use memory if you need to modify the array
solidity// For read-only operations, use calldata function validate(string calldata input) external pure returns (bool) { return bytes(input).length > 0; } // For modifications, use memory function transform(string memory input) internal pure returns (string memory) { bytes memory b = bytes(input); b[0] = 'X'; return string(b); }
solidity// BAD: Overflow checks on every operation (Solidity 0.8+) function sumArray(uint256[] calldata arr) external pure returns (uint256) { uint256 sum = 0; for (uint256 i = 0; i < arr.length; i++) { sum += arr[i]; // Overflow check: ~40 gas per operation } return sum; } // GOOD: Unchecked when overflow is impossible function sumArray(uint256[] calldata arr) external pure returns (uint256) { uint256 sum = 0; uint256 length = arr.length; for (uint256 i = 0; i < length;) { unchecked { sum += arr[i]; ++i; // ++i is cheaper than i++ } } return sum; } // Savings: ~40 gas per iteration
solidity// BAD: Length read from storage each iteration for (uint i = 0; i < array.length; i++) { } // SLOAD per iteration // GOOD: Cache length uint256 length = array.length; for (uint i = 0; i < length; i++) { } // Single SLOAD
solidity// BAD: Post-increment creates temporary for (uint i = 0; i < length; i++) { } // GOOD: Pre-increment is cheaper for (uint i = 0; i < length; ++i) { } // Savings: ~5 gas per iteration
solidity// BAD: String error messages require(balance >= amount, "Insufficient balance"); // Cost: ~50 gas per character + memory expansion // GOOD: Custom errors (Solidity 0.8.4+) error InsufficientBalance(uint256 available, uint256 required); if (balance < amount) revert InsufficientBalance(balance, amount); // Cost: Fixed ~24 gas for error selector // Savings: ~50+ gas for typical error messages
solidity// Solidity function getBalance(address account) external view returns (uint256) { return account.balance; } // Assembly (slightly cheaper) function getBalance(address account) external view returns (uint256 bal) { assembly { bal := balance(account) } }
solidity// Copy 32 bytes efficiently function copy32(bytes32 source) internal pure returns (bytes32 dest) { assembly { dest := source } } // Efficient keccak256 function efficientHash(bytes32 a, bytes32 b) internal pure returns (bytes32 result) { assembly { mstore(0x00, a) mstore(0x20, b) result := keccak256(0x00, 0x40) } }
solidity// BAD: Individual transfers function transferToMany(address[] calldata recipients, uint256 amount) external { for (uint i = 0; i < recipients.length; ++i) { token.transfer(recipients[i], amount); // 21000 base + transfer cost } } // GOOD: Batch transfer (if supported) function batchTransfer( address[] calldata recipients, uint256[] calldata amounts ) external { // Single function call overhead // Reduced SLOAD for token state }
solidityfunction multicall(bytes[] calldata data) external returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; ++i) { (bool success, bytes memory result) = address(this).delegatecall(data[i]); require(success); results[i] = result; } } // Combines multiple operations in single transaction
bash# Run tests with gas report forge test --gas-report # Snapshot gas usage forge snapshot # Compare against previous snapshot forge snapshot --check
| Contract | Function | Min | Avg | Max | # Calls |
|----------|----------|-----|-----|-----|---------|
| Token | transfer | 51234 | 54123 | 65432 | 100 |
| Token | approve | 24356 | 24356 | 24356 | 50 |soliditycontract GasComparison is Test { function test_gasComparison_approach1() public { uint256 gasBefore = gasleft(); // Approach 1 uint256 gasUsed = gasBefore - gasleft(); emit log_named_uint("Approach 1 gas", gasUsed); } function test_gasComparison_approach2() public { uint256 gasBefore = gasleft(); // Approach 2 uint256 gasUsed = gasBefore - gasleft(); emit log_named_uint("Approach 2 gas", gasUsed); } }
| Technique | Savings | Risk | |-----------|---------|------| | Storage packing | 20,000 gas/slot | Low | | Calldata vs memory | 60 gas/32 bytes | Low | | Unchecked arithmetic | 40 gas/op | Medium | | Custom errors | 50+ gas/error | Low | | Cache storage reads | 100-2100 gas | Low | | Loop pre-increment | 5 gas/iteration | Low | | Assembly | Varies | High |
This skill integrates with:
gas-optimization.js - Full optimization processsmart-contract-development-lifecycle.js - Development best practicesamm-pool-development.js - DeFi-specific optimizations| Tool | Purpose | URL | |------|---------|-----| | Foundry | Gas reporting | foundry-rs | | Hardhat Gas Reporter | Gas reports | hardhat-gas-reporter | | evm.codes | Opcode costs | evm.codes | | Solidity Optimizer | Compiler optimization | Solidity Docs |
skills/evm-analysis/SKILL.md - Bytecode analysisagents/gas-optimizer/AGENT.md - Gas optimization agentreferences.md - Gas optimization resources| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 4,442 | 4,481 | +1% | 1 | 1 | 0% | 879 | 3,548 | +304% | 0 | 0 | — |
case-02 | fail→fail | 14,036 | 11,662 | -17% | 1 | 1 | 0% | 2,918 | 4,982 | +71% | 0 | 0 | — |
case-03 | fail→fail | 8,442 | 7,262 | -14% | 1 | 1 | 0% | 1,640 | 4,111 | +151% | 0 | 0 | — |
case-04 | fail→fail | 3,709 | 3,728 | +1% | 1 | 1 | 0% | 769 | 3,386 | +340% | 0 | 0 | — |
case-05 | fail→fail | 4,117 | 3,040 | -26% | 1 | 1 | 0% | 685 | 3,227 | +371% | 0 | 0 | — |
case-06 | fail→fail | 6,837 | 5,376 | -21% | 1 | 1 | 0% | 1,330 | 3,693 | +178% | 0 | 0 | — |
case-07 | fail→fail | 7,671 | 5,916 | -23% | 1 | 1 | 0% | 1,502 | 3,620 | +141% | 0 | 0 | — |
case-08 | fail→fail | 7,385 | 5,620 | -24% | 1 | 1 | 0% | 1,455 | 3,734 | +157% | 0 | 0 | — |
case-09 | fail→fail | 7,353 | 6,264 | -15% | 1 | 1 | 0% | 1,248 | 3,595 | +188% | 0 | 0 | — |
case-10 | fail→pass | 14,001 | 9,159 | -35% | 1 | 1 | 0% | 2,487 | 4,271 | +72% | 0 | 0 | — |
case-11 | fail→fail | 4,242 | 4,441 | +5% | 1 | 1 | 0% | 746 | 3,505 | +370% | 0 | 0 | — |
case-12 | fail→fail | 7,283 | 6,134 | -16% | 1 | 1 | 0% | 1,544 | 3,845 | +149% | 0 | 0 | — |
case-13 | fail→fail | 15,625 | 12,099 | -23% | 1 | 1 | 0% | 3,053 | 5,030 | +65% | 0 | 0 | — |
case-14 | pass→pass | 17,795 | 18,723 | +5% | 1 | 1 | 0% | 3,043 | 5,606 | +84% | 0 | 0 | — |
case-15 | fail→fail | 2,732 | 3,501 | +28% | 1 | 1 | 0% | 448 | 3,122 | +597% | 0 | 0 | — |
case-16 | fail→fail | 3,893 | 2,872 | -26% | 1 | 1 | 0% | 647 | 3,148 | +387% | 0 | 0 | — |
case-17 | fail→fail | 11,569 | 8,386 | -28% | 1 | 1 | 0% | 2,172 | 4,242 | +95% | 0 | 0 | — |
case-18 | fail→fail | 10,429 | 5,639 | -46% | 1 | 1 | 0% | 2,054 | 3,794 | +85% | 0 | 0 | — |
case-19 | fail→pass | 13,278 | 5,481 | -59% | 1 | 1 | 0% | 2,261 | 3,712 | +64% | 0 | 0 | — |
case-20 | fail→fail | 12,710 | 13,874 | +9% | 1 | 1 | 0% | 2,356 | 5,265 | +123% | 0 | 0 | — |
case-21 | fail→fail | 6,847 | 8,417 | +23% | 1 | 1 | 0% | 1,254 | 4,113 | +228% | 0 | 0 | — |
case-22 | fail→fail | 8,059 | 6,796 | -16% | 1 | 1 | 0% | 1,648 | 3,876 | +135% | 0 | 0 | — |
case-23 | fail→fail | 10,503 | 10,396 | -1% | 1 | 1 | 0% | 1,783 | 4,158 | +133% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.