Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
This block post was originally published in October 2023 as a Mantle blog post here, but it looks like the content is not accessible anymore the link returns a 500 error. As the source of the blog post was on hackmd, I just made it visible.

The Ethereum network provides a virtual computer to which users can submit transactions. The execution of a transaction is subject to fees paid in the native currency $ETH by the transaction's originator. The fees depend on how complex the transaction is, measured in gas, and on the gas price, the price of a unit of gas which reflects the network contention at the time the transaction is processed (see Ethereum Gas and Fees). Currently the Ethereum network has a limited throughput of around 15 transactions per seconds, and transaction fees can be very expensive under network contention. This calls for more scalable and affordable solutions.

Rollups address the fees and scalability problems by executing transactions in batches in a separate chain, off-chain, at layer 2 (L2). To guarantee security, the transactions' data and the L2 pre- and post-batch states[1] are committed to the Ethereum network, on-chain, on layer 1 (L1), via a data availability transaction. This guarantees data availability on-chain and the ability for anyone to verify that the L2 states reflect the effect of the transactions processed so far.

Rollups design principles decouple execution and data availability in time and space: execution of a batch

b on L2 at time
t
vs data availability of
b
on L1 at time
t>t
. This results in a more complex L2 fee mechanisms compared to L1. In this post, we explain the difficulties to design a fee mechanism for rollups.

The cost of processing a transaction on a rollup is 2-dimensional:

  1. execution fees: off-chain/L2, an amount that is expressed in the L2 native tokens,
    $L2NT, and
  2. data availability fees: on-chain/L1, expressed in $ETH.

Execution Fees on L1

The amount of resources (CPU, storage) used by the Ethereum Virtual Machine (EVM) to compute the effect of a transaction

tx is
g(tx)
, measured in gas units. This value depends on the complexity of the computation: for instance, a simple transfer transaction consumes 21000 gas irrespective of the time it is executed. More complex transactions that include storage updates are more expensive. It is important that gas consumption be predictable because users have to submit a maximum gas budget
Maxgas(tx)
along with their transaction
tx
. The execution of a transaction can use at most the budgeted gas and if gas runs out before the end of the computation, the transaction aborts i.e. has no effect on the global storage.

The gas fees is the product

g(tx)×gp(L1,t), where
gp(L1,t)
is the L1 gas price, in $ETH, at the time
t
when
tx
is processed and added to a block.

Before executing a transaction, the EVM checks that the originator's account balance can cover the maximum gas fees

Maxgas(tx)×gp(L1,t) for the transaction. If this is not the case, the transaction is not processed.

The L1 gas price evolves to reflect the network load: if recent L1 blocks are more than half-full then the price increases, if they are less than half-full then it decreases. The L1 gas price is dynamically adjusted after each L1-block is produced in accordance with the policy defined in EIP-1559.

Execution Fees on L2

Rollups provide an EVM-equivalent environment. As a result a transaction

tx executed on L2 uses the same amount of gas,
g(tx)
, as if it was executed on L1. To provide an equivalent experience on L2, we want a user to be able to submit a transaction
tx
with a gas budget
Maxgas(tx)
. The gas fees on L2 should have the form
g(tx)×gp(L2,t)
where
g(tx)
integrates the execution cost
g(tx)
and the data availability costs, and
gp(L2,t)
is the gas price on L2 at time
t
when
tx
is processed and added to a block on L2.

To summarise, on the one hand, users expect:
[Rule 1] One-dimensional L2 fees of the form

g(tx)×gp(L2,t).
[Rule 2] Lower L2 fees compared to L1,
g(tx)×gp(L2,t)g(tx)×gp(L1,t)
.
[Rule 3] The maximum gas cost
Maxgas(tx)
should be the same L1 gas budget
Maxgas(tx)
. Users may use standard (L1) tools to estimate how much is needed to execute a transaction and this estimate should be valid on L2.

On the other hand, L2 operators expect to make some marginal profit. If the real cost (execution + data availability) of processing

tx on L2 is
Rcost(tx)
the L2 operator expects:
[Rule 4]
g(tx)×gp(L2,t)>RCost(tx)
.

Overall, as

g integrates execution and data availability costs, we can define
g(tx)=g(tx)+ϵ(tx)
where
ϵ(tx)
is the overhead generated by the data availability costs. To satisfy Rule 3,
ϵ(tx)
should be small. To satisfy Rules 2 and 4,
gp(L2,t)
must satisfy the following contraints:
RCost(tx)<g(tx)×gp(L2,t)g(tx)×gp(L1,t).

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →
Figure 1: Batch Processing Transactions.

Data Availability Costs

On a rollup, transactions are executed in batches. Figure 1 shows the ordering of the main phases to process a batch of transactions

Tx1,Tx2,Tx3.

The rollup sequencer executes the batch at time

t1 and:

  1. collects the L2 transactions' fees for each transaction, and
  2. submits the batch's data availability transaction
    DaTx
    to L1.

Collecting the fees at

t1 requires to factor in the execution and data availability fees. However, the actual fees for processing
DaTx
are not known at
t1
as
DaTx
will be processed at a later time
t2
on L1. In the time interval between
t1
and
t2
the gas price on L1 may change: as mentioned before, the L1 gas price is dynamically adjusted after each L1-block is produced in accordance with the policy defined in EIP-1559. The L2 operator will know the cost of
DaTx
at time
t3
when a batch processing report is received on L2.

The real cost for processing

tx is then:
Rcost(tx)=g(tx)×gp(L2,t1)+g(DaTx)×gp(L1,t2)=(g(tx)+g(DaTx)×gp(L1,t2)gp(L2,t1))×gp(L2,t1)
which has the form of Rule 1 with
g(tx)=g(tx)+ϵ(tx)
. We only need to express the gas prices on L1 and L2 in the same currency, so an exchange rate is needed.

Note that

g(DaTx) is NOT known at
t1
which is when the originator of the transaction is charged.
Nevertheless, the gas cost of a data availability transaction can be estimated precisely. It only depends on the size of the transaction. More precisely, let
xs
be a sequence of bytes and let
|xs|0
denote the number of zero bytes in
xs
. The gas needed to publish
xs
to L1 is
w(xs)=4×|xs|0+16×(|xs||xs|0).
In other words, zero-bytes cost 4 gas units and non-zero bytes cost 16 gas units. An upper bound for posting the data of a transaction is given by
16×|tx|
where
|tx|
is the size of calldata of the transaction. A simple $ETH transfer transaction uses
g=21000
gas (execution) and the corresponding data availability gas is bounded by
35×16
with 35 bytes of calldata and the maximum gas cost of a byte in calldata is
16
.

Prediction of Data Availability Costs

As mentioned above, the actual cost of the batch's data availability transaction is known only at

t2, but the fees are collected on L2 at
t1
. This means that the L2 operator cannot use the exact L1 gas price but has to rely on an estimation. This is the role of a gas oracle to predict the cost of the L1 gas price in the near future. The L2 operator uses a gas oracle
g~
that can predict the gas price at time
t2
, and, for
tx
, the L2 operator charges:
Chcost(tx)=g(tx)×gp(L2,t1)+g(DaTx)×g~(L1,t2)

The profit (surplus or negative) made by the L2 operator is:
Chcost(tx)Rcost(tx)=g(DaTx)×(g~(L1,t2)gp(L1,t2)).

Fortunately the L1 gas price became more predictable with the introduction EIP-1559. The price varies according to how full blocks are with a target of half-full. If the blocks are less than half-full the price decreases. If the blocks fill up more than half it increases. The price is updated at each block, approximately every (slot) 12 seconds, and the variation between successive blocks is bounded by 12.5% (up or down).

It is estimated that the time for a transaction to be processed (not necessarily finalised) is between 12 seconds and a few minutes (depending on the network contention and tip added to the base fees.)

An as example, assume at time

t the L1 gas price is
gp(L1,t)=5
and can go up or down by 12.5%. After 60 seconds (6 new blocks), at
t+60
, we know the price
gp(L1,t+60)
sits in the interval
5×(0.875)62.24gp(L1,t+60)5×(1.125)610.136.

More generally, a sound estimate of

p(t+60) is:
gp(L1,t)×0.44gp(L1,t+60)gp(L1,t)×2.03.
The L1 gas price can actually double or halve within 60 seconds.

Adjusting the L2 Gas Price

Because of the L1 gas price volatility, it is unavoidable that a gas oracle will sometimes underestimate the L1 gas price of the data availability transactions. As a result, the L2 operator may be out of pocket for the processing of some batches.

This can lead to situations described in Arbitrum 2-dimensional fees where the L2 gas price increases but the L2 transaction fees decrease. For instance assume the L2 price at time

t is
10
and L1 price is
120
. A simple $ETH transfer transaction consumes
g=21000
gas (execution) and the data availability gas is bounded by
35×16
with 35 bytes of calldata and the maximum gas cost of a byte in calldata is
16
. The real cost at time
t
is
21000×10+35×16×120=277200
. If at time
t
the L2 gas price is
12
and the L1 gas price is
70
, The real cost at time
t
is
21000×12+35×16×70=270200
which is
7000
less than at
t
.

So what are the strategies adopted by popular rollups like Arbitrum and Optimism to adjust the L2 gas price?

Optimism

The estimation of L1 fees implemented in Optimism (pre-bedrock) is explained in the help and documentation sections How do transaction fees on Optimism work?,
and Transaction fees on L2. Optimism charges a fixed overhead

F on the cost of posting the transaction data to L1, and scales the overall price by a dynamic overhead factor
D
. As a result, the L1 fees charged to the user for transaction
tx
depends on a recent value of the L1 gas price,
L1LastGasPrice
:
L1Fees(tx)=L1LastGasPrice×(w(tx)+F)×D.

According to Transaction Fees,the values of
F
and
D
are respectively
2100
and
1
. The update infees is done by updating the value of
F
.

This strategy tracks the gas price on L1 with a delay and may require frequent updates from L1 to make sure the

L1LastGasPrice is almost in sync with current L1 gas price.

Arbitrum-Nitro

The estimation of the L1 fees in Arbitrum (Nitro) is explained in the following documents: L1
Pricing
, L1 calldatadata pricing / Sequencer reimbursement, "Fee Pool" Model.The L2 fees and overall model are explained in Gas andFees, Understanding Arbitrum:2-Dimensional
Fees
.

Arbitrum uses a fund pool to collect the fees and pay rewards to the batch submitter(s) (who paid for a batch submission). The batch submitters are paid using available funds in the fund pool at regular intervals: every time a batch report is received at L2. The batch report contains the actual price paid for the data availability transaction and can be used to track the difference between what was charged compared to what was paid.

It may happen that the fund pool does not have enough funds to reward the batch submitters and in this case, the batch submitters are paid a portion of what they are owned. The remainder will be paid in the next rounds.

Arbitrum adjusts the L2 gas price to reflect the wealthiness of the fund pool: if the fund pool has a surplus, the L2 gas price goes down, and if it is in deficit the L2 gas price goes up. The details for adjustments of the L2GasPrice are available in the L1PricerFundsPool (Go) code . Depending on whether the last batch report resulted in a surplus or a loss, the L2GasPrice is adjusted, up or down.

Another nice feature of the Arbitrum strategy is that they use the funds collected since the last batch submission to cover the costs of the previous batches. In effect, this strategy is robust against variations in the sizes of the batches and variations in the load of the network.

Conclusion

There is no perfect solution to predict accurately the L1 gas price. Rollups have deployed several strategies to implement mechanisms to adjust L2 gas prices to reflect variations in L1 gas prices and cover the costs of operation.

Storing data permanently on L1 is very expensive. The next main improvement in the Ethereum network is probably dank-sharding which will partially address this issue. For the time being, the data availability cost still represents 90% of the cost of a transaction on a rollup. Transaction fees are a key factor for users when choosing an ecosystem. There is a huge opportunity to foster innovative techniques to reduce
data availability fees. At Mantle, our research team is investigating and deploying new solutions to further reduce the data availability fees including:

  • using a dedicated data availability layer, MantleDA powered by EigenLayer, and
  • designing, modelling and analysing strategies to optimally adjust L2 gas prices.

Finally, there are initiatives Multi-dimensional EIP-1559, Notes on multi-dimensional EIP-1559. to try and formalise the concept of multi-dimensional fees and provide a framework to account for different types of resources' usage on L1.

Feel free to contact me franck.cassez@movementlabs.xyz if you are interested in this topic.

References

[1] Optimism. Transaction fees on L2.

[2] Optimism. How do transaction fees on Optimism work?

[3] Arbitrum. Gas and Fees.

[4] Arbitrum. L1 Pricing.

[5] D. Goldman. Medium, July 1st, 2022. Understanding Arbitrum 2-Dimensional Fees.

[6] DZack23. July 2022. L1 calldata data pricing / Sequencer reimbursement, "Fee Pool" Model.

[7] EIP-1559.

[8] Ethereum. Gas and
Fees.

[9] Multi-dimensional EIP-1559.

[10] Notes on multi-dimensional EIP-1559.

[11] Optimism. Bedrock Fees Dashboard.

[12] Arbitrum Nitro. Fees Dashboard.

[13] EigenLayer.

[14] Ethereum. Scaling.


  1. In a Validity Proof (zk) rollup, a proof of correct execution is submitted instead. ↩︎