Skip to content
Merged
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/2026-02-17-update-min-base-fee/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Required: Git commit hash for https://github.com/ethereum-optimism/optimism
OP_COMMIT=d6aa14e96fe5001b5c23c233d0747078d7ea02fb

# Required: Git commit hash for https://github.com/base/contracts
BASE_CONTRACTS_COMMIT=9526d38b63be2ee10fc905dee60f0b2a0a17e89e

# Required: L1 SystemConfig proxy contract address
SYSTEM_CONFIG=0x73a79Fab69143498Ed3712e519A88a918e1f4072

# Required: Address of the Gnosis Safe that owns SystemConfig
OWNER_SAFE=0x14536667Cd30e52C0b458BaACcB9faDA7046E056

# Min base fee: 2,000,000 wei -> 5,000,000 wei (2.5x increase)
OLD_MIN_BASE_FEE=2000000
NEW_MIN_BASE_FEE=5000000

# Required: Address of a signer on OWNER_SAFE (used for simulation)
SENDER=0x1841CB3C2ce6870D0417844C817849da64E6e937

# Enable state diff recording for validation
RECORD_STATE_DIFF=true
28 changes: 28 additions & 0 deletions mainnet/2026-02-17-update-min-base-fee/FACILITATOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Facilitator Guide

Guide for facilitators after collecting signatures from signers.

### 1. Update repo:

```bash
cd contract-deployments
git pull
cd mainnet/2026-02-17-update-min-base-fee
make deps
```

### 2. Execute upgrade

```bash
SIGNATURES=AAABBBCCC make execute
```

### 3. (**ONLY** if needed) Execute upgrade rollback

> [!IMPORTANT]
>
> THIS SHOULD ONLY BE PERFORMED IN THE EVENT THAT WE NEED TO ROLLBACK

```bash
SIGNATURES=AAABBBCCC make execute-rollback
```
68 changes: 68 additions & 0 deletions mainnet/2026-02-17-update-min-base-fee/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
include ../../Makefile
include ../../Multisig.mk
include ../.env
include .env

ifndef LEDGER_ACCOUNT
override LEDGER_ACCOUNT = 0
endif

ifndef ROLLBACK_NONCE_OFFSET
override ROLLBACK_NONCE_OFFSET = 1
endif

RPC_URL = $(L1_RPC_URL)
SCRIPT_NAME = SetMinBaseFeeScript

# Validate required configuration before execution
.PHONY: validate-config
validate-config:
@test -n "$(OP_COMMIT)" -a "$(OP_COMMIT)" != "TODO" || (echo "OP_COMMIT required" && exit 1)
@test -n "$(BASE_CONTRACTS_COMMIT)" -a "$(BASE_CONTRACTS_COMMIT)" != "TODO" || (echo "BASE_CONTRACTS_COMMIT required" && exit 1)
@test -n "$(OWNER_SAFE)" || (echo "OWNER_SAFE required" && exit 1)
@test -n "$(SYSTEM_CONFIG)" || (echo "SYSTEM_CONFIG required" && exit 1)
@test -n "$(SENDER)" || (echo "SENDER required" && exit 1)
@test -n "$(OLD_MIN_BASE_FEE)" -a "$(OLD_MIN_BASE_FEE)" != "TODO" || (echo "OLD_MIN_BASE_FEE required" && exit 1)
@test -n "$(NEW_MIN_BASE_FEE)" -a "$(NEW_MIN_BASE_FEE)" != "TODO" || (echo "NEW_MIN_BASE_FEE required" && exit 1)
@echo "Configuration validated successfully"

.PHONY: gen-validation
gen-validation: validate-config checkout-signer-tool run-script

.PHONY: run-script
run-script:
cd $(SIGNER_TOOL_PATH); \
npm ci; \
bun run scripts/genValidationFile.ts --rpc-url $(RPC_URL) \
--workdir .. --forge-cmd 'NEW_MIN_BASE_FEE=$(NEW_MIN_BASE_FEE) \
OLD_MIN_BASE_FEE=$(OLD_MIN_BASE_FEE) \
forge script --rpc-url $(RPC_URL) \
$(SCRIPT_NAME) --sig "sign(address[])" [] --sender $(SENDER)' --out ../validations/base-signer.json;

.PHONY: execute
execute: validate-config
NEW_MIN_BASE_FEE=$(NEW_MIN_BASE_FEE) \
OLD_MIN_BASE_FEE=$(OLD_MIN_BASE_FEE) \
$(call MULTISIG_EXECUTE,$(SIGNATURES))

.PHONY: gen-validation-rollback
gen-validation-rollback: validate-config checkout-signer-tool run-script-rollback

.PHONY: run-script-rollback
run-script-rollback:
cd $(SIGNER_TOOL_PATH); \
npm ci; \
bun run scripts/genValidationFile.ts --rpc-url $(RPC_URL) \
--workdir .. --forge-cmd 'OLD_MIN_BASE_FEE=$(NEW_MIN_BASE_FEE) \
NEW_MIN_BASE_FEE=$(OLD_MIN_BASE_FEE) \
SAFE_NONCE=$(shell expr $$(cast call $(OWNER_SAFE) "nonce()" --rpc-url $(RPC_URL) | cast to-dec) + $(ROLLBACK_NONCE_OFFSET)) \
forge script --rpc-url $(RPC_URL) \
$(SCRIPT_NAME) --sig "sign(address[])" [] --sender $(SENDER)' --out ../validations/base-signer-rollback.json;

.PHONY: execute-rollback
execute-rollback: validate-config
OLD_MIN_BASE_FEE=$(NEW_MIN_BASE_FEE) \
NEW_MIN_BASE_FEE=$(OLD_MIN_BASE_FEE) \
SAFE_NONCE=$(shell expr $$(cast call $(OWNER_SAFE) "nonce()" --rpc-url $(RPC_URL) | cast to-dec) + $(ROLLBACK_NONCE_OFFSET)) \
forge script --rpc-url $(RPC_URL) $(SCRIPT_NAME) \
--sig "run(bytes)" $(SIGNATURES) --ledger --hd-paths "m/44'/60'/$(LEDGER_ACCOUNT)'/0/0" --broadcast
69 changes: 69 additions & 0 deletions mainnet/2026-02-17-update-min-base-fee/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Update Min Base Fee in L1 `SystemConfig`

Status: READY TO SIGN

## Description

We are updating the minimum base fee from 2,000,000 wei to 5,000,000 wei (2.5x increase).

This runbook invokes the following script which allows our signers to sign the same call with two different sets of parameters for our Incident Multisig, defined in the [base-org/contracts](https://github.com/base/contracts) repository:

`SetMinBaseFeeScript` -- This script will update the min base fee to 5,000,000 wei if invoked as part of the "upgrade" process, or revert to the old value of 2,000,000 wei if invoked as part of the "rollback" process.

The values we are sending are statically defined in the `.env` file.

> [!IMPORTANT] We have two transactions to sign. Please follow
> the flow for both "Approving the Update transaction" and
> "Approving the Rollback transaction". Hopefully we only need
> the former, but will have the latter available if needed.

## 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 from the list of available tasks to sign (**not** the "Base Signer Rollback" task). Copy the resulting signature and save it.

### 4. Rollback signing

Now, click on the "Base Signer" selection and switch over to the rollback task (called "Base Signer Rollback"). Copy the resulting signature and save it.

### 5. Send signature to facilitator

Send the two signatures to the facilitator and make sure to clearly note which one is the primary one and which one is the rollback.

You may now kill the Signer Tool process in your terminal window by running `Ctrl + C`.
20 changes: 20 additions & 0 deletions mainnet/2026-02-17-update-min-base-fee/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[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/'
]

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
74 changes: 74 additions & 0 deletions mainnet/2026-02-17-update-min-base-fee/script/SetMinBaseFee.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import {Vm} from "forge-std/Vm.sol";
import {Simulation} from "@base-contracts/script/universal/Simulation.sol";
import {IMulticall3} from "forge-std/interfaces/IMulticall3.sol";

import {MultisigScript} from "@base-contracts/script/universal/MultisigScript.sol";

interface ISystemConfig {
function minBaseFee() external view returns (uint64);
function setMinBaseFee(uint64 _minBaseFee) external;
}

contract SetMinBaseFeeScript is MultisigScript {
address internal immutable OWNER_SAFE;
address internal immutable SYSTEM_CONFIG;

uint64 internal immutable MIN_BASE_FEE;
uint64 internal immutable NEW_MIN_BASE_FEE;

constructor() {
OWNER_SAFE = vm.envAddress("OWNER_SAFE");
SYSTEM_CONFIG = vm.envAddress("SYSTEM_CONFIG");

MIN_BASE_FEE = uint64(vm.envUint("OLD_MIN_BASE_FEE"));
NEW_MIN_BASE_FEE = uint64(vm.envUint("NEW_MIN_BASE_FEE"));
}

function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override {
vm.assertEq(ISystemConfig(SYSTEM_CONFIG).minBaseFee(), NEW_MIN_BASE_FEE, "Min base fee mismatch");
}

function _simulationOverrides() internal view override returns (Simulation.StateOverride[] memory _stateOverrides) {
if (MIN_BASE_FEE != ISystemConfig(SYSTEM_CONFIG).minBaseFee()) {
// Override SystemConfig state to the expected "from" values so simulations succeeds even
// when the chain already reflects the post-change values (during rollback simulation).

Simulation.StateOverride[] memory stateOverrides = new Simulation.StateOverride[](1);
Simulation.StorageOverride[] memory storageOverrides = new Simulation.StorageOverride[](1);

// Update minBaseFee (slot 0x6c)
// Storage layout (low to high bits):
// - superchainConfig (address): bits 0-159
// - minBaseFee (uint64): bits 160-223
// Load existing slot to preserve superchainConfig, then update minBaseFee.
bytes32 minBaseFeeSlotKey = bytes32(uint256(0x6c));
uint256 existingMinBaseFeeWord = uint256(vm.load(SYSTEM_CONFIG, minBaseFeeSlotKey));
uint256 updatedMinBaseFeeWord = (existingMinBaseFeeWord & ((1 << 160) - 1)) | (uint256(MIN_BASE_FEE) << 160);
storageOverrides[0] =
Simulation.StorageOverride({key: minBaseFeeSlotKey, value: bytes32(updatedMinBaseFeeWord)});

stateOverrides[0] = Simulation.StateOverride({contractAddress: SYSTEM_CONFIG, overrides: storageOverrides});
return stateOverrides;
}
}

function _buildCalls() internal view override returns (IMulticall3.Call3Value[] memory) {
IMulticall3.Call3Value[] memory calls = new IMulticall3.Call3Value[](1);

calls[0] = IMulticall3.Call3Value({
target: SYSTEM_CONFIG,
allowFailure: false,
callData: abi.encodeCall(ISystemConfig.setMinBaseFee, (NEW_MIN_BASE_FEE)),
value: 0
});

return calls;
}

function _ownerSafe() internal view override returns (address) {
return OWNER_SAFE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"cmd": "OLD_MIN_BASE_FEE=5000000 NEW_MIN_BASE_FEE=2000000 SAFE_NONCE=104 forge script --rpc-url https://eth-mainnet.public.blastapi.io SetMinBaseFeeScript --sig sign(address[]) [] --sender 0x1841CB3C2ce6870D0417844C817849da64E6e937",
"ledgerId": 0,
"rpcUrl": "https://eth-mainnet.public.blastapi.io",
"expectedDomainAndMessageHashes": {
"address": "0x14536667Cd30e52C0b458BaACcB9faDA7046E056",
"domainHash": "0xf3474c66ee08325b410c3f442c878d01ec97dd55a415a307e9d7d2ea24336289",
"messageHash": "0x5fb75649784c7985d9ab466f3fe5937a74f309f7868c77594f2260fdf15b4f9e"
},
"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": "0x0000000000000000000000000000000000000000000000000000000000000005",
"value": "0x0000000000000000000000000000000000000000000000000000000000000068",
"description": "Override the nonce to be our expected value after initial execution.",
"allowDifference": false
},
{
"key": "0x3dc6b9d0cf3ce395987a1645e7ce7647b6141537dd995a2446c20b48a670c29a",
"value": "0x0000000000000000000000000000000000000000000000000000000000000001",
"description": "Simulates an approval from msg.sender in order for the task simulation to succeed.",
"allowDifference": false
}
]
},
{
"name": "System Config - Mainnet",
"address": "0x73a79fab69143498ed3712e519a88a918e1f4072",
"overrides": [
{
"key": "0x000000000000000000000000000000000000000000000000000000000000006c",
"value": "0x0000000000000000004c4b4095703e0982140d16f8eba6d158fccede42f04a4c",
"description": "Overrides min base fee to 5M wei (0x4C4B40) so rollback simulation succeeds.",
"allowDifference": false
}
]
}
],
"stateChanges": [
{
"name": "Incident Safe - Mainnet",
"address": "0x14536667cd30e52c0b458baaccb9fada7046e056",
"changes": [
{
"key": "0x0000000000000000000000000000000000000000000000000000000000000005",
"before": "0x0000000000000000000000000000000000000000000000000000000000000068",
"after": "0x0000000000000000000000000000000000000000000000000000000000000069",
"description": "Increments the nonce",
"allowDifference": false
}
]
},
{
"name": "System Config - Mainnet",
"address": "0x73a79fab69143498ed3712e519a88a918e1f4072",
"changes": [
{
"key": "0x000000000000000000000000000000000000000000000000000000000000006c",
"before": "0x0000000000000000004c4b4095703e0982140d16f8eba6d158fccede42f04a4c",
"after": "0x0000000000000000001e848095703e0982140d16f8eba6d158fccede42f04a4c",
"description": "Updates min base fee from 5M wei (0x4C4B40) to 2M wei (0x1E8480).",
"allowDifference": false
}
]
}
],
"balanceChanges": []
}
Loading