--- title: Fees on Rollups tags: rollups, fees, Ethereum, layer 2, layer 1 breaks: false --- :::warning :warning: This block post was originally published in October 2023 as a Mantle [blog post here](https://www.mantle.xyz/blog/research), but it looks like the content is not accessible anymore [the link returns a 500 error](https://www.mantle.xyz/blog/research/transaction-fees-on-rollups). 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](https://ethereum.org/en/developers/docs/gas/#eip-1559)). 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. <!-- ## Fees on Rollups --> The cost of processing a transaction on a rollup is [2-dimensional](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9): 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 $\textit{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 $\textit{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) \times 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 $\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](https://eips.ethereum.org/EIPS/eip-1559). ### Execution Fees on L2 Rollups provide an [EVM-equivalent](https://medium.com/ethereum-optimism/introducing-evm-equivalence-5c2021deb306) environment. As a result a transaction $\textit{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 $\textit{Maxgas}'(tx)$. The gas fees on L2 should have the form $g'(tx) \times 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 $\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) \times gp(L2, t)$.\ **\[Rule 2\]** Lower L2 fees compared to L1, $g'(tx) \times gp(L2, t) \leq g(tx) \times gp(L1, t)$.\ **\[Rule 3\]** The maximum gas cost $\textit{Maxgas}'(tx)$ should be the same L1 gas budget $\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 $tx$ on L2 is $\textit{Rcost}(tx)$ the L2 operator expects: **\[Rule 4\]** $g'(tx) \times gp(L2, t) > RCost(tx)$. Overall, as $g'$ integrates execution and data availability costs, we can define $g'(tx) = g(tx) + \epsilon(tx)$ where $\epsilon(tx)$ is the overhead generated by the data availability costs. To satisfy Rule 3, $\epsilon(tx)$ should be small. To satisfy Rules 2 and 4, $gp(L2, t)$ must satisfy the following contraints: $$RCost(tx) < g'(tx) \times gp(L2, t) \leq g(tx) \times gp(L1, t)\mathpunct.$$ <!-- ![Batch Processing](figure-fees.png){#fig-batch width="85%"} --> <figure> <img src="https://hackmd.io/_uploads/HyvG2ngW6.png" alt="DA vs Execution costs"> <center><figcaption><b>Figure 1</b>: Batch Processing Transactions.</figcaption></center> </figure> ### Data Availability Costs On a rollup, transactions are executed in batches. Figure [1](#fig-batch) shows the ordering of the main phases to process a batch of transactions $Tx_1, Tx_2, Tx_3$. The rollup *sequencer* executes the batch at time $t_1$ and: 1. collects the L2 transactions' fees for each transaction, and 2. submits the batch's *data availability transaction* $\textit{DaTx}$ to L1. Collecting the fees at $t_1$ requires to factor in the execution and data availability fees. However, the actual fees for processing $\textit{DaTx}$ are not known at $t_1$ as $\textit{DaTx}$ will be processed at a later time $t_2$ on L1. In the time interval between $t_1$ and $t_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](https://eips.ethereum.org/EIPS/eip-1559). The L2 operator will know the cost of $\textit{DaTx}$ at time $t_3$ when a batch processing report is received on L2. The *real* cost for processing $tx$ is then: $$\begin{aligned} \textit{Rcost}(tx) & = g(tx) \times gp(L2, t_1) + g(\textit{DaTx}) \times gp(L1, t_2) & \\ %\label{eq-1}\\ & = \Bigl( g(tx) + \frac{g(\textit{DaTx}) \times gp(L1, t_2)}{gp(L2, t_1)} \Bigr) \times gp(L2, t_1) & % \label{eq-2} \end{aligned}$$ which has the form of Rule 1 with $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 that $g(\textit{DaTx})$ is **NOT** known at $t_1$ 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 $\textit{xs}$ be a sequence of bytes and let $|\textit{xs}|_0$ denote the number of *zero bytes* in $\textit{xs}$. The gas needed to publish $\textit{xs}$ to L1 is $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 \times |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 \times 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 $t_2$, but the fees are collected on L2 at $t_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 $\tilde{g}$ that can predict the gas price at time $t_2$, and, for $tx$, the L2 operator charges: $$\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: $$\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](https://eips.ethereum.org/EIPS/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 $$\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 $p(t + 60)$ is: $$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](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) 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 \times 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 \times 10 + 35 \times 16 \times 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 \times 12 + 35 \times 16 \times 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?](https://help.optimism.io/hc/en-us/articles/4411895794715-How-do-transaction-fees-on-Optimism-work-), and [Transaction fees on L2](https://community.optimism.io/docs/developers/build/transaction-fees/#). 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 $\textit{tx}$ depends on a recent value of the L1 gas price, $\textit{L1LastGasPrice}$: $$L1Fees(tx) = \textit{L1LastGasPrice} \times (w(tx) + F) \times D \mathpunct.$$ According to [Transaction Fees](https://community.optimism.io/docs/developers/build/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 $\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](https://developer.arbitrum.io/arbos/l1-pricing), [L1 calldatadata pricing / Sequencer reimbursement, "Fee Pool" Model](https://research.arbitrum.io/t/l1-calldata-data-pricing-sequencer-reimbursement-fee-pool-model/107).The L2 fees and overall model are explained in [Gas andFees](https://developer.arbitrum.io/arbos/gas), [Understanding Arbitrum:2-Dimensional Fees](https://security.larksuite.com/link/safety?target=https%3A%2F%2Fmedium.com%2Foffchainlabs%2Funderstandingarbitrum-2-dimensional-fees-fd1d582596c9&scene=ccm&logParams=%7B%22location%22%3A%22ccm_default%22%7D&lang=en-AU). 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 ](https://github.com/OffchainLabs/nitro/blob/3f4939df1990320310e7f39e8abb32d5c4d8045f/arbos/l1pricing/l1pricing.go). 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](https://ethereum.org/pt/roadmap/scaling/) 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.](https://ethereum.org/pt/roadmap/scaling/#proto-danksharding) 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](https://mantle.xyz), 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](https://www.eigenlayer.xyz), and - designing, modelling and analysing strategies to *optimally* adjust L2 gas prices. Finally, there are initiatives [Multi-dimensional EIP-1559](https://ethresear.ch/t/multidimensional-eip-1559/11651), [Notes on multi-dimensional EIP-1559.](https://youtu.be/QbR4MTgnCko) 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](mailto:franck.cassez@movementlabs.xyz) if you are interested in this topic. ### References \[1\] Optimism. [Transaction fees on L2.](https://community.optimism.io/docs/developers/build/transaction-fees/#) \[2\] Optimism. [How do transaction fees on Optimism work?](https://help.optimism.io/hc/en-us/articles/4411895794715-How-do-transaction-fees-on-Optimism-work-) \[3\] Arbitrum. [Gas and Fees.](https://developer.arbitrum.io/arbos/gas) \[4\] Arbitrum. [L1 Pricing.](https://developer.arbitrum.io/arbos/l1-pricing) \[5\] D. Goldman. Medium, July 1st, 2022. [Understanding Arbitrum 2-Dimensional Fees.](https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9) \[6\] DZack23. July 2022. [L1 calldata data pricing / Sequencer reimbursement, "Fee Pool" Model.](https://research.arbitrum.io/t/l1-calldata-data-pricing-sequencer-reimbursement-fee-pool-model/107) \[7\] [EIP-1559.](https://eips.ethereum.org/EIPS/eip-1559) \[8\] Ethereum. [Gas and Fees.](https://ethereum.org/en/developers/docs/gas/#eip-1559) \[9\] [Multi-dimensional EIP-1559.](https://ethresear.ch/t/multidimensional-eip-1559/11651) \[10\] [Notes on multi-dimensional EIP-1559.](https://youtu.be/QbR4MTgnCko) \[11\] Optimism. [Bedrock Fees Dashboard.](https://dune.com/oplabspbc/optimism-bedrock-migration-impact-dashboard.) \[12\] Arbitrum Nitro. [Fees Dashboard.](https://dune.com/taem/arbitrum-gas-fee) \[13\] [EigenLayer.](https://www.eigenlayer.xyz) \[14\] Ethereum. [Scaling.](https://ethereum.org/pt/roadmap/scaling/) [^1]: In a *Validity Proof* (zk) rollup, a proof of correct execution is submitted instead.