How to Use Calldata to Cut Storage Gas

How to Use Calldata to Cut Storage Gas


Use calldata for data a transaction needs once, because passing bytes into a contract is usually far cheaper than writing the same data into persistent storage.

The detail that makes this click is that “on-chain” does not mean “contract storage.” Calldata becomes part of the transaction record, but it disappears from the contract’s usable state when execution ends. Storage remains addressable by later transactions, and Ethereum charges heavily for that persistence.

Why calldata costs less

Calldata is transaction input, while storage is persistent contract state. The EVM reads calldata with operations such as calldataload and calldatacopy; it reads and changes storage with SLOAD and SSTORE.

Each zero byte of calldata costs 4 gas and each non-zero byte costs 16 gas. A 32-byte payload therefore costs between 128 and 512 gas for its data bytes. Writing a non-zero value to a previously empty 32-byte storage slot costs about 22,100 gas, before counting the rest of the transaction and the contract’s logic.

That is not a claim that calldata is free. Function selectors, ABI padding, dynamic offsets, memory copies, and transaction overhead all add cost. The point is that a storage write pays for a change to Ethereum’s persistent state, while calldata pays mainly for carrying bytes into one execution.

What belongs in each place

Put a value in calldata when the caller supplies it and the contract only needs it during the current call. Put it in storage when a later call, another contract, or a future state transition must retrieve it.

A swap transaction shows the split clearly. The caller can provide a recipient, input amount, route, deadline, and minimum output in calldata. The contracts still keep balances, allowances, pool reserves, fee settings, and nonces in storage because those values must survive the transaction.

The ERC-20 Standard is a useful boundary here: balances and allowances must behave like persistent state, so token contracts cannot replace them with calldata. A router does not need to persist every route or minimum-output value after the swap succeeds. Protocols associated with Frax Finance and Balancer Protocol use the same general design pressure, even when their internal accounting differs.

In a swap call, Frax Swap illustrates the same boundary between per-call inputs and persistent pool state.

The edge that changes the design

Calldata is cheaper only when its lack of persistence is acceptable. If a later transaction needs the value, passing it again is not a substitute for storage; the caller must possess it, and the contract must verify that it has not been altered or replayed.

That makes validation part of the design. Contracts should check calldata length and offsets before decoding dynamic arrays, verify deadlines and signatures, constrain token addresses and amounts, and enforce slippage limits. Calldata is public and adversarial: anyone who can submit a transaction can construct different bytes.

For data used repeatedly inside one call, copy it to memory or cache decoded values instead of writing temporary state slots. Storage can be especially wasteful for intermediate values because even a value that is created and cleared during one transaction still triggers storage accounting and may not recover all of its cost through refunds.

There is also a difference between calldata and logs. Both can preserve information outside the contract’s state, but contracts cannot use an old event or an old transaction’s calldata as ordinary state in a later execution. Use logs or calldata for data that off-chain software can index; use storage for data that on-chain code must enforce later.

A practical rule

Ask whether the next transaction needs the contract to know the value without trusting the caller to provide it again. If the answer is no, calldata is usually the better location. If the answer is yes, storage is the cost of making that fact part of the protocol’s state.

FAQ

Is calldata permanent?

It remains in the blockchain’s transaction record, but it is not persistent contract storage and cannot be read as ordinary state by a later call.

Is calldata private?

No. Transaction calldata is visible to the network and should be treated as public input.

Can calldata replace storage for balances?

No. A caller-supplied balance is only a claim. Enforceable balances and allowances must live in persistent state controlled by the token contract.

Report Page