A bank ledger, not a jar of coins
In the last guide you spent money the way Bitcoin does: by reaching into a jar of physical-feeling unspent coins, picking ones that add up to enough, handing them over whole, and taking change back. It works, but it is a little like paying for coffee only in exact bills you already hold. Now picture how your bank statement works instead. The bank does not store your specific dollars — it stores a single number, your balance, and a payment just lowers your number and raises someone else's. That second picture is the account model, and it is how Ethereum and most smart-contract chains record money.
The difference is more than taste. In the UTXO world there is no such thing as "Alice's balance" stored anywhere — her balance is something your wallet computes by scanning the chain for coins she can spend. In the account world the balance is a first-class fact written directly into the ledger. Everything else in this guide flows from that one design choice.
Two kinds of account
Ethereum has exactly two account types, and they share the same address space (a 20-byte / 40-hex-character identifier). An externally owned account (EOA) is controlled by a private key — it is what your wallet manages, and only a valid signature can move its funds. A contract account is controlled by code instead of a key; it is a smart contract that runs when something calls it. You cannot tell which is which from the address alone, but the chain stores different things for each.
Every account, of either kind, holds the same four fields. Two — the nonce and the balance — exist for all accounts. The other two — a storage root and a code hash — are only meaningful for contracts; for an EOA they sit empty.
// The state stored for ONE Ethereum account (the "account state")
Account {
nonce: uint // EOA: number of txs sent. Contract: number of contracts it created.
balance: uint // amount owned, in wei (1 ETH = 10^18 wei)
storageRoot: hash // Merkle root of this contract's key->value storage (EOA: empty)
codeHash: hash // hash of this account's EVM code (EOA: hash of "")
}The world state: one giant map
If each account is a record, the world state is the whole filing cabinet: a single mapping from every address to its account state. Conceptually it is just a dictionary.
worldState = {
"0xAlice...": { nonce: 3, balance: 5_000000000000000000, code: none }, // EOA, 5 ETH
"0xBob...": { nonce: 0, balance: 2_000000000000000000, code: none }, // EOA, 2 ETH
"0xToken..": { nonce: 1, balance: 0, code: 0x6080.., storageRoot: 0x9f.. } // a contract
}Of course you cannot fit millions of accounts into a block. So Ethereum does not store the map in the block — it stores one 32-byte fingerprint of the entire map, the state root, in the block header. That fingerprint is the root of a Merkle Patricia Trie: a hash tree (built on Keccak-256) keyed by address, so that any change to any account — one wei moved — changes the root. This is what makes the ledger tamper-evident: a node can be handed the whole state and instantly check it hashes to the root the chain agreed on, the same trick the block's transaction Merkle root played in an earlier guide.
So a block does not really contain balances at all. It contains a commitment to the state, plus the list of transactions that transform the previous state into this one. Running those transactions is the state transition function: `newState = apply(oldState, block.transactions)`. The chain is, at heart, a public record of state transitions everyone can replay and agree on.
Anatomy of a transfer
Now the payoff. In UTXO land, sending money meant selecting coins, building inputs and outputs, and routing change back to yourself. In the account model a transfer is almost embarrassingly simple: decrement the sender, increment the recipient. Let's walk one through, with gas, the way a node actually does it.
Alice has 5 ETH and a nonce of 3. She signs a transaction sending 1 ETH to Bob (who has 2 ETH). Say it costs 0.001 ETH in gas to include. A validating node checks and applies it like this:
- Signature: recover the sender from the signature and confirm it is Alice's address. No valid signature, no transaction.
- Nonce check: the tx's nonce must equal Alice's current account nonce (3). Not 2 (already used), not 4 (too far ahead) — exactly the next one.
- Funds check: Alice's balance must cover value + fee, i.e. 5 ≥ 1 + 0.001. It does.
- Apply effects: subtract 1.001 from Alice (→ 3.999), add 1 to Bob (→ 3.0), and pay 0.001 to the block's fee recipient.
- Bump the nonce: set Alice's nonce to 4, permanently retiring this transaction so it can never be applied again.
def apply_transfer(state, tx):
sender = recover_address(tx.signature, tx) # 1. who signed?
acct = state[sender]
assert tx.nonce == acct.nonce # 2. exactly the next nonce
fee = tx.gas_used * tx.gas_price
assert acct.balance >= tx.value + fee # 3. can she afford it?
acct.balance -= tx.value + fee # 4. debit sender (value + gas)
state[tx.to].balance += tx.value # credit recipient
state[coinbase].balance += fee # pay the block producer
acct.nonce += 1 # 5. retire this tx forever
return stateThe nonce: every account's personal counter
The nonce did quiet but essential work above, so let's give it the spotlight. For an EOA it is simply the number of transactions this account has ever sent — a per-account counter that starts at 0 and ticks up by exactly one each time. It solves two problems at once.
Ordering. Because each transaction must use the next nonce in sequence, the network has an unambiguous order for everything you send: nonce 5 cannot be processed before nonce 4. Replay protection. Once nonce 3 is mined and your account moves to nonce 4, an attacker who copies your already-signed nonce-3 transaction and rebroadcasts it gets rejected — it no longer matches your current nonce. This is exactly the replay attack that UTXOs prevent differently (a coin, once spent, simply isn't in the UTXO set any more).
The sequential rule has a practical consequence wallets must handle. If you broadcast nonce 4 but it's stuck (fee too low), then broadcast nonce 5, the second one cannot be mined until 4 goes first — it sits behind a nonce gap. The fix is nonce management: re-send nonce 4 with a higher fee (a replace-by-fee), or send a do-nothing 0-ETH transaction at nonce 4 to clear the way. This is why a frozen pending transaction can block all your later ones.
Accounts vs UTXOs: the real trade-offs
Neither model is simply "better" — they trade different things, which is why Bitcoin and Ethereum each picked the one that fit their goal.
Where accounts win. The model is intuitive (it's a bank balance), transactions are smaller and simpler (no juggling inputs/outputs or change), and — decisively — accounts give a smart contract a stable home with persistent, mutable state. A token contract can keep a `balances` map that updates in place over years. That fit with stateful programs is the core reason Ethereum chose accounts to be a "world computer."
Where UTXOs win. Independent coins can be validated in parallel — two unrelated UTXO spends touch no shared data, while two transfers from the same account contend over one balance and one nonce, forcing sequential processing. UTXOs also leak less by default (a fresh change address per payment is easy), whereas an account address accrues a permanent, linkable history. And a UTXO's validity is more self-contained, which simplifies light-client proofs.
You now hold both great ways to record money on a ledger, and the world state that ties an account chain together. In the next guide we follow a finished, signed transaction out of your wallet, through the network, and into a block — the journey that turns all this state into a confirmed payment.