solidity gas optimization

Solidity gas optimization โ€” storage patterns, calldata, assembly, and before/after comparisons

๐Ÿ“˜ defi-academy · Part 12 of 15 · Module 4 · Advanced
A free, step-by-step course on buying, selling & trading domains. View the full series →
Solidity gas optimization โ€” storage patterns, calldata, assembly, and before/after comparisons
Key takeaways
  • Storage writes (SSTORE) cost 20,000 gas โ€” pack variables into fewer 32-byte slots for 30-50% savings.
  • Use calldata instead of memory for read-only function parameters โ€” saves 10-20% on arrays.
  • Custom errors save gas over require strings; cache storage reads in local variables; use unchecked for safe math.

Understanding solidity gas optimization is the foundation of this guide. Below we break it down step by step so you can act with confidence.

Gas optimization is essential for DeFi protocols where millions of transactions mean millions of dollars in cumulative fees. A 30% gas reduction in a heavily-used contract saves real money for every user.

This lesson covers the most impactful optimization techniques with before-and-after gas measurements you can verify yourself.

Solidity Gas Optimization: Storage is expensive: minimize SSTORE

Writing to storage (SSTORE) costs 20,000 gas for a new slot and 5,000 for an update. Reading (SLOAD) costs 2,100. Everything else is cheap by comparison. Optimization rule: minimize storage reads and writes.

๐Ÿ”ฌ Demo: Storage packing saves 50%+ gas
// UNOPTIMIZED: each variable uses a full 32-byte slot
contract Unoptimized {
    uint256 public amount;    // slot 0 (32 bytes)
    bool public isActive;     // slot 1 (32 bytes for 1 bit!)
    address public owner;     // slot 2 (32 bytes for 20 bytes)
    uint256 public timestamp; // slot 3
    // 4 storage slots = 4 x SSTORE on write
}

// OPTIMIZED: pack variables into fewer slots
contract Optimized {
    uint128 public amount;    // slot 0, first 16 bytes
    uint64 public timestamp;  // slot 0, next 8 bytes
    bool public isActive;     // slot 0, next 1 byte
    // slot 0 is now 25 bytes โ€” fits in one 32-byte slot!
    address public owner;     // slot 1 (20 bytes)
    // 2 storage slots instead of 4 = ~50% gas savings on writes
}

// Gas comparison:
// Unoptimized setAll(): ~89,000 gas (4 SSTORE operations)
// Optimized setAll():   ~47,000 gas (2 SSTORE operations)

The EVM stores data in 32-byte slots. Variables smaller than 32 bytes that are declared consecutively are packed into the same slot. Order your struct fields by size to maximize packing.

Calldata vs memory vs storage

Function parameters have three data locations, each with different gas costs:

๐Ÿ”ฌ Demo: Use calldata for read-only arrays
// EXPENSIVE: memory copies the entire array
function sumMemory(uint256[] memory values) public pure returns (uint256) {
    uint256 total;
    for (uint256 i = 0; i < values.length; i++) {
        total += values[i];
    }
    return total;
}
// Gas for 100 elements: ~28,500

// CHEAP: calldata reads directly from transaction data
function sumCalldata(uint256[] calldata values) public pure returns (uint256) {
    uint256 total;
    for (uint256 i = 0; i < values.length; i++) {
        total += values[i];
    }
    return total;
}
// Gas for 100 elements: ~23,200 (18% savings)

// Rule: use calldata for read-only external function params
// Use memory only when you need to modify the data

For large arrays, calldata saves significant gas because it avoids copying data to memory. Always use calldata for external function parameters you do not modify.

Loop and computation optimizations

๐Ÿ”ฌ Demo: Common loop optimizations
// UNOPTIMIZED
function processUnoptimized(uint256[] calldata ids) external {
    for (uint256 i = 0; i < ids.length; i++) {    // ids.length read each iteration
        require(ids[i] != 0, "Invalid");           // string error message
        balances[ids[i]] += amounts[ids[i]];       // 2 storage reads per iteration
    }
}

// OPTIMIZED
function processOptimized(uint256[] calldata ids) external {
    uint256 len = ids.length;                      // cache length
    for (uint256 i; i < len; ) {                   // default 0, no i++ overhead
        uint256 id = ids[i];                       // cache in memory
        if (id == 0) revert InvalidId();           // custom error (cheaper)
        unchecked {
            balances[id] += amounts[id];
            ++i;                                   // prefix increment, unchecked
        }
    }
}

// Savings per iteration: ~200-400 gas
// For 1000 iterations: ~200,000-400,000 gas saved

// Custom error vs require string:
error InvalidId();
// revert InvalidId()   costs ~24 gas
// require(..., "Invalid") costs ~180+ gas (string stored in bytecode)

Key optimizations: cache storage reads in local variables, use unchecked blocks for safe arithmetic, prefer custom errors over require strings, and cache array length outside loops.

Explore Crypto Trading

Cryptocurrency trading is one of the fastest-growing financial markets โ€” open 24/7, globally accessible, and full of opportunity for those who learn the fundamentals first.

  • Trade spot, futures & copy-trading on one platform
  • Start with as little as $10 โ€” practice risk-free on the testnet
  • Built-in TradingView charts, grid bots & DCA tools

Free Crypto Trading Academy → ยท Domain Trading Academy →

Advanced: assembly for critical paths

Inline assembly (Yul) provides direct EVM access for maximum optimization. Use sparingly and only in gas-critical paths:

๐Ÿ”ฌ Demo: Assembly optimization examples
// Standard Solidity balance check
function getBalance(address account) public view returns (uint256) {
    return balances[account]; // ~2,100 gas (SLOAD)
}

// Assembly version (same result, slightly cheaper)
function getBalanceAsm(address account) public view returns (uint256 result) {
    assembly {
        // Compute storage slot: keccak256(abi.encode(account, slot))
        mstore(0x00, account)
        mstore(0x20, balances.slot)
        result := sload(keccak256(0x00, 0x40))
    }
}

// Where assembly really shines: batch operations
function batchTransfer(
    address[] calldata recipients,
    uint256[] calldata amounts
) external {
    // Solidity version: ~8,000 gas per transfer
    // Assembly version: ~5,000 gas per transfer (37% savings)
    // At 100 transfers: 300,000 gas saved
}

Assembly is dangerous โ€” it bypasses Solidity's safety checks (overflow, bounds, type checking). Only use it when the gas savings justify the added audit complexity. Always have assembly code professionally reviewed.

When evaluating solidity gas optimization, start with the fundamentals: what problem it solves, who it serves, and how it compares to the alternatives buyers consider first.

Start with the fundamentals. Define who you are serving, the single outcome you want, and the budget you are comfortable committing before you compare any options.

A practical rule is to favour clarity over cleverness. The choice that is easiest to understand, spell, remember and act on tends to win over the long run.

Context changes the answer. What is optimal for a fast-moving startup is rarely optimal for an established brand, so weigh your own stage, market and ambitions.

Avoid the common mistakes: rushing the decision, copying competitors blindly, and ignoring the total cost of ownership beyond the very first year of use.

Think in terms of trade-offs rather than a single perfect answer. Every option trades memorability, cost, reach and flexibility in a slightly different mix.

Document your reasoning. Writing down why you chose one path makes it far easier to revisit the decision later and to explain it to partners and investors.

Test with real people. A quick check with a handful of your actual customers will surface problems that no amount of internal debate ever reveals.

Plan for growth. The option that fits today should still make sense when your audience, catalogue or team is several times larger than it is right now.

Finally, revisit the decision periodically. Markets, tools and best practices move quickly, and a choice that was ideal last year may deserve a fresh look today.

WSOT 2026: Squad Leader Slots Are Open โ€” Up to $128,000 per Leader

Join Now

Frequently asked questions

How much gas can optimization save?

Typical savings: storage packing 30-50%, calldata over memory 10-20%, loop optimizations 20-40%, custom errors 5-10%, assembly 20-40% on critical paths. Combined, you can often reduce gas by 40-60% on write-heavy contracts.

Should I optimize everything?

No. Optimize hot paths โ€” functions called frequently by many users. View functions are free when called externally. Deployment gas is paid once. Focus optimization effort where cumulative savings are highest.

Is assembly safe to use?

Assembly bypasses all Solidity safety checks. A single mistake can corrupt storage, drain funds, or create vulnerabilities invisible to standard audit tools. Use it only in gas-critical paths, always with extensive tests and professional audit review.

Free Courses โ€” Learn New Skills

No sign-up · No fees · Learn at your own pace

Start Trading

Trade crypto and premium domains โ€” digital assets with real opportunities.

Find your name on Atom

DominantBrand curates the best premium, brandable names from Atom.com — the marketplace with a free AI appraisal, a USPTO trademark check, and secure escrow. Every listing even ships with a designed logo.

Browse Domains