The latest tech for scaling your contracts: Optimism

How the blockchain on a blockchain works and how to use it

Have you heard of Optimism? The new Optimistic VM enables Plasma but for smart contracts!

What does that mean? Well read on. But what it enables is having a side chain with guarantees of the Ethereum mainnet chain. How cool is that? And you can already use it for several apps on mainnet. Especially with the increasing gas prices again, it's time to learn what it is, how it works, how to use it and why it's better than a sidechain.

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:

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's Plasma for smart contracts!

Yo Dawg Optimism

The core idea here is actually quite simple. Just like in Plasma you have a layer 2 chain which is running all transactions and you update only the merkle root within layer 1 occasionally. The merkle root in this case is not for UTXO transactions as for regular plasma, but for the full state of a smart contract. Or rather for the full state of all smart contracts being used.

Yes this means we can run arbitrary smart contracts on Optimism! In very short, this is how it works:

  • Represent smart contract states as merkle tree
  • Run all transactions only on the Optimism chain
  • Continuously update the state roots on Ethereum layer 1
  • Optimism chain has low security, but through the state roots on Ethereum, they enable fraud proofs
    • When relayer submits malicious state root and it's contested, they loose their bond.
    • Fraud proofs quite gas expensive, so they really are not meant to actually be done regularly.
    • Run whole transaction which is contested with prover submit any required state.

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?

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

  1. State Commitment Chain (SCC)
  2. Canonical Transaction Chain (CTC)

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.”

optimism-fraud-proof-regular
Right Arrow
optimism-fraud-proof-contested

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

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.

Merkle Proofs Meme

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

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.number behaves 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

Those are the current Optimism networks. You can use the wallet_addEthereumChain functionality from supported wallets like MetaMask or otherwise users will manually need to add the network.

whitelisted https://community.optimism.io/faqs/#mainnet-contract-deployments-are-restricted-by-a-whitelist

For now contract deployments to the mainnet are restricted by a whitelist, but you can fill out a signup form to deploy there. The Kovan testnet is fully accessible.

To get funds use the bridge available at https://gateway.optimism.io/.

const params = [{
  "chainId": "10", // testnet: "69"
  "chainName": "Optimistic Ethereum",
  "rpcUrls": [
    "https://mainnet.optimism.io"
    // testnet: "https://kovan.optimism.io"
  ],
  "nativeCurrency": {
    "name": "Ether",
    "symbol": "ETH",
    "decimals": 18
  },
  "blockExplorerUrls": [
    "https://optimistic.etherscan.io"
   // testnet: "https://kovan-optimistic.etherscan.io"
  ]
}]

try {
    await ethereum.request({
        method: 'wallet_addEthereumChain',
        params,
    })
} catch (error) {
    // something failed, e.g., user denied request
}

How to deploy to the Optimism networks

{
    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
          );
        },
    }
}

First install the Optimism compiler: 

npm i @eth-optimism/solc

Now you can add the Optimistic Kovan into Truffle as shown left, make sure the gasPrice is 15 million and play around with the gas limit where 1.65 million is the minimum. Then add the Optimistic VM compiler as shown below:

{
  compilers: {
    solc: {
      version: "node_modules/@eth-optimism/solc",
      settings: { optimizer: { enabled: true, runs: 800 },
    },
  },
  db: { enabled: false },
}

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.

Infura Optimism

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

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.

2. Synthetix (SNX)

Yesterday also SNX launched on Optimism. You can trade synthetics like sUSD on Optimism in the app.


Markus Waas

Solidity Developer

More great blog posts from Markus Waas

  • MultiTrade

    MultiSwap: How to arbitrage with Solidity

    Making multiple swaps across different decentralized exchanges in a single transaction

    If you want maximum arbitrage performance, you need to swap tokens between exchanges in a single transaction. Or maybe you just want to save gas on certain swaps you perform regularly. Or maybe you have your own custom use case for swapping between decentralized exchanges. And of course maybe you...

  • Aurora NEAR Protocol

    Ultimate Performance: The Aurora Layer2 Network

    Deploying and onboarding users to the Aurora Network powered by NEAR Protocol

    We've covered several Layer 2 sidechains before: Polygon xDAI Binance Smart Chain But today might be the fastest of them all. On top it's tightly connected to the NEAR protocol ecosystem, a PoS chain with a scalable sharding design. And of course they have a bridge to Ethereum! What is the Aurora...

  • ecrecover

    What is ecrecover in Solidity?

    A dive into the waters of signatures for smart contracts

    Ever wondered what the hell the deal is with the ecrecover command in Solidity? It's all about signatures and keys... What is ecrecover ? You may have seen ecrecover in a Solidity contract before and wondered what exactly the deal with this was. Well you came across the EVM precompile ecrecover ....