Orca Crypto
Menu
Start Here
Learn
Chains
Exchanges
Markets
Tools
Safety
Glossary
More
Our tokens Book a session
Expert · Scaling architecture

The EVM under the hood: gas, storage and calldata

Gas is not a fee schedule to memorize. It is a model of what your code costs every node on Earth.

Loading live prices
By the Orca Crypto teamUpdated 2026-09-28How we check this12 min readExpert
The short answer

The EVM is a stack machine with 256 bit words and five places to keep data: the stack, memory, storage, transient storage and calldata. Almost every gas cost follows from one principle: an operation is priced by the burden it puts on every node that has to repeat it, so reading state from disk is expensive the first time, writing new state is the most expensive thing you can do, and anything that disappears when the transaction ends is cheap.

The machine in one paragraph

The Ethereum Virtual Machine executes bytecode one opcode at a time against a stack of 256 bit words, at most 1,024 deep. Every node that validates a block re-executes every transaction in it and must reach the same result, byte for byte. That single requirement explains the whole design: no floating point, no system clock, no randomness, no unbounded loops, and a price on every operation so that no transaction can make every node on the network do unbounded work for free.

Five places to keep data

LocationLifetimeAddressed byCost profile
StackThe current call framePosition, top 16 reachableCheapest. Most arithmetic is 3 to 5 gas
MemoryThe current call frameByte offsetCheap to use, with a quadratic charge for growing it
CalldataThe transactionByte offset, read onlyPaid once per byte in the intrinsic cost, then cheap to read
Transient storageThe transaction32 byte slot100 gas per TLOAD or TSTORE, gone when the transaction ends
StorageForever, in the state trie32 byte slot, 2256 per contractThe expensive one. Priced by whether a slot is cold, warm, empty or changing

Transient storage arrived with the Dencun upgrade in March 2024 (EIP-1153). It behaves like storage for the length of one transaction and then vanishes, which makes it ideal for reentrancy locks, callbacks and flash accounting. Uniswap v4 depends on it.

What a transaction costs before it runs

Every transaction pays an intrinsic cost before a single opcode executes:

ComponentGas
Any transaction21,000
Contract creation, extra32,000, plus 2 per 32 byte word of init code (EIP-3860)
Each zero byte of calldata4
Each nonzero byte of calldata16
Each address in an access list (EIP-2930)2,400
Each storage key in an access list1,900

Since the Pectra upgrade in May 2025, EIP-7623 adds a floor for transactions that are mostly data. Count tokens as zero bytes plus four times nonzero bytes. The transaction pays the greater of its normal cost and 21,000 plus 10 gas per token. Ordinary contract calls rarely hit the floor, because their execution gas dwarfs their calldata; it exists to cut the worst case amount of calldata a block can carry, which made room to raise blob capacity.

cost = max( 21,000 + 4·tokens + execution,  21,000 + 10·tokens )tokens = zero bytes + 4 × nonzero bytes. EIP-7623, live since Pectra.

Fusaka, in December 2025, capped any single transaction at 224 = 16,777,216 gas (EIP-7825), so no transaction can occupy most of a block by itself.

Warm and cold: the first touch is the expensive one

Since the Berlin upgrade in 2021 (EIP-2929), the EVM tracks which accounts and storage slots a transaction has already touched. The first access pays for the disk read a node may have to do. Every later access in the same transaction is priced as a cache hit.

AccessCold (first time)Warm (again)
SLOAD, reading a storage slot2,100100
Touching an account: BALANCE, EXTCODESIZE, EXTCODEHASH, CALL and friends2,600100

Some things start warm: the sender, the recipient, every precompile and, since Shanghai (EIP-3651), the block's fee recipient. An access list lets you pre-warm the rest at 2,400 per address and 1,900 per slot, which is slightly cheaper than paying the cold price during execution, but only when you know in advance exactly what will be touched.

SSTORE: the most expensive thing you can do

Writing storage creates or changes state that every full node must keep. The price depends on what the slot held at the start of the transaction and what you are writing:

WriteGas (slot already warm)Add if the slot is cold
Zero to nonzero (new state)20,0002,100
Nonzero to a different nonzero2,9002,100
Same value as now1002,100
Nonzero to zero2,900, then 4,800 refunded at the end2,100

Refunds are paid back when the transaction finishes, and since London (EIP-3529) they are capped at one fifth of the gas the transaction used, down from one half. The same EIP removed the SELFDESTRUCT refund and cut the clearing refund from 15,000 to 4,800, which together killed the old trick of minting "gas tokens" by filling storage when gas was cheap and clearing it when gas was dear.

Round trips are refunded, mostly
A slot written and then restored to its original value in the same transaction gets most of the write refunded, because nothing about the persistent state changed. It still costs more than using transient storage, which is why locks and temporary flags moved there.

How Solidity lays out storage

State variables are assigned slots in declaration order. Values smaller than 32 bytes are packed into the same slot while they fit, which is why the order you declare variables in can change what every function costs. Mappings and dynamic arrays cannot know their size, so their contents are placed at hashed locations derived from the slot they were declared in:

solidity
contract Vault {
    uint128 public totalShares;   // slot 0, low 16 bytes
    uint64  public lastUpdate;    // slot 0, next 8 bytes (packed)
    address public owner;         // slot 1 (20 bytes do not fit beside 24 used)
    mapping(address => uint256) public shares;   // slot 2 holds nothing itself
    uint256[] public history;     // slot 3 holds the length
}

// shares[user] lives at   keccak256(abi.encode(user, uint256(2)))
// history[i]  lives at    uint256(keccak256(abi.encode(uint256(3)))) + i

Two consequences are worth internalizing. Reading two packed variables costs one cold SLOAD, not two, if they share a slot. And every mapping entry lives at an effectively random slot, so iterating a mapping is impossible onchain and each new key written is a fresh 20,000 gas slot.

Memory: cheap until it is not

Memory starts empty in every call frame and grows in 32 byte words. You pay for its high water mark with a cost that has a linear and a quadratic part:

memory cost(a) = 3a + a² ÷ 512a is the number of 32 byte words touched. You pay the difference each time the high water mark rises.
Memory usedWordsTotal memory cost
1 KB3298 gas
32 KB1,0245,120 gas
1 MB32,7682,195,456 gas

The quadratic term is negligible for normal contracts and prohibitive for anything that tries to hold a large data structure in memory. It is the EVM's way of capping how much RAM a transaction can demand from every node.

Calls, value and the 63/64 rule

A CALL pays the account access cost, plus 9,000 if it sends value and a further 25,000 if that creates a new account. A value transfer also hands the callee a free stipend of 2,300 gas, historically enough to emit an event and famously not enough to do much else, which is why transfer() and send() broke when opcode costs were repriced (EIP-1884 in the Istanbul upgrade of 2019 raised SLOAD from 200 to 800 gas) and why modern code uses call with an explicit check.

Since EIP-150 in 2016 a caller can forward at most 63/64 of its remaining gas to a callee. That bounds call depth in practice and guarantees the caller always keeps enough gas to handle a failed call.

CALL

Runs the callee’s code in the callee’s own storage, with msg.sender set to the caller. The ordinary case.

DELEGATECALL

Runs the callee’s code in the caller’s storage and context. The foundation of every proxy and every upgradeable contract, and of a whole class of storage collision bugs.

STATICCALL

A call that reverts if anything tries to modify state. Used for view functions and for calling code you do not trust to be read only.

Logs: the cheap way to publish data

LOG cost = 375 + 375 per topic + 8 per byte of data

An ERC-20 Transfer event has three topics (the event signature and the two indexed addresses) and 32 bytes of data, so it costs 1,756 gas. Storing the same 32 bytes in a fresh slot costs 20,000 or more. Contracts cannot read logs, but indexers, explorers and wallets can, so anything only needed offchain belongs in an event.

Worked example: one ERC-20 transfer, two prices

Why does sending a token to a new holder cost so much more than sending it to an existing one? Walk through a plain implementation, sending 1,000 USDC worth of a 6 decimal token:

StepTo an existing holderTo a new holder
Intrinsic cost21,00021,000
Calldata: 4 byte selector plus two 32 byte wordsabout 600about 600
Read and write the sender’s balance (cold slot, nonzero to nonzero)5,0005,000
Read and write the recipient’s balance5,000 (nonzero to nonzero)22,100 (zero to nonzero)
Transfer event, three topics and one word of data1,7561,756
Everything else: dispatch, checks, stack worka couple of thousanda couple of thousand
Roughlyabout 35,000about 52,000

The 17,000 gas difference is almost entirely one thing: creating a storage slot that did not exist. Real tokens add their own overhead, and proxies add a DELEGATECALL, so measured figures run higher, but the shape is always the same. The same reasoning tells you why airdropping to thousands of fresh addresses is expensive and why claim based distributions push that cost onto the claimer.

Deploying code

A new contract's address is determined before it exists, which is what makes counterfactual wallets and deterministic deployments possible:

CREATE:  address = keccak256( rlp(sender, nonce) )[12:]Depends on the deployer’s nonce, so it changes with every transaction the deployer sends.
CREATE2:  address = keccak256( 0xff ‖ sender ‖ salt ‖ keccak256(init_code) )[12:]Depends only on the deployer, a chosen salt and the code, so it can be computed and funded in advance.

Deployed code costs 200 gas per byte to store and may not exceed 24,576 bytes (EIP-170), with init code capped at twice that (EIP-3860). The size limit is why large protocols are split into libraries, facets and modules.

Precompiles: native code at fixed addresses

Some operations are far too slow to write in EVM bytecode, so they are built into every client and exposed at fixed addresses, called like contracts and priced like opcodes.

AddressWhat it doesWhy it matters
0x01ecrecover: recover a signer from a secp256k1 signatureEvery permit, every signed order, every meta transaction. 3,000 gas
0x02SHA-256Bitcoin interoperability and light clients
0x05Modular exponentiationRSA style verification
0x06 to 0x08Addition, multiplication and pairing on the BN254 curveWhat makes Groth16 and PLONK proof verification affordable onchain
0x0aKZG point evaluation (EIP-4844)Verifying that data really sits in a blob
0x0b to 0x11BLS12-381 operations (EIP-2537, Pectra)Verifying BLS signatures and newer proof systems
0x100secp256r1 signature verification (EIP-7951, Fusaka)Passkeys and device secure enclaves as wallet signers

What this means when you write code

  1. Treat storage as the budget

    Pack related small values into one slot, read a slot once and keep it on the stack, and use constant and immutable for anything fixed, since those live in bytecode and cost no SLOAD at all.

  2. Use the cheap locations for temporary things

    Locks, flags and values passed between callbacks belong in transient storage. Arguments to external functions can stay in calldata instead of being copied to memory.

  3. Publish, do not store, what only humans read

    History, receipts and analytics belong in events. Contracts only need to store what contracts need to read.

  4. Measure, do not guess

    Foundry gas reports and snapshots show the cost of every function and catch regressions in review. The numbers above explain the results; the tools tell you the truth.

solidity
// A reentrancy lock in transient storage (Solidity 0.8.28 or later).
// TLOAD and TSTORE cost 100 gas each, so the lock costs about 300 gas,
// and the slot clears automatically when the transaction ends:
// no 20,000 gas first write and no refund bookkeeping.
bool transient locked;

modifier nonReentrant() {
    require(!locked, "reentrant call");
    locked = true;
    _;
    locked = false;
}
// If you write it in assembly instead, use a namespaced slot (ERC-7201
// style), never slot 0: transient state variables are laid out from 0.
Cheaper is not the goal, correct and cheaper is
Gas optimizations are the easiest place to introduce a vulnerability. Unchecked arithmetic, hand written assembly and aggressive packing each remove a safety net. Optimize the paths that run often, keep the rest boring, and let the test and audit process decide whether a saving was worth it.

What is changing next

Every number in this guide is the mainnet schedule as of September 2026. The next upgrade, Glamsterdam, was heading for its Sepolia testnet fork in early October 2026 with mainnet targeted for later in the year. Its headline changes are enshrined proposer builder separation (EIP-7732) and block level access lists (EIP-7928), which let clients execute non overlapping transactions in parallel. Neither headline change alters the cost model described here, but Glamsterdam’s candidate list has also carried gas repricing proposals, so check the final EIP list before relying on a specific figure.

Common questions

Why does writing to a new storage slot cost 20,000 gas?

Because it permanently grows the state that every full node must store and serve, forever. Changing an existing nonzero value only alters state that already exists, which is why it costs 2,900 instead. The price reflects the long term burden on the network, not the computation.

What is the difference between memory and storage in the EVM?

Memory is a scratch space that exists only for the current call and is wiped when it returns. Storage is the contract's permanent state, kept in the state trie between transactions. Memory is cheap until it grows large; storage is expensive from the first write.

What are warm and cold access in Ethereum?

The first time a transaction touches an account or a storage slot it is cold and pays 2,600 or 2,100 gas, reflecting a possible disk read. After that it is warm for the rest of the transaction and costs 100. Access lists can pre-warm known accounts and slots.

Why is my transaction gas estimate different from the gas actually used?

Estimates simulate against the current state, but the state can change before inclusion: a balance that was zero may now be nonzero, a slot may already be warm, a branch may take a different path. Refunds are also only applied at the end, which is why estimates include a margin.

Does the EVM have a maximum contract size?

Yes. Deployed code is limited to 24,576 bytes by EIP-170, and init code to 49,152 bytes by EIP-3860. Large systems split logic across libraries, proxies and modules to stay under it.

Kept on this device only. Nothing is sent anywhere.

Where to go next

Next: Inside the Solana runtime: accounts, parallelism and fees

Guide 8 of 16 in the Expert level. Your place is kept on this device, so you can pick up exactly here next time.

Share this page

Was this page useful?