Have you heard of Optimism? It uses an optimistic rollup to execute smart contracts outside Ethereum while publishing data and state commitments back to Ethereum.
This article explores the original 2021 Optimistic Virtual Machine (OVM) design. How cool is that? It is useful history, but today’s OP Stack has a different implementation. Let’s start at the beginning...
What are Merkle Trees?
The most important concept to understand is a merkle tree. This is a merkle tree:

At the root of the merkle tree is the root hash. This is what we later reference as state root. It's created by hashing each data block and storing it as leaf node. Now two leaf hashes are combined by hashing those together. We do this all the way until we have one tree with a single root hash.
A merkle proof now would be proving to someone that L3 did indeed contain a given value. All one needs to do is provide the Hash 0, Hash 1-1 and the L3 block itself. Now for the proof verification one can compute the hash of L3, then the hash 1 and finally the top hash. We can then compare the root hash against our known root hash. For a visual explanation of a merkle proof, check out this great explanation.
Why is this enough? Because when using a secure hash function like keccak256, it's practically impossible to create a hash collision, meaning although you reduce an infinite potential input space to just 256 bits, the likelihood that you find two different sets of inputs which result in the same hash is so low that it simply won't happen in practice. Now if you receive a matching root hash in the merkle proof, you know this one item really must have been part of the original root hash calculation.
State of a smart contract
In Ethereum one merkle tree is the state tree which contains all state like user ETH balances, but it also contains the contract storage itself. This allows us to create merkle proofs on smart contract state!
So it's possible to prove a smart contract has a certain state using the merkle proof mechanism. Keep that in mind for later.
How does Plasma work?
Plasma uses a combination of smart contracts and merkle proofs. Together, these enable fast and cheap transactions by offloading these transactions from the main Ethereum blockchain into a plasma chain. In contrast to regular sidechains, you cannot run any smart contract in here.
In Plasma users send transactions between each other in UTXO style where the results of new balances are continuously updated in the Ethereum smart contract as merkle tree roots. Once a merkle root is updated in the smart contract, it gives users the security over their funds even if the plasma chain operator is malicious. The root encapsulates the result from many sent funds transactions. Should a Plasma operator submit an invalid root, users can contest it and safely get their funds back. For more details have a look here.
But as said before, it cannot run smart contracts. So no Uniswap with Plasma is possible.
Optimism: How to run a blockchain on a blockchain
But this is where Optimism comes in. It brings general smart-contract execution to an optimistic rollup. Plasma is a useful point of comparison, but the data-availability model is different.

An optimistic rollup executes transactions on L2 and commits to the resulting state on Ethereum. It also publishes transaction data to Ethereum, using calldata or blobs, so others can reconstruct and check that state. A Merkle root alone is not enough.
This allows arbitrary smart contracts to run on Optimism. The implementation details below describe the 2021 OVM generation.
- Execute transactions on the L2 and commit to its state.
- Make transaction data available on Ethereum so independent participants can reconstruct the state.
- Use an optimistic dispute process to challenge invalid state claims.
The single-transaction OVM proof design discussed below is historical. The current OP Stack fault-proof system uses interactive disputes and a fault-proof VM; its trust and upgrade assumptions should be assessed separately.
Now you might realize, this is where the scaling comes from. You only run transactions on layer 1 that are contested with a fraud proof. That’s the gain. Running a transaction for a fraud proof is actually more expensive than just running it on layer 1 directly. So the scaling advantage comes solely from the fact that you won’t run 99.9% of transactions on layer 1.
What do we need for fraud proofs?
Historical context: The SCC and CTC architecture and associated source excerpts in this section describe the 2021 OVM system. Bedrock superseded that architecture. These are explanatory historical excerpts, not current deployment contracts.
The fraud proof is where the magic happens. Without it there would be not extra security compared to a side chain. We touched on it on a high level, it basically runs a transaction only when it's contested, but what exactly does that mean?
If we want to run a transaction on a smart contract, but we have not run any no prior transactions, how can we do this? We need two things for this
The state chain is what we already touched on. It's basically the state root hashes from the merkle tree commited to a smart contract. So this captures all relevant smart contract states in a single root. The state chain keeps an ordered list of those roots.
The second chain is the canonical transaction chain. Here are all transactions stored, but merely as the transaction inputs without running the transaction. This costs a bit of gas, but of course much less than running a transaction fully. This serves two functions. For one it brings data availability, because anyone can get the layer 2 states by running the transactions locally. And for two it allows the fraud proof to run the contested state transition.
Every state transition in the SCC corresponds to one sequenced transaction in the CTC. Now you may see how this is a blockchain on a blockchain where the newly added blockchain consists of blocks with only a single transaction.
How exactly does the fraud proof work?
From a high level, the fraud proof statement is “Using S3 as my starting state, I’d like to show that applying T4 on S3 results in *S4 which is different from S4 what the sequencer published (😈). As a result I’d like S4 and everything after it to be deleted and replaced with the correct *S4.”



Now to actually run the transaction, we need some merkle proofs again and also a concept of the Optimism VM.
1. Provide all state required for the transaction
Remember we now have the state root S3 and also the transaction T4, but S3 is only the merkle root of the state. We don't actually have the state for every single smart contract. But using the merkle proof we discussed in the start, one can do one merkle proof for every single storage slot in every required smart contract. It takes some time and gas, but it's possible.
2. Run the transaction within the Optimism VM
Historical context: Opcode replacement below is specific to the older OVM. Current OP Stack execution uses a modified Ethereum client; do not apply these replacements to a new Solidity project.
Now we have all state and the transaction data T4. We can run the transaction! But how do we handle opcodes like TIMESTAMP? This opcode would of course return a different result on our layer 1 now than when it was running on layer 2, because the time of execution is different.
Optimism’s solution is the Optimistic Virtual Machine. The OVM is implemented by replacing context-dependent EVM opcodes with their OVM counterparts. All replaced opcodes can be found here. That also means some opcodes cannot be used in Optimism contracts, so keep that in mind when developing. Basically anything that doesn't make sense anymore in the Optimism VM, see list here.
The replaced opcodes will ensure the transactions run identically now on layer 1 as they did on layer 2. (only block.number behaves slightly differently)
3. Provide the post-states
Are we done? Almost.
To compute the next state root *S4, we need to know the full state of every single smart contract, but currently we only know the state which was required to run T4. So in this step we complete the known state again by running merkle proofs.

4. Finalize state transition
Once all state is known, we can compute the new state root *S4 and store it. If fraud is successfully proven, X% of the bond of whoever submitted the malicious state root S4 gets burned and the remaining (1-X)% gets distributed proportionally to every user that provided data of the fraud proof.
And we are finally done.
So now you also know why bonds are so important. This fraud proof here is very expensive and the bonds are basically paying for it.
How to implement on Optimism yourself
Historical context: The restrictions below apply to the original OVM implementation. They are not a current compatibility checklist for the OP Stack.
Optimism fully supports Solidity, so you can take your contracts as they are with just a few caviats:
- Some Solidity key words cannot be used
block.numberbehaves slightly differently- Some additional opcodes exist for the Optimistic VM context
- The Optimistic VM compiler increases the contract size, so if you're close to the 24kB limit, with Optimism you may have a contract that is too large. Check out my contract downsizing tutorial here.
- Constructor parameters can be problematic which can be avoided by using the initializable pattern.
- Tests need to run on geth. You also cannot use hardhat's stack traces or
console.log, so you might want to consider developing and testing with a regular Solidity setup first and only in the last step convert and test it in Optimism. Just keep the limitations of Optimism in mind from the beginning.
How to use the Optimism networks
Use the current network reference when adding OP Mainnet or OP Sepolia. Wallet chain IDs must be hexadecimal strings. Adding a chain and switching to it are separate requests, and the wallet can decline either one.
The contract deployment whitelist was removed in December 2021. The original signup form and Kovan instructions are obsolete. Use OP Sepolia for test applications and follow the current bridge links in the Optimism documentation for any transfers.
// OP Mainnet: chain ID 10 (decimal).
// OP Sepolia: chain ID 11155420 (0xaa37dc), https://sepolia.optimism.io; use its matching explorer.
const params = [
{
"chainId": "0xa",
"chainName": "OP Mainnet",
"rpcUrls": [
"https://mainnet.optimism.io"
],
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"blockExplorerUrls": [
"https://explorer.optimism.io"
]
}
];
try {
await ethereum.request({ method: "wallet_addEthereumChain", params });
// Adding a network does not guarantee that it is selected.
await ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: params[0].chainId }],
});
} catch (error) {
// The wallet may reject either request; keep the UI on its actual chain.
console.error("Network request was not completed", error);
}
How to deploy to the Optimism networks
// Historical OVM/Kovan configuration; Kovan is retired.
// Preserve with the original OVM toolchain for study, not a current deployment.
{
optimistic_kovan: {
network_id: 69,
chain_id: 69,
gas: 1650000,
gasPrice: 15000000,
provider: function () {
return new HDWalletProvider(
mnemonic,
"https://optimism-kovan.infura.io/v3/"
+ infuraKey,
0,
1
);
},
}
}The next compiler and Truffle snippets are preserved from the 2021 OVM tutorial. The special @eth-optimism/solc package is not required for a current OP Stack application. Bedrock replaced the OVM; use a maintained Ethereum toolchain with the current network’s supported EVM settings.
This is the historical Optimistic Kovan/Truffle configuration and OVM compiler setup. Kovan has been retired, and the gas values here are not current recommendations. The sample is retained to explain the old integration; do not combine its OVM compiler with the current OP Sepolia network configuration.
{
compilers: {
solc: {
version: "node_modules/@eth-optimism/solc",
settings: { optimizer: { enabled: true, runs: 800 },
},
},
db: { enabled: false },
}Historical context: These testing and provider-dashboard instructions refer to the original Truffle/Kovan integration. Current provider plans and OP Sepolia setup differ; follow the current network and provider documentation.
A good practice I would recommend is writing your tests with Hardhat with a regular config, so you can run the tests fast and with console.log/stacktraces. And only occasionally use Truffle to run tests against Kovan (or a local Optimism node). The tutorial repo is also good to take a look at. Lastly you will need to activate Optimism in the Infura settings: https://infura.io/payment.

Further reads
- The documentation is in active development, so check it out here.
- One interesting feature is being able to send data from your contracts between layer 1 and layer 2.
- I also left out a few details about how the protocol works which you can find here.
Live apps on mainnet
1. Uniswap
Historical context: This Uniswap rollout description and token count are a 2021 snapshot, not the current application state.
A few weeks ago Uniswap launched on Optimism. When you have added the Optimism network, change to it and open Uniswap. At this time Uniswap on Optimism is limited to:
- SNX
- DAI
- USDT
- WBTC
- LINK
- EURT
- sUSD
- USDC
A more detailed guide on Uniswap might come later especially when more tokens have been added. Stay tuned.




Join the conversation
Comments are hosted by Disqus and load only when you choose to enable them.