Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions mainnet/2025-12-01-accept-smart-escrow-ownership/.env
Original file line number Diff line number Diff line change
@@ -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
54 changes: 54 additions & 0 deletions mainnet/2025-12-01-accept-smart-escrow-ownership/FACILITATORS.md
Original file line number Diff line number Diff line change
@@ -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
```
48 changes: 48 additions & 0 deletions mainnet/2025-12-01-accept-smart-escrow-ownership/Makefile
Original file line number Diff line number Diff line change
@@ -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)
63 changes: 63 additions & 0 deletions mainnet/2025-12-01-accept-smart-escrow-ownership/README.md
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions mainnet/2025-12-01-accept-smart-escrow-ownership/foundry.toml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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
}
]
}
Loading