Transaction Fees on Rollups

10/17/2311 min read

Franck Cassezby Franck Cassez

Mantle

Web3

Transaction Fees on Rollups

The Ethereum network provides a virtual computer to which users can submit transactions. The execution of a transaction is subject to fees paid in $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 states1 are committed to the Ethereum network, on-chain, at 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 bb on L2 at time tt vs data availability of bb on L1 at time t>tt' > t. This results in 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:

execution fees: off-chain/L2, an amount that is expressed in the L2 native tokens, $L2NT, and

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\textit{tx} is g(tx)g(tx), measured in gas units. This value depends on the complexity of the computation: for instance, a simple transfer transaction consumes 2100021000 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)\textit{Maxgas}(tx) along with their transaction txtx. 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.

The gas fees is the product g(tx)×gp(L1,t)g(tx) \times gp(L1, t), where gp(L1,t)gp(L1, t) is the L1 gas price, in $ETH, at the time tt when txtx 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)\textit{Maxgas}(tx) \times 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\textit{tx} executed on L2 uses the same amount of gas, g(tx)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 txtx with a gas budget Maxgas(tx)\textit{Maxgas}'(tx). The gas fees on L2 should have the form g(tx)×gp(L2,t)g'(tx) \times gp(L2, t) where g(tx)g'(tx) integrates the execution cost g(tx)g(tx) and the data availability costs, and gp(L2,t)gp(L2, t) is the gas price on L2 at time tt when tx\textit{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)g'(tx) \times gp(L2, t).
[Rule 2] Lower L2 fees compared to L1, g(tx)×gp(L2,t)g(tx)×gp(L1,t)g'(tx) \times gp(L2, t) \leq g(tx) \times gp(L1, t).
[Rule 3] The maximum gas cost Maxgas(tx)\textit{Maxgas}'(tx) should be the same L1 gas budget Maxgas(tx)\textit{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 txtx on L2 is Rcost(tx)\textit{Rcost}(tx) the L2 operator expects:

[Rule 4] g(tx)×gp(L2,t)>RCost(tx)g'(tx) \times gp(L2, t) > RCost(tx).

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

RCost(tx)<g(tx)×gp(L2,t)g(tx)×gp(L1,t).RCost(tx) < g'(tx) \times gp(L2, t) \leq g(tx) \times gp(L1, t)\mathpunct.

Batch Processing

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,Tx3Tx_1, Tx_2, Tx_3.

The rollup sequencer executes the batch at time t1t_1 and:

  1. collects the L2 transactions' fees for each transaction, and

  2. submits the batch's data availability transaction DaTx\textit{DaTx} to L1.

Collecting the fees at t1t_1 requires to factor in the execution and data availability fees. However, the actual fees for processing DaTx\textit{DaTx} are not known at t1t_1 as DaTx\textit{DaTx} will be processed at a later time t2t_2 on L1. In the time interval between t1t_1 and t2t_2 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 actual cost of DaTx\textit{DaTx} at time t3t_3 when a batch processing report is received on L2.

The real cost for processing txtx 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)\begin{aligned} \textit{Rcost}(tx) & = g(tx) \times gp(L2, t_1) + g(\textit{DaTx}) \times gp(L1, t_2) & \\ & = \Bigl( g(tx) + \frac{g(\textit{DaTx}) \times gp(L1, t_2)}{gp(L2, t_1)} \Bigr) \times gp(L2, t_1) & \end{aligned}

which has the form of Rule 1 with g(tx)=g(tx)+ϵ(tx)g'(tx) = g(tx) + \epsilon(tx). We only need to express the gas prices on L1 and L2 in the same currency, so an exchange rate is needed.

Note however that g(L1,t2)g({L1,t_2}) is NOT known at t1t_1 which is when the originator of the transaction is charged.

However, 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\textit{xs} be a sequence of bytes and let xs0|\textit{xs}|_0 denote the number of zero bytes in xs\textit{xs}. The gas needed to publish xs\textit{xs} to L1 is w(xs)=4×xs0+16× (xsxs0).w(\textit{xs}) = 4 \times |\textit{xs}|_0 + 16 \times (|\textit{xs}| - |\textit{xs}|_0)\mathpunct. 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×tx16 \times |tx| where tx|tx| is the size of calldata of the transaction. A simple $ETH transfer transaction uses g=21000g = 21000 gas (execution) and the corresponding data availability gas is bounded by 35×1635 \times 16 with 35 bytes of calldata and the maximum gas cost of a byte in calldata is 1616.

Prediction of Data Availability Costs

As mentioned above, the actual cost of the batch's data availability transaction is known only at t2t_2, but the fees are collected on L2 at t1t_1. 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~\tilde{g} that can predict the gas price at time t2t_2, and, for txtx, the L2 operator charges:

Chcost(tx)=g(tx)×gp(L2,t1)+g(DaTx)×g~(L1,t2)\begin{aligned} \textit{Chcost}(tx) & = g(tx) \times gp(L2, t_1) + g(\textit{DaTx}) \times \tilde{g}(L1, t_2) & % \label{eq-3} % & = \Bigl( g(tx) + \frac{g(\datx) \times gp(L1, t_2)}{gp(L2, t_1)} \Bigr) \times gp(L2, t_1) & \end{aligned}

The profit (surplus or negative) made by the L2 operator is:

Chcost(tx)Rcost(tx)=g(DaTx)×(g~(L1,t2)gp(L1,t2)).\begin{aligned} \textit{Chcost}(tx) - Rcost(tx) & = g(\textit{DaTx}) \times \bigl(\tilde{g}(L1, t_2) - gp(L1, t_2) \bigr)\mathpunct. & \end{aligned}

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 tt the L1 gas price is gp(L1,t)=5gp(L1, t)=5 and can go up or down by 12.5%. After 60 seconds (6 new blocks), at t+60t +60, we know the price gp(L1,t+60)gp(L1, t + 60) sits in the interval

5×(0.875)62.24gp(L1,t+60)5×(1.125)610.136.\underbrace{5\times (0.875)^6}_{\approx 2.24} \leq gp(L1, t+ 60) \leq \underbrace{5 \times (1.125)^6}_{\approx 10.136} \mathpunct.

More generally, a sound estimate of gp(L1,t+60)gp(L1,t + 60) is: gp(L1,t)×0.44gp(L1,t+60)gp(L1,t)×2.03.gp(L1, t) \times 0.44 \leq gp(L1, t + 60) \leq gp(L1, t) \times 2.03 \mathpunct. 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 tt is 1010 and L1 price is 120120. A simple $ETH transfer transaction consumes g=21000g = 21000 gas (execution) and the data availability gas is bounded by 35×1635 \times 16 with 35 bytes of calldata and the maximum gas cost of a byte in calldata is 1616. The real cost at time tt is 21000×10+35×16×120=27720021000 \times 10 + 35 \times 16 \times 120 = 277200. If at time tt' the L2 gas price is 1212 and the L1 gas price is 7070, The real cost at time tt' is 21000×12+35×16×70=27020021000 \times 12 + 35 \times 16 \times 70 = 270200 which is 70007000 less than at tt.

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?, Transaction fees on L2. Optimism charges a fixed overhead FF on the cost of posting the transaction data to L1, and scales the overall price by a dynamic overhead factor DD. As a result, the L1 fees charged to the user for transaction tx\textit{tx} depends on a recent value of the L1 gas price, L1LastGasPrice\textit{L1LastGasPrice}:

L1Fees(tx)=L1LastGasPrice×(w(tx)+F)×DL1Fees(tx) = \textit{L1LastGasPrice} \times (w(tx) + F) \times D

According to Transaction Fees, the values of FF and DD are respectively 21002100 and 11. The update in fees is done by updating the value of FF.

This strategy tracks the gas price on L1 with a delay and may require frequent updates from L1 to make sure the L1LastGasPrice\textit{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 calldata data pricing / Sequencer reimbursement, "Fee Pool" Model. The L2 fees and overall model are explained in Gas and Fees, 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 submitter 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 the Research Team at Mantle franck.cassez@mantle.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.

Footnotes

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


Join the Community