Skip to main content

Run a batch poster

The batch poster uses the following default configuration flags and values:

FlagDefault valueDescription
--node.batch-poster.enablefalseEnables posting batchecs to L1
--node.batch-poster.max-size100000Default value is overwritten if it's an L3 (90000, for AnyTrust: 1,000,000)
--node.batch-poster.poll-interval10 secondsPeriod to wait after no batches are ready to be posted before checking again
--node.batch-poster.error-delay10 secondsDelay time after error posting batch
--node.batch-poster.compression-level11Batch compression level (Arbitrum uses Brotli compression which has compression levels from 1-11)
--node.batch-poster.parent-chain-wallet.private-keynoneSets the private key of the parent chains wallet

If you created an L3 Arbitrum chain and generated your node config file for a full node, the only values for the batch poster that will be set are:

  • --node.batch-poster.enable = true
  • --node.batch-poster.max-size = 90000
  • --node.batch-poster.parent-chain-wallet.private-key = YOUR_PRIVATE_KEY

If you're the chain owner, using the Arbitrum Chain SDK to generate your config file is the best option.

To add a new batch poster, call the setIsBatchPoster(address,bool) method of the SequencerInbox contract on the parent chain:

cast send --rpc-url $PARENT_CHAIN_RPC --private-key $OWNER_PRIVATE_KEY $SEQUENCER_INBOX_ADDRESS "setIsBatchPoster(address,bool)()" $NEW_BATCH_POSTER_ADDRESS true

Queued transaction database selection

queuedTxs are transactions that the sequencer has ordered and are ready to be posted:

  • Noop: --node.batch-poster.data-poster.use-noop-storage
  • Redis: --node.batch-poster.redis-url
  • DB: --node.batch-poster.data-poster.use-db-storage

You can use only one database at a time, so you can't have both a Redis server and a DB in use simultaneously.

note

If your parent chain is an Arbitrum chain or doesn't have a mempool, you can ignore this section. Noop storage is automatically chosen because batches post sequentially -- a new batch only posts after the previous transaction goes through. No database tracking for queued transactions is required.

Noop

Noop storage does not store any queuedTxs. This is beneficial when the parent chain is an Arbitrum chain or one without a mempool, since the sequencer processes every transaction immediately and is never stuck in mempool limbo due to low gas.

When Noop is enabled, the batch poster will wait for a confirmation that the transaction has gone through. If the transaction reverts, the batch poster will try again without halting the operation.

There is no Replace By Fee (RBF) logic, since that applies only to chains that use a mempool.

DB

DB will store data locally on the node and support persistent queuedTxs. Only a single batch poster can use the DB. The DB supports RBF, so transactions will not get stuck in the parent chain's mempool. If a transaction is reverted, then the batch poster must halt.

Redis

Redis is a fast local/external database that runs separately from the Nitro software so that node restarts preserve queuedTxs.

Storing transactions also enables RBF, which can prevent transactions from being stuck in the mempool due to insufficient gas. However, using Redis means the batch poster will halt if a transaction reverts.

To configure Redis, you can also specify a redis-signer value with the flag --node.batch-poster.data-poster.redis-signer.signing-key.

Key differences

info

The default values are DB on by default, and Noop on by default if and only if the parent chain does not have a mempool (any L3 whose parent is an Arbitrum chain).

FeatureNoopDBRedis
PersistenceNoneLocal diskExternal Redis
Survives restartsNoYesYes
Replace-by-feeNoYesYes
Tolerates revertsYesNoNo
Waits for receiptsYesNoNo

Enable blob posting

This section explains how to configure an Arbitrum node to post EIP-4844 blob transactions to the parent chain, which can significantly reduce data availability costs.

Prerequisites

Before enabling blob transactions, verify that your setup meets these requirements:

  1. Chain configuration Your Arbitrum chain must be running in Rollup mode.
  2. Parent chain compatibility Your parent chain (typically Ethereum mainnet or a testnet) must support EIP-4844. You can verify this by checking that recent block headers contain:
  • ExcessBlobGas field
  • BlobGasUsed field
  1. ArbOS version

Method 1: Smart contract call

Call the arbOSVersion() function on the ArbSys precompile contract:

  • Contract address: 0x0000000000000000000000000000000000000064
  • Function: arbOSVersion() returns uint256
  • You can call this using any Ethereum client or block explorer on your Arbitrum chain

Method 2: Using cast (if you have Foundry installed)

cast call 0x0000000000000000000000000000000000000064 "arbOSVersion()" --rpc-url YOUR_ARBITRUM_RPC_URL

If your version is below ArbOS 20, upgrade by following the ArbOS upgrade guide.

Configuration
  1. To enable blob transaction posting, add the following configuration to your node:
{
"node": {
"batch-poster": {
"post-4844-blobs": true
}
},
"parent-chain": {
"blob-client": {
"beacon-url": "YOUR_BEACON_URL"
}
}
}
  1. After updating your configuration:

    • Save the configuration file.
    • Restart your Arbitrum node.
    • Monitor the logs to confirm blob posting is active.
Verification
  1. Once restarted, you can verify that blob transactions are being posted successfully by monitoring your node logs.
Log message to look for
  1. When a blob transaction is successfully posted, you'll see a log entry similar to:
INFO [05-23|00:49:16.160] BatchPoster: batch sent  sequenceNumber=6 from=24
to=28 prevDelayed=13 currentDelayed=14 totalSegments=9
numBlobs=1
  1. Key indicator: The numBlobs field shows the number of blobs included in the transaction.

    • numBlobs=0: Traditional calldata transaction was posted
    • numbBlobs>0: Blob transaction was successfully posted (in the example above, a single blob was sent)

Troubleshooting

Why is my node still posting calldata instead of blobs?

  1. Your node may continue using calldata in these scenarios:
    • Cost optimization: When blob gas prices are high, calldata posting may be more economical; you can set --node.batch-poster.ignore-blob-price flag to true to force the batch poster to use blobs.
    • Batch type switching protection: After a non-blob transaction is posted, the following 16 transactions will also use calldata to prevent frequent switching.
  2. Check your node logs for blob-related error messages and verify that your parent chain is accessible and fully synced.

Optional parameters

  1. You can also set the following optional parameters to control blob posting behavior:
FlagDescription
--node.batch-poster.ignore-blob-priceBoolean. Default: false. If the parent chain supports EIP-4844 blobs and ignore-blob-price is set to true, the batch poster will use EIP-4844 blobs even if using calldata is cheaper. Can be true or false.
--parent-chain.blob-client.authorizationString. Default: "". Value to send with the HTTP Authorization: header for Beacon REST requests, must include both scheme and scheme parameters.
--parent-chain.blob-client.secondary-beacon-urlString. Default: "". A secondary Beacon REST endpoint URL to use as a fallback.
--node.batch-poster.data-poster.blob-tx-replacement-timesdurationSlice. Default: [5m0s, 10m0s, 30m0s,1h0m0s,4h0m0s,8h0m0s,16h0m0s,22h0m0s]. Comma-separated list of durations since first posting a blob transaction to attempt a replace-by-fee.
--node.batch-poster.data-poster.max-blob-tx-tip-cap-gweiFloat. Default: 1. The maximum tip cap to post EIP-4844 blob-carrying transactions at.
--node.batch-poster.data-poster.min-blob-tx-tip-cap-gweiFloat. Default: 1. The minimum tip cap to post EIP-4844 blob-carrying transactions at.

Batch poster revenue config

To change revenue configurations for a batch poster, first check the list of current registered batch posters through the ArbAggregator precompile by calling getBatchPosters()(address[]):

cast call --rpc-url $ARB_CHAIN_RPC 0x000000000000000000000000000000000000006D "getBatchPosters() (address[])"

While there are other ways to get the list of batch posters for an Arbitrum chain, this method only lists batch posters who are registered by ArbAggregator.addBatchPoster() or have posted at least a single batch, which is better for revenue reasons.

Once you have the batch poster address, you can obtain the fee collector address for that batch poster using the getFeeCollector(address)(address) from the ArbAggregator precompile.

cast call --rpc-url $ORBIT_CHAIN_RPC 0x000000000000000000000000000000000000006D "getFeeCollector(address) (address)" $BATCH_POSTER_ADDRESS

You can also use the Arbitrum Chain SDK:

const orbitChainClient = createPublicClient({
chain: <OrbitChainDefinition>,
transport: http(),
}).extend(arbAggregatorActions);

const networkFeeAccount = await orbitChainClient.arbAggregatorReadContract({
functionName: 'getFeeCollector',
args: [<BatchPosterAddress>],
});
note

Before setting a fee collector for a batch poster, ensure the batch poster is registered in the BatchPostersTable. This can be achieved by:

  • Manually calling ArbAggregator.addBatchPoster() for the address, or
  • The address has been successfully posted for at least one batch

To set a new fee collector for a specific batch poster, use the method setFeeCollector(address, address) of the ArbAggregator precompile:

cast send --rpc-url $ORBIT_CHAIN_RPC --private-key $OWNER_PRIVATE_KEY 0x000000000000000000000000000000000000006D "setFeeCollector(address,address) ()" $BATCH_POSTER_ADDRESS $NEW_FEECOLLECTOR_ADDRESS

You can also do this with the Arbitrum Chain SDK:

const owner = privateKeyToAccount(<OwnerPrivateKey>);
const orbitChainClient = createPublicClient({
chain: <OrbitChainDefinition>,
transport: http(),
}).extend(arbAggregatorActions);

const transactionRequest = await orbitChainClient.arbAggregatorPrepareTransactionRequest({
functionName: 'setFeeCollector',
args: [<BatchPosterAddress>, <NewFeeCollectorAddress>],
upgradeExecutor: false,
account: owner.address,
});

await orbitChainClient.sendRawTransaction({
serializedTransaction: await owner.signTransaction(transactionRequest),
});

To add a new batch poster, call the setIsBatchPoster(address,bool) method of the SequencerInbox contract on the parent chain:

cast send --rpc-url $PARENT_CHAIN_RPC --private-key $OWNER_PRIVATE_KEY $SEQUENCER_INBOX_ADDRESS "setIsBatchPoster(address,bool) ()" $NEW_BATCH_POSTER_ADDRESS true

Setting revenue values

note

These values are only editable by the chain owner(s).

There are onchain values in ArbOS that set how much ArbOS changes per L1 gas spent on transaction data. This can be set by communicating with the ArbOwner precompile.

cast send --rpc-url $ARB_CHAIN_RPC --private-key $OWNER_PRIVATE_KEY 0x0000000000000000000000000000000000000070 "setL1PricingRewardRate(uint64) ()" NEW_L1_PRICING_REWARD

Along with setPerBatchGasCharge(), which sets the base charge (in L1 gas) attributed to each data batch in the calldata pricer. This can be called with:

cast send --rpc-url ARB_CHAIN_RPC --private-key OWNER_PRIVATE_KEY 0x0000000000000000000000000000000000000070 "setPerBatchGasCharge(int64) ()" NEW_BATCH_GAS_CHARGE

Batch poster interval config

The batch poster has a max delay, primarily set by --node.batch-poster.max-delay parameter in the Nitro node configuration (set via the JSON config file or command-line flags when deploying an Arbitrum chain). It defines the maximum amount of time the batch poster will wait after receiving a transaction before posting a batch that includes it. The default value is one hour (3600 seconds):

  • Configuration options: In the node.batch-poster section of the config, e.g., "max-delay": "30m" for a 30-minute maximum wait. Lower values increase batch posting frequency, but at the cost of potentially smaller, less efficient batches during periods of low activity, which increases gas costs on the parent chain. If there are no transactions in a batch, then this setting does not apply.
  • Prevention of issues: A shorter max delay reduces the opportunity for transaction reordering in the sequencer by requiring shorter waits. It also limits exposure to chain reorgs, since batches post sooner, which anchors them to the parent chain before potential fluctuations can invalidate sequencing. Extremely low posting times aren't recommended, as spamming the parent chain with batches increases costs without providing any benefit.
  • Recommended settings: For high-throughput chains, set to 5-15 minutes to balance latency and efficiency. For low-activity chains, keep the value of one hour.

The batch poster also has --node.batch-poster.max-size parameter represented in bytes. It is the maximum size a batch can be. If the total queued transactions compression estimate exceeds the max size, the batch poster will post the max size of transactions to the L1. The default value is 100000 bytes.

Lower values will result in increased frequency of batch posting during high activity. And because Brotli compression is lossy, smaller batch files almost always result in suboptimal compression compared to larger files. which means the gas price will be higher overall for two smaller batches than for one larger batch, assuming the two smaller batches contain the same transactions as the one larger batch. Calldata and blob posting have an upper limit, so raising this value too high can cause issues, while lowering it can lead to inefficient compression and batch spamming on high-activity chains. The recommended value is 100,000 bytes.