# wstETH on Optimism: deployment artifacts and log ## Cross-chain governance executor 1) For the governance representation, the token and the bridge admin has been set to the Lido DAO agent contract as a final result of deployment The governance executor has been deployed from the following repo: https://github.com/lidofinance/governance-crosschain-bridges/tree/master, the contracts correspond to `master` branch. 2) Deployment instructions: https://github.com/lidofinance/governance-crosschain-bridges#deploying-the-optimismbridgeexecutor, deployment script: https://github.com/lidofinance/governance-crosschain-bridges/blob/master/deploy/gov-bridge-optimism.ts ## Cross-chain governance executor actions log on OP ### Mainnet Deployment (01.08.22) - The expected final audit report commit: https://github.com/aave/governance-crosschain-bridges/commit/8fa25b0080dd3dcc2390313631aea6796a12c9d8 - [x] The commit to deploy from: https://github.com/lidofinance/governance-crosschain-bridges/commit/a47c99b53175c3309165cafdd35c654e6c4e519d - [x] Check the diff between audited and deploy commits: https://github.com/aave/governance-crosschain-bridges/compare/8fa25b...lidofinance:governance-crosschain-bridges:a47c99 - [x] The deployment configuration: https://github.com/lidofinance/governance-crosschain-bridges/blob/a47c99b53175c3309165cafdd35c654e6c4e519d/helpers/gov-constants.ts - [x] Run the deploy script: ```bash npm run deploy:optimism:mainnet ``` - [x] Deploy tx: https://optimistic.etherscan.io/tx/0xb2cac3cea80817ddf1e5ae042a0a3cd6e017e30e663f857ae1be6f171a39e49b - [x] Deployed contract: https://optimistic.etherscan.io/address/0xefa0db536d2c8089685630fafe88cf7805966fc3 - [x] Verify deployed contracts on Etherscan - [x] Validate that contract was deployed correctly: - `getEthereumGovernanceExecutor()` == [0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c](https://etherscan.io/address/0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c) (Lido DAO Agent) - `getDelay() == 0` - `getGracePeriod() == 86400` - `getMinimumDelay() == 0` - `getMaximumDelay() == 1` - `getGuardian() == 0x0000000000000000000000000000000000000000` - `getActionsSetCount() == 0` - [x] Validate the bytecode of the deployed contract - [x] Set the value for `OPT_CONTRACT_ADDRESS` variable in the `validate-deployment.ts` file - [x] Run the command to validate deployed bytecode matches with the expected one ```bash npx hardhat run scripts/validate-deployment.ts ``` ## wstETH Bridge and token GitHub repo: https://github.com/lidofinance/lido-l2, `main` branch. 1) the deployment happened on the pre-bedrock OP version The list of the deployed artifacts together with a governance executor module: https://docs.lido.fi/deployed-contracts/#optimism 2) One can use the deployment instructions described in the following sections: https://github.com/lidofinance/lido-l2#bridge-deployment (general env setup) https://github.com/lidofinance/lido-l2#deploying-optimism-bridge (Optimism case) ## wstETH Bridge deployment actions log on OP ### Stage I: wstETH bridge and token: Mainnet Deployment (03.08.22) - L1 Admin is `0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c` (Aragon Agent) - Additional L1 deposits enabler: `0x3cd9F71F80AB08ea5a7Dca348B5e94BC595f26A0` (Lido’s DEV multisig) - Additional deposits/withdrawals disabler: - L1 gateway: `0x73b047fe6337183A454c5217241D780a932777bD` (Lido’s Ethereum Emergency multi-sig) - L2 gateway: `0x4Cf8fE0A4c2539F7EFDD2047d8A5D46F14613088` (Lido’s Optimism Emergency multisig) - Deployment config: [https://github.com/lidofinance/lido-l2/blob/858da3b85b790cc815a9f31c67cb52b018a5af0e/.env.wsteth.opt_mainnet](https://github.com/lidofinance/lido-l2/blob/e77e634f64627b6656699bbb2725fcee33713e61/.env.wsteth.opt_mainnet) - Commit to deploy from: https://github.com/lidofinance/lido-l2/commit/e77e634f64627b6656699bbb2725fcee33713e61 - [x] Validate version of contracts to be deployed same as in the final [Audit report](https://github.com/lidofinance/audits/blob/main/Oxorio%20Lido%20L2%20Smart%20Contracts%20Security%20Audit%20Report%2007-2022.pdf) - [x] Create `.env` file using deployment config as a seed and fill the rest variables - [x] Run script to deploy and setup contracts on mainnet fork to validate deploy config is correct: ```bash npm run optimism:deploy ``` - [x] Set `forking` variable in the `.env` file to `false` - [x] Run script to deploy and setup contracts on mainnet: ```bash npm run optimism:deploy ``` - [x] Deployed Contract Addresses: - `L1ERC20TokenBridge` Impl : `0x29C5c51A031165CE62F964966A6399b81165EFA4` - `L1ERC20TokenBridge` Proxy: `0x76943C0D61395d8F2edF9060e1533529cAe05dE6` - `ERC20Bridged` Impl: `0x92834c37dF982A13bb0f8C3F6608E26F0546538e` - `ERC20Bridged` Proxy: `0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb` - `L2ERC20TokenBridge` Impl: `0x23B96aDD54c479C6784Dd504670B5376B808f4C7` - `L2ERC20TokenBridge` Proxy: `0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957` - [x] Verify the deployed contracts on Etherscan using the commands generated by the deploy script - [x] Run tests for deployed contracts: - [x] Set the following variables in the `.env` file ```bash TESTING_OPT_L2_TOKEN= TESTING_OPT_L1_ERC20_TOKEN_BRIDGE= TESTING_OPT_L2_ERC20_TOKEN_BRIDGE= TESTING_L1_TOKENS_HOLDER= ``` - [x] Run the forks of the Ethereum and Optimism networks: ```bash npm run fork:eth:mainnet npm run fork:opt:mainnet ``` - [x] Run acceptance test ```bash npm run optimism:test:acceptance ``` - [x] Run integration tests for deployed contracts ```bash npm run optimism:test:integration ``` - [x] Manually validate the state of the contracts: - [x] `OssifiableProxy` for `L1ERC20TokenBridge` (`0x76943C0D61395d8F2edF9060e1533529cAe05dE6`) - [x] `proxy__getAdmin` - `0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c` - [x] `proxy__getImplementation` - `0x29C5c51A031165CE62F964966A6399b81165EFA4` - [x] `proxy__getIsOssified == false` - [x] `isDepositsEnabled == false` - [x] `isWithdrawalsEnabled == true` - [x] `isInitialized == true` - [x] `hasRole(DEFAULT_ADMIN_ROLE, 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c) == true` - [x] `hasRole(DEFAULT_ADMIN_ROLE, 0x2a61d3ba5030Ef471C74f612962c7367ECa3a62d) == false` - [x] `hasRole(DEPOSITS_ENABLER_ROLE, 0x2a61d3ba5030Ef471C74f612962c7367ECa3a62d) == false` - [x] `hasRole(DEPOSITS_ENABLER_ROLE, 0x3cd9F71F80AB08ea5a7Dca348B5e94BC595f26A0) == true` - [x] `hasRole(DEPOSITS_ENABLER_ROLE, 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c) == true` - [x] `hasRole(DEPOSITS_DISABLER_ROLE, 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c) == true` - [x] `hasRole(DEPOSITS_DISABLER_ROLE, 0x2a61d3ba5030Ef471C74f612962c7367ECa3a62d) == false` - [x] `hasRole(DEPOSITS_DISABLER_ROLE, 0x73b047fe6337183A454c5217241D780a932777bD) == true` - [x] `hasRole(WITHDRAWALS_ENABLER_ROLE, 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c) == true` - [x] `hasRole(WITHDRAWALS_ENABLER_ROLE, 0x2a61d3ba5030Ef471C74f612962c7367ECa3a62d) == false` - [x] `hasRole(WITHDRAWALS_DISABLER_ROLE, 0x2a61d3ba5030Ef471C74f612962c7367ECa3a62d) == false` - [x] `hasRole(WITHDRAWALS_DISABLER_ROLE, 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c) == true` - [x] `hasRole(WITHDRAWALS_DISABLER_ROLE, 0x73b047fe6337183A454c5217241D780a932777bD) == true` - [x] `L1ERC20TokenBridge` implementation (`0x29C5c51A031165CE62F964966A6399b81165EFA4`) - [x] `l1Token == 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0` - [x] `l2Token` - `0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb` - [x] `l2TokenBridge` - `0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957` - [x] `messenger == 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1` - [x] `OssifiableProxy` for `L2ERC20TokenBridge` (`0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957`) - [x] `proxy__getAdmin` - `0xEfa0dB536d2c8089685630fafe88CF7805966FC3` (Lido’s Optimism Governance Bridge) - [x] `proxy__getImplementation` - `0x23B96aDD54c479C6784Dd504670B5376B808f4C7` - [x] `proxy__getIsOssified == false` - [x] `isDepositsEnabled == true` - [x] `isWithdrawalsEnabled == true` - [x] `isInitialized == true` - [x] `hasRole(DEFAULT_ADMIN_ROLE, 0xEfa0dB536d2c8089685630fafe88CF7805966FC3) == true` - [x] `hasRole(DEFAULT_ADMIN_ROLE, 0x2C01F405e3C525C0cB867789AAf077f3553ccbB7) == false` - [x] `hasRole(DEPOSITS_ENABLER_ROLE, 0x2C01F405e3C525C0cB867789AAf077f3553ccbB7) == false` - [x] `hasRole(DEPOSITS_ENABLER_ROLE, 0xEfa0dB536d2c8089685630fafe88CF7805966FC3) == true` - [x] `hasRole(DEPOSITS_DISABLER_ROLE, 0xEfa0dB536d2c8089685630fafe88CF7805966FC3) == true` - [x] `hasRole(DEPOSITS_DISABLER_ROLE, 0x4Cf8fE0A4c2539F7EFDD2047d8A5D46F14613088) == true` - [x] `hasRole(DEPOSITS_DISABLER_ROLE, 0x2C01F405e3C525C0cB867789AAf077f3553ccbB7) == false` - [x] `hasRole(WITHDRAWALS_ENABLER_ROLE, 0x2C01F405e3C525C0cB867789AAf077f3553ccbB7) == false` - [x] `hasRole(WITHDRAWALS_ENABLER_ROLE, 0xEfa0dB536d2c8089685630fafe88CF7805966FC3) == true` - [x] `hasRole(WITHDRAWALS_DISABLER_ROLE, 0xEfa0dB536d2c8089685630fafe88CF7805966FC3) == true` - [x] `hasRole(WITHDRAWALS_DISABLER_ROLE, 0x4Cf8fE0A4c2539F7EFDD2047d8A5D46F14613088) == true` - [x] `hasRole(WITHDRAWALS_DISABLER_ROLE, 0x2C01F405e3C525C0cB867789AAf077f3553ccbB7) == false` - [x] `L2ERC20TokenBridge` implementation (`0x23B96aDD54c479C6784Dd504670B5376B808f4C7`) - [x] `l1Token == 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0` - [x] `l2Token` - `0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb` - [x] `l1TokenBridge` - `0x76943C0D61395d8F2edF9060e1533529cAe05dE6` - [x] `messenger == 0x4200000000000000000000000000000000000007` - [x] `OssifiableProxy` for `L2ERC20Bridged` (`0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb`) - [x] `proxy__getAdmin == 0xEfa0dB536d2c8089685630fafe88CF7805966FC3` (Lido’s AAVE Governance Bridge) - [x] `proxy__getImplementation == 0x92834c37dF982A13bb0f8C3F6608E26F0546538e` - [x] `proxy__getIsOssified == false` - [x] `name == Wrapped liquid staked Ether 2.0` - [x] `symbol == wstETH` - [x] `totalSupply == 0` - [x] `L2ERC20Bridged` (`0x92834c37dF982A13bb0f8C3F6608E26F0546538e`) - [x] `decimals == 18` - [x] `bridge == 0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957` - [x] Validate vendor contracts versions are correct - [x] `L1ERC20TokenBridge` Impl : Prev: `0x7E1dBD017973871abcFac9e4B830018812056c17` Current: `0x29C5c51A031165CE62F964966A6399b81165EFA4` - [x] `L2ERC20TokenBridge` Impl: - Prev: `0x69d02FedAE5E289F467280bC25316D2E835C5864` - Current: `0x23B96aDD54c479C6784Dd504670B5376B808f4C7` - [x] `ERC20Bridged` Impl: - Prev: `0x75ff3dd673Ef9fC459A52E1054db5dF2A1101212` - Current: `0x92834c37dF982A13bb0f8C3F6608E26F0546538e` - [x] `ERC20Bridged` Proxy: - Prev `0xbADcCcf4156967783fA818Bc4a8c93b5575A07B0` - Current: `0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb` - [x] `L1ERC20TokenBridge` Proxy: - Prev `0x89057C7E4c5cD283afF5907B816F61e326047C29` - Current `0x76943C0D61395d8F2edF9060e1533529cAe05dE6` - [x] `L2ERC20TokenBridge` Proxy: - Prev `0x0fc3De4B1bbcB315880d328e3f9C81d742D73d01` - Current `0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957` - [x] Initialize Bridge implementations and check that ERC20Bridged impl is initialized #### After Deployment Actions - [x] Save addresses to setup monitoring on mainnet contracts #### Adding deployed wstETH to Optimism Mainnet UI - [x] Open the PR for Mainnet into the https://github.com/ethereum-optimism/ethereum-optimism.github.io for wstETH - Logo: https://github.com/lidofinance/logos/blob/main/wstETH/wstETH.svg - Name: `Wrapped liquid staked Ether 2.0` - Symbol: `wstETH` - Decimals: `18` - L1 Optimism Bridge Address: `0x76943C0D61395d8F2edF9060e1533529cAe05dE6` - L2 Optimism Bridge Address: `0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957` - [x] Validate the opened PR - [x] Notify the Optimism team that contracts were deployed and PR is opened (**AWARE TO NOT MERGE BEFORE LAUNCH DATE**) - [x] Ask the Optimism team when on the merge timing #### If Something Goes Wrong - If the deploy script failed because of environmental reasons (RPC failure or tx frozen up in the mem pool) try to continue it from the failed step or relaunch the whole deploy script - If the deploy script failed because of the error, fix it and relaunch - if on etherscan are submitted excessive contracts - redeploy the contracts and verify them manually using a flattened contract (low-risk issue) - If deployment validation failed because of the wrong values, fix the params and relaunch the script ### Stage II: Mainnet Deployment Validation (09.08.22) - [x] Run forked nodes ```bash npm run fork:eth:mainnet npm run fork:opt:mainnet ``` - [x] Set `.env` variables ```bash TESTING_OPT_NETWORK=mainnet TESTING_OPT_L1_TOKEN=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0 TESTING_OPT_L2_TOKEN=0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb TESTING_OPT_L1_ERC20_TOKEN_BRIDGE=0x76943C0D61395d8F2edF9060e1533529cAe05dE6 TESTING_OPT_L2_ERC20_TOKEN_BRIDGE=0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957 TESTING_OPT_GOV_BRIDGE_EXECUTOR=0xefa0db536d2c8089685630fafe88cf7805966fc3 TESTING_USE_DEPLOYED_CONTRACTS=true L1_DEV_MULTISIG=0x3cd9F71F80AB08ea5a7Dca348B5e94BC595f26A0 TESTING_L1_TOKENS_HOLDER=0x23237b849d0e6f392573ee6ae9e3b1256f058143 ``` - [x] Run executor integration tests ```bash npm run optimism:test:executor ``` - [x] Run launch test ```bash npm run optimism:test:launch ``` - [x] Validate there are no unstructured storage slot collisions: - `OssifiableProxy` implementation slot: - `keccak256("eip1967.proxy.implementation") - 1` - `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` - `OssifiableProxy` admin slot: - `keccak256("eip1967.proxy.admin") - 1` - `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` - `BridgingManager` state slot: - `keccak256("BridgingManager.bridgingState")` - `0x013e929b381f2fbbac854bd18fb8231dc73c4a2eab0d4cbb4db9436b6ff9b2ba` - `ERC20Metadata` state slot: - `keccak256("ERC20Metdata.dynamicMetadata")` - `0x3470f8373d566de7ab61e14a030ae865a1f164b610b931eb8aa08ad044e2e68e` - [x] Validate our own code: - [x] `L1ERC20TokenBridge` Impl : `0x29C5c51A031165CE62F964966A6399b81165EFA4` - [x] `L1ERC20TokenBridge.sol` - [x] `IL1ERC20Bridge.sol` - [x] `IL2ERC20Bridge.sol` - [x] `BridgingManager.sol` - [x] `BridgeableTokens.sol` - [x] `CrossDomainEnabled.sol` - [x] `ICrossDomainMessenger.sol` - [x] `L2ERC20TokenBridge` Impl: `0x23B96aDD54c479C6784Dd504670B5376B808f4C7` - [x] `L2ERC20TokenBridge.sol` - [x] `IL1ERC20Bridge.sol` - [x] `IL2ERC20Bridge.sol` - [x] `IERC20Bridged.sol` - [x] `BridgingManager.sol` - [x] `BridgeableTokens.sol` - [x] `CrossDomainEnabled.sol` - [x] `ICrossDomainMessenger.sol` - [x] `ERC20Bridged` Impl: `0x92834c37dF982A13bb0f8C3F6608E26F0546538e` - [x] `ERC20Bridged.sol` - [x] `IERC20Bridged.sol` - [x] `ERC20Core.sol` - [x] `ERC20Metadata.sol` - [x] `IERC20Metadata.sol` - [x] `ERC20Bridged` Proxy: `0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb` - [x] `OssifiableProxy.sol` - [x] `L1ERC20TokenBridge` Proxy: `0x76943C0D61395d8F2edF9060e1533529cAe05dE6` - [x] `OssifiableProxy.sol` - [x] `L2ERC20TokenBridge` Proxy: `0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957` - [x] `OssifiableProxy.sol` ### Stage III: Mainnet Bridge Launch (04.08.22-08.08.22) - [x] Run launch scenario test - [x] Activate deposits on L1 via dev multisig - address: `0x76943C0D61395d8F2edF9060e1533529cAe05dE6` (`L1ERC20TokenBridge` Proxy) - method: `enableDeposits()` - [x] Renounce role `DEPOSITS_ENABLER_ROLE` from the dev multisig: - address: `0x76943C0D61395d8F2edF9060e1533529cAe05dE6` (`L1ERC20TokenBridge` Proxy) - method: `renounceRole(bytes32 role, address account)` - `role` - `0x4b43b36766bde12c5e9cbbc37d15f8d1f769f08f54720ab370faeb4ce893753a` - `address` - `0x3cd9F71F80AB08ea5a7Dca348B5e94BC595f26A0` - [x] Validate state of the contract on Etherescan - isDepositsEnabled - true - hasRole() - false - [x] Re-run launch scenario test - [x] Run the e2e test for optimism bridging after activation of deposits with addresses of deployed contracts: - [x] Set the following variables in the `.env` file ```bash TESTING_OPT_NETWORK=mainnet TESTING_OPT_L1_TOKEN=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0 TESTING_OPT_L2_TOKEN=0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb TESTING_OPT_L1_ERC20_TOKEN_BRIDGE=0x76943C0D61395d8F2edF9060e1533529cAe05dE6 TESTING_OPT_L2_ERC20_TOKEN_BRIDGE=0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957 TESTING_PRIVATE_KEY= ``` - [x] Run e2e tests for deployed contracts ```bash npx hardhat test ./test/optimism/bridging.e2e.test.ts ``` - [x] Reach out to Optimism team that bridging was enabled and PR might be merged - [x] Test the wstETH bridging via Optimism UI to test that bridging is working - [x] Deposit tx: https://etherscan.io/tx/0x8666e8254022e391804c06f940b72cab8472f65f0be94cfc45088a7d60be9e49 - [x] Relay deposit tx message on L2: https://optimistic.etherscan.io/tx/0xacbdde514c9deec46719ba0b8c77214a9c6b5ada7b2873ad406baccdb6773b28 - [x] Withdrawal tx: https://optimistic.etherscan.io/tx/0x76a6dc65ae01ed54a004359d1b67a0613907707a2fa95db72da5c1aa2677059e - [x] Relay withdrawal tx message on L1: <> (~ **20 Sep** Ready to finalize) - [x] Notify dev teams that bridge is enabled #### If Something Goes Wrong - If the test failed - figure out the reason of the failure, and fix it if possible on deployed setup. If it’s not possible, reach out Optimism team, and a biz dev team that the launch must be postponed. Fix the problem and schedule a new release date. --- ## Notes and remarks - Mind some changes should be made in scripts for other OP-stack compatible networks (e.g., `chainId` retrieving: https://github.com/lidofinance/lido-l2/blob/471993c23f3f8c378b9ef4598e703381e9462189/utils/network.ts#L118) - `LDO` is bridged as a default token with the default OP bridge - `stETH` can't be bridged from the get-go because of rebasable mechanics that doesn't propagate cross-bridges (the Lido on Ethereum core protocol doesn't support this yet)