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:
+
+
+
+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;
+ }
+}