---
id: TIP-1067
title: Dynamic Base Fee
description: Replaces Tempo's fixed base fee with an EIP-1559-style controller that lowers the maximum base fee to 1.2e10 attodollars per gas and lowers the base fee toward one twentieth of that cap when the chain is underutilized, with a 10,000,000 gas target.
authors: Dankrad Feist @dankrad
status: Approved
related: TIP-1010, TIP-1000
protocolVersion: T7
---

# TIP-1067: Dynamic Base Fee

## Abstract

This TIP replaces Tempo's fixed base fee (TIP-1010: `2 × 10^10` attodollars per gas) with an EIP-1559-style per-block controller capped at `1.2 × 10^10` attodollars per gas. The base fee adjusts each block toward a gas target of 10,000,000 gas: it falls when blocks use less than the target and rises when they use more. The base fee is clamped to the range `[6 × 10^8, 1.2 × 10^10]` attodollars per gas, i.e. a 20× range from the floor to the cap.

The effect is that when the chain is used very little, the base fee decays toward the floor and transactions become up to 20× cheaper than the new cap; when the chain is busy, the base fee rises back to the cap and never exceeds it. Because the cap is below today's fixed base fee, any caller that budgets for the current price is never surprised by a higher fee, while underutilized periods are made ultracheap.

## Motivation

TIP-1010 sets a single fixed base fee per protocol version and explicitly has no in-protocol mechanism to raise or lower it with utilization; changing it requires a hardfork. That keeps fees predictable but leaves the chain charging the full target price even when it is almost idle, so there is no benefit to users from low utilization.

Tempo is optimized for stablecoin payments and runs fast (~500 ms) blocks with a large block gas limit relative to typical load. For long stretches the chain is expected to be underutilized. During those stretches the marginal cost of including a transaction is minimal, so charging the full target base fee is unnecessary. A bounded dynamic base fee lets the network pass low-utilization savings on to users automatically while preserving a spam floor and never charging more than today's price.

The design is deliberately a *one-sided discount* relative to the status quo: the cap is below the existing fixed base fee, so this TIP can only ever make fees cheaper than they are now.

## Assumptions

- The current fixed base fee from TIP-1010 is `2 × 10^10` attodollars per gas. This TIP globally lowers the maximum base fee to `1.2 × 10^10` attodollars per gas and derives the floor from that cap.
- The base fee is denominated in attodollars (`10^-18` USD) per gas, as defined by TIP-1000 and TIP-1010. This TIP changes only how the per-gas base fee value is computed each block; it does not change the unit, the conversion to fee tokens, or how collected fees are distributed.
- The block header carries the per-gas base fee (the `baseFeePerGas` field of the Ethereum-derived Tempo header) so that the value computed by this controller is committed to and validated by every node.
- The controller input is the total gas used by all transactions included in a block (the header `gasUsed`), across all lanes (payment and general). The 10,000,000 gas target is compared against this total, so heavy payment-lane usage raises the base fee just as general usage does.
- If an assumption is violated (for example, a header carrying a base fee that does not equal the value this TIP computes), the affected block MUST be rejected as invalid.

---

# Specification

## Constants

| Name | Value | Meaning |
|---|---|---|
| `BASE_FEE_CAP` | `1.2 × 10^10` attodollars/gas | Maximum base fee; lower than TIP-1010's current fixed base fee. |
| `BASE_FEE_FLOOR` | `6 × 10^8` attodollars/gas | Minimum base fee; `BASE_FEE_CAP / 20`. |
| `GAS_TARGET` | `10,000,000` gas | Per-block gas usage the controller steers toward. |
| `BASE_FEE_MAX_CHANGE_DENOMINATOR` | `8` | Determines the rate of change of the basefee at `1 / 8` (12.5%) of the difference to gas target. |

`BASE_FEE_FLOOR`, `BASE_FEE_CAP`, `GAS_TARGET`, and `BASE_FEE_MAX_CHANGE_DENOMINATOR` are protocol parameters configured at the chainspec level and changeable only by hardfork.

All arithmetic below is performed on non-negative integers using floor (truncating) division, exactly as in EIP-1559.

## Base Fee Update Rule

Let `parent_base_fee` be the base fee of the parent block and `parent_gas_used` be the total gas used by the parent block. The base fee of the current (child) block, `base_fee`, is computed as follows:

```text
if parent_gas_used == GAS_TARGET:
    base_fee = parent_base_fee

else if parent_gas_used > GAS_TARGET:
    gas_delta   = parent_gas_used - GAS_TARGET
    fee_delta   = max(1, (parent_base_fee * gas_delta)
                          / GAS_TARGET / BASE_FEE_MAX_CHANGE_DENOMINATOR)
    base_fee    = min(parent_base_fee + fee_delta, BASE_FEE_CAP)

else:  # parent_gas_used < GAS_TARGET
    gas_delta   = GAS_TARGET - parent_gas_used
    fee_delta   = (parent_base_fee * gas_delta)
                          / GAS_TARGET / BASE_FEE_MAX_CHANGE_DENOMINATOR
    base_fee    = max(parent_base_fee - fee_delta, BASE_FEE_FLOOR)
```

This is the EIP-1559 update formula with two changes: the gas target is the fixed `GAS_TARGET` constant rather than half of a per-block gas limit, and the result is clamped to `[BASE_FEE_FLOOR, BASE_FEE_CAP]` after the adjustment.

The clamp is applied last, so:

- The base fee can never exceed `BASE_FEE_CAP` regardless of how full blocks are.
- The base fee can never fall below `BASE_FEE_FLOOR` (one twentieth of the cap) regardless of how empty blocks are.

Because `GAS_TARGET` (10,000,000) is far below the block gas limit (500,000,000 under TIP-1010), a moderately full block produces a large raw upward adjustment that saturates at `BASE_FEE_CAP` within a few blocks. This is intended: under sustained load the base fee returns to the lowered cap quickly, and the dynamic range exists to *discount* the fee during low utilization rather than to surcharge it during congestion.

## Activation

At the activation block of this TIP, the base fee is seeded to `BASE_FEE_CAP`. This applies the lowered cap immediately; subsequent blocks adjust per the update rule above using the activating block's `gasUsed`.

For blocks before activation, the fixed base fee defined by TIP-1010 continues to apply unchanged.

## Header Validation

For every post-activation block:

1. The block's `baseFeePerGas` header field MUST equal the `base_fee` value computed by the update rule from the parent block's base fee and gas used (or `BASE_FEE_CAP` for the activation block).
2. A block whose `baseFeePerGas` does not equal the computed value MUST be rejected as invalid.
3. Transaction validity against the base fee is unchanged: a transaction is includable only if its `maxFeePerGas` is at least the block's `base_fee`, and the effective gas price is computed from `base_fee` exactly as today.

## Out of Scope

This TIP does not change:

- The block gas limit, lane gas limits, or the transaction gas cap (TIP-1010).
- The attodollar unit, the gas schedule (TIP-1000), or any per-operation gas cost.
- Fee token resolution and the conversion from attodollars to TIP-20 fee tokens.
- How collected base fees are distributed (burned, paid to validators, or otherwise). Only the per-gas base fee *value* changes.

## Relationship to TIP-1010

This TIP supersedes the "fixed base fee" property of TIP-1010. TIP-1010's fixed value `2 × 10^10` attodollars/gas is replaced by the lower `BASE_FEE_CAP` value `1.2 × 10^10` attodollars/gas, and `BASE_FEE_FLOOR` is derived as `BASE_FEE_CAP / 20`. All other TIP-1010 parameters (block gas limits, lane budgets, transaction gas cap) are unchanged.

## Dynamics (informative)

The following illustrate the controller with `BASE_FEE_MAX_CHANGE_DENOMINATOR = 8` and ~500 ms (2 blocks/second) block time. All values are approximate.

- **Floor vs cap pricing of a TIP-20 transfer (50,000 gas):**
  - At `BASE_FEE_CAP` (`1.2 × 10^10`): `50,000 × 1.2 × 10^10 = 6 × 10^14` attodollars = `600` microdollars = `$0.0006` (0.06 cent).
  - At `BASE_FEE_FLOOR` (`6 × 10^8`): `50,000 × 6 × 10^8 = 3 × 10^13` attodollars = `30` microdollars = `$0.00003` (0.003 cent) — 20× cheaper than the cap.
- **Decay when idle:** an empty block (`gas_used = 0`) multiplies the base fee by `7/8` (a 12.5% drop). Starting from the cap, the base fee reaches the floor (a 20× decrease) after about 23 consecutive empty blocks (~12 seconds); the decay half-life is about 5 blocks (~2.6 seconds).
- **Rise under load:** a block at the 30,000,000 gas general cap multiplies the base fee by about `1.25` (`gas_delta = 20M`, `20M / 10M / 8 = 0.25`), so the base fee climbs from the floor back to the cap in roughly 14 such blocks (~7 seconds); fuller blocks saturate to the cap faster.

These time constants are a consequence of the chosen denominator and may be retuned by hardfork without changing the rest of the mechanism.

---

# Observability

No new protocol event is required. The base fee is committed in each block header and the controller input is the existing block `gasUsed`, so existing block/header telemetry is sufficient for protocol-level debugging. Production deployments MUST expose the metrics below so operators can tell whether this TIP is producing the intended discount under low load and whether the lower fee floor is enabling unsafe state growth.

| Metric | Fields / labels | Operational question |
|---|---|---|
| `block_base_fee_attodollars_per_gas` | `chain`, `network`, `validator`, `block_number` | What base fee did the controller produce for each block? |
| `block_gas_used` | `chain`, `network`, `validator`, `block_number` | Is block gas usage below, near, or above `GAS_TARGET`? |
| `base_fee_controller_state` | `chain`, `network`, `validator`, `state` (`floor`, `between`, `cap`) | Is the base fee pinned at the floor, pinned at the cap, or moving within the dynamic range? |
| `state_size_bytes` | `chain`, `network`, `validator`, `component` (`hashed_accounts`, `hashed_storages`, `bytecodes`, `accounts_trie`, `storages_trie`, `total`) | How large is total state, split between flat state (`hashed_accounts`, `hashed_storages`, `bytecodes`) and trie state (`accounts_trie`, `storages_trie`)? |
| `state_growth_bytes` | `chain`, `network`, `validator`, `component`, `window` (`1d`, `7d`, `30d`) | How quickly is state growing over day, week, and month windows? |
| `projected_state_size_bytes` | `chain`, `network`, `validator`, `component`, `horizon` (`7d`, `14d`, `30d`) | If recent growth continues, how large will state be in one week, two weeks, and one month? |
| `state_disk_free_bytes` | `chain`, `network`, `validator`, `volume` | How much capacity remains before state growth threatens node operation? |

Alerts SHOULD cover:

1. **Sustained underpriced load:** `block_base_fee_attodollars_per_gas == BASE_FEE_FLOOR` while gas usage is at or above `GAS_TARGET` for a sustained window.
2. **State growth anomaly:** 1-day, 7-day, or 30-day `state_growth_bytes{component="total"}` exceeds the expected operating envelope.
3. **State growth projection:** `projected_state_size_bytes{component="total", horizon="30d"}` exceeds the operator-defined capacity budget.
4. **Disk exhaustion risk:** projected state growth plus current usage leaves less than the operator-defined free-space buffer.
5. **Metric freshness:** state-size and block/base-fee metrics are missing or stale for any production validator.

For reth-based nodes, the state-size metrics SHOULD be derived from `reth_db_table_size` with flat state tracked as `HashedAccounts`, `HashedStorages`, and `Bytecodes`, and trie state tracked as `AccountsTrie` and `StoragesTrie`.

# Invariants

1. **Range clamp:** For every post-activation block, `BASE_FEE_FLOOR ≤ base_fee ≤ BASE_FEE_CAP`.
2. **Cap below current cost:** `BASE_FEE_CAP` is lower than the fixed base fee defined by TIP-1010, so the dynamic base fee is never greater than the price charged before this TIP.
3. **Floor:** `BASE_FEE_FLOOR = BASE_FEE_CAP / 20`, so the base fee is never less than one twentieth of the cap.
4. **Determinism:** `base_fee` is a pure function of the parent block's base fee and gas used (and the constants), so all honest nodes compute the same value for a given parent.
5. **Bounded step:** Excluding the final clamp, the base fee changes by at most `parent_base_fee / BASE_FEE_MAX_CHANGE_DENOMINATOR` between consecutive blocks; the clamp can only reduce the magnitude of a step, never increase it.
6. **Monotonic response:** If `parent_gas_used > GAS_TARGET` then `base_fee ≥ parent_base_fee`; if `parent_gas_used < GAS_TARGET` then `base_fee ≤ parent_base_fee`; if `parent_gas_used == GAS_TARGET` then `base_fee == parent_base_fee` (subject to the range clamp).
7. **Header consistency:** A block is valid only if its `baseFeePerGas` header field equals the computed `base_fee`.
8. **Activation seed:** The base fee of the activation block equals `BASE_FEE_CAP`.
9. **No fee increase relative to status quo:** For identical block contents, the effective gas price under this TIP is always less than or equal to the effective gas price under TIP-1010's fixed base fee.

## Test Cases

The test suite MUST cover:

1. **Steady state:** a block with `gas_used == GAS_TARGET` leaves the base fee unchanged.
2. **Decay to floor:** a run of empty blocks drives the base fee down by 12.5% per block and clamps at `BASE_FEE_FLOOR`, never below.
3. **Rise to cap:** a run of full blocks drives the base fee up and clamps at `BASE_FEE_CAP`, never above.
4. **Clamp boundaries:** starting at the floor, an under-target block keeps the base fee at the floor; starting at the cap, an over-target block keeps it at the cap.
5. **Minimum increase:** when `parent_gas_used` exceeds `GAS_TARGET` by a tiny amount and the parent base fee is at the floor, the base fee increases by at least 1 attodollar/gas.
6. **Activation:** the activation block uses `BASE_FEE_CAP`; the next block adjusts from it using the activation block's `gas_used`.
7. **Header rejection:** a block whose `baseFeePerGas` differs from the computed value is rejected.
8. **Transfer pricing:** a 50,000-gas TIP-20 transfer costs ~0.06 cent at the cap and ~0.003 cent at the floor.
9. **Lane aggregation:** payment-lane gas counts toward `gas_used` for the controller, so a block filled with payment-lane transfers raises the base fee.
