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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ forge-deps:
##
# Task Signer Tool
##
SIGNER_TOOL_COMMIT=1fc76441a1cde3581c224023adfa79b8cfdb2fa4
SIGNER_TOOL_COMMIT=8c19f7ca258de19a8eca2b3e64abdfd8b2d30e7c
SIGNER_TOOL_PATH=signer-tool

.PHONY: checkout-signer-tool
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Facilitator Guide

Guide for updating the Base Sepolia `BalanceTracker` profit wallet.

## 1. Install dependencies and verify current state

Run every task command with the network selected explicitly:

```bash
cd active/evm/tasks/2026-08-25-update-sepolia-balance-tracker-profit-wallet
make TASK_NETWORK=sepolia deps
make TASK_NETWORK=sepolia verify-current
```

## 2. Deploy the implementation

Connect the Ledger account for `PROXY_ADMIN`, then run:

```bash
make TASK_NETWORK=sepolia deploy
```

This deploys a `BalanceTracker` implementation whose immutable `PROFIT_WALLET`
is the company wallet used on mainnet. Review and commit:

- `config/sepolia/addresses.json`
- the deployment record under `records/DeployBalanceTracker.s.sol/11155111/`

## 3. Execute the upgrade

The existing proxy admin is an EOA, not a Safe, so this task does not generate a
signer validation file or collect multisig approvals. The connected
`PROXY_ADMIN` Ledger executes the upgrade directly:

```bash
make TASK_NETWORK=sepolia upgrade
```

The script verifies that the proxy implementation and profit wallet change
while both existing system addresses and target balances remain unchanged.

## 4. Verify and archive

```bash
make TASK_NETWORK=sepolia verify
```

Set `Status: [EXECUTED](<transaction-url>)` in
`config/sepolia/README.md`, commit the upgrade record, then run
`make archive-task` from the repository root.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
include ../../../../Makefile
include $(REPO_ROOT)/Multisig.mk

ifneq ($(origin TASK_NETWORK),command line)
$(error TASK_NETWORK must be set on the command line, for example: make TASK_NETWORK=sepolia <target>)
endif
ifneq ($(TASK_NETWORK),sepolia)
$(error unsupported TASK_NETWORK: $(TASK_NETWORK))
endif

PROJECT_DIR := $(abspath ../..)

include $(REPO_ROOT)/config/$(TASK_NETWORK).env
include config/$(TASK_NETWORK)/.env

ADDRESSES_JSON := $(CURDIR)/config/$(TASK_NETWORK)/addresses.json
export FOUNDRY_BROADCAST := $(CURDIR)/records
DEPLOY_SCRIPT := tasks/2026-08-25-update-sepolia-balance-tracker-profit-wallet/script/DeployBalanceTracker.s.sol:DeployBalanceTracker
UPGRADE_SCRIPT := tasks/2026-08-25-update-sepolia-balance-tracker-profit-wallet/script/UpgradeBalanceTracker.s.sol:UpgradeBalanceTracker
TASK_ENV := BALANCE_TRACKER=$(BALANCE_TRACKER) \
CURRENT_PROFIT_WALLET=$(CURRENT_PROFIT_WALLET) \
NEW_PROFIT_WALLET=$(NEW_PROFIT_WALLET) \
ADDRESSES_JSON=$(ADDRESSES_JSON)
UPGRADE_ENV := $(TASK_ENV) \
PROXY_ADMIN=$(PROXY_ADMIN) \
SYSTEM_ADDRESS_0=$(SYSTEM_ADDRESS_0) \
SYSTEM_ADDRESS_1=$(SYSTEM_ADDRESS_1) \
TARGET_BALANCE_0=$(TARGET_BALANCE_0) \
TARGET_BALANCE_1=$(TARGET_BALANCE_1)

.PHONY: deps
deps: task-extra-deps

.PHONY: task-extra-deps
task-extra-deps:
cd $(PROJECT_DIR)/lib/contracts && $(MISE_EXEC) forge install --no-git \
OpenZeppelin/openzeppelin-contracts-upgradeable@$(OZ_UPGRADEABLE_TAG)

.PHONY: validate-ledger-admin
validate-ledger-admin:
@test "$$($(MISE_EXEC) cast wallet address --ledger --mnemonic-derivation-path $(LEDGER_HD_PATH))" = "$(PROXY_ADMIN)" || \
(echo "connected Ledger account is not PROXY_ADMIN" && exit 1)

.PHONY: validate-deployment
validate-deployment:
@test -f "$(ADDRESSES_JSON)" || (echo "run make TASK_NETWORK=$(TASK_NETWORK) deploy first" && exit 1)
@jq -e '.balanceTrackerImplementation' "$(ADDRESSES_JSON)" >/dev/null

.PHONY: deploy
deploy: validate-ledger-admin
cd $(PROJECT_DIR) && $(TASK_ENV) $(MISE_EXEC) forge script --rpc-url $(L1_RPC_URL) $(DEPLOY_SCRIPT) \
--ledger --hd-paths $(LEDGER_HD_PATH) --sender $(PROXY_ADMIN) --broadcast -vvvv

.PHONY: upgrade
upgrade: validate-ledger-admin validate-deployment
cd $(PROJECT_DIR) && $(UPGRADE_ENV) $(MISE_EXEC) forge script --rpc-url $(L1_RPC_URL) $(UPGRADE_SCRIPT) \
--ledger --hd-paths $(LEDGER_HD_PATH) --sender $(PROXY_ADMIN) --broadcast -vvvv

.PHONY: verify-current
verify-current:
@test "$$($(MISE_EXEC) cast call $(BALANCE_TRACKER) 'PROFIT_WALLET()(address)' --rpc-url $(L1_RPC_URL))" = \
"$(CURRENT_PROFIT_WALLET)"

.PHONY: verify
verify: validate-deployment
@test "$$($(MISE_EXEC) cast call $(BALANCE_TRACKER) 'PROFIT_WALLET()(address)' --rpc-url $(L1_RPC_URL))" = \
"$(NEW_PROFIT_WALLET)"
@test "$$($(MISE_EXEC) cast call $(BALANCE_TRACKER) 'systemAddresses(uint256)(address)' 0 --rpc-url $(L1_RPC_URL))" = \
"$(SYSTEM_ADDRESS_0)"
@test "$$($(MISE_EXEC) cast call $(BALANCE_TRACKER) 'systemAddresses(uint256)(address)' 1 --rpc-url $(L1_RPC_URL))" = \
"$(SYSTEM_ADDRESS_1)"
@test "$$($(MISE_EXEC) cast call $(BALANCE_TRACKER) 'targetBalances(uint256)(uint256)' 0 --rpc-url $(L1_RPC_URL) | cut -d' ' -f1)" = \
"$(TARGET_BALANCE_0)"
@test "$$($(MISE_EXEC) cast call $(BALANCE_TRACKER) 'targetBalances(uint256)(uint256)' 1 --rpc-url $(L1_RPC_URL) | cut -d' ' -f1)" = \
"$(TARGET_BALANCE_1)"
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# https://github.com/base/contracts/commit/b8e2246fd0f8ce342c574e2a783f865ea82d38d4
BASE_CONTRACTS_COMMIT=b8e2246fd0f8ce342c574e2a783f865ea82d38d4
RECORD_STATE_DIFF=true

# Existing Base Sepolia BalanceTracker proxy admin.
PROXY_ADMIN=0x4672425C27A942bB27e7b9709c1b21ab89a3cA13

# Replace the current personal profit wallet with the company wallet used on mainnet.
CURRENT_PROFIT_WALLET=0x5A822ea15764a6090b86B1EABfFc051cEC99AFE9
NEW_PROFIT_WALLET=0xEc8103eb573150cB92f8AF612e0072843db2295F

# Existing BalanceTracker configuration that must remain unchanged.
SYSTEM_ADDRESS_0=0x037637067c1DbE6d2430616d8f54Cb774Daa5999
TARGET_BALANCE_0=200000000000000000000
SYSTEM_ADDRESS_1=0xfc56E7272EEBBBA5bC6c544e159483C4a38f8bA3
TARGET_BALANCE_1=1000000000000000000000
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Update Sepolia `BalanceTracker` Profit Wallet

Status: READY TO SIGN

## Description

Replace the Base Sepolia `BalanceTracker` profit wallet
`0x5A822ea15764a6090b86B1EABfFc051cEC99AFE9` with the company-owned wallet
used on mainnet, `0xEc8103eb573150cB92f8AF612e0072843db2295F`.

The existing system addresses and target balances remain unchanged.

## Execution

The `BalanceTracker` proxy is administered by an EOA, so no multisig signature
is required. The facilitator executes the upgrade with the existing proxy admin
Ledger.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

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

import {BalanceTracker} from "@base-contracts/src/L1/BalanceTracker.sol";

contract DeployBalanceTracker is Script {
address payable internal immutable BALANCE_TRACKER;
address payable internal immutable CURRENT_PROFIT_WALLET;
address payable internal immutable NEW_PROFIT_WALLET;

constructor() {
BALANCE_TRACKER = payable(vm.envAddress("BALANCE_TRACKER"));
CURRENT_PROFIT_WALLET = payable(vm.envAddress("CURRENT_PROFIT_WALLET"));
NEW_PROFIT_WALLET = payable(vm.envAddress("NEW_PROFIT_WALLET"));

require(
BalanceTracker(BALANCE_TRACKER).PROFIT_WALLET() == CURRENT_PROFIT_WALLET, "current profit wallet mismatch"
);
}

function run() external {
vm.broadcast();
BalanceTracker implementation = new BalanceTracker(NEW_PROFIT_WALLET);

require(implementation.PROFIT_WALLET() == NEW_PROFIT_WALLET, "new profit wallet mismatch");

string memory json = vm.serializeAddress("deployment", "balanceTrackerImplementation", address(implementation));
vm.writeJson(json, vm.envString("ADDRESSES_JSON"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

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

import {BalanceTracker} from "@base-contracts/src/L1/BalanceTracker.sol";
import {Proxy} from "@base-contracts/src/universal/Proxy.sol";

contract UpgradeBalanceTracker is Script {
address internal immutable PROXY_ADMIN;
address payable internal immutable BALANCE_TRACKER;
address payable internal immutable CURRENT_PROFIT_WALLET;
address payable internal immutable NEW_PROFIT_WALLET;
address internal immutable NEW_IMPLEMENTATION;
address payable internal immutable SYSTEM_ADDRESS_0;
address payable internal immutable SYSTEM_ADDRESS_1;
uint256 internal immutable TARGET_BALANCE_0;
uint256 internal immutable TARGET_BALANCE_1;

constructor() {
PROXY_ADMIN = vm.envAddress("PROXY_ADMIN");
BALANCE_TRACKER = payable(vm.envAddress("BALANCE_TRACKER"));
CURRENT_PROFIT_WALLET = payable(vm.envAddress("CURRENT_PROFIT_WALLET"));
NEW_PROFIT_WALLET = payable(vm.envAddress("NEW_PROFIT_WALLET"));
SYSTEM_ADDRESS_0 = payable(vm.envAddress("SYSTEM_ADDRESS_0"));
SYSTEM_ADDRESS_1 = payable(vm.envAddress("SYSTEM_ADDRESS_1"));
TARGET_BALANCE_0 = vm.envUint("TARGET_BALANCE_0");
TARGET_BALANCE_1 = vm.envUint("TARGET_BALANCE_1");

string memory json = vm.readFile(vm.envString("ADDRESSES_JSON"));
NEW_IMPLEMENTATION = vm.parseJsonAddress(json, ".balanceTrackerImplementation");

require(NEW_IMPLEMENTATION.code.length > 0, "implementation has no code");
require(
BalanceTracker(payable(NEW_IMPLEMENTATION)).PROFIT_WALLET() == NEW_PROFIT_WALLET,
"implementation profit wallet mismatch"
);
vm.prank(PROXY_ADMIN);
require(Proxy(BALANCE_TRACKER).admin() == PROXY_ADMIN, "proxy admin mismatch");
_checkState(CURRENT_PROFIT_WALLET);
}

function run() external {
vm.broadcast(PROXY_ADMIN);
Proxy(BALANCE_TRACKER).upgradeTo(NEW_IMPLEMENTATION);

vm.prank(PROXY_ADMIN);
require(Proxy(BALANCE_TRACKER).implementation() == NEW_IMPLEMENTATION, "implementation mismatch");
_checkState(NEW_PROFIT_WALLET);
}

function _checkState(address expectedProfitWallet) internal view {
BalanceTracker balanceTracker = BalanceTracker(BALANCE_TRACKER);

require(balanceTracker.PROFIT_WALLET() == expectedProfitWallet, "profit wallet mismatch");
require(balanceTracker.systemAddresses(0) == SYSTEM_ADDRESS_0, "system address 0 mismatch");
require(balanceTracker.systemAddresses(1) == SYSTEM_ADDRESS_1, "system address 1 mismatch");
require(balanceTracker.targetBalances(0) == TARGET_BALANCE_0, "target balance 0 mismatch");
require(balanceTracker.targetBalances(1) == TARGET_BALANCE_1, "target balance 1 mismatch");
}
}
Loading