# EIP-1559 Time-Based Base Fee Adjustments
## Motivation
- current EIP-1559 base fee adjustment: based on block gas usage
- in effect, control loop that targets stable throughput per block
- not ideal under PoW under two aspects:
- block time variability:
- block gas usage tries to measure demand at current base fee level, but gas usage is proportional to block time, introducing noise to the used signal
- block time variability => "incorrect" demand signals => "incorrect" base fee adjustments => increased base fee volatility
- reduced throughput during consensus issues:
- if chain forks, block times go up for a while, before difficulty is adjusted
- gas limit elasticity would give us the capability to compensate for this throughput reduction to some extent
- also not ideal under PoS:
- missed slots:
- while general block times become regular, occasionally slots are missed, doubling the block time for the next block (or more, if that slot is also missed)
- missed slots still send incorrect signal (demand looks 2x as high as it is), leading to base fee spikes after missed slots
- incentive to attack network via block proposer DOS, as each missed slot directly reduces network throughput
- consensus issues:
- worse than under PoW, as longer time for self-healing (several weeks for inactivity leaks as opposed to several hours for difficulty adjustments)
- gas limit elasticity would again give us the capability to compensate for this throughput reduction to some extent
## Proposal
- include block time in base fee adjustment: make "block gas target" proportional to block time
- results in control loop that targets stable throughput per time
- addresses problems listed above:
- reduces / removes base fee volatility inroduced by PoW block time variability
- reduces / removes base fee volatility inroduced by PoS missed slots
- reduces incentive to DOS block proposers
- reduces / removes impact of chain forks on throughput
- drawback: during demand spikes under PoS, missed slots can delay base fee increase
#### Current EIP-1559 Update Rule
$b_{n+1} = b_n * (1 + \frac{1}{8}\frac{g_n-G_n}{G_n})$, where:
$b_n$ : base fee at block $n$
$\frac{1}{8}$ : maximum base fee change rate
$g_n$ : gas used by block $n$
$G_n$ : gas target for block $n$
#### Block Time Based Update Rule
$b_{n+1} = b_n * (1 + \frac{1}{8}\frac{g_n-G_n\frac{t_n}{T_n}}{G_n})$, where:
$t_n$ : block time for block $n$, i.e. `block.timestamp - parent.timestamp`
$T_n$ : block time target
#### Capped Block Time Based Update Rule
In practice it is sensible to introduce an upper limit to the block time used by the update rule:
- during chain forks, block gas targets above 50% reduce the ability to pick up on demand increases
- in the extreme, with 50%+ of proposers offline, the base fee could not increase at all
- fee market would revert to a first-price auction on top of the current base fee level
- base fee would even leak downwards due to small residual fee block space (<21k)
- alternative: bound block gas target to below elasticity limit
$b_{n+1} = b_n * (1 + \frac{1}{8}\frac{g_n-G_n\frac{\text{min}(t_n, T_{\text{max}})}{T_n}}{G_n})$, where:
$T_{\text{max}}$ : maximum block time to consider (e.g. 23s or 24s for PoS)
### Specification
The relevant part of the pseudocode specification of EIP-1559 is:
```python=
BASE_FEE_MAX_CHANGE_DENOMINATOR = 8
parent_gas_target = self.parent(block).gas_limit // ELASTICITY_MULTIPLIER
parent_gas_limit = self.parent(block).gas_limit
parent_base_fee_per_gas = self.parent(block).base_fee_per_gas
parent_gas_used = self.parent(block).gas_used
if parent_gas_used == parent_gas_target:
expected_base_fee_per_gas = parent_base_fee_per_gas
elif parent_gas_used > parent_gas_target:
gas_used_delta = parent_gas_used - parent_gas_target
base_fee_per_gas_delta = max(parent_base_fee_per_gas * gas_used_delta // parent_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR, 1)
expected_base_fee_per_gas = parent_base_fee_per_gas + base_fee_per_gas_delta
else:
gas_used_delta = parent_gas_target - parent_gas_used
base_fee_per_gas_delta = parent_base_fee_per_gas * gas_used_delta // parent_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR
expected_base_fee_per_gas = parent_base_fee_per_gas - base_fee_per_gas_delta
```
This update would only require changes to lines (2, 3, 5), 6, 15, 19:
```python=
BASE_FEE_MAX_CHANGE_DENOMINATOR = 8
BLOCK_TIME_TARGET = 12
BLOCK_TIME_CONSIDERATION_CAP = 23
parent_considered_block_time = min(self.parent(block).timestamp - self.parent(self.parent(block)).timestamp, BLOCK_TIME_CONSIDERATION_CAP)
parent_gas_target = self.parent(block).gas_limit * parent_considered_block_time // ELASTICITY_MULTIPLIER // BLOCK_TIME_TARGET
parent_gas_limit = self.parent(block).gas_limit
parent_base_fee_per_gas = self.parent(block).base_fee_per_gas
parent_gas_used = self.parent(block).gas_used
if parent_gas_used == parent_gas_target:
expected_base_fee_per_gas = parent_base_fee_per_gas
elif parent_gas_used > parent_gas_target:
gas_used_delta = parent_gas_used - parent_gas_target
base_fee_per_gas_delta = max(parent_base_fee_per_gas * gas_used_delta * ELASTICITY_MULTIPLIER // parent_gas_limit // BASE_FEE_MAX_CHANGE_DENOMINATOR, 1)
expected_base_fee_per_gas = parent_base_fee_per_gas + base_fee_per_gas_delta
else:
gas_used_delta = parent_gas_target - parent_gas_used
base_fee_per_gas_delta = parent_base_fee_per_gas * gas_used_delta * ELASTICITY_MULTIPLIER // parent_gas_limit // BASE_FEE_MAX_CHANGE_DENOMINATOR
expected_base_fee_per_gas = parent_base_fee_per_gas - base_fee_per_gas_delta
```
## Future Changes
- exponential base fee update
- more elegant properties
- slightly more involved change to include efficient deterministic exponentiation
- variable block gas limits (dependent on block times)
- feasible to the extent the bottleneck for block gas limits is state growth, not networking / computation