Try   HackMD

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

bn+1=bn(1+18gnGnGn), where:

bn : base fee at block
n

18
: maximum base fee change rate
gn
: gas used by block
n

Gn
: gas target for block
n

Block Time Based Update Rule

bn+1=bn(1+18gnGntnTnGn), where:

tn : block time for block
n
, i.e. block.timestamp - parent.timestamp
Tn
: 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

bn+1=bn(1+18gnGnmin(tn,Tmax)TnGn), where:

Tmax : maximum block time to consider (e.g. 23s or 24s for PoS)

Specification

The relevant part of the pseudocode specification of EIP-1559 is:

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:

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