Back to Lessons
Lesson 18

Gas-Optimized ERC-20 Patterns

+25EFFORT
5 sections
transfer()
01
text

Token transfers happen constantly, so gas efficiency compounds. The dominant cost is storage: an SSTORE (writing a slot) is vastly more expensive than arithmetic or memory. The main levers are: pack storage to write fewer slots, use custom errors instead of revert strings, use unchecked for math that cannot overflow, and cache repeated storage reads in memory.

02
code
solidity
1error InsufficientBalance();
2
3function _transfer(address from, address to, uint256 amount) internal {
4 uint256 fromBal = _balances[from]; // single SLOAD, cached
5 if (fromBal < amount) revert InsufficientBalance(); // custom error
6 unchecked {
7 _balances[from] = fromBal - amount; // can't underflow: checked above
8 _balances[to] += amount; // can't overflow: bounded by supply
9 }
10 emit Transfer(from, to, amount);
11}
03
text

Custom errors replace stored revert strings, cutting both deployment and revert gas. unchecked blocks skip Solidity 0.8's automatic overflow checks — only safe where overflow is provably impossible, like a subtraction guarded by a prior require. Storage packing fits multiple small values (e.g., two uint128, or a uint96 + an address) into one 32-byte slot so a single SSTORE updates both. Libraries like Solady push these patterns to the limit.

04
note
Key Insight

Danger: never reach for unchecked or aggressive packing at the cost of correctness. An unchecked block placed where overflow IS possible reintroduces exactly the silent wraparound bugs that checked arithmetic was added to prevent. Optimize hot paths, measure with gas reports, and keep the math provably safe.

05
code
solidity
1// Storage packing: these two fit in ONE 32-byte slot
2struct Account {
3 uint128 balance; // 16 bytes
4 uint64 lastUpdate; // 8 bytes
5 // 8 bytes spare in the same slot
6}
7// vs. two separate uint256 fields = two slots = two SSTOREs
Complete

Connect your wallet to mark this lesson as complete

Earn 25 EFFORT tokens