Transaction Fees on Scroll

Overview

Scroll fees are notably lower than on its supporting layer. For users and developers, transaction fees on Scroll appear to work similarly to those on Ethereum mainnet, and existing tools, wallets, and code will likely work as if they were. However, the transaction fee shown in the wallet isn’t the whole picture unless the software is specifically aware of Scroll’s fee calculations.

Due to the design of L2 rollups, transaction costs depend on the L1’s costs. To leverage Ethereum’s security, Scroll must account for the cost of the transaction data and proofs that must be stored and verified on the L1.

Compared to Ethereum mainnet, Scroll introduces some new dimensions to transaction fee calculation to do this. The final cost of a transaction is constructed from several parts:

  • L2 fee
    • Calculated in the same manner as on the L1, equaling gas_price * gas_used
  • L1 fee
    • This additional fee covers sending data to L1 for data availability. Transactions initiated on the L1 do not pay this fee.
    • It is calculated based on the size of the transaction’s calldata
    • ETH is automatically deducted from the user’s balance on Scroll for the fee

At a high level, the L2 fee is the cost of executing your transaction on the L2 sequencer, and the L1 fee is the cost of committing that transaction onto L1.

In short, totalTxFee = l2Fee + l1Fee, all denominated in ETH, the native gas token for the Scroll network.

L2 Fee

Transactions on Scroll, like on Ethereum, must pay the cost of executing their computations and storing the data they produce.

Calculating the Execution Fee

The L2 execution fee is straightforward:

l2TransactionExecutionFee = l2TransactionGasUsed * l2TransactionGasPrice

The total fee depends on what the transaction does (l2TransactionGasUsed) as well as the current market conditions (l2TransactionGasPrice). Users set the gas price, and the “gas used” is assessed by calling the estimateGas endpoint on a Scroll node.

In other words, the execution fee is calculated precisely like pre-EIP1559 Ethereum.

L1 Fee

Every transaction’s calldata must be committed to Ethereum, which incurs an additional transaction fee, referred to as the “L1 Fee”. Without doing this, Scroll couldn’t be reconstructed from only L1 data.

Transactions aren’t committed 1-by-1 — they are collected in batches of blocks (and blocks of transactions). The cost of an individual transaction is computed based on the zeros and non-zero bytes in its payload.

Estimating the L1 Data Fee

Scroll has a pre-deployed L1GasPriceOracle at 0x5300000000000000000000000000000000000002, accessible on both Scroll Mainnet and Scroll Sepolia. It provides a getL1Fee method to estimate the L1 data fee for a given transaction’s raw data.

function getL1Fee(bytes memory _data) external view override returns (uint256);

Calculating the L1 Data Fee with Gas Oracle

As mentioned, the L1GasPriceOracle is used to estimate the L1 gas fee given raw transaction data. This is a push oracle, updated by a relayer run by Scroll.

Post-Curie Fee Calculation (Current)

Since the Curie upgrade, transaction data is stored in blobs, and the fee is calculated as:

l1Fee = (commitScalar * l1BaseFee + blobScalar * txDataLength * l1BlobBaseFee) / PRECISION

Where PRECISION = 1e9.

Pre-Curie Fee Calculation (Legacy)

Before Curie, the fee was based on calldata gas costs:

  1. Read three fields l1BaseFee, overhead, scalar from the L1GasPriceOracle contract.

  2. Count the number of zero bytes and non-zero bytes from the transaction.

  3. Calculate the L1 data fee, given TX_DATA_ZERO_GAS = 4 and TX_DATA_NON_ZERO_GAS = 161 and PRECISION = 1e9:

    l1Gas = zeros * TX_DATA_ZERO_GAS + (nonzeros + 4) * TX_DATA_NON_ZERO_GAS
    l1GasFee = ((l1Gas + overhead) * l1BaseFee * scalar) / PRECISION

    We reserve an additional 4 bytes in the non-zero bytes to store the number of bytes in the RLP-encoded transaction.

L1GasPriceOracle API

overhead

function overhead() external view returns (uint256);

Returns the current L1 fee overhead (used in pre-Curie fee calculation).

scalar

function scalar() external view returns (uint256);

Returns the current L1 fee scalar (used in pre-Curie fee calculation).

l1BaseFee

function l1BaseFee() external view returns (uint256);

Returns the latest known L1 base fee.

l1BlobBaseFee

function l1BlobBaseFee() external view returns (uint256);

Returns the latest known L1 blob base fee (introduced in Curie upgrade).

commitScalar

function commitScalar() external view returns (uint256);

Returns the current L1 commit fee scalar (introduced in Curie upgrade).

blobScalar

function blobScalar() external view returns (uint256);

Returns the current L1 blob fee scalar (introduced in Curie upgrade).

penaltyFactor

function penaltyFactor() external view returns (uint256);

Returns the current compression penalty factor (introduced in Feynman upgrade).

getL1Fee

function getL1Fee(bytes memory data) external view returns (uint256);

Computes the L1 portion of the fee based on the size of the RLP encoded input transaction, the current L1 base fee, and the various dynamic parameters. The calculation method depends on the current network fork (pre-Curie, Curie, Feynman, or Galileo).

Returns: L1 fee that should be paid for the transaction

ParameterDescription
dataSigned fully RLP-encoded transaction to get the L1 fee for.

getL1GasUsed

function getL1GasUsed(bytes memory data) external view returns (uint256);

Computes the amount of L1 gas used for a transaction. After the Curie upgrade, this returns 0 since all transaction data is stored in blobs.

Returns: Amount of L1 gas used to publish the transaction (0 post-Curie).

ParameterDescription
dataSigned fully RLP-encoded transaction to get the L1 fee for.

L1 Originated Transactions

When messaging from L1 to L2, the user pays all transaction fees on L1. The user pays L1 gas fees, but because of this, doesn’t need to pay Scroll an L1 Data Fee. They will need to account for L2 Execution Fees in their L1 transaction though, and will need to know how much L2 gas to pay.

Contracts on L1 can use an L2 Gas Price Oracle deployed to the L1 to get the gas fee for a given transaction. The oracle also exposes the current l2BaseFee and estimated cross-domain message fee for a given gas limit.

On mainnet, the L2GasPriceOracle is deployed at 0x987e300fDfb06093859358522a79098848C33852.

On Sepolia, an upgraded L1MessageQueueWithGasPriceOracle should be used, deployed to 0xF0B2293F5D834eAe920c6974D50957A1732de763.

If your system supports off-chain mechanisms, you can also call eth_estimateGas and eth_gasPrice on any Scroll RPC node to get an estimate of the gas required for a given transaction.

Future Roadmap

Currently, the computation required for proof generation and gas for proof verification on L1 is subsidized by Scroll and various proving partners. It is partially covered by setting a floor to L2 Gas Price.

As the prover network becomes decentralized, incentives for proof generation will need to be incorporated into the protocol for the system to be sustainable and scalable.

We expect the final gas cost will explicitly include the cost of this proof generation. With further protocol optimization, this cost for the user should be minimal, as each proof covers many transactions.

Footnotes

  1. See EIP-2028 for more info.

What's Next

Resources

Follow Us