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
13 changes: 7 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -171,30 +171,31 @@ sign-task: bootstrap-mise checkout-signer-tool
# These targets are designed to be invoked from task subdirectories
# (e.g. sepolia/2026-02-19-superchain-separation/) that include this Makefile.
TASK_NAME ?= $(notdir $(CURDIR))
TASK_ORIGIN_DIR ?= $(CURDIR)
SIGNATURE_DIR ?= $(CURDIR)/../signatures/$(TASK_NAME)

.PHONY: sign-as-task-creator
sign-as-task-creator: deps-signer-tool
mkdir -p "$(SIGNATURE_DIR)"
cd $(SIGNER_TOOL_PATH) && \
$(MISE_EXEC) npx tsx scripts/genTaskOriginSig.ts sign \
--task-folder $(CURDIR) \
--signature-path $(SIGNATURE_DIR)
--task-folder "$(TASK_ORIGIN_DIR)" \
--signature-path "$(SIGNATURE_DIR)"

.PHONY: sign-as-base-facilitator
sign-as-base-facilitator: deps-signer-tool
mkdir -p "$(SIGNATURE_DIR)"
cd $(SIGNER_TOOL_PATH) && \
$(MISE_EXEC) npx tsx scripts/genTaskOriginSig.ts sign \
--task-folder $(CURDIR) \
--signature-path $(SIGNATURE_DIR) \
--task-folder "$(TASK_ORIGIN_DIR)" \
--signature-path "$(SIGNATURE_DIR)" \
--facilitator base

.PHONY: sign-as-sc-facilitator
sign-as-sc-facilitator: deps-signer-tool
mkdir -p "$(SIGNATURE_DIR)"
cd $(SIGNER_TOOL_PATH) && \
$(MISE_EXEC) npx tsx scripts/genTaskOriginSig.ts sign \
--task-folder $(CURDIR) \
--signature-path $(SIGNATURE_DIR) \
--task-folder "$(TASK_ORIGIN_DIR)" \
--signature-path "$(SIGNATURE_DIR)" \
--facilitator security-council
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Facilitator Guide

Guide for the Base mainnet `FeeDisburser` upgrade.

## 1. Deploy `FeeDisburser`

```bash
cd contract-deployments/active/evm
TASK_ID=2026-07-23-upgrade-mainnet-fee-disburser TASK_NETWORK=mainnet make deps
TASK_ID=2026-07-23-upgrade-mainnet-fee-disburser TASK_NETWORK=mainnet make deploy-fee-disburser
```

Set `FEE_DISBURSER_IMPL_ADDR` in `config/mainnet/.env` to the deployed implementation.

The initial L2 refund configuration is intentionally empty.

## 2. Generate the validation file

```bash
TASK_ID=2026-07-23-upgrade-mainnet-fee-disburser TASK_NETWORK=mainnet make gen-validation-fee-disburser
```

This produces:

```text
tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/validations/base-signer.json
```

## 3. Generate task-origin signatures

After the task configuration, scripts, and validation file are final, generate the task-origin signatures:

```bash
TASK_ID=2026-07-23-upgrade-mainnet-fee-disburser TASK_NETWORK=mainnet make sign-as-task-creator
TASK_ID=2026-07-23-upgrade-mainnet-fee-disburser TASK_NETWORK=mainnet make sign-as-base-facilitator
TASK_ID=2026-07-23-upgrade-mainnet-fee-disburser TASK_NETWORK=mainnet make sign-as-sc-facilitator
```

Commit the generated files under:

```text
tasks/2026-07-23-upgrade-mainnet-fee-disburser/signatures/mainnet/
```

## 4. Collect and execute signatures

Ask signers to run `make sign-task` from the repository root and select **Upgrade Mainnet FeeDisburser**.

After collecting the Safe signatures:

```bash
SIGNATURES=AAABBBCCC TASK_ID=2026-07-23-upgrade-mainnet-fee-disburser TASK_NETWORK=mainnet make execute-fee-disburser
```

## 5. Verify

On Base mainnet, verify:

- `FeeDisburser.version()` returns `1.1.0`.
- `FeeDisburser.L1_WALLET()` returns `0x23B597f33f6f2621F77DA117523Dffd634cDf4ea`.
- The proxy implementation is the newly deployed implementation.
- `systemAddresses` and `targetBalances` remain empty.

Then update the README status to `EXECUTED` with the relevant transaction links.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
BASE_CONTRACTS_COMMIT=a2c10a5ffa24b5b4364aed268e1848166631ec6a

BALANCE_TRACKER=0x23B597f33f6f2621F77DA117523Dffd634cDf4ea

FEE_DISBURSER=0x09C7bAD99688a55a2e83644BFAed09e62bDcCcBA
FEE_DISBURSER_IMPL_ADDR=0x0000000000000000000000000000000000000000
FEE_DISBURSEMENT_INTERVAL=86400
OWNER_SAFE=0x9855054731540A48b28990B63DcF4f33d8AE46A1
OPTIMISM_PORTAL=0x49048044D57e1C92A77f79988d21Fa8fAF74E97e
L2_GAS_LIMIT=200000
VALIDATION_SENDER=0x6CD3850756b7894774Ab715D136F9dD02837De50

RECORD_STATE_DIFF=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Upgrade Mainnet `FeeDisburser`

Status: READY TO SIGN

## Description

Upgrade Base mainnet `FeeDisburser` to version `1.1.0`, which can refund configured L2 system addresses before bridging remaining fees to L1.

## Changes

- Deploy the `FeeDisburser` implementation.
- Upgrade the L2 proxy through an L1-to-L2 deposit transaction.
- Initialize the L2 system-address refund configuration with empty arrays.

## Sign

From the repository root:

```bash
make sign-task
```

Select this mainnet task, sign, and send the signature to the facilitator.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[profile.default]
src = 'src'
out = 'out'
libs = ['lib']
broadcast = 'records'
fs_permissions = [{ access = "read-write", path = "./" }]
optimizer = true
optimizer_runs = 999999
auto_detect_solc = true
via-ir = false
evm_version = "prague"
remappings = [
'@base-contracts/=lib/contracts/',
'forge-std/=lib/forge-std/src/',
'interfaces/=lib/contracts/interfaces/',
'scripts/=lib/contracts/scripts/',
'src/=lib/contracts/src/',
]

[lint]
lint_on_build = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
NETWORK=mainnet
L1_RPC_URL=https://eth-mainnet.public.blastapi.io
L2_RPC_URL=https://mainnet.base.org
L1_CHAIN_ID=1
L2_CHAIN_ID=8453
LEDGER_ACCOUNT=0
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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/L2/FeeDisburser.sol";

contract DeployFeeDisburser is Script {
address internal immutable BALANCE_TRACKER;
uint256 internal immutable FEE_DISBURSEMENT_INTERVAL;

constructor() {
BALANCE_TRACKER = vm.envAddress("BALANCE_TRACKER");
FEE_DISBURSEMENT_INTERVAL = vm.envUint("FEE_DISBURSEMENT_INTERVAL");
}

function run() external {
vm.startBroadcast();
FeeDisburser implementation = new FeeDisburser(BALANCE_TRACKER, FEE_DISBURSEMENT_INTERVAL);
vm.stopBroadcast();

require(implementation.L1_WALLET() == BALANCE_TRACKER, "L1 wallet mismatch");
require(
implementation.FEE_DISBURSEMENT_INTERVAL() == FEE_DISBURSEMENT_INTERVAL, "disbursement interval mismatch"
);
require(keccak256(bytes(implementation.version())) == keccak256(bytes("1.1.0")), "version mismatch");
console.log("FeeDisburser implementation:", address(implementation));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import {Vm} from "forge-std/Vm.sol";

import {MultisigScript} from "@base-contracts/scripts/universal/MultisigScript.sol";
import {Enum} from "@base-contracts/scripts/universal/IGnosisSafe.sol";
import {Simulation} from "@base-contracts/scripts/universal/Simulation.sol";

interface IFeeDisburser {
function initialize(address payable[] memory systemAddresses, uint256[] memory targetBalances) external;
}

interface IOptimismPortal2 {
function depositTransaction(address to, uint256 value, uint64 gasLimit, bool isCreation, bytes memory data)
external
payable;
}

interface IProxy {
function upgradeToAndCall(address implementation, bytes memory data) external payable returns (bytes memory);
}

contract UpgradeFeeDisburser is MultisigScript {
address internal immutable OWNER_SAFE;
address internal immutable OPTIMISM_PORTAL;
address internal immutable FEE_DISBURSER;
address internal immutable FEE_DISBURSER_IMPL;
uint64 internal immutable L2_GAS_LIMIT;

constructor() {
OWNER_SAFE = vm.envAddress("OWNER_SAFE");
OPTIMISM_PORTAL = vm.envAddress("OPTIMISM_PORTAL");
FEE_DISBURSER = vm.envAddress("FEE_DISBURSER");
FEE_DISBURSER_IMPL = vm.envAddress("FEE_DISBURSER_IMPL_ADDR");
uint256 l2GasLimit = vm.envUint("L2_GAS_LIMIT");
require(l2GasLimit <= type(uint64).max, "L2 gas limit too large");
L2_GAS_LIMIT = uint64(l2GasLimit);

require(FEE_DISBURSER_IMPL != address(0), "implementation not set");
}

function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal override {}

function _buildCalls() internal view override returns (Call[] memory calls) {
address payable[] memory systemAddresses = new address payable[](0);
uint256[] memory targetBalances = new uint256[](0);
bytes memory initializeCall = abi.encodeCall(IFeeDisburser.initialize, (systemAddresses, targetBalances));
bytes memory upgradeCall = abi.encodeCall(IProxy.upgradeToAndCall, (FEE_DISBURSER_IMPL, initializeCall));

calls = new Call[](1);
calls[0] = Call({
operation: Enum.Operation.Call,
target: OPTIMISM_PORTAL,
data: abi.encodeCall(
IOptimismPortal2.depositTransaction, (FEE_DISBURSER, 0, L2_GAS_LIMIT, false, upgradeCall)
),
value: 0
});
}

function _ownerSafe() internal view override returns (address) {
return OWNER_SAFE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export FEE_DISBURSER
export FEE_DISBURSER_IMPL_ADDR
export FEE_DISBURSEMENT_INTERVAL
export OWNER_SAFE
export OPTIMISM_PORTAL
export L2_GAS_LIMIT

TASK_ORIGIN_DIR = $(CURDIR)/$(TASK_CONFIG_DIR)
SIGNATURE_DIR = $(CURDIR)/$(TASK_DIR)/signatures/$(TASK_NETWORK)

FEE_DISBURSER_DEPLOY_SCRIPT = --root $(TASK_CONFIG_DIR) $(TASK_CONFIG_DIR)/script/DeployFeeDisburser.s.sol:DeployFeeDisburser
FEE_DISBURSER_UPGRADE_SCRIPT = --root $(TASK_CONFIG_DIR) $(TASK_CONFIG_DIR)/script/UpgradeFeeDisburser.s.sol:UpgradeFeeDisburser

.PHONY: deploy-fee-disburser
deploy-fee-disburser:
$(MISE_EXEC) forge script --rpc-url $(L2_RPC_URL) $(FEE_DISBURSER_DEPLOY_SCRIPT) \
--ledger --hd-paths $(LEDGER_HD_PATH) --broadcast -vvvv

.PHONY: gen-validation-fee-disburser
gen-validation-fee-disburser: deps-signer-tool
$(call GEN_VALIDATION,$(FEE_DISBURSER_UPGRADE_SCRIPT),,$(VALIDATION_SENDER),base-signer.json,)

.PHONY: execute-fee-disburser
execute-fee-disburser: SCRIPT_NAME := $(FEE_DISBURSER_UPGRADE_SCRIPT)
execute-fee-disburser:
$(call MULTISIG_EXECUTE,$(SIGNATURES))
Loading