diff --git a/mainnet/2026-02-19-superchain-separation/.env b/mainnet/2026-02-19-superchain-separation/.env new file mode 100644 index 00000000..d2640b69 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/.env @@ -0,0 +1,130 @@ +# ============================================================================== +# Environment Configuration +# ============================================================================== +# This file contains all environment variables required for the Superchain +# separation contract deployment and upgrade scripts. +# ============================================================================== + +# ============================================================================== +# DEPENDENCY VERSIONS +# ============================================================================== +# Git commits for library dependencies checked out during `make deps` + +# Optimism monorepo commit hash (ethereum-optimism/optimism) +# Used for: SystemConfig, SuperchainConfig, and other OP Stack contracts +OP_COMMIT=018f5ae926ec3277746b56a1c4ddb715c568603d + +# Base contracts repository commit hash (base/contracts) +# Used for: MultisigScript and other base-specific utilities +BASE_CONTRACTS_COMMIT=be7c7a642e430fa64b04b63203839f8c81f48466 + +# ============================================================================== +# SAFE ADDRESSES - L1 (Ethereum) +# ============================================================================== +# Gnosis Safe multisig addresses on L1 for governance and operations + +# Main owner Safe that controls the ProxyAdmin +# Used by: DeploySuperchainConfigAndSystemConfig, UpdateProxyAdminOwnerSigners, UpgradeSystemConfig +OWNER_SAFE=0x7bB41C3008B3f03FE483B28b8DB90e19Cf07595c + +# Coinbase signer Safe (will have EOA signers after UpdateCBSafeSigners) +# Used by: UpdateCBSafeSigners (as the target Safe being updated) +CB_SIGNER_SAFE_ADDR=0x9855054731540A48b28990B63DcF4f33d8AE46A1 + +# Coinbase nested Safe (contains EOAs that will become direct signers) +# Used by: UpdateCBSafeSigners, UpgradeFeeDisburser +CB_NESTED_SAFE_ADDR=0x9C4a57Feb77e294Fd7BF5EBE9AB01CAA0a90A110 + +# Coinbase Security Council Safe +# Used by: UpdateCBSafeSigners, UpdateProxyAdminOwnerSigners, AddSecurityCouncilSigner +CB_SC_SAFE_ADDR=0x20AcF55A3DCfe07fC4cecaCFa1628F788EC8A4Dd + +# New Security Council signer to be added +# Used by: AddSecurityCouncilSigner +NEW_SC_SIGNER_ADDR= + +# Optimism Foundation signer Safe (mainnet only - to be replaced by SC) +# Used by: UpdateProxyAdminOwnerSigners +OP_SIGNER_SAFE_ADDR=0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A + +# Incident response multisig (guardian for SuperchainConfig) +# Used by: DeploySuperchainConfigAndSystemConfig +INCIDENT_MULTISIG=0x14536667Cd30e52C0b458BaACcB9faDA7046E056 + +# ============================================================================== +# SAFE ADDRESSES - OP Mainnet (for SmartEscrow operations) +# ============================================================================== +# Gnosis Safe addresses on OP Mainnet for SmartEscrow termination/withdrawal +# Note: These are only needed for mainnet SmartEscrow operations + +# Safe with TERMINATOR_ROLE on SmartEscrow contract +# Used by: TerminateSmartEscrow +CB_SAFE_ON_OP=0x6e1DFd5C1E22A4677663A81D24C6BA03561ef0f6 + +# Required signer for approving the SmartEscrow withdrawal +# Used by: WithdrawSmartEscrow +OP_SAFE_ON_OP=0x2501c477D0A35545a387Aa4A3EEe4292A9a8B3F0 + +# Safe with DEFAULT_ADMIN_ROLE on SmartEscrow contract +# Used by: WithdrawSmartEscrow +OWNER_SAFE_ON_OP=0x0a7361e734cf3f0394B0FC4a45C74E7a4Ec70940 + +# ============================================================================== +# DeployFeeDisburser.s.sol +# ============================================================================== +# Configuration for deploying the FeeDisburser contract on L2 + +# L1 address that receives bridged fees from L2 FeeVaults +# This is typically the BalanceTracker contract address +BALANCE_TRACKER=0x23B597f33f6f2621F77DA117523Dffd634cDf4ea + +# Minimum time in seconds between fee disbursements (must be >= 86400 / 24 hours) +FEE_DISBURSEMENT_INTERVAL=86400 + +# ============================================================================== +# UpgradeSystemConfig.s.sol +# ============================================================================== +# Configuration for upgrading the SystemConfig proxy + +# ProxyAdmin contract that manages the SystemConfig proxy +PROXY_ADMIN=0x0475cBCAebd9CE8AfA5025828d5b98DFb67E059E + +# Current SystemConfig proxy address to be upgraded +SYSTEM_CONFIG=0x73a79Fab69143498Ed3712e519A88a918e1f4072 + +# New SystemConfig implementation address (deployed by DeploySuperchainConfigAndSystemConfig) +SYSTEM_CONFIG_IMPLEMENTATION= + +# New SuperchainConfig proxy address (deployed by DeploySuperchainConfigAndSystemConfig) +# This replaces the OP Superchain's SuperchainConfig with Base's own +NEW_SUPERCHAIN_CONFIG= + +# ============================================================================== +# UpgradeFeeDisburser.s.sol +# ============================================================================== +# Configuration for upgrading the FeeDisburser proxy on L2 via deposit transaction + +# OptimismPortal2 contract address on L1 +OPTIMISM_PORTAL_ADDR=0x49048044D57e1C92A77f79988d21Fa8fAF74E97e + +# FeeDisburser proxy contract address on L2 +FEE_DISBURSER_ADDR=0x09C7bAD99688a55a2e83644BFAed09e62bDcCcBA + +# New FeeDisburser implementation address on L2 +FEE_DISBURSER_IMPL_ADDR= + +# ============================================================================== +# TerminateSmartEscrow.s.sol / WithdrawSmartEscrow.s.sol (Mainnet only) +# ============================================================================== +# Configuration for SmartEscrow termination and withdrawal on OP Mainnet + +# SmartEscrow contract address on OP Mainnet +SMART_ESCROW=0xb3C2f9fC2727078EC3A2255410e83BA5B62c5B5f + +# ============================================================================== +# MULTISIG OPERATIONS (runtime variables) +# ============================================================================== +# These variables are typically passed at runtime during signing/approval + +# Required for the task signer tool to work properly +RECORD_STATE_DIFF=true \ No newline at end of file diff --git a/mainnet/2026-02-19-superchain-separation/FACILITATORS.md b/mainnet/2026-02-19-superchain-separation/FACILITATORS.md new file mode 100644 index 00000000..9a2cf76d --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/FACILITATORS.md @@ -0,0 +1,228 @@ +# Facilitator Instructions + +This document describes how to execute the Superchain separation mainnet transactions after collecting signatures. + +## Prerequisites + +### 1. Update repo and install dependencies + +```bash +cd contract-deployments +git pull +cd mainnet/2026-02-19-superchain-separation +make deps +``` + +## General Procedure + +1. Collect outputs from all participating signers. +2. Concatenate all signatures and export as the `SIGNATURES` environment variable: + `export SIGNATURES="[SIGNATURE1][SIGNATURE2]..."` +3. Run the appropriate `make approve-*` command(s) for each nested Safe. +4. Run the appropriate `make execute-*` command to execute the transaction. + +## Important: Execution Order + +**Transactions MUST be executed in order** due to nonce dependencies: + +1. CB Nested Safe: Approve UpdateProxyAdminOwnerSigners + +```bash +make approve-proxyadminowner-signers-cb +``` + +2. SC Safe: Approve UpdateProxyAdminOwnerSigners + +```bash +make approve-proxyadminowner-signers-sc +``` + +3. CB Parent Safe: Approve UpdateProxyAdminOwnerSigners + +```bash +make approve-proxyadminowner-signers-cb-coord +``` + +4. OP Safe: Approve UpdateProxyAdminOwnerSigners + +```bash +make approve-proxyadminowner-signers-op +``` + +5. ProxyAdminOwner: Execute UpdateProxyAdminOwnerSigners + +```bash +make execute-proxyadminownersigners +``` + +6. CB Nested Safe: Approve CBSafeSigners + +```bash +make approve-cbsafesigners-cb +``` + +7. SC Safe: Approve CBSafeSigners + +```bash +make approve-cbsafesigners-sc +``` + +8. CB Parent Safe: Execute CBSafeSigners + +```bash +make execute-cbsafesigners +``` + +9. CB Parent Safe: Approve UpgradeSystemConfig + +```bash +make approve-systemconfig-cb +``` + +10. SC Safe: Approve UpgradeSystemConfig + +```bash +make approve-systemconfig-sc +``` + +11. ProxyAdminOwner: Execute UpgradeSystemConfig + +```bash +make execute-systemconfig +``` + +12. CB Nested Safe: Execute UpgradeFeeDisburser + +```bash +make execute-feedisburser +``` + +13. OP CB Safe: Execute TerminateSmartEscrow + +```bash +make execute-terminate-smartescrow +``` + +14. OP CB Safe: Approve WithdrawSmartEscrow + +```bash +make approve-withdraw-smartescrow-cb +``` + +15. OP OP Safe: Approve WithdrawSmartEscrow + +```bash +make approve-withdraw-smartescrow-op +``` + +16. OP Parent Safe: Execute WithdrawSmartEscrow + +```bash +make execute-withdraw-smartescrow +``` + +17. SC Safe: Execute AddSecurityCouncilSigner + +```bash +make execute-add-signer +``` + +### Example Signature Output + +If the quorum is 3 and you receive the following outputs: + +```shell +Data: 0xDEADBEEF +Signer: 0xC0FFEE01 +Signature: AAAA +``` + +```shell +Data: 0xDEADBEEF +Signer: 0xC0FFEE02 +Signature: BBBB +``` + +```shell +Data: 0xDEADBEEF +Signer: 0xC0FFEE03 +Signature: CCCC +``` + +Concatenate: `SIGNATURES=AAAABBBBCCCC` + +--- + +## Part 1: UpdateCBSafeSigners (CB + SC) + +Updates the signers on the Coinbase Safe. + +### Approve + +Coinbase facilitator (with CB signer signatures): + +```bash +SIGNATURES= make approve-cbsafesigners-cb +``` + +Coinbase facilitator (with SC signer signatures): + +```bash +SIGNATURES= make approve-cbsafesigners-sc +``` + +### Execute + +Once all approvals are submitted: + +```bash +make execute-cbsafesigners +``` + +--- + +## Part 2: UpgradeSystemConfig (CB + SC) + +Upgrades the SystemConfig contract with the new SuperchainConfig. + +### Approve + +Coinbase facilitator (with CB signer signatures): + +```bash +SIGNATURES= make approve-systemconfig-cb +``` + +Coinbase facilitator (with SC signer signatures): + +```bash +SIGNATURES= make approve-systemconfig-sc +``` + +### Execute + +Once all approvals are submitted: + +```bash +make execute-systemconfig +``` + +--- + +## Part 3: UpgradeFeeDisburser (CB only) + +Upgrades the FeeDisburser contract via a deposit transaction on L1. + +> **Note:** This must be executed AFTER Part 1 (execute-cbsafesigners) due to nonce dependencies on `CB_NESTED_SAFE_ADDR`. + +### Approve (if using nested safe) + +```bash +SIGNATURES= make approve-feedisburser +``` + +### Execute + +```bash +SIGNATURES= make execute-feedisburser +``` diff --git a/mainnet/2026-02-19-superchain-separation/Makefile b/mainnet/2026-02-19-superchain-separation/Makefile new file mode 100644 index 00000000..0677d94c --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/Makefile @@ -0,0 +1,243 @@ +include ../../Makefile +include ../../Multisig.mk + +include ../.env +include .env + +## +# Configuration +## +ifndef LEDGER_ACCOUNT +override LEDGER_ACCOUNT = 0 +endif + +OP_RPC_URL = https://mainnet.optimism.io +RPC_URL = $(L1_RPC_URL) + +# Comma variable for use in $(call ...) arguments +COMMA := , + +# Patch file for SystemConfig to use init version 4 +OP_CONTRACT_PATCH ?= patch/init-version.patch + +# Sender addresses for validation generation +CB_NESTED_SENDER := 0x6CD3850756b7894774Ab715D136F9dD02837De50 +CB_SIGNER_SENDER := 0x9C4a57Feb77e294Fd7BF5EBE9AB01CAA0a90A110 +SC_SENDER := 0x5ff5C78ff194acc24C22DAaDdE4D639ebF18ACC6 +OP_MAINNET_SENDER := 0x42d27eEA1AD6e22Af6284F609847CB3Cd56B9c64 +OP_OP_SENDER := 0x4d494C5F61b60752D3A10062276a0eFC22596151 + +## +# Phony Targets +## +.PHONY: deps-signer-tool \ + gen-all-validations \ + gen-validation-cb-1 gen-validation-cb-sc-1 gen-validation-op-1 \ + gen-validation-cb-2 gen-validation-cb-sc-2 gen-validation-op-2 \ + gen-validation-cb-3 gen-validation-cb-sc-3 \ + gen-validation-cb-4 gen-validation-cb-sc-4 \ + gen-validation-cb-5 gen-validation-cb-6 \ + deploy-fee-disburser deploy-l1-contracts \ + approve-proxyadminowner-signers-cb approve-proxyadminowner-signers-sc approve-proxyadminowner-signers-op \ + approve-cbsafesigners-cb approve-cbsafesigners-sc \ + approve-systemconfig-cb approve-systemconfig-sc \ + approve-withdraw-smartescrow-cb approve-withdraw-smartescrow-op \ + execute-proxyadminownersigners execute-cbsafesigners execute-systemconfig \ + execute-feedisburser execute-terminate-smartescrow execute-withdraw-smartescrow \ + execute-add-signer + +## +# Signer Tool Setup +## +deps-signer-tool: checkout-signer-tool + cd $(SIGNER_TOOL_PATH) && npm ci + +## +# Validation File Generation +## +# Generates validation files for multisig signing +# Usage: GEN_VALIDATION(script_name, safe_addr, sender, output_file, env_vars) +# Note: env_vars (5th param) is optional, used for nonce overrides when pre-signing multiple txs +define GEN_VALIDATION + cd $(SIGNER_TOOL_PATH) && \ + bun run scripts/genValidationFile.ts \ + --rpc-url $(RPC_URL) \ + --workdir .. \ + --forge-cmd '$(5)forge script --rpc-url $(RPC_URL) $(1) --sig "sign(address[])" "[$(2)]" --sender $(3)' \ + --ledger-id $(LEDGER_ACCOUNT) \ + --out ../validations/$(4) +endef + +# Helper to get current nonce for a safe +# Usage: $(call GET_NONCE,safe_address) +define GET_NONCE +$(shell cast call $(1) "nonce()" --rpc-url $(RPC_URL) | cast to-dec) +endef + +# Helper to convert address to uppercase for SAFE_NONCE env var +# Usage: $(call ADDR_UPPER,safe_address) +define ADDR_UPPER +$(shell echo $(1) | tr '[:lower:]' '[:upper:]') +endef + +# Part 1: UpdateProxyAdminOwnerSigners +gen-validation-cb-1: deps-signer-tool + $(call GEN_VALIDATION,UpdateProxyAdminOwnerSigners,$(CB_NESTED_SAFE_ADDR)$(COMMA)$(CB_SIGNER_SAFE_ADDR),$(CB_NESTED_SENDER),coinbase-signer-part-1.json,) + +gen-validation-cb-sc-1: deps-signer-tool + $(call GEN_VALIDATION,UpdateProxyAdminOwnerSigners,$(CB_SC_SAFE_ADDR)$(COMMA)$(CB_SIGNER_SAFE_ADDR),$(SC_SENDER),security-council-signer-part-1.json,) + +gen-validation-op-1: deps-signer-tool + $(call GEN_VALIDATION,UpdateProxyAdminOwnerSigners,$(OP_SIGNER_SAFE_ADDR),$(OP_MAINNET_SENDER),optimism-signer.json,) + +# Part 2: UpdateCBSafeSigners +# Task uses CB_SIGNER_SAFE_ADDR which will approve part 1 - needs nonce+1 +gen-validation-cb-2: deps-signer-tool + $(eval CB_PARENT_NONCE := $(shell expr $(call GET_NONCE,$(CB_SIGNER_SAFE_ADDR)) + 1)) + $(eval CB_SIGNER_NONCE := $(shell expr $(call GET_NONCE,$(CB_NESTED_SAFE_ADDR)) + 1)) + $(call GEN_VALIDATION,UpdateCBSafeSigners,$(CB_NESTED_SAFE_ADDR),$(CB_NESTED_SENDER),coinbase-signer-part-2.json,SAFE_NONCE_$(call ADDR_UPPER,$(CB_NESTED_SAFE_ADDR))=$(CB_SIGNER_NONCE) SAFE_NONCE_$(call ADDR_UPPER,$(CB_SIGNER_SAFE_ADDR))=$(CB_PARENT_NONCE) ) + +# SC uses CB_SC_SAFE_ADDR which will approve part 1 - needs nonce+1 +gen-validation-cb-sc-2: deps-signer-tool + $(eval CB_PARENT_NONCE := $(shell expr $(call GET_NONCE,$(CB_SIGNER_SAFE_ADDR)) + 1)) + $(eval SC_SIGNER_NONCE := $(shell expr $(call GET_NONCE,$(CB_SC_SAFE_ADDR)) + 1)) + $(call GEN_VALIDATION,UpdateCBSafeSigners,$(CB_SC_SAFE_ADDR),$(SC_SENDER),security-council-signer-part-2.json,SAFE_NONCE_$(call ADDR_UPPER,$(CB_SC_SAFE_ADDR))=$(SC_SIGNER_NONCE) SAFE_NONCE_$(call ADDR_UPPER,$(CB_SIGNER_SAFE_ADDR))=$(CB_PARENT_NONCE) ) + +# Part 3: UpgradeSystemConfig +# ProxyAdminOwner will execute part 1 - needs nonce+1 +# CB uses CB_SIGNER_SAFE_ADDR which will approve part 1 and execute part 2 - needs nonce+2 +gen-validation-cb-3: deps-signer-tool + $(eval PROXY_ADMIN_OWNER_NONCE := $(shell expr $(call GET_NONCE,$(OWNER_SAFE)) + 1)) + $(eval CB_PARENT_NONCE := $(shell expr $(call GET_NONCE,$(CB_SIGNER_SAFE_ADDR)) + 2)) + $(call GEN_VALIDATION,UpgradeSystemConfig,$(CB_SIGNER_SAFE_ADDR),$(CB_SIGNER_SENDER),coinbase-signer-part-3.json,SAFE_NONCE_$(call ADDR_UPPER,$(CB_SIGNER_SAFE_ADDR))=$(CB_PARENT_NONCE) SAFE_NONCE_$(call ADDR_UPPER,$(OWNER_SAFE))=$(PROXY_ADMIN_OWNER_NONCE) ) + +# SC uses CB_SC_SAFE_ADDR which has Part 1 and 2 txs pending - needs nonce+2 +gen-validation-cb-sc-3: deps-signer-tool + $(eval PROXY_ADMIN_OWNER_NONCE := $(shell expr $(call GET_NONCE,$(OWNER_SAFE)) + 1)) + $(eval SC_NONCE := $(shell expr $(call GET_NONCE,$(CB_SC_SAFE_ADDR)) + 2)) + $(call GEN_VALIDATION,UpgradeSystemConfig,$(CB_SC_SAFE_ADDR),$(SC_SENDER),security-council-signer-part-3.json,SAFE_NONCE_$(call ADDR_UPPER,$(CB_SC_SAFE_ADDR))=$(SC_NONCE) SAFE_NONCE_$(call ADDR_UPPER,$(OWNER_SAFE))=$(PROXY_ADMIN_OWNER_NONCE) ) + +# Part 4: UpgradeFeeDisburser +# Uses CB_NESTED_SAFE_ADDR which has Part 1 and 2 txs pending - needs nonce+2 +gen-validation-cb-4: deps-signer-tool + $(eval CB_NESTED_NONCE := $(shell expr $(call GET_NONCE,$(CB_NESTED_SAFE_ADDR)) + 2)) + $(call GEN_VALIDATION,UpgradeFeeDisburser,,$(CB_NESTED_SENDER),coinbase-signer-part-4.json,SAFE_NONCE_$(call ADDR_UPPER,$(CB_NESTED_SAFE_ADDR))=$(CB_NESTED_NONCE) ) + +# Part 5: TerminateSmartEscrow +gen-validation-cb-5: RPC_URL = $(OP_RPC_URL) +gen-validation-cb-5: deps-signer-tool + $(call GEN_VALIDATION,TerminateSmartEscrow,,$(CB_NESTED_SENDER),coinbase-signer-part-5.json,) + +# Part 6: WithdrawSmartEscrow +# Uses CB_SAFE_ON_OP which executes part 5 - needs nonce+1 +gen-validation-cb-6: RPC_URL = $(OP_RPC_URL) +gen-validation-cb-6: deps-signer-tool + $(eval CB_NONCE := $(shell expr $(call GET_NONCE,$(CB_SAFE_ON_OP)) + 1)) + $(call GEN_VALIDATION,WithdrawSmartEscrow,$(CB_SAFE_ON_OP),$(CB_NESTED_SENDER),coinbase-signer-part-6.json,SAFE_NONCE_$(call ADDR_UPPER,$(CB_SAFE_ON_OP))=$(CB_NONCE) ) + +gen-validation-op-2: RPC_URL = $(OP_RPC_URL) +gen-validation-op-2: deps-signer-tool + $(call GEN_VALIDATION,WithdrawSmartEscrow,$(OP_SAFE_ON_OP),$(OP_OP_SENDER),optimism-op-mainnet-signer.json,) + +# Part 7: AddSecurityCouncilSigner +# Uses CB_SIGNER_SAFE_ADDR which will approve part 1, 2 and 3 - needs nonce+3 +gen-validation-cb-sc-4: deps-signer-tool + $(eval SC_NONCE := $(shell expr $(call GET_NONCE,$(CB_SC_SAFE_ADDR)) + 3)) + $(call GEN_VALIDATION,AddSecurityCouncilSigner,,$(SC_SENDER),security-council-signer-part-4.json,SAFE_NONCE_$(call ADDR_UPPER,$(CB_SC_SAFE_ADDR))=$(SC_NONCE) ) + +## +# Deployments +## +deploy-fee-disburser: +ifndef VERIFIER_API_KEY + $(error VERIFIER_API_KEY is not set) +endif + forge script script/DeployFeeDisburser.s.sol:DeployFeeDisburser \ + --rpc-url $(L2_RPC_URL) \ + --broadcast \ + --verify \ + --verifier-api-key $(VERIFIER_API_KEY) \ + --ledger --hd-paths $(LEDGER_HD_PATH) \ + -vvvv + +deploy-l1-contracts: +ifndef VERIFIER_API_KEY + $(error VERIFIER_API_KEY is not set) +endif + forge script script/DeploySuperchainConfigAndSystemConfig.s.sol:DeploySuperchainConfigAndSystemConfig \ + --rpc-url $(L1_RPC_URL) \ + --broadcast \ + --verify \ + --verifier-api-key $(VERIFIER_API_KEY) \ + --ledger --hd-paths $(LEDGER_HD_PATH) \ + -vvvv + +## +# Approvals - UpdateProxyAdminOwnerSigners +## +approve-proxyadminowner-signers-cb: + SCRIPT_NAME=UpdateProxyAdminOwnerSigners $(call MULTISIG_APPROVE,$(CB_NESTED_SAFE_ADDR) $(CB_SIGNER_SAFE_ADDR),$(SIGNATURES)) + +approve-proxyadminowner-signers-sc: + SCRIPT_NAME=UpdateProxyAdminOwnerSigners $(call MULTISIG_APPROVE,$(CB_SC_SAFE_ADDR) $(CB_SIGNER_SAFE_ADDR),$(SIGNATURES)) + +approve-proxyadminowner-signers-op: + SCRIPT_NAME=UpdateProxyAdminOwnerSigners $(call MULTISIG_APPROVE,$(OP_SIGNER_SAFE_ADDR),$(SIGNATURES)) + +approve-proxyadminowner-signers-cb-coord: + SCRIPT_NAME=UpdateProxyAdminOwnerSigners $(call MULTISIG_APPROVE,$(CB_SIGNER_SAFE_ADDR),0x) + +## +# Approvals - UpdateCBSafeSigners +## +approve-cbsafesigners-cb: + SCRIPT_NAME=UpdateCBSafeSigners $(call MULTISIG_APPROVE,$(CB_NESTED_SAFE_ADDR),$(SIGNATURES)) + +approve-cbsafesigners-sc: + SCRIPT_NAME=UpdateCBSafeSigners $(call MULTISIG_APPROVE,$(CB_SC_SAFE_ADDR),$(SIGNATURES)) + +## +# Approvals - UpgradeSystemConfig +## +approve-systemconfig-cb: + SCRIPT_NAME=UpgradeSystemConfig $(call MULTISIG_APPROVE,$(CB_SIGNER_SAFE_ADDR),$(SIGNATURES)) + +approve-systemconfig-sc: + SCRIPT_NAME=UpgradeSystemConfig $(call MULTISIG_APPROVE,$(CB_SC_SAFE_ADDR),$(SIGNATURES)) + +## +# Approvals - WithdrawSmartEscrow +## +approve-withdraw-smartescrow-cb: RPC_URL = $(OP_RPC_URL) +approve-withdraw-smartescrow-cb: + SCRIPT_NAME=WithdrawSmartEscrow $(call MULTISIG_APPROVE,$(CB_SAFE_ON_OP),$(SIGNATURES)) + +approve-withdraw-smartescrow-op: RPC_URL = $(OP_RPC_URL) +approve-withdraw-smartescrow-op: + SCRIPT_NAME=WithdrawSmartEscrow $(call MULTISIG_APPROVE,$(OP_SAFE_ON_OP),$(SIGNATURES)) + +## +# Execute +## +execute-proxyadminownersigners: + SCRIPT_NAME=UpdateProxyAdminOwnerSigners $(call MULTISIG_EXECUTE,0x) + +execute-cbsafesigners: + SCRIPT_NAME=UpdateCBSafeSigners $(call MULTISIG_EXECUTE,0x) + +execute-systemconfig: + SCRIPT_NAME=UpgradeSystemConfig $(call MULTISIG_EXECUTE,0x) + +execute-feedisburser: + SCRIPT_NAME=UpgradeFeeDisburser $(call MULTISIG_EXECUTE,$(SIGNATURES)) + +execute-terminate-smartescrow: RPC_URL = $(OP_RPC_URL) +execute-terminate-smartescrow: + SCRIPT_NAME=TerminateSmartEscrow $(call MULTISIG_EXECUTE,$(SIGNATURES)) + +execute-withdraw-smartescrow: RPC_URL = $(OP_RPC_URL) +execute-withdraw-smartescrow: + SCRIPT_NAME=WithdrawSmartEscrow $(call MULTISIG_EXECUTE,0x) + +execute-add-signer: + SCRIPT_NAME=AddSecurityCouncilSigner $(call MULTISIG_EXECUTE,$(SIGNATURES)) diff --git a/mainnet/2026-02-19-superchain-separation/README.md b/mainnet/2026-02-19-superchain-separation/README.md new file mode 100644 index 00000000..c98483c9 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/README.md @@ -0,0 +1,57 @@ +# Mainnet OP Stack Separation + +Status: READY TO SIGN + +## Description + +This task executes our migration away from the shared Superchain configuration to a Base-owned configuration. + +## Procedure + +### Install dependencies + +#### 1. Update foundry + +```bash +foundryup +``` + +#### 2. Install Node.js if needed + +First, check if you have node installed: + +```bash +node --version +``` + +If you see a version output from the above command, you can move on. Otherwise, install node: + +```bash +brew install node +``` + +### Approve the transaction + +#### 1. Update repo + +```bash +cd contract-deployments +git pull +``` + +#### 2. Run the signing tool + +```bash +make sign-task +``` + +#### 3. Open the UI at [http://localhost:3000](http://localhost:3000) + +- Select the correct signer role from the list of available users to sign. +- After completion, close the signer tool with `Ctrl + C`. + +#### 4. Send signature to facilitator + +Copy the signature output and send it to the designated facilitator via the agreed communication channel. The facilitator will collect all signatures and execute the transaction. + +For facilitator instructions, see [FACILITATORS.md](./FACILITATORS.md). diff --git a/mainnet/2026-02-19-superchain-separation/foundry.toml b/mainnet/2026-02-19-superchain-separation/foundry.toml new file mode 100644 index 00000000..7245813a --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/foundry.toml @@ -0,0 +1,23 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +broadcast = 'records' +fs_permissions = [{ access = "read-write", path = "./" }] +optimizer = true +optimizer_runs = 999999 +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', + '@rari-capital/solmate/=lib/solmate/', + '@base-contracts/=lib/base-contracts', + 'solady/=lib/solady/src/', + '@lib-keccak/=lib/lib-keccak/contracts/lib', +] + +[lint] +lint_on_build = false + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/mainnet/2026-02-19-superchain-separation/script/AddSecurityCouncilSigner.s.sol b/mainnet/2026-02-19-superchain-separation/script/AddSecurityCouncilSigner.s.sol new file mode 100644 index 00000000..e293e3b4 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/AddSecurityCouncilSigner.s.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {MultisigScript} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; +import {IGnosisSafe, Enum} from "@base-contracts/script/universal/IGnosisSafe.sol"; + +/// @title AddSecurityCouncilSigner +/// @notice Adds a new signer to the Security Council Safe and increases the threshold to 3. +/// @dev Uses addOwnerWithThreshold to add the new signer and set the threshold in a single call. +contract AddSecurityCouncilSigner is MultisigScript { + /// @notice The new threshold after adding the signer. + uint256 public constant NEW_THRESHOLD = 8; + + /// @notice The Security Council Safe whose signers are being updated. + address public immutable SECURITY_COUNCIL = vm.envAddress("CB_SC_SAFE_ADDR"); + + /// @notice The new signer address to be added. + address public immutable NEW_SIGNER = vm.envAddress("NEW_SC_SIGNER_ADDR"); + + /// @notice The current threshold before the update. + uint256 public currentThreshold; + + /// @notice The current number of owners before the update. + uint256 public currentOwnerCount; + + function setUp() external { + // Prechecks: Verify initial state + require(!IGnosisSafe(SECURITY_COUNCIL).isOwner(NEW_SIGNER), "Precheck: NEW_SIGNER must not already be an owner"); + + currentThreshold = IGnosisSafe(SECURITY_COUNCIL).getThreshold(); + currentOwnerCount = IGnosisSafe(SECURITY_COUNCIL).getOwners().length; + + require(currentThreshold < NEW_THRESHOLD, "Precheck: Current threshold must be less than NEW_THRESHOLD"); + require(currentOwnerCount + 1 >= NEW_THRESHOLD, "Precheck: Owner count after adding must be >= NEW_THRESHOLD"); + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + // Verify new signer was added + require(IGnosisSafe(SECURITY_COUNCIL).isOwner(NEW_SIGNER), "Postcheck: NEW_SIGNER must be an owner"); + + // Verify threshold was updated + require( + IGnosisSafe(SECURITY_COUNCIL).getThreshold() == NEW_THRESHOLD, "Postcheck: Threshold must be NEW_THRESHOLD" + ); + + // Verify owner count increased by 1 + require( + IGnosisSafe(SECURITY_COUNCIL).getOwners().length == currentOwnerCount + 1, + "Postcheck: Owner count must increase by 1" + ); + } + + function _buildCalls() internal view override returns (MultisigScript.Call[] memory) { + MultisigScript.Call[] memory calls = new MultisigScript.Call[](1); + + // Add new signer and set threshold to NEW_THRESHOLD in a single call + calls[0] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: SECURITY_COUNCIL, + data: abi.encodeCall(IGnosisSafe.addOwnerWithThreshold, (NEW_SIGNER, NEW_THRESHOLD)), + value: 0 + }); + + return calls; + } + + function _ownerSafe() internal view override returns (address) { + return SECURITY_COUNCIL; + } +} diff --git a/mainnet/2026-02-19-superchain-separation/script/DeployFeeDisburser.s.sol b/mainnet/2026-02-19-superchain-separation/script/DeployFeeDisburser.s.sol new file mode 100644 index 00000000..9fef1e31 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/DeployFeeDisburser.s.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; + +import {FeeDisburser} from "@base-contracts/src/revenue-share/FeeDisburser.sol"; + +/// @title DeployFeeDisburser +/// @notice Deploys the FeeDisburser contract which collects fees from L2 FeeVaults and bridges them to L1. +/// @dev Required environment variables: +/// - L1_WALLET: The L1 address that will receive bridged fees +/// - FEE_DISBURSEMENT_INTERVAL: Minimum time in seconds between disbursements (must be >= 24 hours) +contract DeployFeeDisburser is Script { + /// @notice The L1 address that will receive the bridged fees. + address public immutable L1_WALLET = vm.envAddress("BALANCE_TRACKER"); + + /// @notice The minimum time in seconds between fee disbursements. + uint256 public immutable FEE_DISBURSEMENT_INTERVAL = vm.envUint("FEE_DISBURSEMENT_INTERVAL"); + + function run() external { + vm.startBroadcast(); + + FeeDisburser feeDisburser = new FeeDisburser(L1_WALLET, FEE_DISBURSEMENT_INTERVAL); + + console.log("FeeDisburser deployed at:", address(feeDisburser)); + console.log(" L1_WALLET:", L1_WALLET); + console.log(" FEE_DISBURSEMENT_INTERVAL:", FEE_DISBURSEMENT_INTERVAL); + + vm.stopBroadcast(); + + // Post-deployment checks to ensure immutables match expected values + // (guards against shell env variables overriding .env file) + require( + feeDisburser.L1_WALLET() == 0x23B597f33f6f2621F77DA117523Dffd634cDf4ea, + "DeployFeeDisburser: L1_WALLET mismatch" + ); + require( + feeDisburser.FEE_DISBURSEMENT_INTERVAL() == 86400, "DeployFeeDisburser: FEE_DISBURSEMENT_INTERVAL mismatch" + ); + } +} diff --git a/mainnet/2026-02-19-superchain-separation/script/DeploySuperchainConfigAndSystemConfig.s.sol b/mainnet/2026-02-19-superchain-separation/script/DeploySuperchainConfigAndSystemConfig.s.sol new file mode 100644 index 00000000..4536925f --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/DeploySuperchainConfigAndSystemConfig.s.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Script} from "forge-std/Script.sol"; + +import {Proxy} from "@base-contracts/src/universal/Proxy.sol"; +import {ProxyAdmin} from "@base-contracts/src/universal/ProxyAdmin.sol"; +import {SystemConfig} from "@base-contracts/src/L1/SystemConfig.sol"; +import {SuperchainConfig} from "@base-contracts/src/L1/SuperchainConfig.sol"; + +/// @title DeploySuperchainConfigAndSystemConfig +/// @notice Deploys a new SuperchainConfig proxy and a patched SystemConfig implementation. +/// The SuperchainConfig proxy is initialized with the guardian address and admin +/// is transferred to the ProxyAdmin contract. The SystemConfig implementation uses +/// init version 4 to allow re-initialization on existing proxies. +contract DeploySuperchainConfigAndSystemConfig is Script { + /// @notice The ProxyAdmin contract that will be the admin of the SuperchainConfig proxy. + address public immutable PROXY_ADMIN = vm.envAddress("PROXY_ADMIN"); + + /// @notice The Safe that owns the ProxyAdmin. + address public immutable OWNER_SAFE = vm.envAddress("OWNER_SAFE"); + + /// @notice The guardian address that can pause and perform dispute game operations. + address public immutable GUARDIAN = vm.envAddress("SECURITY_COUNCIL"); + + /// @notice The incident responder address that can only pause. + address public immutable INCIDENT_RESPONDER = vm.envAddress("INCIDENT_MULTISIG"); + + function run() external { + vm.startBroadcast(); + + // Deploy SuperchainConfig implementation with guardian and incident responder as constructor args + SuperchainConfig superchainConfigImpl = new SuperchainConfig(GUARDIAN, INCIDENT_RESPONDER); + + // Deploy SuperchainConfig proxy + Proxy superchainConfigProxy = new Proxy(address(this)); + superchainConfigProxy.upgradeTo(address(superchainConfigImpl)); + superchainConfigProxy.changeAdmin(PROXY_ADMIN); + + // Deploy patched SystemConfig implementation (uses init version 4) + new SystemConfig(); + + vm.stopBroadcast(); + + // Post-deployment verification + SuperchainConfig superchainConfig = SuperchainConfig(address(superchainConfigProxy)); + require(superchainConfig.GUARDIAN() == GUARDIAN, "Postcheck: Guardian not set correctly"); + require( + superchainConfig.INCIDENT_RESPONDER() == INCIDENT_RESPONDER, + "Postcheck: Incident responder not set correctly" + ); + require(!superchainConfig.paused(), "Postcheck: SuperchainConfig should not be paused"); + + // Verify proxy admin is set correctly + vm.prank(address(0)); + require(superchainConfigProxy.admin() == PROXY_ADMIN, "Postcheck: Proxy admin not set correctly"); + + // Verify proxy admin owner is correct + require(ProxyAdmin(PROXY_ADMIN).owner() == OWNER_SAFE, "Postcheck: ProxyAdmin owner not set correctly"); + } +} diff --git a/mainnet/2026-02-19-superchain-separation/script/TerminateSmartEscrow.s.sol b/mainnet/2026-02-19-superchain-separation/script/TerminateSmartEscrow.s.sol new file mode 100644 index 00000000..74c798d2 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/TerminateSmartEscrow.s.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {MultisigScript, Enum} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; + +interface ISmartEscrow { + function terminate() external; + function contractTerminated() external view returns (bool); +} + +/// @title TerminateSmartEscrow +/// @notice Script to terminate the SmartEscrow contract via a multisig transaction. +/// The caller must have the TERMINATOR_ROLE on the SmartEscrow contract. +/// Termination releases any vested tokens to the beneficiary before setting +/// the contract to a terminated state. +contract TerminateSmartEscrow is MultisigScript { + /// @notice The Safe address that has the TERMINATOR_ROLE on the SmartEscrow contract. + address public immutable OWNER_SAFE = vm.envAddress("CB_SAFE_ON_OP"); + + /// @notice The SmartEscrow contract address to terminate. + address public immutable SMART_ESCROW = vm.envAddress("SMART_ESCROW"); + + /// @notice Verifies that the SmartEscrow contract was successfully terminated. + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + require(ISmartEscrow(SMART_ESCROW).contractTerminated(), "Postcheck: SmartEscrow contract was not terminated"); + } + + /// @notice Builds the call to SmartEscrow.terminate(). + function _buildCalls() internal view override returns (MultisigScript.Call[] memory) { + MultisigScript.Call[] memory calls = new MultisigScript.Call[](1); + + calls[0] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: SMART_ESCROW, + data: abi.encodeCall(ISmartEscrow.terminate, ()), + value: 0 + }); + + return calls; + } + + /// @notice Returns the Safe address that will execute this transaction. + function _ownerSafe() internal view override returns (address) { + return OWNER_SAFE; + } +} diff --git a/mainnet/2026-02-19-superchain-separation/script/UpdateCBSafeSigners.s.sol b/mainnet/2026-02-19-superchain-separation/script/UpdateCBSafeSigners.s.sol new file mode 100644 index 00000000..b45fd552 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/UpdateCBSafeSigners.s.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {MultisigScript} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; +import {IGnosisSafe, Enum} from "@base-contracts/script/universal/IGnosisSafe.sol"; + +/// @title UpdateCBSafeSigners +/// @notice Updates the CB signer safe to replace nested safe signers with individual EOAs. +/// @dev Execution order: +/// 1. Remove SECURITY_COUNCIL from OWNER_SAFE (threshold temporarily set to 1) +/// 2. Add each CB EOA from CB_NESTED_SAFE as direct signers (threshold stays at 1) +/// 3. Remove CB_NESTED_SAFE and set final threshold to FINAL_THRESHOLD +contract UpdateCBSafeSigners is MultisigScript { + /// @notice Sentinel address used by Safe's linked list implementation. + address internal constant SENTINEL = address(0x1); + + /// @notice Interim threshold used during owner modifications. + uint256 internal constant INTERIM_THRESHOLD = 1; + + /// @notice Final threshold after all owner modifications are complete. + uint256 public constant FINAL_THRESHOLD = 3; + + /// @notice The Safe whose signers are being updated. + address public immutable OWNER_SAFE = vm.envAddress("CB_SIGNER_SAFE_ADDR"); + + /// @notice The security council Safe to be removed as a signer. + address public immutable SECURITY_COUNCIL = vm.envAddress("CB_SC_SAFE_ADDR"); + + /// @notice The nested Safe whose EOA owners will become direct signers. + address public immutable CB_NESTED_SAFE = vm.envAddress("CB_NESTED_SAFE_ADDR"); + + /// @notice The previous owner in Safe's linked list, needed for removeOwner. + address public prevOwnerOfSecurityCouncil = SENTINEL; + + /// @notice The EOA addresses from CB_NESTED_SAFE that will become direct signers. + address[] public cbEoas; + + function setUp() external { + // Prechecks: Verify initial state + require(IGnosisSafe(OWNER_SAFE).isOwner(SECURITY_COUNCIL), "Precheck: SECURITY_COUNCIL must be an owner"); + require(IGnosisSafe(OWNER_SAFE).isOwner(CB_NESTED_SAFE), "Precheck: CB_NESTED_SAFE must be an owner"); + + // Find prevOwner for SECURITY_COUNCIL in Safe's linked list. + // Safe's getOwners() returns owners in linked list order. + // The first owner has SENTINEL (0x1) as its prevOwner. + address[] memory owners = IGnosisSafe(OWNER_SAFE).getOwners(); + + for (uint256 i; i < owners.length; i++) { + if (owners[i] == SECURITY_COUNCIL) break; + prevOwnerOfSecurityCouncil = owners[i]; + } + + // Get EOAs from nested safe to add as direct signers + cbEoas = IGnosisSafe(CB_NESTED_SAFE).getOwners(); + require(cbEoas.length > 0, "Precheck: CB_NESTED_SAFE must have at least one owner"); + require(cbEoas.length >= FINAL_THRESHOLD, "Precheck: CB_NESTED_SAFE must have at least FINAL_THRESHOLD owners"); + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + // Verify removed signers + require(!IGnosisSafe(OWNER_SAFE).isOwner(SECURITY_COUNCIL), "Postcheck: SECURITY_COUNCIL must be removed"); + require(!IGnosisSafe(OWNER_SAFE).isOwner(CB_NESTED_SAFE), "Postcheck: CB_NESTED_SAFE must be removed"); + + // Verify all EOAs are now direct signers + for (uint256 i = 0; i < cbEoas.length; i++) { + require(IGnosisSafe(OWNER_SAFE).isOwner(cbEoas[i]), "Postcheck: CB EOA must be an owner"); + } + + // Verify threshold + require( + IGnosisSafe(OWNER_SAFE).getThreshold() == FINAL_THRESHOLD, "Postcheck: Threshold must be FINAL_THRESHOLD" + ); + } + + function _buildCalls() internal view override returns (MultisigScript.Call[] memory) { + // Total calls: 1 (remove SECURITY_COUNCIL) + cbEoas.length (add EOAs) + 1 (remove CB_NESTED_SAFE) + MultisigScript.Call[] memory calls = new MultisigScript.Call[](2 + cbEoas.length); + + // Step 1: Remove SECURITY_COUNCIL, set threshold to 1 + calls[0] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: OWNER_SAFE, + data: abi.encodeCall( + IGnosisSafe.removeOwner, (prevOwnerOfSecurityCouncil, SECURITY_COUNCIL, INTERIM_THRESHOLD) + ), + value: 0 + }); + + // Step 2: Add each CB EOA as a direct signer (keep threshold at 1) + // Note: addOwnerWithThreshold adds new owners to the FRONT of Safe's linked list. + // After adding cbEoas[0..n-1], the order will be: + // SENTINEL -> cbEoas[n-1] -> ... -> cbEoas[0] -> CB_NESTED_SAFE -> ... + // Therefore, cbEoas[0] will be the prevOwner of CB_NESTED_SAFE. + for (uint256 i = 0; i < cbEoas.length; i++) { + calls[i + 1] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: OWNER_SAFE, + data: abi.encodeCall(IGnosisSafe.addOwnerWithThreshold, (cbEoas[i], INTERIM_THRESHOLD)), + value: 0 + }); + } + + // Step 3: Remove CB_NESTED_SAFE and set final threshold + // cbEoas[0] is the prevOwner because it was added first and is now + // immediately before CB_NESTED_SAFE in the linked list. + calls[calls.length - 1] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: OWNER_SAFE, + data: abi.encodeCall(IGnosisSafe.removeOwner, (cbEoas[0], CB_NESTED_SAFE, FINAL_THRESHOLD)), + value: 0 + }); + + return calls; + } + + function _ownerSafe() internal view override returns (address) { + return OWNER_SAFE; + } +} diff --git a/mainnet/2026-02-19-superchain-separation/script/UpdateProxyAdminOwnerSigners.s.sol b/mainnet/2026-02-19-superchain-separation/script/UpdateProxyAdminOwnerSigners.s.sol new file mode 100644 index 00000000..d8f68bac --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/UpdateProxyAdminOwnerSigners.s.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {MultisigScript} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; +import {IGnosisSafe, Enum} from "@base-contracts/script/universal/IGnosisSafe.sol"; + +/// @title UpdateProxyAdminOwnerSigners +/// @notice Swaps OP_SAFE for SECURITY_COUNCIL as an owner on the ProxyAdmin owner Safe. +/// @dev This script expects the Safe to have exactly 2 owners and threshold of 2 before and after the swap. +contract UpdateProxyAdminOwnerSigners is MultisigScript { + /// @notice Sentinel address used by Safe's linked list implementation. + address internal constant SENTINEL = address(0x1); + + /// @notice Expected owner count before and after the swap. + uint256 internal constant EXPECTED_OWNER_COUNT = 2; + + /// @notice The Safe whose signers are being updated. + address public immutable OWNER_SAFE = vm.envAddress("OWNER_SAFE"); + + /// @notice The OP signer Safe to be removed as an owner. + address public immutable OP_SAFE = vm.envAddress("OP_SIGNER_SAFE_ADDR"); + + /// @notice The security council Safe to be added as an owner. + address public immutable SECURITY_COUNCIL = vm.envAddress("CB_SC_SAFE_ADDR"); + + /// @notice The previous owner in Safe's linked list, needed for swapOwner. + address public prevOwner = SENTINEL; + + function setUp() external { + // Prechecks: Verify initial state + require(IGnosisSafe(OWNER_SAFE).isOwner(OP_SAFE), "Precheck: OP_SAFE must be an owner"); + require( + !IGnosisSafe(OWNER_SAFE).isOwner(SECURITY_COUNCIL), + "Precheck: SECURITY_COUNCIL must not already be an owner" + ); + require( + IGnosisSafe(OWNER_SAFE).getOwners().length == EXPECTED_OWNER_COUNT, + "Precheck: Must have EXPECTED_OWNER_COUNT owners" + ); + + // Find prevOwner for OP_SAFE in Safe's linked list. + // Safe's getOwners() returns owners in linked list order. + // The first owner has SENTINEL (0x1) as its prevOwner. + address[] memory owners = IGnosisSafe(OWNER_SAFE).getOwners(); + + for (uint256 i; i < owners.length; i++) { + if (owners[i] == OP_SAFE) break; + prevOwner = owners[i]; + } + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + require(IGnosisSafe(OWNER_SAFE).isOwner(SECURITY_COUNCIL), "Postcheck: SECURITY_COUNCIL must be an owner"); + require(!IGnosisSafe(OWNER_SAFE).isOwner(OP_SAFE), "Postcheck: OP_SAFE must be removed"); + require( + IGnosisSafe(OWNER_SAFE).getOwners().length == EXPECTED_OWNER_COUNT, + "Postcheck: Must have EXPECTED_OWNER_COUNT owners" + ); + } + + function _buildCalls() internal view override returns (MultisigScript.Call[] memory) { + MultisigScript.Call[] memory calls = new MultisigScript.Call[](1); + + calls[0] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: OWNER_SAFE, + data: abi.encodeCall(IGnosisSafe.swapOwner, (prevOwner, OP_SAFE, SECURITY_COUNCIL)), + value: 0 + }); + + return calls; + } + + function _ownerSafe() internal view override returns (address) { + return OWNER_SAFE; + } +} diff --git a/mainnet/2026-02-19-superchain-separation/script/UpgradeFeeDisburser.s.sol b/mainnet/2026-02-19-superchain-separation/script/UpgradeFeeDisburser.s.sol new file mode 100644 index 00000000..e089edb1 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/UpgradeFeeDisburser.s.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {MultisigScript, Enum} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; + +interface IOptimismPortal2 { + function depositTransaction(address _to, uint256 _value, uint64 _gasLimit, bool _isCreation, bytes memory _data) + external + payable; +} + +interface IProxy { + function upgradeTo(address newImplementation) external; +} + +/// @title UpgradeFeeDisburser +/// @notice Script to upgrade the FeeDisburser proxy on L2 via a deposit transaction from L1. +/// The FeeDisburser proxy is owned by CB_NESTED_SAFE_ADDR on L1, so this upgrade +/// must be executed as a deposit transaction through OptimismPortal2. +contract UpgradeFeeDisburser is MultisigScript { + /// @notice The L1 Safe that owns the FeeDisburser proxy (aliased address is the admin). + address public immutable OWNER_SAFE = vm.envAddress("CB_NESTED_SAFE_ADDR"); + + /// @notice The OptimismPortal2 contract on L1 used for deposit transactions. + address public immutable OPTIMISM_PORTAL = vm.envAddress("OPTIMISM_PORTAL_ADDR"); + + /// @notice The FeeDisburser proxy contract on L2. + address public immutable FEE_DISBURSER_PROXY = vm.envAddress("FEE_DISBURSER_ADDR"); + + /// @notice The new FeeDisburser implementation contract on L2. + address public immutable FEE_DISBURSER_IMPL = vm.envAddress("FEE_DISBURSER_IMPL_ADDR"); + + /// @notice Post-check is a no-op since we cannot verify L2 state from L1 simulation. + /// The upgrade result should be verified on L2 after the deposit transaction is processed. + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal override {} + + /// @notice Builds the call to OptimismPortal2.depositTransaction that will upgrade + /// the FeeDisburser proxy on L2. + function _buildCalls() internal view override returns (MultisigScript.Call[] memory) { + MultisigScript.Call[] memory calls = new MultisigScript.Call[](1); + + // Build the upgradeTo calldata to call directly on the FeeDisburser proxy + bytes memory upgradeCalldata = abi.encodeCall(IProxy.upgradeTo, (FEE_DISBURSER_IMPL)); + + // Parameters for the deposit transaction + address to = FEE_DISBURSER_PROXY; + uint256 value = 0; + uint64 gasLimit = 100_000; // Sufficient gas for proxy upgrade + bool isCreation = false; + + calls[0] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: OPTIMISM_PORTAL, + data: abi.encodeCall( + IOptimismPortal2.depositTransaction, (to, value, gasLimit, isCreation, upgradeCalldata) + ), + value: 0 + }); + + return calls; + } + + /// @notice Returns the Safe address that will execute this transaction on L1. + function _ownerSafe() internal view override returns (address) { + return OWNER_SAFE; + } +} diff --git a/mainnet/2026-02-19-superchain-separation/script/UpgradeSystemConfig.s.sol b/mainnet/2026-02-19-superchain-separation/script/UpgradeSystemConfig.s.sol new file mode 100644 index 00000000..fe2a60f1 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/UpgradeSystemConfig.s.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {MultisigScript, Enum} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; +import {SystemConfig, IResourceMetering, ISuperchainConfig} from "@base-contracts/src/L1/SystemConfig.sol"; +import {IProxyAdmin} from "@base-contracts/interfaces/universal/IProxyAdmin.sol"; + +/// @title UpgradeSystemConfig +/// @notice Script to upgrade the SystemConfig proxy to a new implementation and reinitialize +/// it with a new SuperchainConfig address. This preserves all existing configuration +/// parameters while updating the SuperchainConfig reference. +contract UpgradeSystemConfig is MultisigScript { + /// @notice The Safe that owns the ProxyAdmin and will execute this upgrade. + address public immutable OWNER_SAFE = vm.envAddress("OWNER_SAFE"); + + /// @notice The ProxyAdmin contract that manages the SystemConfig proxy. + address public immutable PROXY_ADMIN = vm.envAddress("PROXY_ADMIN"); + + /// @notice The SystemConfig proxy contract to be upgraded. + address public immutable SYSTEM_CONFIG = vm.envAddress("SYSTEM_CONFIG"); + + /// @notice The new SystemConfig implementation contract. + address public immutable SYSTEM_CONFIG_IMPLEMENTATION = vm.envAddress("SYSTEM_CONFIG_IMPLEMENTATION"); + + /// @notice The new SuperchainConfig address to set during reinitialization. + address public immutable NEW_SUPERCHAIN_CONFIG = vm.envAddress("NEW_SUPERCHAIN_CONFIG"); + + /// @notice Validates the post-upgrade state. + /// @dev Verifies that: + /// 1. The implementation was updated correctly + /// 2. The SuperchainConfig was set to the new address + /// 3. All existing parameters were preserved + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + SystemConfig systemConfig = SystemConfig(SYSTEM_CONFIG); + + // Verify the implementation was updated + address currentImpl = IProxyAdmin(PROXY_ADMIN).getProxyImplementation(SYSTEM_CONFIG); + require(currentImpl == SYSTEM_CONFIG_IMPLEMENTATION, "Postcheck: Implementation not updated"); + + // Verify the SuperchainConfig was updated + require( + address(systemConfig.superchainConfig()) == NEW_SUPERCHAIN_CONFIG, "Postcheck: SuperchainConfig not updated" + ); + } + + /// @notice Builds the upgrade call to ProxyAdmin.upgradeAndCall. + /// @dev Reads all existing SystemConfig parameters and rebuilds the initialize calldata + /// with the new SuperchainConfig address. All other parameters are preserved. + function _buildCalls() internal view override returns (MultisigScript.Call[] memory) { + SystemConfig systemConfig = SystemConfig(SYSTEM_CONFIG); + + // Get all existing SystemConfig parameters + address owner = systemConfig.owner(); + uint32 basefeeScalar = systemConfig.basefeeScalar(); + uint32 blobbasefeeScalar = systemConfig.blobbasefeeScalar(); + bytes32 batcherHash = systemConfig.batcherHash(); + uint64 gasLimit = systemConfig.gasLimit(); + address unsafeBlockSigner = systemConfig.unsafeBlockSigner(); + IResourceMetering.ResourceConfig memory resourceConfig = systemConfig.resourceConfig(); + address batchInbox = systemConfig.batchInbox(); + uint256 l2ChainId = systemConfig.l2ChainId(); + + // Get the Addresses struct + SystemConfig.Addresses memory addresses = systemConfig.getAddresses(); + + // Build the initialize calldata with the new SuperchainConfig + bytes memory initializeCalldata = abi.encodeCall( + SystemConfig.initialize, + ( + owner, + basefeeScalar, + blobbasefeeScalar, + batcherHash, + gasLimit, + unsafeBlockSigner, + resourceConfig, + batchInbox, + addresses, + l2ChainId, + ISuperchainConfig(NEW_SUPERCHAIN_CONFIG) + ) + ); + + MultisigScript.Call[] memory calls = new MultisigScript.Call[](1); + + calls[0] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: PROXY_ADMIN, + data: abi.encodeCall( + IProxyAdmin.upgradeAndCall, (payable(SYSTEM_CONFIG), SYSTEM_CONFIG_IMPLEMENTATION, initializeCalldata) + ), + value: 0 + }); + + return calls; + } + + /// @notice Returns the Safe address that will execute this transaction. + function _ownerSafe() internal view override returns (address) { + return OWNER_SAFE; + } +} diff --git a/mainnet/2026-02-19-superchain-separation/script/WithdrawSmartEscrow.s.sol b/mainnet/2026-02-19-superchain-separation/script/WithdrawSmartEscrow.s.sol new file mode 100644 index 00000000..976b2169 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/script/WithdrawSmartEscrow.s.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {MultisigScript, Enum} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; + +interface ISmartEscrow { + function withdrawUnvestedTokens() external; + function contractTerminated() external view returns (bool); +} + +/// @title WithdrawSmartEscrow +/// @notice Script to withdraw unvested tokens from the SmartEscrow contract via a multisig transaction. +/// The caller must have the DEFAULT_ADMIN_ROLE on the SmartEscrow contract. +/// The contract must be terminated before calling this function. +/// This withdraws all remaining OP tokens to the benefactor address. +contract WithdrawSmartEscrow is MultisigScript { + /// @notice OP token contract address. + IERC20 public constant OP_TOKEN = IERC20(0x4200000000000000000000000000000000000042); + + /// @notice Storage slot for the `contractTerminated` variable in the SmartEscrow contract. + /// @dev This slot was determined using `forge inspect SmartEscrow storage-layout`. + bytes32 public constant CONTRACT_TERMINATED_SLOT = bytes32(uint256(6)); + + /// @notice The Safe address that has the DEFAULT_ADMIN_ROLE on the SmartEscrow contract. + address public immutable OWNER_SAFE = vm.envAddress("OWNER_SAFE_ON_OP"); + + /// @notice The SmartEscrow contract address to withdraw from. + address public immutable SMART_ESCROW = vm.envAddress("SMART_ESCROW"); + + /// @notice Verifies that the SmartEscrow contract's OP token balance is zero after withdrawal. + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + require(ISmartEscrow(SMART_ESCROW).contractTerminated(), "Postcheck: SmartEscrow contract must be terminated"); + require(OP_TOKEN.balanceOf(SMART_ESCROW) == 0, "Postcheck: SmartEscrow OP token balance is not zero"); + } + + /// @notice Builds the call to SmartEscrow.withdrawUnvestedTokens(). + function _buildCalls() internal view override returns (MultisigScript.Call[] memory) { + MultisigScript.Call[] memory calls = new MultisigScript.Call[](1); + + calls[0] = MultisigScript.Call({ + operation: Enum.Operation.Call, + target: SMART_ESCROW, + data: abi.encodeCall(ISmartEscrow.withdrawUnvestedTokens, ()), + value: 0 + }); + + return calls; + } + + function _simulationOverrides() + internal + view + virtual + override + returns (Simulation.StateOverride[] memory overrides_) + { + // If the contract is not yet terminated, override the storage slot to simulate termination. + if (!ISmartEscrow(SMART_ESCROW).contractTerminated()) { + Simulation.StorageOverride[] memory storageOverrides = new Simulation.StorageOverride[](1); + storageOverrides[0] = Simulation.StorageOverride({ + key: CONTRACT_TERMINATED_SLOT, + value: bytes32(uint256(1)) // true + }); + + overrides_ = new Simulation.StateOverride[](1); + overrides_[0] = Simulation.StateOverride(SMART_ESCROW, storageOverrides); + } + } + + /// @notice Returns the Safe address that will execute this transaction. + function _ownerSafe() internal view override returns (address) { + return OWNER_SAFE; + } +} diff --git a/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-1.json b/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-1.json new file mode 100644 index 00000000..1ddadb23 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-1.json @@ -0,0 +1,131 @@ +{ + "cmd": "forge script --rpc-url https://eth-mainnet.public.blastapi.io UpdateProxyAdminOwnerSigners --sig sign(address[]) [0x9C4a57Feb77e294Fd7BF5EBE9AB01CAA0a90A110,0x9855054731540A48b28990B63DcF4f33d8AE46A1] --sender 0x6CD3850756b7894774Ab715D136F9dD02837De50", + "ledgerId": 0, + "rpcUrl": "https://eth-mainnet.public.blastapi.io", + "expectedDomainAndMessageHashes": { + "address": "0x7bB41C3008B3f03FE483B28b8DB90e19Cf07595c", + "domainHash": "0xfb308368b8deca582e84a807d31c1bfcec6fda754061e2801b4d6be5cb52a8ac", + "messageHash": "0xe3a5f42b5613c94fe4d367f9eb630b04bb659529c04b6a094f49ecf3ac33f4ef" + }, + "stateOverrides": [ + { + "name": "Proxy Admin Owner - Mainnet", + "address": "0x7bb41c3008b3f03fe483b28b8db90e19cf07595c", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + } + ] + }, + { + "name": "CB Coordinator Safe - Mainnet", + "address": "0x9855054731540a48b28990b63dcf4f33d8ae46a1", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + } + ] + }, + { + "name": "CB Signer Safe - Mainnet", + "address": "0x9c4a57feb77e294fd7bf5ebe9ab01caa0a90a110", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0xfbb587efcd31a015069060729500c9da1b0b21ab0266eeb64ac69ae06fa73749", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Simulates an approval from msg.sender in order for the task simulation to succeed.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "Proxy Admin Owner - Mainnet", + "address": "0x7bb41c3008b3f03fe483b28b8db90e19cf07595c", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x000000000000000000000000000000000000000000000000000000000000000c", + "after": "0x000000000000000000000000000000000000000000000000000000000000000d", + "description": "Increments the Safe nonce from 12 to 13 after executing the swapOwner transaction.", + "allowDifference": false + }, + { + "key": "0x656ef5b4b14cbca072b202c6f6f3a34c59dc29c67dfbbbd0f8de50e5f9e11249", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets approvedHashes[CB Coordinator Safe][txHash] = 1, recording the CB Coordinator Safe's approval for this transaction.", + "allowDifference": false + }, + { + "key": "0xad7f222f29a1b375bb6b2847405c3279f1dff83a30de9e555815353d94c2306b", + "before": "0x0000000000000000000000000000000000000000000000000000000000000001", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Removes OP Safe (0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a) from the owners linked list by clearing its entry.", + "allowDifference": false + }, + { + "key": "0xb79e2255a19f37f0fc20cbcd8e46bdb84bf2577e147dcf50c4471a96c0623f53", + "before": "0x0000000000000000000000009ba6e03d8b90de867373db8cf1a58d2f7f006b3a", + "after": "0x00000000000000000000000020acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "description": "Updates the owners linked list pointer to replace OP Safe with Security Council Safe (0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd).", + "allowDifference": false + }, + { + "key": "0xc68c82056a4782e6920506248dec06b7949eabf81d66a13e5e8ec70d4e8f4b13", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Adds Security Council Safe to the owners linked list by setting its entry to point to SENTINEL (0x1).", + "allowDifference": false + } + ] + }, + { + "name": "CB Coordinator Safe - Mainnet", + "address": "0x9855054731540a48b28990b63dcf4f33d8ae46a1", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000019", + "after": "0x000000000000000000000000000000000000000000000000000000000000001a", + "description": "Increments the Safe nonce from 25 to 26 after executing the nested approval transaction.", + "allowDifference": false + }, + { + "key": "0x5a2e7399a603e2f21ccde8c01d9d1493ce6bcd93b3bc7c7d048defe77279ec45", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets approvedHashes[CB Signer Safe][txHash] = 1, recording the CB Signer Safe's approval for the nested transaction to the Proxy Admin Owner.", + "allowDifference": false + } + ] + }, + { + "name": "CB Signer Safe - Mainnet", + "address": "0x9c4a57feb77e294fd7bf5ebe9ab01caa0a90a110", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000005", + "after": "0x0000000000000000000000000000000000000000000000000000000000000006", + "description": "Increments the Safe nonce from 5 to 6 after the signer submits their approval.", + "allowDifference": false + } + ] + } + ], + "balanceChanges": [] +} diff --git a/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-2.json b/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-2.json new file mode 100644 index 00000000..15e8b82e --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-2.json @@ -0,0 +1,167 @@ +{ + "cmd": "SAFE_NONCE_0X9C4A57FEB77E294FD7BF5EBE9AB01CAA0A90A110=6 SAFE_NONCE_0X9855054731540A48B28990B63DCF4F33D8AE46A1=26 forge script --rpc-url https://eth-mainnet.public.blastapi.io UpdateCBSafeSigners --sig sign(address[]) [0x9C4a57Feb77e294Fd7BF5EBE9AB01CAA0a90A110] --sender 0x6CD3850756b7894774Ab715D136F9dD02837De50", + "ledgerId": 0, + "rpcUrl": "https://eth-mainnet.public.blastapi.io", + "expectedDomainAndMessageHashes": { + "address": "0x9855054731540A48b28990B63DcF4f33d8AE46A1", + "domainHash": "0xfb308368b8deca582e84a807d31c1bfcec6fda754061e2801b4d6be5cb52a8ac", + "messageHash": "0xcf218ba5d243072a2ee37f449c5fde85ca0d6f6629545e6f5fa8a257603b01d5" + }, + "stateOverrides": [ + { + "name": "CB Coordinator Safe - Mainnet", + "address": "0x9855054731540a48b28990b63dcf4f33d8ae46a1", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur with a single signer.", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "value": "0x000000000000000000000000000000000000000000000000000000000000001a", + "description": "Override the nonce to 26, the expected value after Part 1 (UpdateProxyAdminOwnerSigners) executes.", + "allowDifference": false + } + ] + }, + { + "name": "CB Signer Safe - Mainnet", + "address": "0x9c4a57feb77e294fd7bf5ebe9ab01caa0a90a110", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur with a single signer.", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "value": "0x0000000000000000000000000000000000000000000000000000000000000006", + "description": "Override the nonce to 6, the expected value after Part 1 (UpdateProxyAdminOwnerSigners) executes.", + "allowDifference": false + }, + { + "key": "0x02096db9dce13194ab343de91965eb80e6976f198a5bbec4430338949271d2fb", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets approvedHashes[msg.sender][txHash] = 1 to simulate an approval from the signer, allowing the transaction simulation to succeed.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "CB Coordinator Safe - Mainnet", + "address": "0x9855054731540a48b28990b63dcf4f33d8ae46a1", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000003", + "before": "0x0000000000000000000000000000000000000000000000000000000000000002", + "after": "0x0000000000000000000000000000000000000000000000000000000000000006", + "description": "Updates the owner count from 2 to 6 (removes Security Council Safe and CB Nested Safe, adds 6 individual EOA signers).", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "before": "0x0000000000000000000000000000000000000000000000000000000000000001", + "after": "0x0000000000000000000000000000000000000000000000000000000000000003", + "description": "Updates the Safe execution threshold from 1 to 3 (3-of-6 required for execution).", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x000000000000000000000000000000000000000000000000000000000000001a", + "after": "0x000000000000000000000000000000000000000000000000000000000000001b", + "description": "Increments the Safe nonce from 26 to 27 after executing the UpdateCBSafeSigners transaction.", + "allowDifference": false + }, + { + "key": "0x1772a703ae675a1daf302df5578a40ec15df2da0f370aaae76c24b3dc3cf5a82", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Adds EOA signer to owners linked list by setting its entry to point to SENTINEL (0x1), marking it as the last owner.", + "allowDifference": false + }, + { + "key": "0x41ed7d57be3aeb16e937147407ec4fe9778850776e44a977f984860d4294c66f", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000003cd692ece8b6573a2220ae00d0deb98f0dffa9a1", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0x3cd692ece8b6573a2220ae00d0deb98f0dffa9a1).", + "allowDifference": false + }, + { + "key": "0x758f7362f1c252e82fc720287a93c81366f743607812809705593fe1d7c3853f", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x000000000000000000000000b011a32ed8b4f70d9943a2199f539bbecd7b62f7", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0xb011a32ed8b4f70d9943a2199f539bbecd7b62f7).", + "allowDifference": false + }, + { + "key": "0xc2eb038642409cacb7427667d194e7ffc6322f605a293c3088ea50e7387c0be8", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000001c870776b168a9ffae80c51f050c611edd246741", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0x1c870776b168a9ffae80c51f050c611edd246741).", + "allowDifference": false + }, + { + "key": "0xc68c82056a4782e6920506248dec06b7949eabf81d66a13e5e8ec70d4e8f4b13", + "before": "0x0000000000000000000000009c4a57feb77e294fd7bf5ebe9ab01caa0a90a110", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Removes CB Nested Safe (0x9c4a57feb77e294fd7bf5ebe9ab01caa0a90a110) from the owners linked list by clearing its entry.", + "allowDifference": false + }, + { + "key": "0xdaf8a00b5bed19da86efcb9f47bb7ded536a0ee70f1c43b062cb09e630709778", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000003dad2200849925bb46d9bf05afa5f7f213f4c18e", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0x3dad2200849925bb46d9bf05afa5f7f213f4c18e).", + "allowDifference": false + }, + { + "key": "0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0", + "before": "0x00000000000000000000000020acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "after": "0x000000000000000000000000f9e320f3da12e68af219d9e2a490dd649f6b177c", + "description": "Updates SENTINEL pointer (head of owners linked list) from Security Council Safe (0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd) to new first EOA signer (0xf9e320f3da12e68af219d9e2a490dd649f6b177c).", + "allowDifference": false + }, + { + "key": "0xf2703bf6aed371ed0006c3f1370d925046c90122272e607d356ede2c94165357", + "before": "0x0000000000000000000000000000000000000000000000000000000000000001", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Removes Security Council Safe (0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd) from owners linked list by clearing its entry (was pointing to SENTINEL).", + "allowDifference": false + }, + { + "key": "0xf50027dc233102bb13bb30a38326315505fe2452eaf2e2f78f1c0da0084d86c4", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000006cd3850756b7894774ab715d136f9dd02837de50", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0x6cd3850756b7894774ab715d136f9dd02837de50).", + "allowDifference": false + }, + { + "key": "0xfc3210f35f35f3bd1bd9c69b0b63bcfe5c94eefb24b5d3ec1c36a82d273ec2f7", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Stores approval on behalf of CB Safe.", + "allowDifference": false + } + ] + }, + { + "name": "CB Signer Safe - Mainnet", + "address": "0x9c4a57feb77e294fd7bf5ebe9ab01caa0a90a110", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000006", + "after": "0x0000000000000000000000000000000000000000000000000000000000000007", + "description": "Increments the Safe nonce from 6 to 7 after the signer submits their approval.", + "allowDifference": false + } + ] + } + ], + "balanceChanges": [] +} diff --git a/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-5.json b/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-5.json new file mode 100644 index 00000000..eec657db --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-5.json @@ -0,0 +1,86 @@ +{ + "cmd": "forge script --rpc-url https://mainnet.optimism.io TerminateSmartEscrow --sig sign(address[]) [] --sender 0x6CD3850756b7894774Ab715D136F9dD02837De50", + "ledgerId": 0, + "rpcUrl": "https://mainnet.optimism.io", + "expectedDomainAndMessageHashes": { + "address": "0x6e1DFd5C1E22A4677663A81D24C6BA03561ef0f6", + "domainHash": "0x4ac8f60706331537a33c4a4cbda024cb722855e6b160a3e8b28ab487510598b1", + "messageHash": "0xff53888d026d3cf3557a3c91bd99ca58e97d342cef8dc2ed5a190c4a3352f799" + }, + "stateOverrides": [ + { + "name": "CB Signer Safe - Optimism", + "address": "0x6e1dfd5c1e22a4677663a81d24c6ba03561ef0f6", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0x13c86125570d7981706f2b7a58917ffc9e8ea8faf77052182023566ddb61da6c", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Simulates an approval from msg.sender in order for the task simulation to succeed.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "Governance Token - Optimism", + "address": "0x4200000000000000000000000000000000000042", + "changes": [ + { + "key": "0x9c7ffc2d159d7a0a8143e9bb815194a3d0239701011646f61c42aefd6122e68c", + "before": "0x0000000000000000000000000000000000000000000766c7cfe16d7e0d200000", + "after": "0x00000000000000000000000000000000000000000003b363e7f0b6bf06900000", + "description": "Decreases SmartEscrow's (0xb3c2f9fc2727078ec3a2255410e83ba5b62c5b5f) OP token balance from 8,947,848 OP to 4,473,924 OP as vested tokens are released to the beneficiary.", + "allowDifference": true + }, + { + "key": "0xa25c112a98bc980e2e953a0c73e7bfb4ea80467bb8f79b4fb803243e3595db67", + "before": "0x0000000000000000000000000000000000000000002a303fb9d34d5f1c0b0000", + "after": "0x0000000000000000000000000000000000000000002de3a3a1c4041e229b0000", + "description": "Increases the beneficiary's (0x635fb974f09b269bc750bf96338c29cf41430125) OP token balance by 4,473,924 OP, receiving the vested tokens released during termination.", + "allowDifference": true + } + ] + }, + { + "name": "CB Signer Safe - Optimism", + "address": "0x6e1dfd5c1e22a4677663a81d24c6ba03561ef0f6", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000003", + "after": "0x0000000000000000000000000000000000000000000000000000000000000004", + "description": "Increments the Safe nonce from 3 to 4 after executing the TerminateSmartEscrow transaction.", + "allowDifference": false + } + ] + }, + { + "name": "SmartEscrow - Optimism", + "address": "0xb3c2f9fc2727078ec3a2255410e83ba5b62c5b5f", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000002a303fcdf2bcb0685c0000", + "after": "0x0000000000000000000000000000000000000000002de3a3b5e3736f6eec0000", + "description": "Increases totalAmountReleased from 51,002,735 OP to 55,476,659 OP, tracking the cumulative 4,473,924 OP released to the beneficiary during termination.", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000006", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets contractTerminated from false (0) to true (1), marking the SmartEscrow contract as permanently terminated.", + "allowDifference": false + } + ] + } + ], + "balanceChanges": [] +} diff --git a/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-6.json b/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-6.json new file mode 100644 index 00000000..bcccc192 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/validations/coinbase-signer-part-6.json @@ -0,0 +1,116 @@ +{ + "cmd": "SAFE_NONCE_0X6E1DFD5C1E22A4677663A81D24C6BA03561EF0F6=4 forge script --rpc-url https://mainnet.optimism.io WithdrawSmartEscrow --sig sign(address[]) [0x6e1DFd5C1E22A4677663A81D24C6BA03561ef0f6] --sender 0x6CD3850756b7894774Ab715D136F9dD02837De50", + "ledgerId": 0, + "rpcUrl": "https://mainnet.optimism.io", + "expectedDomainAndMessageHashes": { + "address": "0x0a7361e734cf3f0394B0FC4a45C74E7a4Ec70940", + "domainHash": "0x4ac8f60706331537a33c4a4cbda024cb722855e6b160a3e8b28ab487510598b1", + "messageHash": "0xe47bca5a208fb4739ae2381f86cb91d5386307481a7381c8381fe0e5f99186bb" + }, + "stateOverrides": [ + { + "name": "Owner Safe - Optimism", + "address": "0x0a7361e734cf3f0394b0fc4a45c74e7a4ec70940", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + } + ] + }, + { + "name": "CB Signer Safe - Optimism", + "address": "0x6e1dfd5c1e22a4677663a81d24c6ba03561ef0f6", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "value": "0x0000000000000000000000000000000000000000000000000000000000000004", + "description": "Override the nonce to 4 to simulate the state after parts 1-5 have been executed.", + "allowDifference": false + }, + { + "key": "0x3d9213bb9c16b3af7b48531e517a418f86c1ed190cf932410b9396a84c06eb31", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Simulates an approval from msg.sender in order for the task simulation to succeed.", + "allowDifference": false + } + ] + }, + { + "name": "SmartEscrow - Optimism", + "address": "0xb3c2f9fc2727078ec3a2255410e83ba5b62c5b5f", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000006", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Overrides contractTerminated from false (0) to true (1), marking the SmartEscrow contract as permanently terminated.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "Owner Safe - Optimism", + "address": "0x0a7361e734cf3f0394b0fc4a45c74e7a4ec70940", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000002", + "after": "0x0000000000000000000000000000000000000000000000000000000000000003", + "description": "Increments the Safe nonce from 2 to 3 after executing the WithdrawSmartEscrow transaction.", + "allowDifference": false + }, + { + "key": "0xdaefa18710789b315c073a0a6715e936e4dd091921c4de5bca445545fd20e45a", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets approvedHashes[CB Signer Safe][txHash] = 1, recording the CB Signer Safe's (0x6e1dfd5c1e22a4677663a81d24c6ba03561ef0f6) approval for this transaction.", + "allowDifference": false + } + ] + }, + { + "name": "Governance Token - Optimism", + "address": "0x4200000000000000000000000000000000000042", + "changes": [ + { + "key": "0x9c7ffc2d159d7a0a8143e9bb815194a3d0239701011646f61c42aefd6122e68c", + "before": "0x0000000000000000000000000000000000000000000766c7cfe16d7e0d200000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Decreases SmartEscrow's (0xb3c2f9fc2727078ec3a2255410e83ba5b62c5b5f) OP token balance to 0 as all remaining unvested tokens are withdrawn to the benefactor. Note: The 'before' value reflects current state; after part-5 executes, the actual balance will be ~4,473,924 OP.", + "allowDifference": true + }, + { + "key": "0xcac14c40c8d3847e740432064e9977516347ef1cecdd901d8d3a7d24e118edde", + "before": "0x000000000000000000000000000000000000000001ae577902e80b80e9414e71", + "after": "0x000000000000000000000000000000000000000001b5be40d2c978fef6614e71", + "description": "Increases the benefactor's (Owner Safe: 0x0a7361e734cf3f0394b0fc4a45c74e7a4ec70940) OP token balance, receiving the unvested tokens withdrawn from the SmartEscrow contract. Note: The actual amount will be ~4,473,924 OP after part-5 executes.", + "allowDifference": true + } + ] + }, + { + "name": "CB Signer Safe - Optimism", + "address": "0x6e1dfd5c1e22a4677663a81d24c6ba03561ef0f6", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000004", + "after": "0x0000000000000000000000000000000000000000000000000000000000000005", + "description": "Increments the Safe nonce from 4 to 5 after the CB Signer Safe submits its approval for the WithdrawSmartEscrow transaction.", + "allowDifference": false + } + ] + } + ], + "balanceChanges": [] +} diff --git a/mainnet/2026-02-19-superchain-separation/validations/optimism-op-mainnet-signer.json b/mainnet/2026-02-19-superchain-separation/validations/optimism-op-mainnet-signer.json new file mode 100644 index 00000000..eedfc36c --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/validations/optimism-op-mainnet-signer.json @@ -0,0 +1,110 @@ +{ + "cmd": "forge script --rpc-url https://mainnet.optimism.io WithdrawSmartEscrow --sig sign(address[]) [0x2501c477D0A35545a387Aa4A3EEe4292A9a8B3F0] --sender 0x4d494C5F61b60752D3A10062276a0eFC22596151", + "ledgerId": 0, + "rpcUrl": "https://mainnet.optimism.io", + "expectedDomainAndMessageHashes": { + "address": "0x0a7361e734cf3f0394B0FC4a45C74E7a4Ec70940", + "domainHash": "0xb34978142f4478f3e5633915597a756daa58a1a59a3e0234f9acd5444f1ca70e", + "messageHash": "0x3ffed7c94bddf882232c2ec9ff3ccabb16ea3fb6396e716a53b9310b867c8e31" + }, + "stateOverrides": [ + { + "name": "Owner Safe - Optimism", + "address": "0x0a7361e734cf3f0394b0fc4a45c74e7a4ec70940", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + } + ] + }, + { + "name": "Optimism Signer Safe - Optimism", + "address": "0x2501c477d0a35545a387aa4a3eee4292a9a8b3f0", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0x5aafabfc11918d52e3ca9c466bb8697b5dd2c738d05a136f44e4855dd8ba934f", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Simulates an approval from msg.sender in order for the task simulation to succeed.", + "allowDifference": false + } + ] + }, + { + "name": "SmartEscrow - Optimism", + "address": "0xb3c2f9fc2727078ec3a2255410e83ba5b62c5b5f", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000006", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Overrides contractTerminated from false (0) to true (1), marking the SmartEscrow contract as permanently terminated.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "Owner Safe - Optimism", + "address": "0x0a7361e734cf3f0394b0fc4a45c74e7a4ec70940", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000002", + "after": "0x0000000000000000000000000000000000000000000000000000000000000003", + "description": "Increments the Safe nonce from 2 to 3 after executing the WithdrawSmartEscrow transaction.", + "allowDifference": false + }, + { + "key": "0xb1c95fcac4fdae1ba0795d84c4fcffb6e0c71dec884cdedbdde2ed87bdc4541f", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets approvedHashes[Optimism Signer Safe][txHash] = 1, recording the Optimism Signer Safe's (0x2501c477d0a35545a387aa4a3eee4292a9a8b3f0) approval for this transaction.", + "allowDifference": false + } + ] + }, + { + "name": "Optimism Signer Safe - Optimism", + "address": "0x2501c477d0a35545a387aa4a3eee4292a9a8b3f0", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x00000000000000000000000000000000000000000000000000000000000000b5", + "after": "0x00000000000000000000000000000000000000000000000000000000000000b6", + "description": "Increments the Safe nonce from 181 to 182 after the Optimism Signer Safe submits its approval for the WithdrawSmartEscrow transaction.", + "allowDifference": false + } + ] + }, + { + "name": "Governance Token - Optimism", + "address": "0x4200000000000000000000000000000000000042", + "changes": [ + { + "key": "0x9c7ffc2d159d7a0a8143e9bb815194a3d0239701011646f61c42aefd6122e68c", + "before": "0x0000000000000000000000000000000000000000000766c7cfe16d7e0d200000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Decreases SmartEscrow's (0xb3c2f9fc2727078ec3a2255410e83ba5b62c5b5f) OP token balance to 0 as all remaining unvested tokens are withdrawn to the benefactor. Note: The 'before' value reflects current state; after part-5 executes, the actual balance will be ~4,473,924 OP.", + "allowDifference": true + }, + { + "key": "0xcac14c40c8d3847e740432064e9977516347ef1cecdd901d8d3a7d24e118edde", + "before": "0x000000000000000000000000000000000000000001ae577902e80b80e9414e71", + "after": "0x000000000000000000000000000000000000000001b5be40d2c978fef6614e71", + "description": "Increases the benefactor's (Owner Safe: 0x0a7361e734cf3f0394b0fc4a45c74e7a4ec70940) OP token balance, receiving the unvested tokens withdrawn from the SmartEscrow contract. Note: The actual amount will be ~4,473,924 OP after part-5 executes.", + "allowDifference": true + } + ] + } + ], + "balanceChanges": [] +} diff --git a/mainnet/2026-02-19-superchain-separation/validations/optimism-signer.json b/mainnet/2026-02-19-superchain-separation/validations/optimism-signer.json new file mode 100644 index 00000000..99f4c629 --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/validations/optimism-signer.json @@ -0,0 +1,99 @@ +{ + "cmd": "forge script --rpc-url https://eth-mainnet.public.blastapi.io UpdateProxyAdminOwnerSigners --sig sign(address[]) [0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A] --sender 0x42d27eEA1AD6e22Af6284F609847CB3Cd56B9c64", + "ledgerId": 0, + "rpcUrl": "https://eth-mainnet.public.blastapi.io", + "expectedDomainAndMessageHashes": { + "address": "0x7bB41C3008B3f03FE483B28b8DB90e19Cf07595c", + "domainHash": "0x2e5ad244d335c45fbace4ebd1736b0fad81b01591a2819baedad311ead5bce76", + "messageHash": "0x7f5de36359f775e1c37d61905081bc7dffd5ed6905f209a3d24fcc2ef7171014" + }, + "stateOverrides": [ + { + "name": "Proxy Admin Owner - Mainnet", + "address": "0x7bb41c3008b3f03fe483b28b8db90e19cf07595c", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + } + ] + }, + { + "name": "OP Signer Safe - Mainnet", + "address": "0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0x084f23a835476254f554ff008fb4f329b511becce8e5c2e5beb3e32ffaf3cf68", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Simulates an approval from msg.sender in order for the task simulation to succeed.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "Proxy Admin Owner - Mainnet", + "address": "0x7bb41c3008b3f03fe483b28b8db90e19cf07595c", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x000000000000000000000000000000000000000000000000000000000000000c", + "after": "0x000000000000000000000000000000000000000000000000000000000000000d", + "description": "Increments the Safe nonce from 12 to 13 after executing the swapOwner transaction.", + "allowDifference": false + }, + { + "key": "0x32a1a1697b7d58db65fedf41799bee0099320ee6b08d854617ae83bf8f45f996", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets approvedHashes[OP Safe][txHash] = 1, recording the OP Safe's approval for this transaction.", + "allowDifference": false + }, + { + "key": "0xad7f222f29a1b375bb6b2847405c3279f1dff83a30de9e555815353d94c2306b", + "before": "0x0000000000000000000000000000000000000000000000000000000000000001", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Removes OP Safe (0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a) from the owners linked list by clearing its entry.", + "allowDifference": false + }, + { + "key": "0xb79e2255a19f37f0fc20cbcd8e46bdb84bf2577e147dcf50c4471a96c0623f53", + "before": "0x0000000000000000000000009ba6e03d8b90de867373db8cf1a58d2f7f006b3a", + "after": "0x00000000000000000000000020acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "description": "Updates the owners linked list pointer to replace OP Safe with Security Council Safe (0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd).", + "allowDifference": false + }, + { + "key": "0xc68c82056a4782e6920506248dec06b7949eabf81d66a13e5e8ec70d4e8f4b13", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Adds Security Council Safe to the owners linked list by setting its entry to point to SENTINEL (0x1).", + "allowDifference": false + } + ] + }, + { + "name": "OP Signer Safe - Mainnet", + "address": "0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000071", + "after": "0x0000000000000000000000000000000000000000000000000000000000000072", + "description": "Increments the Safe nonce from 113 to 114 after the signer submits their approval.", + "allowDifference": false + } + ] + } + ], + "balanceChanges": [] +} diff --git a/mainnet/2026-02-19-superchain-separation/validations/security-council-signer-part-1.json b/mainnet/2026-02-19-superchain-separation/validations/security-council-signer-part-1.json new file mode 100644 index 00000000..1bf491fb --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/validations/security-council-signer-part-1.json @@ -0,0 +1,131 @@ +{ + "cmd": "forge script --rpc-url https://eth-mainnet.public.blastapi.io UpdateProxyAdminOwnerSigners --sig sign(address[]) [0x20AcF55A3DCfe07fC4cecaCFa1628F788EC8A4Dd,0x9855054731540A48b28990B63DcF4f33d8AE46A1] --sender 0x5ff5C78ff194acc24C22DAaDdE4D639ebF18ACC6", + "ledgerId": 0, + "rpcUrl": "https://eth-mainnet.public.blastapi.io", + "expectedDomainAndMessageHashes": { + "address": "0x7bB41C3008B3f03FE483B28b8DB90e19Cf07595c", + "domainHash": "0x1fbfdc61ceb715f63cb17c56922b88c3a980f1d83873df2b9325a579753e8aa3", + "messageHash": "0xbf33ec62fca0fa5d0ac4e2cc63314a9c881452ce8f46e5371c8cd062559b69db" + }, + "stateOverrides": [ + { + "name": "Security Council Safe - Mainnet", + "address": "0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0x4e0751c71d4768f76bad2873e575def814ef86ee49f2b483a42df7f3a38af0cb", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Simulates an approval from msg.sender in order for the task simulation to succeed.", + "allowDifference": false + } + ] + }, + { + "name": "Proxy Admin Owner - Mainnet", + "address": "0x7bb41c3008b3f03fe483b28b8db90e19cf07595c", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + } + ] + }, + { + "name": "CB Coordinator Safe - Mainnet", + "address": "0x9855054731540a48b28990b63dcf4f33d8ae46a1", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "Security Council Safe - Mainnet", + "address": "0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000004", + "after": "0x0000000000000000000000000000000000000000000000000000000000000005", + "description": "Increments the Safe nonce from 4 to 5 after the signer submits their approval.", + "allowDifference": false + } + ] + }, + { + "name": "Proxy Admin Owner - Mainnet", + "address": "0x7bb41c3008b3f03fe483b28b8db90e19cf07595c", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x000000000000000000000000000000000000000000000000000000000000000c", + "after": "0x000000000000000000000000000000000000000000000000000000000000000d", + "description": "Increments the Safe nonce from 12 to 13 after executing the swapOwner transaction.", + "allowDifference": false + }, + { + "key": "0x656ef5b4b14cbca072b202c6f6f3a34c59dc29c67dfbbbd0f8de50e5f9e11249", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets approvedHashes[CB Coordinator Safe][txHash] = 1, recording the CB Coordinator Safe's approval for this transaction.", + "allowDifference": false + }, + { + "key": "0xad7f222f29a1b375bb6b2847405c3279f1dff83a30de9e555815353d94c2306b", + "before": "0x0000000000000000000000000000000000000000000000000000000000000001", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Removes OP Safe (0x9ba6e03d8b90de867373db8cf1a58d2f7f006b3a) from the owners linked list by clearing its entry.", + "allowDifference": false + }, + { + "key": "0xb79e2255a19f37f0fc20cbcd8e46bdb84bf2577e147dcf50c4471a96c0623f53", + "before": "0x0000000000000000000000009ba6e03d8b90de867373db8cf1a58d2f7f006b3a", + "after": "0x00000000000000000000000020acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "description": "Updates the owners linked list pointer to replace OP Safe with Security Council Safe (0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd).", + "allowDifference": false + }, + { + "key": "0xc68c82056a4782e6920506248dec06b7949eabf81d66a13e5e8ec70d4e8f4b13", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Adds Security Council Safe to the owners linked list by setting its entry to point to SENTINEL (0x1).", + "allowDifference": false + } + ] + }, + { + "name": "CB Coordinator Safe - Mainnet", + "address": "0x9855054731540a48b28990b63dcf4f33d8ae46a1", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000019", + "after": "0x000000000000000000000000000000000000000000000000000000000000001a", + "description": "Increments the Safe nonce from 25 to 26 after executing the nested approval transaction.", + "allowDifference": false + }, + { + "key": "0xaa10226b3a9d61778d3a357d07f03e02ac17f7c61d581b9724ea2e14760ce99b", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Sets approvedHashes[SC Signer Safe][txHash] = 1, recording the SC Signer Safe's approval for the nested transaction to the Proxy Admin Owner.", + "allowDifference": false + } + ] + } + ], + "balanceChanges": [] +} diff --git a/mainnet/2026-02-19-superchain-separation/validations/security-council-signer-part-2.json b/mainnet/2026-02-19-superchain-separation/validations/security-council-signer-part-2.json new file mode 100644 index 00000000..71ba7c4b --- /dev/null +++ b/mainnet/2026-02-19-superchain-separation/validations/security-council-signer-part-2.json @@ -0,0 +1,167 @@ +{ + "cmd": "SAFE_NONCE_0X20ACF55A3DCFE07FC4CECACFA1628F788EC8A4DD=5 SAFE_NONCE_0X9855054731540A48B28990B63DCF4F33D8AE46A1=26 forge script --rpc-url https://eth-mainnet.public.blastapi.io UpdateCBSafeSigners --sig sign(address[]) [0x20AcF55A3DCfe07fC4cecaCFa1628F788EC8A4Dd] --sender 0x5ff5C78ff194acc24C22DAaDdE4D639ebF18ACC6", + "ledgerId": 0, + "rpcUrl": "https://eth-mainnet.public.blastapi.io", + "expectedDomainAndMessageHashes": { + "address": "0x9855054731540A48b28990B63DcF4f33d8AE46A1", + "domainHash": "0x1fbfdc61ceb715f63cb17c56922b88c3a980f1d83873df2b9325a579753e8aa3", + "messageHash": "0x7154e24460ea33c5b171dfc4afdea3d8dbf68aeeeda39ddefb156e2a843ff580" + }, + "stateOverrides": [ + { + "name": "Security Council Safe - Mainnet", + "address": "0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "value": "0x0000000000000000000000000000000000000000000000000000000000000005", + "description": "Override the nonce to be our expected value after initial execution.", + "allowDifference": false + }, + { + "key": "0x20e68b4998d5b9dcd509344b3da5ccb127c8777b46427794ec01b23b3134f849", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Simulates an approval from msg.sender in order for the task simulation to succeed.", + "allowDifference": false + } + ] + }, + { + "name": "CB Coordinator Safe - Mainnet", + "address": "0x9855054731540a48b28990b63dcf4f33d8ae46a1", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "value": "0x000000000000000000000000000000000000000000000000000000000000001a", + "description": "Override the nonce to be our expected value after initial execution.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "Security Council Safe - Mainnet", + "address": "0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x0000000000000000000000000000000000000000000000000000000000000005", + "after": "0x0000000000000000000000000000000000000000000000000000000000000006", + "description": "Increments the Safe nonce from 5 to 6 after the signer submits their approval.", + "allowDifference": false + } + ] + }, + { + "name": "CB Coordinator Safe - Mainnet", + "address": "0x9855054731540a48b28990b63dcf4f33d8ae46a1", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000003", + "before": "0x0000000000000000000000000000000000000000000000000000000000000002", + "after": "0x0000000000000000000000000000000000000000000000000000000000000006", + "description": "Updates the owner count from 2 to 6 (removes Security Council Safe and CB Nested Safe, adds 6 individual EOA signers).", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "before": "0x0000000000000000000000000000000000000000000000000000000000000001", + "after": "0x0000000000000000000000000000000000000000000000000000000000000003", + "description": "Updates the Safe execution threshold from 1 to 3 (3-of-6 required for execution).", + "allowDifference": false + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x000000000000000000000000000000000000000000000000000000000000001a", + "after": "0x000000000000000000000000000000000000000000000000000000000000001b", + "description": "Increments the Safe nonce from 26 to 27 after executing the UpdateCBSafeSigners transaction.", + "allowDifference": false + }, + { + "key": "0x1772a703ae675a1daf302df5578a40ec15df2da0f370aaae76c24b3dc3cf5a82", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Adds EOA signer to owners linked list by setting its entry to point to SENTINEL (0x1), marking it as the last owner.", + "allowDifference": false + }, + { + "key": "0x2147295fdb477f732b4c1cdbe7372bb22db8dfb3d987f35032751f057d24cd71", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Stores approval on behalf of SC Safe.", + "allowDifference": false + }, + { + "key": "0x41ed7d57be3aeb16e937147407ec4fe9778850776e44a977f984860d4294c66f", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000003cd692ece8b6573a2220ae00d0deb98f0dffa9a1", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0x3cd692ece8b6573a2220ae00d0deb98f0dffa9a1).", + "allowDifference": false + }, + { + "key": "0x758f7362f1c252e82fc720287a93c81366f743607812809705593fe1d7c3853f", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x000000000000000000000000b011a32ed8b4f70d9943a2199f539bbecd7b62f7", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0xb011a32ed8b4f70d9943a2199f539bbecd7b62f7).", + "allowDifference": false + }, + { + "key": "0xc2eb038642409cacb7427667d194e7ffc6322f605a293c3088ea50e7387c0be8", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000001c870776b168a9ffae80c51f050c611edd246741", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0x1c870776b168a9ffae80c51f050c611edd246741).", + "allowDifference": false + }, + { + "key": "0xc68c82056a4782e6920506248dec06b7949eabf81d66a13e5e8ec70d4e8f4b13", + "before": "0x0000000000000000000000009c4a57feb77e294fd7bf5ebe9ab01caa0a90a110", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Removes CB Nested Safe (0x9c4a57feb77e294fd7bf5ebe9ab01caa0a90a110) from the owners linked list by clearing its entry.", + "allowDifference": false + }, + { + "key": "0xdaf8a00b5bed19da86efcb9f47bb7ded536a0ee70f1c43b062cb09e630709778", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000003dad2200849925bb46d9bf05afa5f7f213f4c18e", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0x3dad2200849925bb46d9bf05afa5f7f213f4c18e).", + "allowDifference": false + }, + { + "key": "0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0", + "before": "0x00000000000000000000000020acf55a3dcfe07fc4cecacfa1628f788ec8a4dd", + "after": "0x000000000000000000000000f9e320f3da12e68af219d9e2a490dd649f6b177c", + "description": "Updates SENTINEL pointer (head of owners linked list) from Security Council Safe (0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd) to new first EOA signer (0xf9e320f3da12e68af219d9e2a490dd649f6b177c).", + "allowDifference": false + }, + { + "key": "0xf2703bf6aed371ed0006c3f1370d925046c90122272e607d356ede2c94165357", + "before": "0x0000000000000000000000000000000000000000000000000000000000000001", + "after": "0x0000000000000000000000000000000000000000000000000000000000000000", + "description": "Removes Security Council Safe (0x20acf55a3dcfe07fc4cecacfa1628f788ec8a4dd) from owners linked list by clearing its entry (was pointing to SENTINEL).", + "allowDifference": false + }, + { + "key": "0xf50027dc233102bb13bb30a38326315505fe2452eaf2e2f78f1c0da0084d86c4", + "before": "0x0000000000000000000000000000000000000000000000000000000000000000", + "after": "0x0000000000000000000000006cd3850756b7894774ab715d136f9dd02837de50", + "description": "Adds EOA signer to owners linked list, setting its entry to point to next owner (0x6cd3850756b7894774ab715d136f9dd02837de50).", + "allowDifference": false + } + ] + } + ], + "balanceChanges": [] +}