Customize genesis state
Arbitrum chain operators are increasingly seeking to deploy chains with pre-existing state by loading an initial state in a file such as genesis.json. Specifically, chain operators want to predeploy smart contracts (like Gnosis Safe) to an Arbitrum chain so they exist from genesis, before any user interaction or post-launch governance.
Initializing from a genesis state helps in the following scenarios:
- Redeploying a new testnet with the pre-existing state if the current testnet is broken.
- Deploying more sibling chains with pre-existing contracts.
- Simplifying self-serve backends for Rollup-as-a-Service (RaaS) providers, so that hundreds of testnets can be deployed with similar contracts.
- Reducing the work for third-party infrastructure teams that redeploy contracts for new chains.
Why should I use a custom genesis state?
A custom genesis lets you start your Arbitrum chain with a customized genesis state and network configuration:
- Predeployed contracts: standard chains start without any smart contracts. This feature allows you to preload contract bytecode in the very first block, so infrastructure is available at launch.
- Initial account state (allocations): you can pre-configure the ledger—including account balances and contract storage—before the network opens for transactions.
- Enable advanced features: chain operators can use this feature to launch a chain with advanced customizations, such as minting/burning gas tokens via third-party bridges and compliance-focused transaction filtering.
Preallocating account balances in the genesis state credits ETH (or your custom gas token) on your Arbitrum chain without depositing anything into the parent-chain bridge. The protocol does not require the bridge to hold ETH equal to the balances you preallocate, and there is no deploy-time check.
If your chain will accept user deposits, a chain whose total preallocated balances exceed the bridge's backing is insolvent from genesis: withdrawals can fail once the bridge is drained. Only preallocate non-zero balances if you accept responsibility for backing them—for example, a private or test chain, or one where you deposit the equivalent amount into the bridge before opening it to users.
How to configure
- Nitro contracts >= v3.2
- First version to include full support for custom initialization in
genesis.json.
- First version to include full support for custom initialization in
- Nitro node >= v3.11.0 is needed for using custom-genesis.
Choose one of these two tools:
-
Chain SDK >= v0.28.0
- The Chain SDK Docker image supports predeployed contracts and custom account allocations.
-
- Use the
genesis-file-generatorto customize thegenesis.jsonfile or pass accounts/custom chain config.
Note: The chain configuration must be exactly the same at the string level, including the order of fields, potential whitespace, or special characters for the
serializedChainConfigstring. - Use the
Use the Chain SDK
The Docker command completes the following automated steps:
- Generates a
genesis.jsonfile with the predeployed contracts whenloadDefaultPredeploysis enabled. You can also supply custom account allocations. - Calculates the
blockHashandsendRoothash and returns them for your use.
You can use the output from the previous step to set up the chain separately. Deploying the Rollup is a separate SDK call; the Docker command does not prompt you to deploy it.
Core configuration reference
The following parameters configure the genesis.json file. The environment variable names below are used by the standalone generator; the Chain SDK Docker command takes the corresponding JSON fields listed in the Docker command reference. The Chain SDK Docker image bundles the Nitro binary, so you only need NITRO_NODE_IMAGE when you calculate hashes separately.
| Variable | Description |
|---|---|
CHAIN_ID | The unique numeric identifier for your chain. |
IS_ANYTRUST | Whether the chain is an AnyTrust chain (true) or a Rollup chain (false). |
ARBOS_VERSION | The version of ArbOS to use for the genesis block. |
CHAIN_OWNER | The address that has admin ownership of the deployed chain. |
L1_BASE_FEE | The initial L1 gas price (in wei) used to calibrate the chain creation. |
NITRO_NODE_IMAGE | The Nitro node Docker image used for hashing and node operations. |
ENABLE_NATIVE_TOKEN_SUPPLY | Flag to launch your chain with native interop tokens as minting/burning gas tokens via third-party bridges. |
ENABLE_TRANSACTION_FILTERING | Flag to launch your chain with protocol-level transaction filtering for regulatory or compliance purposes. Note: This feature requires ArbOS60 and Nitro node v3.10.0. |
Deployment configuration (optional)
This step is only required if you choose to deploy the Rollup to the parent chain (Step 3 below).
| Variable | Description |
|---|---|
DEPLOYER_PRIVATE_KEY | Private key of the account responsible for the Rollup deployment. |
BATCH_POSTER_PRIVATE_KEY | Private key for the sequencer's batch-posting address. |
VALIDATOR_PRIVATE_KEY | Private key for the validator/bonder address. |
PARENT_CHAIN_RPC | RPC endpoint for the parent chain (for example, Arbitrum Sepolia or Ethereum). |
DEPLOYER_PRIVATE_KEY, BATCH_POSTER_PRIVATE_KEY, and VALIDATOR_PRIVATE_KEY are secrets. Keep them in your .env (which should be .gitignored) and never commit your .env—or any generated node configuration that embeds these keys—to version control.
Initialize every Nitro node
Execution steps
-
Prepare the environment
- Install Docker and
jq, and prepare the input files as shown in the Docker command reference.
- Install Docker and
-
Generate genesis
-
Ensure your
genesis-input.jsonis configured and run the Docker command below. -
To preallocate custom accounts, set
customAllocAccountFiletocustom-alloc.jsoningenesis-input.jsonand place the file (standard Gethallocformat) in the working directory. See thegenesis-file-generatorsection below for a sample, and review the solvency warning above before setting non-zero balances.ConfigurationDouble-check that your configuration values match your intended chain specs before running the command.
-
-
Create Rollup (optional)
- After generating the
genesis.jsonfile, use the Chain SDK to deploy your Rollup as shown in Prepare the Rollup deployment, or deploy the Rollup later.
- After generating the
-
Configure and launch your node:
- Set up your node as usual (see the full node guide), but include the following properties to point to your custom state:
--init.genesis-json-file=/path/to/genesis.json: the path to your customgenesis.jsonfile--init.empty=false: (Required) Forces the node to load the provided genesis file instead of initializing a blank state.
- Set up your node as usual (see the full node guide), but include the following properties to point to your custom state:
-
Start your chain with the correct preloaded state.
Your custom genesis is loaded via the --init.genesis-json-file and --init.empty=false flags when you start the node—if you generate your node configuration with the Chain SDK's prepareNodeConfig, it won't include these, so make sure to pass them on the command line.
Give every node the same genesis.json on its first startup. 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.
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.
Docker command reference
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"
Pin the same image tag or digest for every operator. Different versions of the bundled Nitro genesis-generator binary can produce a different genesis block hash.
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 | — | The unique numeric identifier for your chain. |
arbosVersion | string | Yes | — | The version of ArbOS to use for the genesis block. |
chainOwner | string | Yes | — | The address that has admin ownership of the deployed chain. |
l1BaseFee | string | Yes | — | The initial L1 gas price (in wei) used to calibrate the chain creation. Use a nonzero value. |
isAnyTrust | boolean | No | false | Whether the chain is an AnyTrust chain (true) or a Rollup chain (false). |
loadDefaultPredeploys | boolean | No | false | Include the default contracts supplied by the genesis file generator. |
enableNativeTokenSupply | boolean | No | false | Flag to launch your chain with native interop tokens as minting/burning gas tokens via third-party bridges. |
enableTransactionFiltering | boolean | No | false | Flag to launch your chain with protocol-level transaction filtering for regulatory or compliance purposes. Note: This feature requires ArbOS60 and Nitro node v3.10.0. |
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.
Use the genesis-file-generator tool
Environment variables reference (.env)
These parameters define the identity of your chain.
| Variable | Description | Default |
|---|---|---|
CHAIN_ID | The unique numeric identifier for your new chain. | 31337 |
IS_ANYTRUST | Whether the chain is an AnyTrust chain (true) or a Rollup chain (false). | false |
ARBOS_VERSION | The version of ArbOS to use for the genesis block. | 51 |
CHAIN_OWNER | The address that has admin ownership of the deployed chain. | -- |
L1_BASE_FEE | The initial L1 gas price (in wei) used to calibrate the chain creation. | 1000000000 (1 gwei) |
NITRO_NODE_IMAGE | The Nitro node Docker image used for hashing and node operations. | -- |
CUSTOM_ALLOC_ACCOUNT_FILE | (optional) Path to a JSON file containing your own account balances, contract bytecode, and storage slots. The file should be in the standard Geth alloc format. | " " (empty) |
ENABLE_NATIVE_TOKEN_SUPPLY | (optional) Set to true if you want to launch your chain with native interop tokens as minting/burning gas tokens via third-party bridges. | false |
LOAD_DEFAULT_PREDEPLOYS | (optional) Set to false if you don't want the default predeploys. | true |
ENABLE_TRANSACTION_FILTERING | (optional) Set to true if you want to launch your chain with protocol-level transaction filtering for regulatory or compliance purposes. Note: This feature requires ArbOS60 and Nitro node v3.10.0. | false |
Execution steps
-
Prepare the genesis state
- Set up the
.envfile with the required parameters. - Run the
genesis-file-generatorDocker image to generate agenesis.jsonfile with the required predeployed contracts and more configurations.
mkdir -p genesisdocker run --rm \--env-file .env \-v "$(pwd)/genesis":/app/genesis \offchainlabs/genesis-file-generator:v0.0.3-rc-deffec7-
You can also generate your own
genesis.jsonfile, but carefully read this notice about the chain configuration property before proceeding with the next steps. -
To preallocate your own accounts—balances, contract bytecode, or storage slots—create a
custom-alloc.jsonfile (standard Gethallocformat) next to your.env, setCUSTOM_ALLOC_ACCOUNT_FILE=custom-alloc.jsonin the.env, and add a read-only bind mount for it to thedocker runcommand:
docker run --rm \--env-file .env \-v "$(pwd)/genesis":/app/genesis \-v "$(pwd)/custom-alloc.json":/app/custom-alloc.json:ro \offchainlabs/genesis-file-generator:v0.0.3-rc-deffec7Example
custom-alloc.json(preallocates 1 ETH—review the solvency warning above before setting non-zero balances):{"0x299a89EE3Ee2BBC2cf9586ABd9AB1b57CF51B41F": { "balance": "0xde0b6b3a7640000" }} - Set up the
-
Generate the required hashes:
- After the
genesis.jsonfile is created, run the Nitro container to compute the genesisblockHashwith thegenesis-generatorendpoint:
source .envdocker run --rm \-v "$(pwd)/genesis":/data/genesisDir \--entrypoint genesis-generator \"$NITRO_NODE_IMAGE" \--genesis-json-file /data/genesisDir/genesis.json- This bind-mounts the current directory into the container so it can read the
genesis.jsonfile generated in the previous step, and outputs the genesisblockHashandsendRoothash. The container logs both values as:
genesis-hash-calculator | BlockHash: 0xd636d2cae7a75bf41f471639f1cbf98fe2a24216147792510e664a65496f27ed, SendRoot: 0x0000000000000000000000000000000000000000000000000000000000000000, Batch: 1, PosInBatch: 0 - After the
-
Deploy the Rollup:
- Use the
blockHash,sendRoot,Batch, andPosInBatchin the Chain SDK. The SDK uses these to generate theassertion_hashneeded to register your Rollup's core smart contracts on the parent chain. - If you write your own deploy script with
@arbitrum/chain-sdk(instead of the bundledgenerate-genesis-fileexample), make sure the Rollup config includes thedataCostEstimatefield. The bundled example sets it, and omitting it is a common cause of failed custom-genesis deployments.
const genesisAssertionState = {globalState: {bytes32Vals: [genesisBlockHash as `0x${string}`, sendRootHash as `0x${string}`] as [`0x${string}`,`0x${string}`,],// Set inbox position to 1u64Vals: [1n, 0n] as [bigint, bigint],},machineStatus: 1, // FINISHEDendHistoryRoot: toHex(0, { size: 32 }),}; - Use the
-
Configure and launch your node:
- Set up your node as usual (see the full node guide), but include the following properties to point to your custom state:
--init.genesis-json-file=/path/to/genesis.json: the path to your customgenesis.jsonfile--init.empty=false: (Required) Forces the node to load the provided genesis file instead of initializing a blank state.
- Set up your node as usual (see the full node guide), but include the following properties to point to your custom state:
-
Start your chain with the correct preloaded state.
Predeployed contracts registry
The following contracts are included by default in the standard genesis.json file. For the authoritative, maintained list—and details on how each contract is deployed—see the genesis-file-generator README.
| Category | Contract name | Address | Note |
|---|---|---|---|
| Factories | Safe Singleton Factory v1.0.43 | 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7 | Deterministic Proxy (Safe Key) |
| Create2Deployer | 0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2 | CREATE (Deployer: 0x5542..., Nonce 0) | |
| CreateX v1.0.0 | 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed | Pre-signed Transaction | |
| Arachnid Proxy | 0x4e59b44847b379578588920cA78FbF26c0B4956C | Deterministic Proxy (Arachnid) | |
| Zoltu Deployment Proxy | 0x7A0D94F55792C434d74a40883C6ed8545E406D12 | Deterministic Proxy (Zoltu) | |
| ERC-2470 Singleton Factory | 0xce0042B868300000d44A59004Da54A005ffdcf9f | Singleton Factory (ERC-2470) | |
| Safe v1.3.0 | GnosisSafe (Canonical) | 0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552 | Via Arachnid CREATE2 Proxy |
| GnosisSafe (EIP-155) | 0x69f4D1788e39c87893C980c06EdF4b7f686e2938 | Via Safe Singleton Factory | |
| GnosisSafeL2 (Canonical) | 0x3e5c63644e683549055b9be8653de26e0b4cd36e | Via Arachnid CREATE2 Proxy | |
| GnosisSafeL2 (EIP-155) | 0xfb1bffC9d739B8D520DaF37dF666da4C687191EA | Via Safe Singleton Factory | |
| SafeProxyFactory (Canonical) | 0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2 | Via Arachnid CREATE2 Proxy | |
| SafeProxyFactory (EIP-155) | 0xC22834581EbC8527d974F8a1c97E1bEA4EF910BC | Via Safe Singleton Factory | |
| MultiSend v1.3.0 (Canonical) | 0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761 | Via Arachnid CREATE2 Proxy | |
| MultiSend v1.3.0 (EIP-155) | 0x998739BFdAAdde7C933B942a68053933098f9EDa | Via Safe Singleton Factory | |
| MultiSendCallOnly v1.3.0 (Canonical) | 0x40A2aCCbd92BCA938b02010E17A5b8929b49130D | Via Arachnid CREATE2 Proxy | |
| MultiSendCallOnly v1.3.0 (EIP-155) | 0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B | Via Safe Singleton Factory | |
| Safe v1.4.1 | Safe | 0x41675C099F32341bf84BFc5382aF534df5C7461a | Via Safe Singleton Factory |
| SafeL2 | 0x29fcB43b46531BcA003ddC8FCB67FFE91900C762 | Via Safe Singleton Factory | |
| SafeProxyFactory | 0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67 | Via Safe Singleton Factory | |
| MultiSend v1.4.1 | 0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526 | Via Safe Singleton Factory | |
| MultiSendCallOnly v1.4.1 | 0x9641d764fc13c8B624c04430C7356C1C7C8102e2 | Via Safe Singleton Factory | |
| ERC-4337 Core | EntryPoint v0.6.0 | 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 | Standard v0.6 |
| SenderCreator v0.6.0 | 0x7fc98430eAEdbb6070B35B39D798725049088348 | Created during EP v0.6.0 deploy | |
| EntryPoint v0.7.0 | 0x0000000071727De22E5E9d8BAf0edAc6f37da032 | Standard v0.7 | |
| SenderCreator v0.7.0 | 0xEFC2c1444eBCC4Db75e7613d20C6a62fF67A167C | Created during EP v0.7.0 deploy | |
| EntryPoint v0.8.0 | 0x4337084d9e255ff0702461cf8895ce9e3b5ff108 | Standard v0.8 | |
| SenderCreator v0.8.0 | 0x449ED7C3e6Fee6a97311d4b55475DF59C44AdD33 | Created during EP v0.8.0 deploy | |
| Account Modules | Safe Module Setup v0.3.0 | 0x2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47 | ERC-4337 Initializer |
| Safe 4337 Module v0.3.0 | 0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226 | Associated with EntryPoint v0.7.0 | |
| Kernel v3.3 | 0xd6CEDDe84be40893d153Be9d467CD6aD37875b28 | Associated with EntryPoint v0.7.0 | |
| KernelFactory v3.3 | 0x2577507b78c2008Ff367261CB6285d44ba5eF2E9 | Associated with EntryPoint v0.7.0 | |
| MetaFactory v3.0 | 0xd703aaE79538628d27099B8c4f621bE4CCd142d5 | ZeroDev FactoryStaker | |
| ECDSAValidator v3.1 | 0x845ADb2C711129d4f3966735eD98a9F09fC4cE57 | Compiled from commit 8f7fd99 | |
| Infrastructure | Multicall3 | 0xcA11bde05977b3631167028862bE2a173976CA11 | Pre-signed Transaction |
| ERC-1820 Registry | 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 | Pseudo-introspection Registry | |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 | Uniswap Permit2 | |
| EAS v1.4.0 | 0xF4C9CCaf46A866e2c12C5Bd95A39694718044444 | Ethereum Attestation Service | |
| EAS SchemaRegistry | 0x822B0B93BE3f3B8Da35a2E90e877C01215be8506 | EAS Registry |