Stake and validator configurations
- Base Stake: Minimum bond required for validators.
- Stake Token: Token used for staking.
- Loser Stake Escrow: Address for escrowed funds from losing validators. Arbitrum chains are customizable Layer 3 (L3) chains that settle to an Arbitrum Layer 2 (L2) chain, such as Arbitrum One. They support validator configurations to ensure chain security through bonding and assertion challenges. Validators post assertions about the chain's state on the parent L2 chain and can challenge incorrect assertions. Arbitrum chains can be permissioned, meaning validators must be allowlisted. For chains that use that elect to use the BoLD protocol, permissionless validation is an option (BoLD also supports permissioned validation).
Bonding is required for active validation, where validators place bond funds to participate. If a validator loses a challenge (e.g., due to a faulty assertion), their bond is escrowed or burned. Configurations such as the stakeToken, baseStake, and loserStakeEscrow are configurable during chain deployment or post-deployment via contract calls.
Arbitrum chains may utilize the BoLD (Bounded Liquidity Delay) protocol for efficient dispute resolution, which affects bonding tokens (e.g., WETH for BoLD-enabled chains like Arbitrum One/Nova, or ETH/native for other chains).
stakeToken
The bonded token is the asset that validators must bond to participate in asserting the chain's state on the parent L2 chain. It serves as collateral for challenges. A bonded token can be WETH or the parent chain's native token.
Configuration details
- Can be
ETH(native gas token) or anyERC-20token. - For BoLD-enabled chains (e.g., settling to Arbitrum One or Nova), it defaults to
WETH. - For non-BoLD chains, it defaults to the parent chain's native token (usually
ETH). - Currently, its value is often hardcoded to
ETHin basic deployments, but customizable to anERC-20contract address in advanced setups. Future updates will expandERC-20support.
How to configure
-
Prepare the chain configuration: Generate the base chain config using
prepareChainConfig. Set thechainIdand initial owner.import { prepareChainConfig } from '@arbitrum/chain-sdk';
const chainConfig = prepareChainConfig({
chainId: 123456, // Replace with your desired chain ID
arbitrum: {
InitialChainOwner: '0xYourOwnerAddressHere', // Wallet address that will own the chain
DataAvailabilityCommittee: false, // Set to true for AnyTrust chains (DAC)
},
}); -
Set up the public client for the parent chain: Create a public client to interact with the parent chain.
import { createPublicClient, http } from 'viem';
import { sepolia } from 'viem/chains'; // Example: Use 'mainnet' or 'arbitrumOne' as needed
const parentChainPublicClient = createPublicClient({
chain: sepolia, // Replace with your parent chain (e.g., arbitrumOne)
transport: http('https://your-parent-chain-rpc-url'), // Replace with actual RPC URL
}); -
Prepare deployment parameters, including
stakeToken: UsecreateRollupPrepareDeploymentParamsConfigto define the rollout config. This is where you configure thestakeToken(theERC-20address on the parent chain) andbaseStake(minimum stake amount in wei).import { createRollupPrepareDeploymentParamsConfig } from '@arbitrum/chain-sdk';
const createRollupConfig = await createRollupPrepareDeploymentParamsConfig(
parentChainPublicClient,
{
chainId: 123456, // Must match the chainId from Step 1
owner: '0xYourOwnerAddressHere', // Must match InitialChainOwner from Step 1
chainConfig,
stakeToken: '0xYourStakeTokenERC20AddressHere', // ERC-20 token address on parent chain for validator staking
baseStake: 1000000000000000000n, // Example: 1 token (adjust based on token decimals)
// Optional: Other params like confirmPeriodBlocks, loserStakeEscrow, etc.
},
); -
Deploy the chain: Use
createRollupto deploy. Provide validator and batch poster addresses. This step sends the transaction to the parent chain.import { createWalletClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { createRollup } from '@arbitrum/chain-sdk';
const deployerAccount = privateKeyToAccount('0xYourDeployerPrivateKeyHere'); // Securely manage this
const walletClient = createWalletClient({
account: deployerAccount,
chain: sepolia, // Match parent chain
transport: http('https://your-parent-chain-rpc-url'),
});
const createRollupResults = await createRollup({
params: {
config: createRollupConfig,
batchPosters: ['0xYourBatchPosterAddressHere'], // Address for posting batches
validators: ['0xYourValidatorAddressHere'], // Validator addresses
// Optional: nativeToken: '0xCustomGasTokenAddress' for custom fee tokens
},
account: deployerAccount,
publicClient: parentChainPublicClient,
walletClient,
});
console.log('Deployment Transaction Hash:', createRollupResults.transactionHash);
console.log('Core Contracts:', createRollupResults.coreContracts); -
Post-deployment (if using AnyTrust): If
DataAvailabilityCommitteeistrue, set the DAC keyset in theSequencerInbox.import { setValidKeyset } from '@arbitrum/chain-sdk';
// Generate or provide your keyset (BLS public keys for the committee)
const keyset = '0xYourGeneratedKeysetHere';
const setKeysetResults = await setValidKeyset({
coreContracts: createRollupResults.coreContracts,
keyset,
publicClient: parentChainPublicClient,
walletClient,
});
Additional notes
- Ensure the
stakeTokenis a compliantERC-20on the parent chain; mismatches can cause deployment failures. - Test on a testnet (e.g., Sepolia) first, as deployments are irreversible and cost gas.
- The owner address gains control over upgrades and settings—use a secure multisig in production.
- For production, consider a Rollup-as-a-Service (RaaS) provider for monitoring and scaling.
- Verification: This SDK-based method deploys directly via smart contract interactions and does not involve any UI or portal (the portal is for asset bridging, not chain deployment).
baseStake
The baseStake is the minimum amount of the bond token that validators must deposit to bond and post assertions.
Configuration details
- Specified as a float value (e.g., in
weiforETH). - Must be greater than 0.
- Balances security: Low values ease entry but risk malicious challenges; high values deter attacks but exclude smaller validators.
How to configure
-
Prepare the chain configuration: Generate the base chain config using
prepareChainConfig. Set thechainIdand initial owner.import { prepareChainConfig } from '@arbitrum/chain-sdk';
const chainConfig = prepareChainConfig({
chainId: 123456, // Replace with your desired chain ID
arbitrum: {
InitialChainOwner: '0xYourOwnerAddressHere', // Wallet address that will own the chain
DataAvailabilityCommittee: false, // Set to true for AnyTrust chains (DAC)
},
}); -
Set up the public client for the parent chain: Create a public client to interact with the parent chain.
import { createPublicClient, http } from 'viem';
import { sepolia } from 'viem/chains'; // Example: Use 'mainnet' or 'arbitrumOne' as needed
const parentChainPublicClient = createPublicClient({
chain: sepolia, // Replace with your parent chain (e.g., arbitrumOne)
transport: http('https://your-parent-chain-rpc-url'), // Replace with actual RPC URL
}); -
Prepare deployment parameters, including
stakeToken: UsecreateRollupPrepareDeploymentParamsConfigto define the rollout config. This is where you configure thestakeToken(theERC-20address on the parent chain) andbaseStake(minimum stake amount in wei).import { createRollupPrepareDeploymentParamsConfig } from '@arbitrum/chain-sdk';
const createRollupConfig = await createRollupPrepareDeploymentParamsConfig(
parentChainPublicClient,
{
chainId: 123456, // Must match the chainId from Step 1
owner: '0xYourOwnerAddressHere', // Must match InitialChainOwner from Step 1
chainConfig,
stakeToken: '0xYourStakeTokenERC20AddressHere', // ERC-20 token address on parent chain for validator staking
baseStake: 1000000000000000000n, // Example: 1 token (adjust based on token decimals)
// Optional: Other params like confirmPeriodBlocks, loserStakeEscrow, etc.
},
); -
Deploy the chain: Use
createRollupto deploy. Provide validator and batch poster addresses. This step sends the transaction to the parent chain.import { createWalletClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { createRollup } from '@arbitrum/chain-sdk';
const deployerAccount = privateKeyToAccount('0xYourDeployerPrivateKeyHere'); // Securely manage this
const walletClient = createWalletClient({
account: deployerAccount,
chain: sepolia, // Match parent chain
transport: http('https://your-parent-chain-rpc-url'),
});
const createRollupResults = await createRollup({
params: {
config: createRollupConfig,
batchPosters: ['0xYourBatchPosterAddressHere'], // Address for posting batches
validators: ['0xYourValidatorAddressHere'], // Validator addresses
// Optional: nativeToken: '0xCustomGasTokenAddress' for custom fee tokens
},
account: deployerAccount,
publicClient: parentChainPublicClient,
walletClient,
});
console.log('Deployment Transaction Hash:', createRollupResults.transactionHash);
console.log('Core Contracts:', createRollupResults.coreContracts); -
Post-deployment (if using AnyTrust): If
DataAvailabilityCommitteeistrue, set the DAC keyset in theSequencerInbox.import { setValidKeyset } from '@arbitrum/chain-sdk';
// Generate or provide your keyset (BLS public keys for the committee)
const keyset = '0xYourGeneratedKeysetHere';
const setKeysetResults = await setValidKeyset({
coreContracts: createRollupResults.coreContracts,
keyset,
publicClient: parentChainPublicClient,
walletClient,
});
Additional notes
- Validators and staking: After deployment, validators bond by depositing at least
baseStakeof thestakeTokeninto the Rollup contract. This can be done via contract calls (e.g., using theRollupUserLogicinterface). - Warnings:
- Ensure the
stakeTokenis a compliantERC-20on the parent chain; mismatches can cause deployment failures. - Test on a testnet (e.g., Sepolia) first, as deployments are irreversible and cost gas.
- The owner address gains control over upgrades and settings—use a secure multisig in production.
- For production, consider a Rollup-as-a-Service (RaaS) provider for monitoring and scaling.
- Ensure the
- Verification: This SDK-based method deploys directly via smart contract interactions and does not involve any UI or portal (the portal is for asset bridging, not chain deployment).
loserStakeEscrow
The loserStakeEscrow is the address where a validator's bonded funds are sent if they lose a challenge (e.g., due to an incorrect assertion). This mechanism acts as a penalty. The default configuration has no default specified; must be configured.
Configuration details
- Funds are escrowed rather than immediately burned, allowing potential recovery or governance decisions.
- Recommended: Set to an address controlled by the chain owner(s) for management, or a burn address (e.g.,
0x000000000000000000000000000000000000dEaD) if funds should be permanently removed.
Configuring during deployment
-
Prepare chain config:
import { prepareChainConfig } from '@arbitrum/chain-sdk';
const chainConfig = prepareChainConfig({
chainId: 123456, // Your unique chain ID
arbitrum: {
InitialChainOwner: '0xYourOwnerAddressHere',
DataAvailabilityCommittee: false, // True for AnyTrust chains
},
}); -
Set up parent chain client:
import { createPublicClient, http } from 'viem';
import { sepolia } from 'viem/chains';
const parentChainPublicClient = createPublicClient({
chain: sepolia, // Replace with your parent chain
transport: http('https://your-parent-rpc-url'),
}); -
Prepare deployment params, including
loserStakeEscrow: SetloserStakeEscrowas an address (string). There is no explicit default documented, but if omitted, it may revert to a system default (e.g., zero address)—always specify for control.import { createRollupPrepareDeploymentParamsConfig } from '@arbitrum/chain-sdk';
const createRollupConfig = await createRollupPrepareDeploymentParamsConfig(
parentChainPublicClient,
{
chainId: 123456,
owner: '0xYourOwnerAddressHere',
chainConfig,
loserStakeEscrow: '0xYourEscrowAddressHere', // e.g., owner-controlled or burn address
// Optional: baseStake: 100000000000000000n, stakeToken: '0xERC20Address', etc.
},
); -
Deploy the chain:
import { createWalletClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { createRollup } from '@arbitrum/chain-sdk';
const deployerAccount = privateKeyToAccount('0xYourPrivateKey');
const walletClient = createWalletClient({
account: deployerAccount,
chain: sepolia,
transport: http('https://your-parent-rpc-url'),
});
const createRollupResults = await createRollup({
params: {
config: createRollupConfig,
batchPosters: ['0xYourBatchPosterAddress'],
validators: ['0xYourValidatorAddress'],
},
account: deployerAccount,
publicClient: parentChainPublicClient,
walletClient,
});
console.log('Rollup Address:', createRollupResults.coreContracts.rollup); -
For AnyTrust chains: If
DataAvailabilityCommitteeistrue, configure the DAC keyset post-deployment usingsetValidKeysetfrom the SDK.
Updating loserStakeEscrow post-deployment
The chain owner can update it by calling setLoserStakeEscrow on the deployed Rollup contract (address from createRollupResults.coreContracts.rollup):
import { parseAbi } from 'viem';
const rollupAbi = parseAbi(['function setLoserStakeEscrow(address newLoserStakedEscrow) external']);
await walletClient.writeContract({
address: '0xYourRollupAddress',
abi: rollupAbi,
functionName: 'setLoserStakeEscrow',
args: ['0xNewEscrowAddressHere'],
});
Additional notes
- This process requires the caller to be the owner. Changes affect how challenge losses are handled, so test thoroughly.
- Setting
loserStakeEscrowto a burn address increases the cost of failed challenges, enhancing security. - Deploy on testnets first; mainnet deployments are costly and permanent.
Validator configurations and how to set them up
Validators are configured during chain deployment and can be run post-launch. Arbitrum chains support permissioned validators, that can be added to an allowlist.
Step 1: Configure and run the validator node
Start with the base configuration for a full node, then add validator-specific flags. Use a Docker command to run the Nitro node image (replace placeholders like version numbers, RPC URLs, and chain IDs with your specifics). For example, on Arbitrum One (chain ID 42161):
docker run --rm -it -v /path/to/local/dir/arbitrum:/home/user/.arbitrum offchainlabs/nitro-node:v3.8.0-62c0aa7 \
--parent-chain.connection.url=https://your-l1-rpc-url:8545 \
--chain.id=42161 \
--node.staker.enable=true \
--node.staker.strategy=Defensive \
--node.staker.parent-chain-wallet.password="YOUR_SECURE_PASSWORD"
Key configuration parameters to include:
--node.staker.enable=true: This flag enables validation mode.--node.staker.strategy=[Strategy]: Choose a strategy based on your intended behavior:Defensive: Monitors the chain and challenges incorrect assertions by posting a bond (recommended for most users).StakeLatest: Bonds on the latest correct assertion and challenges bad ones (available only on pre-BoLD chains).ResolveNodes: Bonds on the latest assertion, resolves unconfirmed ones, and challenges bad assertions.MakeNodes: Creates new assertions, resolves unconfirmed ones, and challenges bad ones (use cautiously, as it may lead to reverted transactions if multiple validators act at once).Watchtower: Passively monitors and logs errors without active challenging (enabled by default; no wallet needed, but set--node.staker.enable=falseto disable if not wanted).
--node.staker.parent-chain-wallet.private-key=[0xYourPrivateKey]or--node.staker.parent-chain-wallet.password=[YourPassword]: Provides access to the wallet for on-chain operations. Use a secure method; password-protected keystores are safer than direct private keys.--node.bold.enable=true: Enable this if the chain has BoLD activated (required for versions before Nitro v3.6.0).
For custom Arbitrum chains:
- Add
--chain.info-json=[JSON string or file path with Arbitrum chain info]to specify the chain's details. - BoLD parameters may not be needed if BoLD isn't activated on that chain.
Run the command in a persistent setup (e.g., using Docker Compose or a systemd service) to keep the node online.
Step 2: Verify the validator is running correctly
- Monitor the node logs for confirmation messages:
- Look for
INFO [...] running as validator txSender=[your_wallet_address] actingAsWallet=[your_wallet_address] whitelisted=true strategy=[YourChosenStrategy]. This indicates the node is in validator mode with a valid wallet. validation succeeded: Shows the node is successfully validating blocks.found correct assertion: Confirms the node is detecting and agreeing with on-chain assertions.
- Look for
- If issues arise, check for errors related to wallet funding, RPC connectivity, or chain syncing.
Advanced: Creating a dedicated validator wallet
If you need a new wallet specifically for validation, use Nitro to generate one:
docker run --rm -it -v /path/to/local/dir/arbitrum:/home/user/.arbitrum offchainlabs/nitro-node:v3.8.0-62c0aa7 \
--parent-chain.connection.url=https://your-l1-rpc-url:8545 \
--chain.id=42161 \
--node.staker.enable=true \
--node.staker.parent-chain-wallet.only-create-key \
--node.staker.parent-chain-wallet.password="YOUR_SECURE_PASSWORD"
This creates a wallet file in your mounted directory (e.g., under arb1/wallet/ for Arbitrum One). Back it up securely, as it's needed to withdraw any staked funds later. Load it in your node run command using the password.
For permissioned chains
If the Arbitrum chain is permissioned (not fully permissionless), add your validator wallet address to the allowlist:
- Identify the
upgradeExecutorcontract address for the chain. - Call the
executeCallmethod on it:- Set
targetto the Rollup contract address. - Set
targetCalldatato0xa3ffb772followed by your validator address (this is the encoded signature forsetValidator(address[],bool[])).
- Set
- Verify by calling
isValidator(your_address)on the Rollup contract, which should returntrue.
This step requires administrative access to the chain.
Keep your node synced and monitor it regularly to ensure it contributes effectively to chain security. Always use the latest Nitro version for security and compatibility.
Refer to the Run a full node article for instructions on how to get up and running.
Assertion interval
Default to 15 minutes for new assertions (via --node.bold.assertion-posting-interval (BoLD) or --node.staker.make-assertion-interval (Legacy ~1 hour is default)); must exceed the Rollup's minimumAssertionPeriod (~15 minutes).
For production, run multiple validators in a network. Test on devnets first. Always back up keys, as they're needed to withdraw bonds. If issues arise, refer to the official Arbitrum docs for updates, as configurations evolve.