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:
- The
genesis.jsonfile used to initialize every Nitro node. - The
chainConfigand initial L1 base fee passed tocreateRollup. - The genesis assertion state passed to
createRollup.
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:
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.
{
"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.
{
"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:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
chainId | string | Yes | — | Unique chain ID. |
arbosVersion | string | Yes | — | Initial ArbOS version. Use a version supported by the Nitro release that will run the chain. |
chainOwner | string | Yes | — | Initial owner stored in ArbOS. |
l1BaseFee | string | Yes | — | Initial parent-chain base fee in wei. Use a nonzero value. |
isAnyTrust | boolean | No | false | Set to true for an AnyTrust chain. |
loadDefaultPredeploys | boolean | No | false | Include the default contracts supplied by the genesis file generator. |
enableNativeTokenSupply | boolean | No | false | Enable native-token supply management at genesis. |
enableTransactionFiltering | boolean | No | false | Enable transaction filtering at genesis. |
customAllocAccountFile | string | No | — | Path to the custom allocation file, relative to the container's working directory. |
maxCodeSize | string | No | 24576 | Maximum deployed contract bytecode size in bytes. |
maxInitCodeSize | string | No | 49152 | Maximum 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.
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.