From 26c4f0d9b69f59a3f34fcb82be320f29a84f911a Mon Sep 17 00:00:00 2001 From: Thanh Trinh Date: Thu, 23 Jul 2026 14:01:20 -0400 Subject: [PATCH] feat(CHAIN-4644): add mainnet FeeDisburser upgrade task Co-authored-by: Codex --- Makefile | 13 ++-- .../FACILITATOR.md | 64 ++++++++++++++++++ .../config/mainnet/.env | 13 ++++ .../config/mainnet/README.md | 23 +++++++ .../config/mainnet/foundry.toml | 21 ++++++ .../config/mainnet/network.env | 6 ++ .../mainnet/script/DeployFeeDisburser.s.sol | 30 +++++++++ .../mainnet/script/UpgradeFeeDisburser.s.sol | 65 +++++++++++++++++++ .../config/mainnet/task.mk | 26 ++++++++ .../config/mainnet/validations/.gitkeep | 0 .../signatures/mainnet/.gitkeep | 0 11 files changed, 255 insertions(+), 6 deletions(-) create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/FACILITATOR.md create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/.env create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/README.md create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/foundry.toml create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/network.env create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/script/DeployFeeDisburser.s.sol create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/script/UpgradeFeeDisburser.s.sol create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/task.mk create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/validations/.gitkeep create mode 100644 active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/signatures/mainnet/.gitkeep diff --git a/Makefile b/Makefile index e71f932c9..e15777382 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,7 @@ 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 @@ -178,16 +179,16 @@ 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 @@ -195,6 +196,6 @@ 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 diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/FACILITATOR.md b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/FACILITATOR.md new file mode 100644 index 000000000..0c1a6edeb --- /dev/null +++ b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/FACILITATOR.md @@ -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. diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/.env b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/.env new file mode 100644 index 000000000..4dab8213d --- /dev/null +++ b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/.env @@ -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 diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/README.md b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/README.md new file mode 100644 index 000000000..64072618e --- /dev/null +++ b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/README.md @@ -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. diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/foundry.toml b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/foundry.toml new file mode 100644 index 000000000..9c9212a06 --- /dev/null +++ b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/foundry.toml @@ -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 diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/network.env b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/network.env new file mode 100644 index 000000000..99c9cc373 --- /dev/null +++ b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/network.env @@ -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 diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/script/DeployFeeDisburser.s.sol b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/script/DeployFeeDisburser.s.sol new file mode 100644 index 000000000..5ca7e12ac --- /dev/null +++ b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/script/DeployFeeDisburser.s.sol @@ -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)); + } +} diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/script/UpgradeFeeDisburser.s.sol b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/script/UpgradeFeeDisburser.s.sol new file mode 100644 index 000000000..7a6dcca4f --- /dev/null +++ b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/script/UpgradeFeeDisburser.s.sol @@ -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; + } +} diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/task.mk b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/task.mk new file mode 100644 index 000000000..1a8f71b2c --- /dev/null +++ b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/task.mk @@ -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)) diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/validations/.gitkeep b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/config/mainnet/validations/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/signatures/mainnet/.gitkeep b/active/evm/tasks/2026-07-23-upgrade-mainnet-fee-disburser/signatures/mainnet/.gitkeep new file mode 100644 index 000000000..e69de29bb