moonbeam icon indicating copy to clipboard operation
moonbeam copied to clipboard

Use `transaction-payment`'s congestion modifier for Ethereum base-fee

Open notlesh opened this issue 2 years ago • 3 comments

What does it do?

This PR explores using a common congestion-based modifier for both Ethereum- and Substrate-based fees. Specifically, it attempts to transform the transaction-payment NextFeeMultiplier to achieve something suitable for an EIP-1559 base-fee.

I break down issues I've encountered below into "design" issues and "technical" issues.

Design issues

transaction-payment only applies its congestion modifier to the weight portion of its fee.

Like Ethereum, it contains several components in its fee calculation:

  • weight-fee: this accounts for resources used during execution
  • length-fee: this pays for the size of the signed txn
  • extrinsic-base-fee: this handles signature verification and any other work common to all transactions
  • tip: a flat amount (expressed in currency) that is not scaled

Unlike Ethereum, the congestion modifier only applies to the weight portion of the fee.

Ethereum lumps all categories of fees into a total gas used and then applies the congestion modifier to the entire sum.

This means that converting the modifier in the way I have done here is going to work better for some transactions (those whose fee is based mostly on computation resources) than others.

Technical issue: Default modifier value in transaciton-payment

One of the first hurdles I encountered in exploring this is that transaction-payment does not provide any mechanism for specifying the initial modifier at genesis.

It defaults to 1 (the type is a FixedU128, which contains 18 decimal places). Our runtimes currently specify a minimum of 0.0000001, and in practice tend to be close to this value. This huge discrepancy means that our tests will operate on a very different modifier than in real-world.

Solutions

  • PR Substrate and introduce a proper GenesisConfig
  • Tune other parts of our fee mechanism to work properly with a modifier value closer to 1 (side-note: Polkadot chains use a minimum of 0.1)

Convert<Multiplier, Multiplier>

Note that transaction-payment allows its config to specify a Convert<Multiplier, Multiplier> impl which can entirely replace the congestion modifier calculation. We could potentially use this to make this pallet work closer to EIP-1559.

Testing / Tuning

One way to help us tune (and also test) this could be to have a suite of tests which create pairs of substrate + ethereum transactions which are similar (using precompiles might be an easy way to make similar txns). Then we can use the tests to show that each pair of tests has similar txn cost even as the modifier grows across a range of values.

Analysis

image image

The fees converge as the network becomes more congested (logarithmic scale). If the network remains empty, then the fees are also held at a constant value.

The following table exhibits the effect of the multiplier starting from 0.000003 (currently in moonriver) to increasing magnitudes. We see that the substrate and evm fees converge on higher multiplier values but substrate fees will always be higher than evm fees by a factor of base + length fees.

image

Multiplier                      Fees
------------------------------------
  0.000003        12,746,162,435,424 | substrate
                         147,518,810 | evm
                  12,746,014,916,614 | diff
                              199.9% | diff%
                         147,548,488 | substrate-weight-fee-part
                  12,500,000,000,000 | substrate-base-fee-part
------------------------------------ | 
     0.001        12,783,532,386,936 | substrate
                      37,517,500,000 | evm
                  12,746,014,886,936 | diff
                              198.8% | diff%
                      37,517,500,000 | substrate-weight-fee-part
                  12,500,000,000,000 | substrate-base-fee-part
------------------------------------ | 
       0.1        16,497,764,886,936 | substrate
                   3,751,750,000,000 | evm
                  12,746,014,886,936 | diff
                              125.8% | diff%
                   3,751,750,000,000 | substrate-weight-fee-part
                  12,500,000,000,000 | substrate-base-fee-part
------------------------------------ | 
         1        50,226,016,145,686 | substrate
                  37,480,001,258,750 | evm
                  12,746,014,886,936 | diff
                                 29% | diff%
                  37,480,001,258,750 | substrate-weight-fee-part
                  12,500,000,000,000 | substrate-base-fee-part
------------------------------------ | 
        10       387,546,027,474,436 | substrate
                 374,800,012,587,500 | evm
                  12,746,014,886,936 | diff
                                3.3% | diff%
                 374,800,012,587,500 | substrate-weight-fee-part
                  12,500,000,000,000 | substrate-base-fee-part
------------------------------------ | 
       100     3,760,746,140,761,936 | substrate
               3,748,000,125,875,000 | evm
                  12,746,014,886,936 | diff
                                0.3% | diff%
               3,748,000,125,875,000 | substrate-weight-fee-part
                  12,500,000,000,000 | substrate-base-fee-part

From the above table the range of 1.0 - 200.0 seems good so far.

TODO

  • [ ] Find solution for no GenesisConfig in transaction-payment
  • [ ] Fix existing test cases
  • [ ] Tune values to work for various transaction types
  • [ ] New tests
  • [ ] Document breaking changes

notlesh avatar Aug 19 '22 16:08 notlesh

The NextFeeMultiplier is much similar to the base-fee pallet but on the substrate side. Why not use base-fee pallet here?

boundless-forest avatar Sep 08 '22 08:09 boundless-forest

The NextFeeMultiplier is much similar to the base-fee pallet but on the substrate side. Why not use base-fee pallet here?

@AsceticBear The initial reason being having to maintain/re-implement less code/functionality which is quite easy to get wrong (e.g. fees will never recover or continuously drift in a single direction). The original motivation we have here is to adapt the EVM fees to the changing network traffic and substrate already provides us with a well-tested mechanism in place - this also means the network fee would be more in tandem between Substrate and EVM.

nbaztec avatar Sep 09 '22 13:09 nbaztec

The NextFeeMultiplier is much similar to the base-fee pallet but on the substrate side. Why not use base-fee pallet here?

A bit more detail:

I explored using this and came to the conclusion that EIP1559's design isn't well suited for a network that doesn't experience consistent congestion.

Basically, I think the original design creates a feedback mechanism that keeps the congestion modifier within a reasonable range, something like this:

congestion up -> fees up -> fewer txns eligible -> blocks less full -> congestion down

and

congestion down -> fees down -> more txns eligible -> blocks more full -> congestion up

Without this, it becomes critical to tune pallet base-fee's Threshold parameters because incorrect tuning will create a permanent upward or downward trend. The pallet has no bounds on the fee itself (nor does EIP1559) which can eventually result in fees being either near-zero or impossibly expensive.

While pallet transaction-payment has a similar mechanism, it has far more knobs to tune (including a minimum fee) to alleviate this.

notlesh avatar Sep 13 '22 21:09 notlesh