Smart Contract Gas Optimization

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Smart Contract Gas Optimization
Medium
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1217
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1046
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Smart Contract Gas Optimization

The contract deployment cost 0.8 ETH instead of expected 0.3. Or users pay $15 per transfer() at 30 gwei gas price, while competitors charge $4. This isn't a question of "when network load drops". It's a question of storage layout, unoptimized opcodes, and patterns that Solidity compiler doesn't fix for you.

Where the Extra Gas Comes From

Storage — The Main Source of Losses

SSTORE costs 20,000 gas when writing to a cold slot, 2,900 gas when updating a warm one. SLOAD — 2,100 gas for cold, 100 for warm (EIP-2929). This is why storage architecture determines 60-80% of contract cost.

Slot packing — the first tool. EVM stores data in 32-byte slots. If you declare variables like this:

uint128 a; // slot 0
uint256 b; // slot 1 — SEPARATE slot, though it could be packed
uint128 c; // slot 2

You get three slots. Reordering:

uint128 a; // slot 0, bytes 0-15
uint128 c; // slot 0, bytes 16-31 — packed!
uint256 b; // slot 1

Gives you two slots. On a contract with 10,000 deployments, the economy is hundreds of ETH total across the ecosystem.

In practice: one major DeFi project came to us with an ERC-1155 contract where a TokenInfo struct occupied 3 slots instead of 1. Reordering fields and replacing uint256 decimals with uint8 decimals cut 62% gas on mint().

Mappings vs Arrays

mapping(uint256 => address) — O(1) access, gas-efficient. address[] with value lookup — O(n) and 99% of the time an architecture mistake. If iteration is needed — index through events, read off-chain via The Graph.

Unexpected source: keccak256 on short strings

string memory name in a function called thousands of times — this is ABI encoding overhead. Replacing strings with bytes32 constants where strings are known beforehand, gives 200-500 gas per call.

Analysis Tools

Tool What it shows
Hardhat Gas Reporter Gas per function call in tests
Foundry forge test --gas-report Same, but faster with diff across commits
eth-gas-reporter Breakdown by opcodes with --verbose
Tenderly Gas Profiler Breakdown by EVM trace of real transaction
Remix Gas Estimation Quick check without setup

Foundry is the preferred choice. forge snapshot creates a .gas-snapshot file that can be committed to the repository and tracked for gas regressions in CI:

forge snapshot
# change code
forge snapshot --diff

The difference is immediately visible line-by-line for each function.

Specific Optimization Patterns

Custom Errors Instead of Require with Strings

// Before: 24,000 gas to deploy one string
require(amount > 0, "Amount must be positive");

// After: save ~200 gas per revert + less bytecode
error AmountZero();
if (amount == 0) revert AmountZero();

Custom errors (EIP-838) became standard with Solidity 0.8.4. Strings in require are bytecode, which increases deployment cost and revert cost.

Unchecked Arithmetic

With Solidity 0.8.0, all arithmetic operations check overflow by default. The check costs ~100 gas per operation. Where overflow is mathematically impossible:

unchecked {
    ++i; // in for loop — standard pattern
    total += amounts[i]; // if sums are bounded and checked above
}

On a loop of 100 iterations — saves 10,000+ gas.

Immutable and Constant

constant — value is embedded in bytecode, SLOAD not needed. immutable — value is written to bytecode at deploy, read as PUSH32. Both are ~3x cheaper than reading from storage. Token address, fee basis points, owner address in a contract that won't upgrade — all candidates for immutable.

Calldata vs Memory for Input Parameters

// memory — copies data to memory
function process(uint256[] memory ids) external

// calldata — reads directly from calldata, doesn't copy
function process(uint256[] calldata ids) external

For external functions where data is only read — calldata is cheaper. The difference grows with array size: on an array of 50 elements — 3,000-5,000 gas.

Optimization Process

Baseline audit. Run all tests with forge test --gas-report, fix baseline. No changes without before/after measurements.

Profiling via Tenderly. Take real transactions from mainnet (if contract already deployed) or simulate in Tenderly fork. Look at breakdown by EVM opcodes — where percentually most is spent.

Iterations. Apply changes one by one, measure. Slot packing usually gives the biggest effect — start with it.

Regression testing. forge snapshot in CI. Any PR that increases gas by more than 1% requires explicit justification in review.

Timeline Estimates

Audit of existing contract + recommendations report: 1-2 days. Optimization with implementation of changes and tests: 2-3 days depending on contract complexity. Complete storage layout rewrite (if architecture is initially unoptimized): from 1 week, as it requires migration scripts for existing data.

Cost is calculated after analyzing the contract and current gas profile.