Antler by Autoflux

Examples

End-to-end walkthroughs: run a node, deposit/withdraw, use precompiles, and inspect protocol state.

Path: examples

Third-party documentation. This is independently authored analysis of the public Arbitrum Nitro codebase — not the official docs, and not reviewed or endorsed by the Arbitrum Nitro team.

Examples

The patterns below cover the two things almost every Arbitrum builder does: move assets across the bridge, and inspect the L2's L1-aware state. They use the @arbitrum/sdk and ethers v6, and assume an L1 and L2 RPC URL.

Bridging ETH and ERC-20s

The bridge objects collapse the multi-step flows (approval, inbox submission, retryable execution, outbox redemption) into a few awaited calls. The examples in this module's manifest show the deposit, the direct retryable, and the withdrawal; all three use the same provider/signer conventions.

Running nodes and inspecting state

For infrastructure work, the single nitro-node binary and its env-var configuration make it straightforward to run a full node or a devnet. Combined with the arb_* RPC extensions and the precompiles, it's possible to observe exactly what the sequencer and validators are doing in real time.

A note on precompiles

Several "L2-native" behaviors (reading the L2 block number anchored to L1, estimating L1 data fees, sending to L1) are one call to a precompile away. They are the closest thing Arbitrum has to a system API, and they are stable across versions — worth learning even if the SDK handles most high-level flows.

Examples

Run a Nitro full node

Run a Nitro full node
bash
# clone and build
git clone https://github.com/OffchainLabs/nitro.git
cd nitro
docker build . -t nitro-node
 
# configure via env vars (single binary = sequencer/validator/relayer)
docker run --rm -p 8547:8547 -p 8548:8548 \
-e CHAIN_ID=42161 \
-e L1_URL=https://ethereum-rpc.publicnode.com \
-e L2_URL=http://arb1.arbitrum.io/rpc \
-e STEPS_PER_LEVEL=3 \
-e VALIDATOR__ENABLE=false \
-e SEQUENCER__ENABLE=false \
nitro-node --parent-chain.connection.url https://ethereum-rpc.publicnode.com
 
# node exposes eth JSON-RPC on 8547 and feed on 8548
STATUSexample

One binary runs all roles; you enable/disable them with env flags. A full node needs no validator/sequencer roles.

Read L2 block data with eth_getBlockByNumber

Read L2 block data with eth_getBlockByNumber
typescript
const l2 = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc");
 
const block = await l2.getBlock("latest");
console.log("number:", block.number);
console.log("timestamp:", block.timestamp);
 
// Arbitrum L2s use a l1BlockNumber field; every L2 block is anchored to L1.
const raw = await l2.send("eth_getBlockByNumber", ["latest", false]);
console.log("l1BlockNumber:", raw.l1BlockNumber);
STATUSexample

Nitro exposes an l1BlockNumber per block, useful for timestamp/sequencing reasoning.

Query the ArbGasInfo precompile

Query the ArbGasInfo precompile
typescript
const ARB_GAS_INFO = "0x000000000000000000000000000000000000006c";
const abi = ["function getPricesInWei() view returns (uint256, uint256, uint256, uint256, uint256, uint256)"];
 
const l2 = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc");
const gasInfo = new ethers.Contract(ARB_GAS_INFO, abi, l2);
 
const [l1Price, , , , , ] = await gasInfo.getPricesInWei();
console.log("L1 data price (wei):", l1Price);
 
// estimate the L1 cost of a call:
const calldata = "0x1234";
const l1Fee = await gasInfo.getL1FeesApproximate(calldata, 1n, 0n, 0n, 0n, 0n);
console.log("approx L1 fee:", l1Fee.toString());
STATUSexample

Arbitrum separates L2 execution gas from L1 data gas; ArbGasInfo exposes the underlying prices.

Edge Cases

  • When running a node, never set both SEQUENCER__ENABLE and VALIDATOR__ENABLE unless you know what you're doing — the two roles conflict over block production.
  • The feed (8548) and RPC (8547) use different ports; tools that need the fastest updates subscribe to the feed, not the RPC.
  • A validator that enables staking starts posting assertions and can be slashed for faulty ones — run validators read-only (enable=false) until you understand the challenge protocol.