Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2f987e0
Add genesis file generator example
TucksonDev Oct 21, 2025
ad0ec61
Merge branch 'main' into add-genesis-file-generator-script
TucksonDev Oct 21, 2025
6ff5128
Format
TucksonDev Oct 21, 2025
b5238bb
Merge branch 'add-genesis-file-generator-script' of https://github.co…
TucksonDev Oct 21, 2025
42c263f
Add rollup creation
TucksonDev Oct 23, 2025
aa7e0af
Prompt questions for generating the genesis file and creating the rollup
TucksonDev Nov 6, 2025
00d933a
Add instructions in README file
TucksonDev Nov 6, 2025
885a644
Use main branch instead of containerization
TucksonDev Nov 7, 2025
603027f
Add function to minify the chainConfig property in the genesis.json file
TucksonDev Nov 20, 2025
62b9af4
Remove minification of chain config since it is now done in the genes…
TucksonDev Nov 21, 2025
56323cf
Remove genesis-file-generator tag and use the main branch
TucksonDev Nov 24, 2025
3a2679a
Merge branch 'main' into add-genesis-file-generator-script
TucksonDev Nov 24, 2025
107bc11
add v3.2 abi
spsjvc Dec 8, 2025
911a7b2
reorder
spsjvc Jan 16, 2026
5eb7a82
wip
spsjvc Jan 16, 2026
f0c14ea
use v3.2 in integration tests
spsjvc Jan 16, 2026
016db4e
fix other tests
spsjvc Jan 16, 2026
32a8f89
add in testnode addresses
spsjvc Jan 16, 2026
a9372df
fix testnode addresses
spsjvc Jan 16, 2026
6e86ca5
patch wagmi cli to fix etherscan rate limits
spsjvc Jan 21, 2026
a17bb30
increase wait time between requests
spsjvc Jan 21, 2026
310e67d
generate
spsjvc Jan 21, 2026
f45d29b
fix test
spsjvc Jan 21, 2026
8fd543a
fix
spsjvc Jan 21, 2026
bd14849
make v3.2 imports explicit
spsjvc Jan 21, 2026
0a25e0d
fix transaction receipt
spsjvc Jan 21, 2026
127d5b5
Merge branch 'main' into feat-nitro-contracts-v3.2
spsjvc Jan 21, 2026
d842aa6
rename v3.1 to v3.2
spsjvc Jan 21, 2026
1d6bb19
rename v3.1 to v3.2
spsjvc Jan 21, 2026
7e8b105
update snapshots
spsjvc Jan 21, 2026
5ceede7
add missing test
spsjvc Jan 21, 2026
69934da
add more missing tests
spsjvc Jan 21, 2026
70f0d80
pin to v3.2.0
spsjvc Jan 22, 2026
8b63ec8
update name
spsjvc Jan 22, 2026
083591a
add isAnyTrust tests
spsjvc Jan 22, 2026
c7a18c9
add getBatchPosters test for v3.2
spsjvc Jan 22, 2026
cd46813
add getValidators test for v3.2
spsjvc Jan 22, 2026
0329054
Merge branch 'main' into add-genesis-file-generator-script
Jason-W123 Feb 11, 2026
c538b17
Merge remote-tracking branch 'origin/feat-nitro-contracts-v3.2' into …
Jason-W123 Feb 11, 2026
dae5678
set machineStatus to 1
Jason-W123 Feb 11, 2026
cc61b31
Merge branch 'main' into add-genesis-file-generator-script
spsjvc Mar 11, 2026
c942f18
fix: update generate-genesis-file example for v0.0.2+ / v3.9.9+
Jason-W123 Apr 17, 2026
cdd4f5b
feat: expose LOAD_DEFAULT_PREDEPLOYS and ArbOS init flags in .env.exa…
Jason-W123 Apr 17, 2026
3bf9cfb
Update examples/generate-genesis-file/.env.example
Jason-W123 May 28, 2026
c5611e2
fix(generate-genesis-file): mount custom-alloc file + pnpm/comment cl…
Jason-W123 Jul 1, 2026
6f0893d
Potential fix for pull request finding 'CodeQL / Unnecessary use of `…
Jason-W123 Jul 1, 2026
a1f2d1b
fix(generate-genesis-file): bump genesis-file-generator image to v0.0…
Jason-W123 Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions examples/generate-genesis-file/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#######################
# Chain configuration #
#######################

# If of the chain you're going to create
CHAIN_ID=31337

# Whether it'll be an Anytrust chain
IS_ANYTRUST=false

# ArbOS version to use
ARBOS_VERSION=51

# Chain owner
CHAIN_OWNER=0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E

############################
# Predeploys configuration #
############################

# Whether to include the default predeploys (Safe, Multicall3, EntryPoint, etc.) in the genesis file
LOAD_DEFAULT_PREDEPLOYS=true

# (Optional) Path to a JSON file with custom preallocated accounts/contracts,
# in the standard Geth `alloc` format. Must be a filename in this example
# directory; it is mounted into the generator container when set.
# CUSTOM_ALLOC_ACCOUNT_FILE=custom-alloc.json

############################
# ArbOS Init configuration #
############################

# Initial L1 base fee
# (you should set this to your estimate of the parent chain's gas price)
L1_BASE_FEE=1000000000

# Whether to enable native token supply management in ArbOS (e.g. mint/burn via third-party bridges)
ENABLE_NATIVE_TOKEN_SUPPLY=false

# Whether to enable protocol-level transaction filtering (for regulatory/compliance use cases)
ENABLE_TRANSACTION_FILTERING=false

# Genesis file generator Docker image (required)
GENESIS_FILE_GENERATOR_IMAGE=offchainlabs/genesis-file-generator:v0.0.3-rc-deffec7

# Nitro node image (required)
NITRO_NODE_IMAGE=offchainlabs/nitro-node:v3.9.9-rc.3-3a38667

#################################
# Rollup creation configuration #
#################################
# Required
DEPLOYER_PRIVATE_KEY=

# Optional (these will be randomly generated if not provided)
BATCH_POSTER_PRIVATE_KEY=
VALIDATOR_PRIVATE_KEY=

# (Optional) Infura RPC endpoint for the parent chain (Arbitrum Sepolia), if not provided, the script might fail with timeout
PARENT_CHAIN_RPC=https://arbitrum-sepolia.infura.io/v3/YOUR_API_KEY
1 change: 1 addition & 0 deletions examples/generate-genesis-file/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
genesis.json
36 changes: 36 additions & 0 deletions examples/generate-genesis-file/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Arbitrum Orbit SDK

## Generating a genesis.json file and genesis blockhash and sendRoot hash

This is an example for generating a genesis.json file and obtaining the genesis blockhash and sendRoot hash based on that file.

For the contents of the genesis.json file, and what predeploys it contains, you can see the [genesis-file-generator](https://github.com/OffchainLabs/genesis-file-generator) script.

## Setup

1. Install dependencies and build the SDK project (from the root of the repository)

```bash
pnpm install && pnpm build
```

2. Move to the example script directory

```bash
cd examples/generate-genesis-file
```

3. Create .env file and add the env vars

```bash
cp .env.example .env
```

> [!NOTE]
> Make sure you set the correct values of the environment variables for your chain

4. Run the example

```bash
pnpm dev
```
235 changes: 235 additions & 0 deletions examples/generate-genesis-file/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import { Address, createPublicClient, http, toHex } from 'viem';
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import { arbitrumSepolia } from 'viem/chains';
import { createRollupPrepareDeploymentParamsConfig, createRollup } from '@arbitrum/chain-sdk';
import { sanitizePrivateKey } from '@arbitrum/chain-sdk/utils';
import { config } from 'dotenv';
import { execSync } from 'child_process';
import * as fs from 'fs';
import { createInterface } from 'readline';
config();

// Env variables check
if (typeof process.env.NITRO_NODE_IMAGE === 'undefined') {
throw new Error(`Please provide the "NITRO_NODE_IMAGE" environment variable`);
}
if (typeof process.env.GENESIS_FILE_GENERATOR_IMAGE === 'undefined') {
throw new Error(`Please provide the "GENESIS_FILE_GENERATOR_IMAGE" environment variable`);
}
if (typeof process.env.L1_BASE_FEE === 'undefined') {
throw new Error(`Please provide the "L1_BASE_FEE" environment variable`);
}
const nitroNodeImage = process.env.NITRO_NODE_IMAGE;
const genesisFileGeneratorImage = process.env.GENESIS_FILE_GENERATOR_IMAGE;
const l1BaseFee = BigInt(process.env.L1_BASE_FEE);

// Optional checks when creating a rollup
if (process.env.CREATE_ROLLUP === 'true') {
Comment thread
TucksonDev marked this conversation as resolved.
// Check environment variables
if (typeof process.env.DEPLOYER_PRIVATE_KEY === 'undefined') {
throw new Error(`Please provide the "DEPLOYER_PRIVATE_KEY" environment variable`);
}

if (typeof process.env.PARENT_CHAIN_RPC === 'undefined' || process.env.PARENT_CHAIN_RPC === '') {
console.warn(
`Warning: you may encounter timeout errors while running the script with the default rpc endpoint. Please provide the "PARENT_CHAIN_RPC" environment variable instead.`,
);
}
}

// Helpers
function withFallbackPrivateKey(privateKey: string | undefined): `0x${string}` {
if (typeof privateKey === 'undefined' || privateKey === '') {
return generatePrivateKey();
}

return sanitizePrivateKey(privateKey);
}

async function askQuestion(question: string): Promise<string> {
const rl = createInterface({
input: process.stdin,
output: process.stdout,
});

return new Promise((resolve) =>
rl.question(question, (answer: string) => {
rl.close();
resolve(answer);
}),
);
}

async function main() {
// Step 0 - Check if there's a genesis.json file present in the directory
let generateGenesisFile = true;
if (fs.existsSync('genesis.json')) {
// Ask the user if they want to overwrite the existing file
const generateGenesisuserResponse = await askQuestion(
'A genesis.json file already exists in the current directory. Do you want to overwrite it? (y/n): ',
);
if (generateGenesisuserResponse.toLowerCase() !== 'y') {
generateGenesisFile = false;
console.log('Using existing genesis.json file.');
}
}

// Step 1 - If needed, generate the genesis file
if (generateGenesisFile) {
// Generate genesis file
console.log(`Generate genesis file...`);

// (Optional) mount a custom alloc file into the generator container.
// Only added when CUSTOM_ALLOC_ACCOUNT_FILE is set. The file must live in
// this example directory; the generator reads it relative to its /app workdir.
let customAllocMount = '';
const customAllocFile = process.env.CUSTOM_ALLOC_ACCOUNT_FILE?.trim();
if (customAllocFile) {
if (!fs.existsSync(customAllocFile)) {
throw new Error(
`"CUSTOM_ALLOC_ACCOUNT_FILE" is set to "${customAllocFile}" but that file was not found in ${process.cwd()}. ` +
`Place the file in this example directory and set CUSTOM_ALLOC_ACCOUNT_FILE to its filename.`,
);
}
customAllocMount = `-v "$(pwd)/${customAllocFile}:/app/${customAllocFile}:ro"`;
}

execSync(
`docker run --env-file ./.env ${customAllocMount} ${genesisFileGeneratorImage} > genesis.json`,
);
}

// Step 2 - Obtain genesis block hash and sendRoot hash
console.log(`Obtain genesis block hash and sendRoot hash...`);
console.log(`Using image "${nitroNodeImage}" and L1 base fee "${l1BaseFee}".`);
console.log(fs.readFileSync('genesis.json', 'utf8'));
const genesisHashes = execSync(
`docker run -v $(pwd):/data/genesisDir --entrypoint genesis-generator ${nitroNodeImage} --genesis-json-file /data/genesisDir/genesis.json`,
);
Comment on lines +106 to +108

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we want to wrap a docker run command

prepareGenesisHashes(params: {
  genesis,
  nitroNodeImageTag,
  initialParentBaseFee
})


// Step 3 - Extract hashes and output results
// genesisHashes has the following structure:
// BlockHash: 0xabcd, SendRoot: 0x1234, Batch: 1, PosInBatch: 0
const genesisHashesStr = genesisHashes.toString();
const genesisBlockHash = genesisHashesStr.split(',')[0].split(':')[1].trim();
const sendRootHash = genesisHashesStr.split(',')[1].split(':')[1].trim();
Comment on lines +113 to +115

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we don't end up creating a function that wraps the call to run the docker container, we might do something like this:

parseGenesisHashes(hashes: Hex)


console.log('');
console.log('-------------------');
console.log('Genesis information');
console.log('-------------------');
console.log('Genesis file generated: genesis.json');
console.log(`Block hash: ${genesisBlockHash}`);
console.log(`SendRoot hash: ${sendRootHash}`);

// Step 4 - Create Rollup (optional)
// Ask the user if they want to create a rollup
const createRollupUserResponse = await askQuestion(
'Do you want to continue creating the rollup with the current genesis.json file? (y/n)',
);
if (createRollupUserResponse.toLowerCase() === 'y') {
// load or generate a random batch poster account
const batchPosterPrivateKey = withFallbackPrivateKey(process.env.BATCH_POSTER_PRIVATE_KEY);
const batchPoster = privateKeyToAccount(batchPosterPrivateKey).address;

// load or generate a random validator account
const validatorPrivateKey = withFallbackPrivateKey(process.env.VALIDATOR_PRIVATE_KEY);
const validator = privateKeyToAccount(validatorPrivateKey).address;

// set the parent chain and create a public client for it
const parentChain = arbitrumSepolia;
const parentChainPublicClient = createPublicClient({
chain: parentChain,
transport: http(process.env.PARENT_CHAIN_RPC),
});

// load the deployer account
const deployer = privateKeyToAccount(sanitizePrivateKey(process.env.DEPLOYER_PRIVATE_KEY!));

// The following env variables must exist, otherwise the genesis generation would have failed
const chainId = Number(process.env.CHAIN_ID!);
const chainOwner = process.env.CHAIN_OWNER as Address;

// Load chain config from the genesis file
let genesisFileContents: string;
try {
genesisFileContents = fs.readFileSync('genesis.json', { encoding: 'utf8' });
} catch (err) {
console.error('Failed to read generated genesis.json file');
throw err;
}
const genesisConfiguration = JSON.parse(genesisFileContents);

// Parse the chain config from the serializedChainConfig field (genesis-file-generator v0.0.2+)
if (typeof genesisConfiguration.serializedChainConfig !== 'string') {
throw new Error(
'genesis.json must contain a "serializedChainConfig" field (string). ' +
'Make sure you are using genesis-file-generator v0.0.2 or newer.',
);
}
const chainConfig = JSON.parse(genesisConfiguration.serializedChainConfig);

// Check whether or not we need to deploy the L2 factories (since they are included in the genesis file by default)
const l2Factories = [
// Arachnid's deterministic deployment proxy
'0x4e59b44847b379578588920cA78FbF26c0B4956C',
// Zoltu's deterministic deployment proxy
'0x7A0D94F55792C434d74a40883C6ed8545E406D12',
// ERC-2470 Singleton Factory
'0xce0042B868300000d44A59004Da54A005ffdcf9f',
// ERC-1820: Pseudo-introspection Registry Contract
'0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24',
] as const;

const allocKeys = new Set(
Object.keys(genesisConfiguration.alloc ?? {}).map((k) => k.toLowerCase()),
);
const hasAllFactories = l2Factories.every((a) => allocKeys.has(a.toLowerCase()));

// Prepare the genesis assertion state
const genesisAssertionState = {
globalState: {
bytes32Vals: [genesisBlockHash as `0x${string}`, sendRootHash as `0x${string}`] as [
`0x${string}`,
`0x${string}`,
],
// Set inbox position to 1
u64Vals: [1n, 0n] as [bigint, bigint],
},
machineStatus: 1, // FINISHED
endHistoryRoot: toHex(0, { size: 32 }),
};

console.log('');
console.log('------------------');
console.log('Creating Rollup...');
console.log('------------------');
const createRollupConfig = createRollupPrepareDeploymentParamsConfig(parentChainPublicClient, {
chainId: BigInt(chainId),
owner: chainOwner,
chainConfig,
genesisAssertionState,
});
const createRollupConfigWithDataCostEstimate = {
...createRollupConfig,
dataCostEstimate: l1BaseFee,
} as typeof createRollupConfig;

try {
await createRollup({
params: {
config: createRollupConfigWithDataCostEstimate,
batchPosters: [batchPoster],
validators: [validator],
deployFactoriesToL2: !hasAllFactories,
},
account: deployer,
parentChainPublicClient,
});
} catch (error) {
console.error(`Rollup creation failed with error: ${error}`);
}
}
}

main();
13 changes: 13 additions & 0 deletions examples/generate-genesis-file/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "generate-genesis-file",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "tsc --outDir dist && node ./dist/index.js"
},
"devDependencies": {
"@types/node": "^20.9.0",
"typescript": "^5.2.2"
}
}
4 changes: 4 additions & 0 deletions examples/generate-genesis-file/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.base.json",
"include": ["./**/*"]
}
Loading