Skip to main content

How to estimate gas in Arbitrum

Looking for Stylus guidance?

Head over to the Stylus gas docs for Stylus-specific guidance.

This how-to covers how gas operates in Arbitrum, how it's calculated, and how to estimate it before submitting transactions. For a deeper understanding of the underlying pricing mechanisms, see the Gas and Fees concept page.

Quick start: use eth_estimateGas

If you don't need to understand the formula, you can rely on the standard gas estimation process. Call an Arbitrum node's eth_estimateGas RPC, which returns a gas limit sufficient to cover the entire transaction fee at the current child chain gas price.

Multiplying the value from eth_estimateGas by the child chain gas price gives you the total ETH required for the transaction to succeed. Note that for a given operation, the eth_estimateGas value may vary over time as the parent chain calldata price fluctuates.

Alternatively, call NodeInterface.gasEstimateComponents() and use the first result (gasEstimate) as your gas limit. Multiply by the third result (baseFee) to get the total cost.

Note that when working with parent to child chain messages (also known as retryable tickets), you can use the function ParentToChildMessageGasEstimator.estimateAll() of the Arbitrum SDK or NodeInterface.estimateRetryableTicket() to get all the gas information needed to send a successful transaction.

The fee formula

L1 fee "baked in"

The model below explains a two-dimensional fee model that captures both L1 and L2 costs. However, it is important to note that users will see a single fee—the L2 cost with the L1 fee "baked-in." This differs from other Rollups.

Arbitrum converts L1 calldata costs into equivalent L2 gas, so the user sees a single fee rather than two (as explained in the model below). The formula detailed below is precisely how it is calculated—but just remember it is shown as a single fee to the user.

Arbitrum uses a two-dimensional fee model where a transaction's total fee has two components: child chain execution costs and parent chain data posting costs. The total transaction fee is:

Transaction fees (TXFEES) = L2 Gas Price (P) * Gas Limit (G)

The gas limit includes the gas for child chain computation plus an additional buffer to cover the parent chain gas the Sequencer pays when posting the batch:

Gas Limit (G) = Gas used on L2 (L2G) + Extra Buffer for L1 cost (B)

The buffer accounts for the cost of posting the transaction (batched and compressed) on the parent chain. The parent chain estimated posting cost is:

L1 Estimated Cost (L1C) = L1 price per byte of data (L1P) * Size of data to be posted in bytes (L1S)

Where:

  • L1P estimates the current parent chain price per byte of data, dynamically adjusted by the child chain over time
  • L1S estimates how many bytes the transaction will occupy in the batch by compressing it with Brotli

The buffer converts this cost into child chain gas units:

Extra Buffer (B) = L1 Estimated Cost (L1C) / L2 Gas Price (P)

Combining everything:

TXFEES = P * (L2G + ((L1P * L1S) / P))

Where to get each variable

You can use the NodeInterface to retrieve the fee components:

  • P (L2 Gas Price): Price per gas unit. Starts at a gas floor price and increases with demand.
    • Call NodeInterface.GasEstimateComponents() and get the third element, baseFee.
  • L2G (Gas used on L2): Gas consumed by child chain computation, excluding parent chain posting costs.
    • Call NodeInterface.GasEstimateComponents() with the transaction data and subtract the second element (gasEstimateForL1) from the first (gasEstimate).
  • L1P (L1 estimated price per byte of data): Estimated cost of posting 1 byte of data on the parent chain.
    • Call NodeInterface.GasEstimateComponents(), get the fourth element l1BaseFeeEstimate and multiply it by 16.
  • L1S (Size of data to be posted on L1, in bytes): Depends on the transaction data. Arbitrum adds a fixed amount (~140 bytes) for the static part of the transaction.
    • Call NodeInterface.GasEstimateComponents(), take the second element gasEstimateForL1 (this is B in the formula), multiply by P and divide by L1P.
    • For Arbitrum Nova (AnyTrust), the data size is a fixed value since only the Data Availability Certificate (DAC) is posted on the parent chain, as explained here.
note

For L1P and L1S, you can also call NodeInterface.gasEstimateL1Component() to get l1BaseFeeEstimate and gasEstimateForL1.

Code example

Here's how to estimate gas using the Arbitrum SDK and NodeInterface:

First, instantiate the NodeInterface:

const { NodeInterface__factory } = require("@arbitrum/sdk/dist/lib/abi/factories/NodeInterface__factory");
const { NODE_INTERFACE_ADDRESS } = require("@arbitrum/sdk/dist/lib/dataEntities/constants");

...

// Instantiation of the NodeInterface object
const nodeInterface = NodeInterface__factory.connect(
NODE_INTERFACE_ADDRESS,
baseL2Provider
);

Call gasEstimateComponents() with your transaction's destination address and data:

// Getting the gas prices from ArbGasInfo.getPricesInWei()
const gasComponents = await arbGasInfo.callStatic.getPricesInWei();

// And the estimations from NodeInterface.GasEstimateComponents()
const gasEstimateComponents = await nodeInterface.callStatic.gasEstimateComponents(destinationAddress, false, txData, {
blockTag: 'latest',
});

Extract the formula variables:

// Getting useful values for calculating the formula
const l1GasEstimated = gasEstimateComponents.gasEstimateForL1;
const l2GasUsed = gasEstimateComponents.gasEstimate.sub(gasEstimateComponents.gasEstimateForL1);
const l2EstimatedPrice = gasEstimateComponents.baseFee;
const l1EstimatedPrice = gasEstimateComponents.l1BaseFeeEstimate.mul(16);

// Calculating some extra values to be able to apply all variables of the formula
// -------------------------------------------------------------------------------
// NOTE: This one might be a bit confusing, but l1GasEstimated (B in the formula) is calculated based on l2 gas fees
const l1Cost = l1GasEstimated.mul(l2EstimatedPrice);
// NOTE: This is similar to 140 + utils.hexDataLength(txData);
const l1Size = l1Cost.div(l1EstimatedPrice);

// Setting the basic variables of the formula
const P = l2EstimatedPrice;
const L2G = l2GasUsed;
const L1P = l1EstimatedPrice;
const L1S = l1Size;

Calculate the total fee:

// L1C (L1 Cost) = L1P * L1S
const L1C = L1P.mul(L1S);

// B (Extra Buffer) = L1C / P
const B = L1C.div(P);

// G (Gas Limit) = L2G + B
const G = L2G.add(B);

// TXFEES (Transaction fees) = P * G
const TXFEES = P.mul(G);

Refer to our tutorials repository for a complete working example.

Final note

Gas estimations from the above techniques are approximate and the actual gas fees may differ. We encourage developers to set this expectation explicitly wherever this information is shared with end-users.