DField SolutionsMérnöki stúdió · Budapest
Loading · Töltődik
Skip to content
Back to blog
·13 min read
Blockchain··13 min read

Solidity gas optimization: 16 patterns we use on audits

Sixteen gas patterns, ranked by real-world impact on the contracts we audited in 2025.

Last verified
Listen
Dezső Mező
Founder, DField Solutions
ShareXLinkedIn#
Solidity gas optimization: 16 patterns we use on audits

We audited 11 Solidity codebases in 2025. Every single one had 15-40% avoidable gas cost, mostly in the same patterns. Here they are in order of impact.

1 · Pack storage slots

A `uint256` and a `bool` in separate slots cost 20k gas per write. Pack them into one slot (`uint128 + bool + address`) and the second write is a few hundred gas. Look for structs with mixed-size fields.

2 · Custom errors over require strings

`require(x, "NotOwner")` deploys the string to bytecode. `error NotOwner(); revert NotOwner();` is ~50 bytes smaller and costs less at runtime.

// before
require(msg.sender == owner, "NotOwner");

// after
error NotOwner();
if (msg.sender != owner) revert NotOwner();

3 · `calldata` over `memory` for read-only params

External function arrays/structs: use `calldata` unless you mutate. Saves the copy + a chunk of stack.

4 · `unchecked` on safe arithmetic

Solidity 0.8+ checks every math op. Where you've already proven no overflow (post-condition from a prior `require`, or a bounded loop counter), wrap in `unchecked { ... }` for significant gas savings.

5 · `immutable` > `constant` for computed-once values

`immutable` is cheaper to read than `constant` when the value is computed at construction (like `owner = msg.sender`). `constant` wins only for literals.

6 · Short-circuit loops

`for (uint i; i < n; ++i)` beats `i++` (pre-increment saves a few gas per iter). Cache `n` if it's a storage read. If the work can stop early, `break` · don't run the full loop.

7 · Events for historical state

Every `SSTORE` is ~20k gas. An `emit` is ~1.5k. If data doesn't need on-chain reads - historical logs, UI feeds, analytics - use events, not storage.

Run forge snapshot before and after each change. Measure, don't guess · a 'clever' optimization can cost gas via bytecode bloat.

8-16 · The shortlist

  • Use `>=` instead of `>` when semantically equivalent (saves 3 gas).
  • `++` costs less than `+= 1` (same reason).
  • Cache storage reads in a local variable for loops.
  • Use `assembly { mstore(0x40, ...) }` for hot paths only · readability cost is real.
  • Prefer `type(uint256).max` over hardcoded max values.
  • `transfer`/`send` are gas-stipended · `call{value: ...}` is cheaper and more flexible.
  • Batch similar ops into one function to share the function-selector jump cost.
  • `external` functions are cheaper than `public` when not called internally.
  • Avoid double-SSTORE on initialization · use `immutable` where possible.

The anti-pattern

Don't optimize gas before you've got correctness + audit. A gas-optimized contract that reverts under edge conditions is a catastrophe, not a win. Correctness first, then gas · in that order.

ShareXLinkedIn#
Dezső Mező
By

Dezső Mező

Founder, DField Solutions

I'm a full-stack engineer and I build across the whole stack myself · AI agents, web and mobile apps, blockchain, backends, security, right down to the OS layer. If it's software, I've probably built it and broken it.

Keep reading
RELATED PROJECTS
Let's talk

Would rather build together?

Let's talk about your project. 30 minutes, no strings.