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
50 changes: 50 additions & 0 deletions crates/execution-config/src/gas_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,4 +387,54 @@ mod tests {
);
}
}

/// Empty blocks stop lowering the fee once `base_fee * k_rate < 10_000`
/// (integer truncation on the decrease path). At production-scale gas
/// limits the resting value is `ceil(10000/k_rate) - 1`.
///
/// `k_rate = 1250` / `iem = 5000` is the EIP-1559 equivalence point
/// (`10000/8`, `10000/2`) already exercised by `test_calc_next_block_base_fee`
/// — not a historical Arc testnet parameter set (see #367 / #372).
#[test]
fn empty_blocks_rest_at_truncation_floor() {
let k_rate = 1250;
let iem = 5000; // 50% target
let gas_limit = 30_000_000;
let resting = 7u64; // ceil(10000/1250) - 1
assert_eq!(
arc_calc_next_block_base_fee(0, gas_limit, 8, k_rate, iem),
7,
"one step above resting must fall"
);
assert_eq!(
arc_calc_next_block_base_fee(0, gas_limit, resting, k_rate, iem),
resting,
"at resting value must stay put"
);
assert_eq!(
arc_calc_next_block_base_fee(0, gas_limit, resting - 1, k_rate, iem),
resting - 1,
"below resting value must stay put"
);
let mut base_fee = 1_000_000_000u64; // 1 gwei
for _ in 0..2_000 {
base_fee = arc_calc_next_block_base_fee(0, gas_limit, base_fee, k_rate, iem);
}
assert_eq!(
base_fee, resting,
"long empty-block run reaches resting value"
);

// Max ProtocolConfig kRate: decrease == base_fee, so one empty block
// reaches 0 (resting = ceil(10000/10000) - 1 = 0) before output clamps.
assert_eq!(
arc_calc_next_block_base_fee(0, gas_limit, 42, 10_000, iem),
0,
"kRate=10000 empties the fee in one step"
);
assert_eq!(
arc_calc_next_block_base_fee(0, gas_limit, 0, 10_000, iem),
0
);
}
}
15 changes: 15 additions & 0 deletions docs/adr/0004-base-fee-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,21 @@ assert decode(header.extra_data) == expected

This replaces the current `parent.extra_data → child.base_fee` check with a post-execution invariant: the proposer's `extra_data` must match the deterministic output of execution.

### Effective base-fee floor vs `minBaseFee`

`arc_calc_next_block_base_fee` guarantees a minimum **increase** of 1 when utilization is above target, but the **decrease** path truncates to zero with no equivalent floor. For `k_rate > 0`, an empty block stops lowering the fee once `base_fee * k_rate < 10_000`. Under sustained empty blocks at production-scale gas limits, the resting value is therefore:

```text
resting_value = ceil(10000 / kRate) - 1
effective_floor ≈ max(minBaseFee, resting_value)
```

`10000 / kRate` is the truncation *threshold*; the fee comes to rest one wei below it (e.g. `kRate = 1250` ⇒ threshold 8, rest 7; `kRate = 200` ⇒ threshold 50, rest 49; `kRate = 10000` ⇒ rest 0, so a single empty block reaches zero before the output clamp). Operators reading only `feeParams.minBaseFee` can miss this interaction.

This is a **forward-looking governance risk**, not a past Arc-curve incident: since the Arc fee curve has been live on testnet (`kRate = 200` from block ~21,659,090), `minBaseFee` has stayed well above the resting value (49 wei). The 7 wei floor observed earlier on testnet came from plain EIP-1559 dynamics (`/8`), not from Arc `kRate` truncation — do not treat that history as evidence the Arc curve already gapped. A future `updateFeeParams` that lowers `minBaseFee` below `ceil(10000/kRate) - 1` (or raises that resting value above `minBaseFee`) would create the gap. See #367.

Also note: `assets/testnet/config.json` / `assets/devnet/config.json` fee params (`kRate: 25`, `minBaseFee: 1`) are **historical genesis values** (predicted Arc resting value 399). Live testnet `ProtocolConfig.feeParams()` currently matches the mainnet-like set (`kRate: 200`).

## Consequences

### Positive
Expand Down