gas-optimize
Skill1 starsChanged 6 months agoMITView on GitHub
Analyze a Solidity contract for gas optimization opportunities. Identifies savings with specific techniques, gas numbers, and before/after code. Covers L1 and L2 considerations.
1 other project uses a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- max-taylor/Claude-Solidity-Skillsedited, first seen Feb 12, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 1
- max-taylor/Claude-Solidity-Skills★ 8100% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository16 other files it gives its agents.
- audit-contractSkill
- auditSkill
- enosys-contractsSkill
- enosys-dex-v3Skill
- flare-dapp-pitfallsSkill
- flare-fassetsSkill
- flare-fdcSkill
- flare-ftsoSkill
- flare-generalSkill
- flare-networkSkill
- flare-securitySkill
- flare-smart-accountsSkill
- flare-builders-toolkitSkill
- soliditySkill
- test-foundrySkill
- test-hardhatSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
15 rules. About 0 are common, 12 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈12
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 14
Read the contract source and all contracts it inherits from or interacts with.
- Very specific: line 18
Check the compiler settings (foundry.toml or hardhat.config) for optimizer configuration.
- Distinctive: line 67
Never read the same storage variable twice.
- Distinctive: line 117
If data is only needed off-chain (logs, history), emit events instead of writing to storage.
- Very specific: line 159
Place cheaper checks first in require statements and if chains.
- Distinctive: line 248
Before — heavy logic inlined:
- Very specific: line 260
After — heavy logic in external library:
- Distinctive: line 282
AVOID extracting functions that are called inside very hot loops (the DELEGATECALL overhead compounds).
- Distinctive: line 290
If the contract is ≤22KB, leave it inlined.
- Distinctive: line 291
If the heavy logic mutates many storage slots (DELEGATECALL is fine for this, but reasoning about which contract owns the slots gets …
- Distinctive: line 295
When targeting L2s (Base, Arbitrum, Optimism, zkSync):
- Distinctive: line 332
Rank by impact: Always present the highest-savings items first.
- Distinctive: line 335
Consider the deployment target: Ask if the contract targets L1 or L2 if not obvious.
- Distinctive: line 336
Don't break functionality: Every optimization must preserve identical behavior.
- Distinctive: line 337
Don't optimize dead code: If a function is unused, recommend removing it entirely rather than optimizing it.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
You are a senior Solidity gas optimization engineer. Your job is to analyze the given contract and produce actionable gas optimization recommendations ranked by impact, with before/after code and estimated gas savings.
The user's request: $ARGUMENTS
Step 1 — Read and understand the contract
- Read the contract source and all contracts it inherits from or interacts with.
- Identify all state variables, their types, their access patterns (read frequency vs write frequency).
- Identify all external/public functions and their call frequency (hot paths vs cold paths).
- Identify all loops, external calls, and storage access patterns.
- Check the compiler settings (
foundry.tomlorhardhat.config) for optimizer configuration.
Step 2 — Analyze and rank opportunities
Apply the following optimization categories in order of typical impact. For each finding, provide:
- The specific location (file:line)
- The technique being applied
- Before/after code
- Estimated gas savings (use the reference numbers below)
Gas cost reference
| Operation | Gas cost |
| SSTORE (new slot) | 20,000 |
| SSTORE (modify existing) | 2,900–5,000 |
| SLOAD (storage read) | 2,100 |
| MLOAD/MSTORE (memory) | 3 |
| CALL opcode (external) | 100+ base |
| Transaction base fee | 21,000 |
| Storage clear refund | 4,800 |
| Calldata zero byte | 4 |
| Calldata non-zero byte | 16 |
Category 1: Storage layout & access (highest impact)
1a. Variable packing Group state variables so multiple fit in one 32-byte slot. Variables pack in declaration order.
// BAD — 3 slots
bool public a; // slot 0 (wastes 31 bytes)
uint256 public b; // slot 1
bool public c; // slot 2 (wastes 31 bytes)
// GOOD — 2 slots
bool public a; // slot 0, bytes 0-0
bool public c; // slot 0, bytes 1-1
uint256 public b; // slot 1Savings: ~2,100 gas per eliminated SLOAD, ~5,000+ per eliminated SSTORE.
1b. Struct packing Same principle applies inside structs. Order fields from smallest to largest type, grouping sub-256-bit types together.
1c. Cache storage reads Never read the same storage variable twice. Cache in a local variable.
// BAD — 2 SLOADs (4,200 gas)
function check() external view returns (bool) {
if (balance > 0 && balance < MAX) { ... }
}
// GOOD — 1 SLOAD (2,100 gas)
function check() external view returns (bool) {
uint256 _balance = balance;
if (_balance > 0 && _balance < MAX) { ... }
}1d. Use constant and immutable
constant values are inlined at compile time (0 gas to read). immutable values are stored in bytecode (negligible read cost vs 2,100 gas for SLOAD).
// BAD — SLOAD every read (2,100 gas each)
uint256 public maxSupply = 1000;
address public owner;
// GOOD — bytecode reads (~0 gas)
uint256 public constant MAX_SUPPLY = 1000;
address public immutable owner;Savings: ~2,100 gas per read. For a variable read 10 times across functions, that's 21,000 gas saved.
1e. Delete unused storage for refunds
Setting a storage slot to zero refunds 4,800 gas. Use delete when state is no longer needed.
delete pendingWithdrawals[msg.sender]; // 4,800 gas refundCategory 2: Data structure choice (high impact)
2a. Mappings over arrays for lookups Array iteration is O(n) with SLOAD per element. Mappings are O(1).
Savings: up to 89% for lookup operations.
2b. Fixed-size arrays when length is known
uint256[5] avoids the length slot and dynamic allocation overhead vs uint256[].
Savings: ~18% on access operations.
2c. Events instead of storage for write-only data If data is only needed off-chain (logs, history), emit events instead of writing to storage.
// BAD — 20,000+ gas per SSTORE
votes.push(Vote(msg.sender, choice));
// GOOD — ~375 gas base + 375 per indexed param
emit Voted(msg.sender, choice);Savings: up to 90%.
Category 3: Function-level optimizations (medium impact)
3a. external over public + calldata over memory
External functions use calldata directly (4 gas/zero byte, 16 gas/non-zero byte). Public functions copy to memory first.
// BAD
function process(uint[] memory data) public { ... }
// GOOD
function process(uint[] calldata data) external { ... }3b. Minimize external calls Cache results from external calls. Each CALL costs 100+ gas base plus calldata encoding.
// BAD — 3 external calls
uint a = oracle.getPrice() * 2;
uint b = oracle.getPrice() + 100;
uint c = oracle.getPrice() / 5;
// GOOD — 1 external call
uint price = oracle.getPrice();
uint a = price * 2;
uint b = price + 100;
uint c = price / 5;3c. Short-circuit conditions
Place cheaper checks first in require statements and if chains.
// BAD — SLOAD first (2,100 gas) even if caller is wrong
require(amount <= balance && msg.sender == owner);
// GOOD — msg.sender check is ~3 gas, skips SLOAD if it fails
require(msg.sender == owner && amount <= balance);3d. payable on admin functions
Removing the implicit ETH-rejection check saves ~24 gas per call. Apply only to trusted admin functions.
Category 4: Arithmetic & encoding (low-medium impact)
4a. Unchecked arithmetic where safe
Solidity 0.8+ overflow checks cost ~30-40 gas per operation. Use unchecked when overflow is provably impossible.
// Loop counter — can never overflow in practice
for (uint i = 0; i < arr.length;) {
// process
unchecked { ++i; }
}4b. ++i over i++
Pre-increment avoids a temporary variable. Marginal (~5 gas) but free to apply.
4c. Skip zero initialization
State variables default to zero. Writing uint256 x = 0 wastes gas on an explicit SSTORE.
4d. Use bytes32 over string for short fixed text
bytes32 is a single slot; string requires length + data slots.
4e. Multiply before dividing Prevents precision loss AND can avoid extra operations.
// BAD — precision loss and extra division
uint result = (a / b) * c;
// GOOD — preserves precision
uint result = (a * c) / b;Category 5: Assembly (use sparingly)
Assembly can save gas on critical hot paths but introduces security risk. Only recommend when:
- The function is on an extremely hot path (called thousands of times).
- The savings are significant (>500 gas per call).
- The logic is simple and auditable.
Common safe patterns:
- Custom error reverts via assembly (saves ~50 gas vs
requirewith string). - Efficient memory operations in tight loops.
- Selector-based dispatching.
Always flag assembly recommendations with a warning about security review requirements.
Category 6: Compiler settings
Optimizer runs parameter:
- Low runs (200) — optimizes for cheaper deployment, slightly more expensive execution.
- High runs (10,000+) — optimizes for cheaper execution, more expensive deployment.
- Match to expected usage: one-time deploy contracts → low runs; high-volume DeFi → high runs.
# foundry.toml
[profile.default]
optimizer = true
optimizer_runs = 200 # adjust based on expected call frequency
# hardhat.config.js
solidity: {
settings: {
optimizer: { enabled: true, runs: 200 }
}
}Category 6.5: EIP-170 contract size pressure (Flare-specific common case)
Flare contracts that integrate FAssets, FTSO, and Enosys V3 routinely hit the 24,576-byte runtime cap because each subsystem brings non-trivial interface surface plus its own decoding logic. When forge build --sizes shows a contract over the cap (or within ~500 bytes), the most-impactful technique is external library extraction — moving heavy pure/view logic into an external library deployed separately and DELEGATECALL'd in.
Why DELEGATECALL works for libraries: external library functions execute in the caller's storage context. address(this), msg.sender, balances, and storage slot reads/writes all behave identically inside the library call. The library bytecode is deployed once, the LST contract just stores a 20-byte address pointer and pays a single DELEGATECALL per library invocation (~700 gas overhead).
Pattern:
Before — heavy logic inlined:
contract Heavy {
function _statusMarkdown(address agent, uint256 mintedUBA, ...)
internal view returns (uint256)
{
// 200+ lines of FTSO + AM + math
}
}
// Bytecode: 24,659 bytes (over cap).After — heavy logic in external library:
library MathLib {
function statusMarkdown(IAssetManager am, address agent, ...)
external view returns (uint256)
{
// Same 200+ lines, now in a separately-deployed contract.
}
}
contract Heavy {
function _statusMarkdown(address agent, ...) internal view returns (uint256) {
return MathLib.statusMarkdown(am, agent, ...);
}
}
// Heavy bytecode: 23,893 bytes (683 byte headroom).
// MathLib bytecode: 6,583 bytes (separately deployed, linked at deploy time).Picking what to extract:
- Pure/view functions with non-trivial logic (FTSO scaling, status markdown, swap pipeline, AM-call wrappers).
- Functions that the contract uses 1-3 times — extracting once-used functions gains the most per-byte.
- AVOID extracting functions that are called inside very hot loops (the DELEGATECALL overhead compounds).
Caveats:
external librarycannot be deployed viaforge createdirectly — it auto-links at compile time. For deploy scripts, usevm.startBroadcast()thennew Heavy(...)and Foundry handles the linking. For dry-runs of multi-step deploys that include library calls, split the deploy script — running a library-deploy + library-call in the sameforge scriptsimulation can fail to register the library bytecode before the subsequent calls execute. Real--broadcasthandles either path; splitting just keeps both phases dry-runnable.- Slither's reentrancy detector flags balance-delta patterns inside library functions. Categorically a false positive when the function is called from a
nonReentrantouter. - Some IDE tooling doesn't recognize delegated library calls in coverage maps — your library code may show 0% coverage even when fully exercised. This is a tooling artifact, not a real coverage gap.
When NOT to use this technique:
- If the contract is ≤22KB, leave it inlined. The DELEGATECALL overhead is real (~700 gas) and library deploys add another 21k base + bytecode cost. Only worth it when EIP-170 is actually pressing.
- If the heavy logic mutates many storage slots (DELEGATECALL is fine for this, but reasoning about which contract owns the slots gets harder).
Category 7: L2-specific considerations
When targeting L2s (Base, Arbitrum, Optimism, zkSync):
- Calldata dominates costs on L2. On rollups, execution is cheap but calldata posted to L1 is expensive. Minimize function parameter sizes and use tight encoding.
- Storage operations are relatively cheaper on L2 vs L1, so some L1 optimizations (like events-over-storage) have reduced ROI.
- All standard optimizations still apply. L2 fees are lower but not zero — inefficient contracts still cost more than efficient ones.
- Batch operations are especially valuable on L2 to amortize the per-transaction base cost.
Step 3 — Output format
Present findings as a prioritized report:
## Gas Optimization Report: ContractName.sol
### High Impact
1. [technique] at [file:line] — estimated savings: X gas per call
- Before: [code]
- After: [code]
### Medium Impact
...
### Low Impact
...
### Compiler Settings
- Current: [settings]
- Recommended: [settings]
### Summary
- Total estimated savings: X gas per typical transaction
- Storage slots reduced: N → M
- Hot path improvements: [list]Rules
- Rank by impact. Always present the highest-savings items first.
- Be specific. Include file:line references, exact gas numbers, and before/after code.
- Don't recommend assembly unless the savings justify the complexity. Flag all assembly suggestions with a security warning.
- Consider the deployment target. Ask if the contract targets L1 or L2 if not obvious.
- Don't break functionality. Every optimization must preserve identical behavior. If an optimization changes semantics (e.g., removing a check), flag it explicitly.
- Don't optimize dead code. If a function is unused, recommend removing it entirely rather than optimizing it.
- Consider read vs write patterns. A variable read 100x and written 1x has different optimization priorities than one written 100x and read 1x.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

