Ethereum contracts own their storage and discover what they touch while running. Solana transactions declare every account up front, and that one rule explains its parallelism, its fee markets, and why a hot account is a hot row.
Learning in public. I haven’t shipped anything on-chain yet. These posts map what I know from production payments and ledgers onto how blockchains handle the same problems.
I learn new systems by asking what the schema is and where the locks are. I did that for Snowflake and Kafka at CoStar and for Postgres in FieldDay, and it's how I've been teaching myself Solana and the EVM. The two chains answer both questions very differently, and most of the differences people argue about (parallelism, fees, storage cost) follow from the answers.
The EVM: every contract is its own database.
On Ethereum, a smart contract is code plus a private key-value store. Its storage is a
huge map from 256-bit slots to 256-bit values, and only that contract's code can write
to it. An ERC-20 token's balances aren't stored with your wallet. They're a mapping
inside the token contract: balances[you] = 100.
In database terms, each contract is its own schema with one wide table, and the contract code is the only stored procedure allowed to touch it.
The important part is when the database finds out what a transaction will touch: during execution. A transaction calls a contract, which might read a price from another contract, which might call a third. Nobody knows the full read and write set until the code has run. That's why EVM execution has historically been serial. If you can't predict which rows two transactions touch, you can't safely run them at the same time.
Ethereum has been chipping away at this. EIP-2930 added optional access lists that let a transaction declare what it expects to touch. The bigger step is in the Glamsterdam upgrade: block-level access lists (EIP-7928), where every block records the accounts and storage slots its transactions touched, so clients can validate it in parallel. As I write this, Glamsterdam is live on the Sepolia testnet and mainnet is still being scheduled.
Solana: programs are stateless, and the data is in accounts.
Solana splits code from data. A program (Solana's word for a smart contract) holds no
state of its own. All state lives in accounts: each one has an address, a lamport
balance, a blob of data bytes, and an owner, the program allowed to change that data.
The runtime enforces that only the owner program can modify an account's data or debit
its lamports.
So a token balance isn't an entry in a mapping. It's its own account, owned by the Token program, holding the mint, the owner, and the amount. Your USDC balance and mine are two separate rows.
Here's the rule everything else follows from: a Solana transaction must list every account it will read or write before it runs, and mark each one writable or read-only. It's as if every SQL transaction had to declare its row locks up front.
Declared locks are what make Solana parallel.
Once every transaction has declared its read and write set, the scheduler (the runtime is called Sealevel) can do what a database lock manager does. Transactions whose writable accounts don't overlap run in parallel on different cores. Ones that write the same account run one after another. Read-only accounts can be shared.
The EVM has to discover conflicts by running code. Solana makes the transaction author state them, and the runtime rejects any access that wasn't declared. That's the trade: more work for the client building the transaction, much less guessing for the chain.
A hot account is a hot row.
Anyone who has run a busy Postgres table knows the failure mode: a single row everybody updates, such as a global counter or a shared balance, becomes the bottleneck, because row locks serialize every writer. Adding cores doesn't help.
Solana has the same failure mode under a different name. A popular trading pool's state account, or a mint during a hyped launch, is a writable account that thousands of transactions want at once, and those transactions serialize behind it. That's also why Solana's fees are partly local: priority fees mostly compete for the contended account, not the whole chain, so a frenzy around one market doesn't have to raise the price of an unrelated transfer.
I designed around this in FieldDay without a blockchain involved. The ledger is one append-only table with a composite primary key and quarterly partitions, and nothing updates a running total in place. If I were porting that to Solana, the first thing I'd avoid is a single writable "platform fees" account that every payment touches. That's the global counter again. Shard it into several fee accounts keyed by something deterministic, and sum them when you need the total, the same way you'd shard a hot counter in any database.
Addresses as composite keys.
Solana has a neat answer to "where does this row live?": program-derived addresses
(PDAs). A program can derive an address deterministically from seeds, such as
["vault", userPubkey] plus its own program ID. The derived address has no private key,
so only the program can sign for it.
That's a composite natural key. The address of user X's vault is a pure function of (program, "vault", X), so anyone can compute where it lives without looking it up. The associated token account works the same way: your token account for a given mint has a predictable address derived from your wallet and the mint.
Storage has a deposit.
In Postgres, storage is the operator's problem. On Solana, it's the user's. An account has to hold a minimum lamport balance proportional to its size to stay rent-exempt: (128 bytes of overhead + data size) times a per-byte rate. Close the account and the deposit comes back. Account data is also capped at 10 MiB, so "one big table" isn't an option. Big state means many accounts.
The rate is moving right now. SIMD-0437 cuts the per-byte rate by 90% in five
feature-gated steps. The first two went live on mainnet in September 2026, and the rest
are scheduled behind further review. That's the strongest argument I know for never
hard-coding the number: ask the RPC with getMinimumBalanceForRentExemption(size) every
time you size an account.
The schema view, side by side.
| Question | EVM | Solana |
|---|---|---|
| Where does state live? | Inside the contract that owns it | In separate accounts, owned by programs |
| What's a token balance? | An entry in the token contract's mapping | Its own account |
| When are reads and writes known? | During execution (access lists are optional) | Before execution, always |
| Parallelism | Historically serial; block-level access lists are coming | Built in: non-conflicting writes run concurrently |
| Hot-spot failure | Gas spikes chain-wide | Contention on one account, mostly priced locally |
| Storage cost | Gas when you write slots | A refundable rent-exempt deposit |
The more Solana I read, the more it looks like the database discipline I already practise, enforced by the runtime instead of by code review: declare what you touch, keep hot rows rare, and make every key derivable. The first project I want to build is that sharded-fee-account design, as an Anchor program on devnet, to measure what happens when one account gets hot.
Owen
Thanks for reading. Cheers.
If you spot something wrong in this post, or you’d have done it differently, tell me. I’d rather be corrected than confidently wrong. owenlrichards2000@gmail.com
— Owen