Here’s how you can reproduce OP Mainnet’s migration to Bedrock

On June 6, 2023, OP Mainnet upgraded to Bedrock, and the entire migration process was designed to be verifiable and reproducible.

Here’s how you can reproduce OP Mainnet’s migration to Bedrock

On June 6, 2023, OP Mainnet upgraded to Bedrock. This was no small feat, requiring contributions from community members across the Collective. The successful Bedrock migration marked the start a new era for Optimism, one in which we can accelerate our progress towards technical decentralization and achieving the Superchain vision.

There is a lot to celebrate related to the migration, but there is one detail in particular that the team at OP Labs is proud to share, and that is that the entire migration process was designed to be verifiable and reproducible!

This blog post recaps the work OP Labs engineers did to prepare for migration day, and shares how to run the migration script and confirm that the upgrade proceeded as expected.

Planning & rehearsing the migration

The migration was achieved using a well-documented and audited open-source migration tool built by the team at OP Labs. This tool took the legacy network's database and converted it into a format compatible with the Bedrock network.

Practically, the upgrade involved migrating ERC-20s Wrapped Ether to native Ether and configuring pre-deploys, which function similarly to Ethereum's precompiles and are used for special operations like bridging or minting.

The team rehearsed for weeks, tracking what needed to happen at each step of the process in granular detail. Each item on the migration checklist was meticulously specified so that execution required minimal effort. The goal was to make the process as “boring” as possible as to prevent operator errors that might arise from having to think too much about the process in the moment. Each rehearsal network operated on a shadow fork, similar to how the L1 Ethereum developers practiced for the merge upgrade.

The migration process went through a Sherlock audit where issues were found and fixed. One example of an issue uncovered through the auditing process was a bug that could allow a user to halt the withdrawal partway through by writing malicious state to storage that is used as input to the migration tool. Before the migration, the team combed through the code looking for any additional issues, no matter how minor. During migration day the team’s diligence prevailed, as the upgrade went smoothly and the sequencer was back up within 3 hours.

Designing a verifiable migration

The procedure required certain input files, which were obtained by syncing a legacy node. After syncing, the legacy node produced the required witness files, and the migration tool utilized these to verify the full migration's smooth execution. Given this setup, anyone with the ability to sync a legacy node can use the migration script to verify the migration took place as we have claimed it did!

We designed the migration output to be verifiable, for an added layer of transparency. The output generates a unique state root corresponding to a successful migration. Any discrepancy or alteration in the information during migration would lead to the production of an incorrect state root. Therefore, the state root serves as a verifiable testament to the fidelity of the migration process.

How to run the migration

The necessary code for the migration is available in the optimism-legacy repository. We kept it separate from the OP Mainnet repository to simplify updates to the main codebase.

In order to run the migration script, you first need a special 'witness' file, which can be generated by synchronizing with a legacy l2geth node. The environment variable L2GETH_STATE_DUMP_PATH needs to be set for this.

Once you have a fully synchronized legacy system, you can run the following command from the op-chain-ops package. Make sure to review all the arguments and environment variables carefully, as they may need adjustment based on your setup.

INPUT_DATA=$MONOREPO_BASE/packages/migration-data/data DEPLOY_CONFIG=$MONOREPO_BASE/packages/contracts-bedrock/deploy-config/mainnet.json DEPLOYMENTS=$MONOREPO_BASE/packages/contracts/deployments,$MONOREPO_BASE/packages/contracts-periphery/deployments,$MONOREPO_BASE/packages/contracts-bedrock/deployments DB_PATH=/mnt/geth

go run cmd/op-migrate/main.go \
--l1-rpc-url "$L1_RPC" \
--ovm-addresses $INPUT_DATA/ovm-addresses.json \
--ovm-allowances $INPUT_DATA/ovm-allowances.json \
--ovm-messages $INPUT_DATA/ovm-messages.json \
--witness-file $DB_PATH/l2geth-state \
--db-path $DB_PATH \\
--deploy-config $DEPLOY_CONFIG \
--network mainnet \
--hardhat-deployments $DEPLOYMENTS \

The witness file includes information on all accounts that held ether so that it can be transitioned from its ERC20 representation to the native format. It also has information on accounts that initiated withdrawals to ensure that these transactions can transition seamlessly to the new withdrawal system. This way, users won't have to restart their withdrawal after the network upgrade.

If you're unable to fully synchronize a legacy system, you should still be able to download the database at the block either just before or just after the migration using the following command:


You'll know that the migration has been successfully reproduced if you can spin up op-geth on the migrated database and sync to the latest point of the network.

💡 For a more user-friendly experience in syncing, we've made some improvements via this PR.

Explore the Bedrock release of the OP Stack

The upgrade to Bedrock has transformed the OP Stack into a more flexible and modular system. OP Mainnet can now support multiple clients and multiple proof schemes, and data costs have been significantly reduced. Bedrock has also laid the foundation for the Superchain vision. It prepares us for a future where multiple chains merge, enabling us to accommodate unprecedented growth in a harmonious blockchain ecosystem.

If you want to contribute to this vision, get started by seeing what is made possible by the OP Stack.