Skip to main content

Generate a custom genesis file with the Chain SDK

A custom genesis file lets you launch an Arbitrum chain with contracts, balances, and storage already present. The Arbitrum Chain SDK Docker image generates the file and the two hashes required by the Rollup contract: the genesis block hash and send root.

You must use the same generated values in three places:

  1. The genesis.json file used to initialize every Nitro node.
  2. The chainConfig and initial L1 base fee passed to createRollup.
  3. The genesis assertion state passed to createRollup.
Generate the genesis before deploying the chain

The Rollup contract commits to the genesis state during deployment. If you change an allocation or any genesis parameter afterward, the resulting block hash no longer matches the onchain genesis assertion. Generate and review the final file before you call createRollup.

Prerequisites

Install:

  • Docker
  • jq, to separate the genesis file from the hashes returned by the SDK

The generateGenesis command requires Chain SDK v0.28.0 or later. Pull and pin the corresponding image:

export CHAIN_SDK_IMAGE=offchainlabs/arbitrum-chain-sdk:v0.28.0
docker pull "$CHAIN_SDK_IMAGE"

Use the same image tag or digest for every operator so that they use the same genesis generator and Nitro genesis-generator binary.

1. Define custom account allocations

Create custom-alloc.json if you want to add balances, bytecode, nonces, or storage. The file must contain an object keyed by account address. It must not contain an outer alloc property.

custom-alloc.json
{
"0x1111111111111111111111111111111111111111": {
"balance": "1000000000000000000"
},
"0x2222222222222222222222222222222222222222": {
"nonce": "1",
"code": "0x<deployed-bytecode>",
"storage": {
"0x<32-byte-slot>": "0x<32-byte-value>"
}
}
}

If a custom allocation uses the same address as a default predeploy, the custom allocation replaces the default entry at that address.

2. Configure the generateGenesis command

Create genesis-input.json in the same directory as custom-alloc.json. This file supplies the arguments to the SDK's Docker-only generateGenesis command. The example below configures a Rollup chain, loads the SDK's default predeploys, and merges custom-alloc.json into the genesis allocation.

genesis-input.json
{
"chainId": "123456",
"arbosVersion": "51",
"chainOwner": "0x3333333333333333333333333333333333333333",
"l1BaseFee": "1000000000",
"isAnyTrust": false,
"loadDefaultPredeploys": true,
"enableNativeTokenSupply": false,
"enableTransactionFiltering": false,
"customAllocAccountFile": "custom-alloc.json",
"maxCodeSize": "24576",
"maxInitCodeSize": "49152"
}

The generator accepts these fields:

FieldTypeRequiredDefaultDescription
chainIdstringYesUnique chain ID.
arbosVersionstringYesInitial ArbOS version. Use a version supported by the Nitro release that will run the chain.
chainOwnerstringYesInitial owner stored in ArbOS.
l1BaseFeestringYesInitial parent-chain base fee in wei. Use a nonzero value.
isAnyTrustbooleanNofalseSet to true for an AnyTrust chain.
loadDefaultPredeploysbooleanNofalseInclude the default contracts supplied by the genesis file generator.
enableNativeTokenSupplybooleanNofalseEnable native-token supply management at genesis.
enableTransactionFilteringbooleanNofalseEnable transaction filtering at genesis.
customAllocAccountFilestringNoPath to the custom allocation file, relative to the container's working directory.
maxCodeSizestringNo24576Maximum deployed contract bytecode size in bytes.
maxInitCodeSizestringNo49152Maximum contract initialization bytecode size in bytes.

The chainId, chain owner, ArbOS version, chain type, and code-size limits become part of serializedChainConfig. Keep them identical when you prepare the Rollup deployment.

3. Generate the genesis file and hashes

Run the SDK's generateGenesis command through the Docker image. Genesis generation is not exported as a function from the SDK's public TypeScript entry point because it depends on tools bundled only in the image.

Mount the working directory so the command can read both input files. The CLI reserves standard output for its JSON result, so redirecting it produces a valid result file.

docker run --rm \
-v "$(pwd):/work" \
-w /work \
"$CHAIN_SDK_IMAGE" \
generateGenesis @genesis-input.json > genesis-result.json

The result contains:

{
"genesis": { "...": "generated genesis object" },
"blockHash": "0x<genesis-block-hash>",
"sendRoot": "0x<genesis-send-root>"
}

Extract the file that Nitro consumes and record the two hashes:

jq '.genesis' genesis-result.json > genesis.json
jq '{blockHash, sendRoot}' genesis-result.json

Store genesis-result.json with your deployment records. It ties the exact genesis file to the values committed on the parent chain.

4. Prepare the Rollup deployment

Map the generated values into createRollupPrepareDeploymentParamsConfig. The first global-state value is the block hash. The second is the send root. A custom genesis starts after batch 1 at position 0.

import { readFileSync } from 'node:fs';
import { zeroHash } from 'viem';
import { createRollupPrepareDeploymentParamsConfig } from '@arbitrum/chain-sdk';

const { genesis, blockHash, sendRoot } = JSON.parse(readFileSync('genesis-result.json', 'utf8'));
const chainConfig = JSON.parse(genesis.serializedChainConfig);

const createRollupConfig = createRollupPrepareDeploymentParamsConfig(parentChainPublicClient, {
chainId: BigInt(chainConfig.chainId),
owner: rollupOwner,
chainConfig,
dataCostEstimate: BigInt(genesis.arbOSInit.initialL1BaseFee),
genesisAssertionState: {
globalState: {
bytes32Vals: [blockHash, sendRoot],
u64Vals: [1n, 0n],
},
machineStatus: 1,
endHistoryRoot: zeroHash,
},
});

Continue with createRollup in the chain deployment guide, passing createRollupConfig as params.config.

Preserve the generated chain configuration and base fee

Pass the parsed serializedChainConfig without changing its values. Set dataCostEstimate to the generated arbOSInit.initialL1BaseFee, and keep it non-zero. If either value differs from the genesis file, validators can fail to find the onchain genesis assertion when they stake on the first assertion.

5. Initialize every Nitro node

Give every node the same genesis.json on its first startup. Set --init.genesis-json-file to the mounted path of the file alongside the node configuration described in Configure your Arbitrum chain's node.

nitro --conf.file nodeConfig.json --init.genesis-json-file /path/to/genesis.json

Keep Nitro's default genesis-assertion validation enabled. The node recalculates the genesis block hash and verifies it against the assertion posted during Rollup deployment. A mismatch means that the node received a different genesis file or deployment configuration.