diff --git a/mainnet/2024-04-23-migrate-to-security-council/.env b/mainnet/2024-04-23-migrate-to-security-council/.env new file mode 100644 index 00000000..24241a2c --- /dev/null +++ b/mainnet/2024-04-23-migrate-to-security-council/.env @@ -0,0 +1,9 @@ +OP_COMMIT=08f3dbed90faccb36135fc4bd1d40bfa8ed4066f +BASE_CONTRACTS_COMMIT=a0f86fcf67e61f08ba7d7a8b26256963379b8a60 + +# L1 configuration +L1_PROXY_ADMIN=0x0475cBCAebd9CE8AfA5025828d5b98DFb67E059E +L1_NESTED_SAFE=0x7bB41C3008B3f03FE483B28b8DB90e19Cf07595c +L1_BASE_SAFE=0x9855054731540A48b28990B63DcF4f33d8AE46A1 +L1_OP_SAFE=0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A +L1_DELAYED_VETOABLE=0x000000000000000000000000000000000000cafe diff --git a/mainnet/2024-04-23-migrate-to-security-council/Makefile b/mainnet/2024-04-23-migrate-to-security-council/Makefile new file mode 100644 index 00000000..db7e322d --- /dev/null +++ b/mainnet/2024-04-23-migrate-to-security-council/Makefile @@ -0,0 +1,37 @@ +include ../../Makefile +include ../.env +include .env + +ifndef LEDGER_ACCOUNT +override LEDGER_ACCOUNT = 0 +endif + +.PHONY: sign-cb-l1 +sign-cb-l1: + $(GOPATH)/bin/eip712sign --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0" -- \ + forge script --rpc-url $(L1_RPC_URL) MigrateL1ToSecurityCouncil \ + --sig "sign(address)" $(L1_BASE_SAFE) + +.PHONY: sign-op-l1 +sign-op-l1: + $(GOPATH)/bin/eip712sign --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0" -- \ + forge script --rpc-url $(L1_RPC_URL) MigrateL1ToSecurityCouncil \ + --sig "sign(address)" $(L1_OP_SAFE) + + +.PHONY: approve-cb-l1 +approve-cb-l1: + forge script --rpc-url $(L1_RPC_URL) MigrateL1ToSecurityCouncil \ + --sig "approve(address,bytes)" $(L1_BASE_SAFE) $(SIGNATURES) \ + --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0" + +.PHONY: approve-op-l1 +approve-op-l1: + forge script --rpc-url $(L1_RPC_URL) MigrateL1ToSecurityCouncil \ + --sig "approve(address,bytes)" $(L1_OP_SAFE) $(SIGNATURES) \ + --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0" + +.PHONY: execute-l1 +execute-l1: + forge script --rpc-url $(L1_RPC_URL) MigrateL1ToSecurityCouncil \ + --sig "run()" --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0" diff --git a/mainnet/2024-04-23-migrate-to-security-council/README.md b/mainnet/2024-04-23-migrate-to-security-council/README.md new file mode 100644 index 00000000..6d3dd6e4 --- /dev/null +++ b/mainnet/2024-04-23-migrate-to-security-council/README.md @@ -0,0 +1,195 @@ +# Migrate to Security Council on L1 + +Status: READY TO SIGN + +## Objective + +This task performs the migration from the 2/2 Nested Safe to the Security Council on Ethereum mainnet by udpating the ProxyAdmin [owner](https://etherscan.io/address/0x0475cBCAebd9CE8AfA5025828d5b98DFb67E059E#readContract#F6) from the [Nested Safe](https://etherscan.io/address/0x7bB41C3008B3f03FE483B28b8DB90e19Cf07595c#code) to the [DeleayedVetoable](TBD). + +## Approving the transaction + +### 1. Update repo and move to the appropriate folder: + +``` +cd contract-deployments +git pull +cd mainnet/2024-04-23-migrate-to-security-council +make deps +``` + +### 2. Setup Ledger + +Your Ledger needs to be connected and unlocked. The Ethereum +application needs to be opened on Ledger with the message "Application +is ready". + +### 3. Simulate and validate the transaction + +Make sure your ledger is still unlocked and run the following. + + +``` shell +make sign-op-l1 # or make sign-cb-l1 for Coinbase signers +``` + +Once you run the make sign command successfully, you will see a "Simulation link" from the output. + +Paste this URL in your browser. A prompt may ask you to choose a +project, any project will do. You can create one if necessary. + +Click "Simulate Transaction". + +We will be performing 3 validations and then we'll extract the domain hash and +message hash to approve on your Ledger then verify completion: + +1. Validate integrity of the simulation. +2. Validate correctness of the state diff. +3. Validate and extract domain hash and message hash to approve. +4. Validate that the transaction completed successfully + + +#### 3.1. Validate integrity of the simulation. + +Make sure you are on the "Overview" tab of the tenderly simulation, to +validate integrity of the simulation, we need to check the following: + +1. "Network": Check the network is Ethereum Mainnet. +2. "Timestamp": Check the simulation is performed on a block with a + recent timestamp (i.e. close to when you run the script). +3. "Sender": Check the address shown is your signer account. If not, + you will need to determine which “number” it is in the list of + addresses on your ledger. +4. "Success" with a green check mark + + +#### 3.2. Validate correctness of the state diff. + +Now click on the "State" tab. Verify that: + +1. Verify that the nonce is incremented for the Nested Multisig under the "GnosisSafeProxy" at address `0x7bb41c3008b3f03fe483b28b8db90e19cf07595c`: + +``` +Key: 0x0000000000000000000000000000000000000000000000000000000000000005 +Before: 0x0000000000000000000000000000000000000000000000000000000000000001 +After: 0x0000000000000000000000000000000000000000000000000000000000000002 +``` + +2. And for the same contract, verify that this specific execution is approved: + +``` +Key (if you are an OP signer): 0xe90bab20817c4baefb196a6db6043a5e4ab858154ae4c68fac9560335412107f +Key (if you are a CB signer): 0x63f218de6984b9cd553109ae3cb8e4da02ad64220d1bae968ff0833a72234d4d +Before: 0x0000000000000000000000000000000000000000000000000000000000000000 +After: 0x0000000000000000000000000000000000000000000000000000000000000001 +``` + +3. Verify that the nonce is incremented for your multisig: + +If you are an OP signer - the OP Foundation Multisig should be under the "GnosisSafeProxy" at address `0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A`: + +``` +Key: 0x0000000000000000000000000000000000000000000000000000000000000005 +Before: 0x000000000000000000000000000000000000000000000000000000000000005c +After: 0x000000000000000000000000000000000000000000000000000000000000005d +``` + +If you are a CB signer - the Coinbase Multisig should be under the address `0x9855054731540a48b28990b63dcf4f33d8ae46a1`: + +``` +Key: 0x0000000000000000000000000000000000000000000000000000000000000005 +Before: 0x000000000000000000000000000000000000000000000000000000000000000b +After: 0x000000000000000000000000000000000000000000000000000000000000000c +``` + +4. Verify that the ProxyAdmin contract at address `0x0475cbcaebd9ce8afa5025828d5b98dfb67e059e` has its owner updated to the `DelayedVetoable` contract at address `0x000000000000000000000000000000000000cafe`: + +``` +Key: 0x0000000000000000000000000000000000000000000000000000000000000000 +Before: 0x0000000000000000000000007bb41c3008b3f03fe483b28b8db90e19cf07595c +After: 0x000000000000000000000000000000000000000000000000000000000000cafe +``` + +5. Verify that the nonce for the sending address is appropriately incremented: + +``` +Nonce 0 -> 1 +``` + +#### 3.3. Extract the domain hash and the message hash to approve. + +Now that we have verified the transaction performs the right +operation, we need to extract the domain hash and the message hash to +approve. + +Go back to the "Overview" tab, and find the +`GnosisSafeL2.checkSignatures` call. This call's `data` parameter +contains both the domain hash and the message hash that will show up +in your Ledger. + +Here is an example screenshot. Note that the hash value may be +different: + +image + +It will be a concatenation of `0x1901`, the domain hash, and the +message hash: `0x1901[domain hash][message hash]`. + +Note down this value. You will need to compare it with the ones +displayed on the Ledger screen at signing. + +### 4. Approve the signature on your ledger + +Once the validations are done, it's time to actually sign the +transaction. Make sure your ledger is still unlocked and run the +following: + +``` shell +make sign-op-l1 # or make sign-cb-l1 for Coinbase signers +``` + +> [!IMPORTANT] This is the most security critical part of the +> playbook: make sure the domain hash and message hash in the +> following two places match: + +1. on your Ledger screen. +2. in the Tenderly simulation. You should use the same Tenderly + simulation as the one you used to verify the state diffs, instead + of opening the new one printed in the console. + +There is no need to verify anything printed in the console. There is +no need to open the new Tenderly simulation link either. + +After verification, sign the transaction. You will see the `Data`, +`Signer` and `Signature` printed in the console. Format should be +something like this: + +``` +Data: +Signer:
+Signature: +``` + +Double check the signer address is the right one. + +### 5. Send the output to Facilitator(s) + +Nothing has occurred onchain - these are offchain signatures which +will be collected by Facilitators for execution. Execution can occur +by anyone once a threshold of signatures are collected, so a +Facilitator will do the final execution for convenience. + +Share the `Data`, `Signer` and `Signature` with the Facilitator, and +congrats, you are done! + +## [For Facilitator ONLY] How to execute the rehearsal + +### [After the rehearsal] Execute the output + +1. Collect outputs from all participating signers. +2. Concatenate all signatures and export it as the `SIGNATURES` + environment variable, i.e. `export + SIGNATURES="0x[SIGNATURE1][SIGNATURE2]..."`. +3. Run `make approve-cb-l1` with Coinbase signer signatures. +4. Run `make approve-op-l1` with Optimism signer signatures. +4. Run `make execute-l1` to execute the transaction onchain. + diff --git a/mainnet/2024-04-23-migrate-to-security-council/foundry.toml b/mainnet/2024-04-23-migrate-to-security-council/foundry.toml new file mode 100644 index 00000000..991457c3 --- /dev/null +++ b/mainnet/2024-04-23-migrate-to-security-council/foundry.toml @@ -0,0 +1,19 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +broadcast = 'records' +fs_permissions = [{ access = "read-write", path = "./" }] +optimizer = true +optimizer_runs = 999999 +solc_version = "0.8.15" +via-ir = true +remappings = [ + '@eth-optimism-bedrock/=lib/optimism/packages/contracts-bedrock/', + '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts', + '@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts', + '@base-contracts/=lib/base-contracts', + 'solady/=lib/solady/src/', +] + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/mainnet/2024-04-23-migrate-to-security-council/script/MigrateL1ToSecurityCouncil.sol b/mainnet/2024-04-23-migrate-to-security-council/script/MigrateL1ToSecurityCouncil.sol new file mode 100644 index 00000000..62b4e734 --- /dev/null +++ b/mainnet/2024-04-23-migrate-to-security-council/script/MigrateL1ToSecurityCouncil.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import "@base-contracts/script/universal/NestedMultisigBuilder.sol"; + +interface IProxyAdmin { + function owner() external returns (address); + function transferOwnership(address newOwner) external; +} + +contract MigrateL1ToSecurityCouncil is NestedMultisigBuilder { + address proxyAdmin = vm.envAddress("L1_PROXY_ADMIN"); + address delayedVetoable = vm.envAddress("L1_DELAYED_VETOABLE"); + address nestedSafe = vm.envAddress("L1_NESTED_SAFE"); + + function _postCheck(Vm.AccountAccess[] memory, SimulationPayload memory) internal override { + address newOwner = IProxyAdmin(proxyAdmin).owner(); + if (newOwner != delayedVetoable) { + revert("New owner not correctly set"); + } + } + + function _buildCalls() internal view override returns (IMulticall3.Call3[] memory) { + IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](1); + calls[0] = IMulticall3.Call3({ + target: address(proxyAdmin), + allowFailure: false, + callData: abi.encodeCall(IProxyAdmin.transferOwnership, (delayedVetoable)) + }); + + return calls; + } + + function _ownerSafe() internal view override returns (address) { + return nestedSafe; + } +}