diff --git a/mainnet/2025-12-01-accept-smart-escrow-ownership/.env b/mainnet/2025-12-01-accept-smart-escrow-ownership/.env new file mode 100644 index 00000000..09e51227 --- /dev/null +++ b/mainnet/2025-12-01-accept-smart-escrow-ownership/.env @@ -0,0 +1,21 @@ +OP_COMMIT=d09c836f818c73ae139f60b717654c4e53712743 +BASE_CONTRACTS_COMMIT=9526d38b63be2ee10fc905dee60f0b2a0a17e89e + +# TODO: Update to set the Safe receiving ownership +OWNER_SAFE=TODO + +# CB Safe receiving ownership +CB_SAFE=0x9C4a57Feb77e294Fd7BF5EBE9AB01CAA0a90A110 +# OP Safe receiving ownership +OP_SAFE=0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A + +# Update to be a member of the CB Safe receiving ownership +CB_SIGNER=0x6CD3850756b7894774Ab715D136F9dD02837De50 +# Update to be a member of the OP Safe receiving ownership +OP_SIGNER=0x42d27eEA1AD6e22Af6284F609847CB3Cd56B9c64 + +L2_GAS_LIMIT=200000 +SMART_ESCROW=0xb3C2f9fC2727078EC3A2255410e83BA5B62c5B5f +PORTAL=0xbEb5Fc579115071764c7423A4f12eDde41f106Ed + +RECORD_STATE_DIFF=true diff --git a/mainnet/2025-12-01-accept-smart-escrow-ownership/FACILITATORS.md b/mainnet/2025-12-01-accept-smart-escrow-ownership/FACILITATORS.md new file mode 100644 index 00000000..8e282baa --- /dev/null +++ b/mainnet/2025-12-01-accept-smart-escrow-ownership/FACILITATORS.md @@ -0,0 +1,54 @@ +#### Execute the transaction + +## 1. Update repo: + +```bash +cd contract-deployments +git pull +cd mainnet/2025-12-01-accept-smart-escrow-ownership +make deps +``` + +1. Collect outputs from all participating signers. +1. Concatenate all signatures and export it as the `SIGNATURES` + environment variable, i.e. `export +SIGNATURES="[SIGNATURE1][SIGNATURE2]..."`. +1. Run the `make execute` or `make approve` command as described below to execute the transaction. + +For example, if the quorum is 3 and you get the following outputs: + +```shell +Data: 0xDEADBEEF +Signer: 0xC0FFEE01 +Signature: AAAA +``` + +```shell +Data: 0xDEADBEEF +Signer: 0xC0FFEE02 +Signature: BBBB +``` + +```shell +Data: 0xDEADBEEF +Signer: 0xC0FFEE03 +Signature: CCCC +``` + +Coinbase facilitator: + +```bash +SIGNATURES=AAAABBBBCCCC make approve-cb +``` + +Optimism facilitator: + +```bash +SIGNATURES=AAAABBBBCCCC make approve-op +``` + +Once the signatures have been submitted approving the transaction for all nested Safes run: + +```bash +make execute +``` diff --git a/mainnet/2025-12-01-accept-smart-escrow-ownership/Makefile b/mainnet/2025-12-01-accept-smart-escrow-ownership/Makefile new file mode 100644 index 00000000..0a4b79df --- /dev/null +++ b/mainnet/2025-12-01-accept-smart-escrow-ownership/Makefile @@ -0,0 +1,48 @@ +include ../../Makefile +include ../../Multisig.mk +include ../.env +include .env + +ifndef LEDGER_ACCOUNT +override LEDGER_ACCOUNT = 0 +endif + +RPC_URL = $(L1_RPC_URL) +SCRIPT_NAME = AcceptSmartEscrowOwnership + +.PHONY: gen-validation +gen-validation: gen-validation-cb gen-validation-op + +.PHONY: gen-validation-cb +gen-validation-cb: checkout-signer-tool run-script-cb + +.PHONY: gen-validation-op +gen-validation-op: checkout-signer-tool run-script-op + +.PHONY: run-script-cb +run-script-cb: + cd $(SIGNER_TOOL_PATH); \ + npm ci; \ + bun run scripts/genValidationFile.ts --rpc-url $(RPC_URL) \ + --workdir .. --forge-cmd 'forge script --rpc-url $(RPC_URL) \ + $(SCRIPT_NAME) --sig "sign(address[])" "[$(CB_SAFE)]" --sender $(CB_SIGNER)' --out ../validations/cb-signer.json; + +.PHONY: run-script-op +run-script-op: + cd $(SIGNER_TOOL_PATH); \ + npm ci; \ + bun run scripts/genValidationFile.ts --rpc-url $(RPC_URL) \ + --workdir .. --forge-cmd 'forge script --rpc-url $(RPC_URL) \ + $(SCRIPT_NAME) --sig "sign(address[])" "[$(OP_SAFE)]" --sender $(OP_SIGNER)' --out ../validations/op-signer.json; + +.PHONY: approve-cb +approve-cb: + $(call MULTISIG_APPROVE,$(CB_SAFE),$(SIGNATURES)) + +.PHONY: approve-op +approve-op: + $(call MULTISIG_APPROVE,$(OP_SAFE),$(SIGNATURES)) + +.PHONY: execute +execute: + $(call MULTISIG_EXECUTE,0x) diff --git a/mainnet/2025-12-01-accept-smart-escrow-ownership/README.md b/mainnet/2025-12-01-accept-smart-escrow-ownership/README.md new file mode 100644 index 00000000..d9064ab2 --- /dev/null +++ b/mainnet/2025-12-01-accept-smart-escrow-ownership/README.md @@ -0,0 +1,63 @@ +# Accept SmartEscrow Ownership + +Status: READY TO SIGN + +## Description + +This task finalises the `SmartEscrow` ownership transfer that was initiated in [`2025-04-07-init-smart-escrow-ownership-transfer`](https://github.com/base/contract-deployments/blob/main/mainnet/2025-04-07-init-smart-escrow-ownership-transfer/README.md). The pending admin is the L1 `ProxyAdmin` owner Safe (aliased on L2); after the mandatory delay expires we must relay an L1 transaction that calls `AccessControlDefaultAdminRules.acceptDefaultAdminTransfer()` on the SmartEscrow contract (`0xb3C2f9fC2727078EC3A2255410e83BA5B62c5B5f`). + +We send this call from the L1 owner Safe by depositing a transaction through the Optimism Portal (`0xbEb5Fc579115071764c7423A4f12eDde41f106Ed`) with the Safe as the sender. The call includes no ETH transfer to SmartEscrow, but the Safe must cover the L2 execution fee (`L2_FEE`). + +Environment variables: + +- `OWNER_SAFE`: L1 Safe that owns SmartEscrow after acceptance. +- `SMART_ESCROW`: SmartEscrow contract on OP Mainnet. +- `PORTAL`: Optimism Portal on Ethereum mainnet. +- `L2_GAS_LIMIT`: Gas limit forwarded to L2 for `acceptDefaultAdminTransfer`. +- `L2_FEE`: ETH sent to the portal to pay the L2 execution fee (set at runtime). + +## 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 +``` + +## Approving the Update transaction + +### 1. Update repo: + +```bash +cd contract-deployments +git pull +``` + +### 2. Run the signing tool (NOTE: do not enter the task directory. Run this command from the project's root). + +```bash +make sign-task +``` + +### 3. Open the UI at [http://localhost:3000](http://localhost:3000) + +Be sure to select the correct task user from the list of available users to sign. +After completion, the signer tool can be closed by using Ctrl + C. + +### 4. Send the signature to the facilitator diff --git a/mainnet/2025-12-01-accept-smart-escrow-ownership/foundry.toml b/mainnet/2025-12-01-accept-smart-escrow-ownership/foundry.toml new file mode 100644 index 00000000..ba63c28b --- /dev/null +++ b/mainnet/2025-12-01-accept-smart-escrow-ownership/foundry.toml @@ -0,0 +1,27 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +broadcast = 'records' +fs_permissions = [{ access = "read-write", path = "./" }] +optimizer = true +optimizer_runs = 999999 +solc_version = "0.8.15" +via-ir = false +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/', + "interfaces/=lib/optimism/packages/contracts-bedrock/interfaces", + "src/dispute/=lib/optimism/packages/contracts-bedrock/src/dispute", + "src/libraries/=lib/optimism/packages/contracts-bedrock/src/libraries", + "src/cannon/=lib/optimism/packages/contracts-bedrock/src/cannon", + "src/L1/=lib/optimism/packages/contracts-bedrock/src/L1", + "scripts/=lib/optimism/packages/contracts-bedrock/scripts", +] + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/mainnet/2025-12-01-accept-smart-escrow-ownership/script/AcceptSmartEscrowOwnership.s.sol b/mainnet/2025-12-01-accept-smart-escrow-ownership/script/AcceptSmartEscrowOwnership.s.sol new file mode 100644 index 00000000..e704281d --- /dev/null +++ b/mainnet/2025-12-01-accept-smart-escrow-ownership/script/AcceptSmartEscrowOwnership.s.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; +import {IMulticall3} from "forge-std/interfaces/IMulticall3.sol"; + +import {MultisigScript} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; +import {IOptimismPortal2} from "@eth-optimism-bedrock/interfaces/L1/IOptimismPortal2.sol"; +import {AccessControlDefaultAdminRules} from "@openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol"; + +contract AcceptSmartEscrowOwnership is MultisigScript { + address internal immutable OWNER_SAFE; + address internal immutable PORTAL; + address internal immutable SMART_ESCROW; + uint64 internal immutable L2_GAS_LIMIT; + + constructor() { + OWNER_SAFE = vm.envAddress("OWNER_SAFE"); + PORTAL = vm.envAddress("PORTAL"); + SMART_ESCROW = vm.envAddress("SMART_ESCROW"); + L2_GAS_LIMIT = uint64(vm.envUint("L2_GAS_LIMIT")); + + require(OWNER_SAFE != address(0), "OWNER_SAFE env var not set"); + require(PORTAL != address(0), "PORTAL env var not set"); + require(SMART_ESCROW != address(0), "SMART_ESCROW env var not set"); + } + + function setUp() public view { + require(L2_GAS_LIMIT > 0, "L2_GAS_LIMIT must be > 0"); + } + + function _buildCalls() internal view override returns (IMulticall3.Call3Value[] memory calls) { + calls = new IMulticall3.Call3Value[](1); + + address payable target = SMART_ESCROW; + uint256 value = 0; + uint64 gasLimit = L2_GAS_LIMIT; + bool isCreation = false; + bytes memory acceptData = abi.encodeCall(AccessControlDefaultAdminRules.acceptDefaultAdminTransfer, ()); + + calls[0] = IMulticall3.Call3Value({ + target: PORTAL, + allowFailure: false, + callData: abi.encodeCall( + IOptimismPortal2.depositTransaction, (target, value, gasLimit, isCreation, acceptData) + ), + value: 0 + }); + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override {} + + function _ownerSafe() internal view override returns (address) { + return OWNER_SAFE; + } +} diff --git a/mainnet/2025-12-01-accept-smart-escrow-ownership/validations/base-signer.json b/mainnet/2025-12-01-accept-smart-escrow-ownership/validations/base-signer.json new file mode 100644 index 00000000..bd29e4f1 --- /dev/null +++ b/mainnet/2025-12-01-accept-smart-escrow-ownership/validations/base-signer.json @@ -0,0 +1,78 @@ +{ + "cmd": "forge script --rpc-url https://eth-mainnet.public.blastapi.io AcceptSmartEscrowOwnership --sig sign(address[]) [] --sender 0x1841CB3C2ce6870D0417844C817849da64E6e937", + "ledgerId": 0, + "rpcUrl": "https://eth-mainnet.public.blastapi.io", + "expectedDomainAndMessageHashes": { + "address": "0x14536667Cd30e52C0b458BaACcB9faDA7046E056", + "domainHash": "0xf3474c66ee08325b410c3f442c878d01ec97dd55a415a307e9d7d2ea24336289", + "messageHash": "0x70129c7514a5fba8c013faa35eb4e84a25e6185010800c50faafc73e727c489e" + }, + "stateOverrides": [ + { + "name": "Incident Safe - Mainnet", + "address": "0x14536667cd30e52c0b458baaccb9fada7046e056", + "overrides": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Override the threshold to 1 so the transaction simulation can occur.", + "allowDifference": false + }, + { + "key": "0x95d2e559ecc4123c992191c982fcacdff23ac220c7a38b92ea163a056c1c3dd0", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001", + "description": "Simulates an approval from msg.sender in order for the task simulation to succeed.", + "allowDifference": false + } + ] + } + ], + "stateChanges": [ + { + "name": "Incident Safe - Mainnet", + "address": "0x14536667cd30e52c0b458baaccb9fada7046e056", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000005", + "before": "0x000000000000000000000000000000000000000000000000000000000000005f", + "after": "0x0000000000000000000000000000000000000000000000000000000000000060", + "description": "Increments the nonce", + "allowDifference": false + } + ] + }, + { + "name": "Optimism Portal", + "address": "0xbeb5fc579115071764c7423a4f12edde41f106ed", + "changes": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000001", + "before": "0x00000000016cf17500000000000afd1d0000000000000000000000003b9aca00", + "after": "0x00000000016cf1890000000000030d400000000000000000000000003b9aca00", + "description": "Updates the portal's ResourceMetering params with the latest block number and deposit gas usage.", + "allowDifference": true + } + ] + } + ], + "balanceChanges": [ + { + "name": "Incident Safe - Mainnet", + "address": "0x14536667cd30e52c0b458baaccb9fada7046e056", + "field": "ETH Balance (wei)", + "before": "0x0000000000000000000000000000000000000000000000001a4e86c4783b08bb", + "after": "0x0000000000000000000000000000000000000000000000001a4da364cf0968bb", + "description": "ETH balance change for this account", + "allowDifference": false + }, + { + "name": "Optimism ETHLockbox", + "address": "0x322b47ff1fa8d5611f761e3e275c45b71b294d43", + "field": "ETH Balance (wei)", + "before": "0x000000000000000000000000000000000000000000002b0d1bc83b08a187dc11", + "after": "0x000000000000000000000000000000000000000000002b0d1bc91e684ab97c11", + "description": "ETH balance change for this account", + "allowDifference": true + } + ] +}