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.
apm install @a5c-ai/gas-optimization[](https://apm-p1ls2dz87-atlamors-projects.vercel.app/packages/@a5c-ai/gas-optimization)---
name: gas-optimization
description: 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.
allowed-tools: Read, Grep, Write, Bash, Edit, Glob, WebFetch
---
# Gas Optimization Skill
Advanced gas optimization techniques for EVM smart contracts with benchmark-driven analysis.
## Capabilities
- **Storage Optimization**: Storage packing, slot management, SLOAD/SSTORE minimization
- **Memory Management**: Memory vs calldata optimization, expansion costs
- **Assembly/Yul**: Low-level optimization for critical paths
- **Data Structures**: Gas-efficient mappings, arrays, and structs
- **Batch Operations**: Multi-call patterns, bulk transfers
- **Benchmarking**: Gas profiling, comparison analysis
## MCP/Tool Integration
| Tool | Purpose | Reference |
|------|---------|-----------|
| **Foundry MCP** | Gas reports, testing | [foundry-mcp-server](https://github.com/PraneshASP/foundry-mcp-server) |
| **EVM MCP Tools** | Opcode analysis | [evm-mcp-tools](https://github.com/0xGval/evm-mcp-tools) |
## Storage Optimization
### Storage Layout Packing
```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
```
### Minimize Storage Writes
```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
}
```
### Use Mappings Over Arrays for Lookups
```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
}
```
## Memory vs Calldata
### Function Parameters
```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
```
### String/Bytes Handling
```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);
}
```
## Unchecked Arithmetic
```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
```
## Loop Optimizations
### Cache Array Length
```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
```
### Pre-increment
```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
```
## Custom Errors
```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
```
## Assembly/Yul Optimization
### Efficient Balance Check
```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)
}
}
```
### Efficient Memory Operations
```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)
}
}
```
## Batch Operations
### Batch Transfers
```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
}
```
### Multicall Pattern
```solidity
function 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
```
## Benchmarking Workflow
### Using Foundry Gas Reports
```bash
# Run tests with gas report
forge test --gas-report
# Snapshot gas usage
forge snapshot
# Compare against previous snapshot
forge snapshot --check
```
### Gas Snapshot Format
```
| Contract | Function | Min | Avg | Max | # Calls |
|----------|----------|-----|-----|-----|---------|
| Token | transfer | 51234 | 54123 | 65432 | 100 |
| Token | approve | 24356 | 24356 | 24356 | 50 |
```
### Comparison Testing
```solidity
contract 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);
}
}
```
## Common Optimizations Summary
| 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 |
## Process Integration
This skill integrates with:
- `gas-optimization.js` - Full optimization process
- `smart-contract-development-lifecycle.js` - Development best practices
- `amm-pool-development.js` - DeFi-specific optimizations
## Tools Reference
| Tool | Purpose | URL |
|------|---------|-----|
| **Foundry** | Gas reporting | [foundry-rs](https://github.com/foundry-rs/foundry) |
| **Hardhat Gas Reporter** | Gas reports | [hardhat-gas-reporter](https://github.com/cgewecke/hardhat-gas-reporter) |
| **evm.codes** | Opcode costs | [evm.codes](https://www.evm.codes/) |
| **Solidity Optimizer** | Compiler optimization | [Solidity Docs](https://docs.soliditylang.org/en/latest/internals/optimizer.html) |
## See Also
- `skills/evm-analysis/SKILL.md` - Bytecode analysis
- `agents/gas-optimizer/AGENT.md` - Gas optimization agent
- `references.md` - Gas optimization resources