Skip to main content
Unlisted page
This page is unlisted. Search engines will not index it, and only users having a direct link can access it.

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 any ERC-20 token.
  • 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 ETH in basic deployments, but customizable to an ERC-20 contract address in advanced setups. Future updates will expand ERC-20 support.

How to configure

  1. Prepare the chain configuration: Generate the base chain config using prepareChainConfig. Set the chainId and 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)
    },
    });
  2. 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
    });
  3. Prepare deployment parameters, including stakeToken: Use createRollupPrepareDeploymentParamsConfig to define the rollout config. This is where you configure the stakeToken (the ERC-20 address on the parent chain) and baseStake (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.
    },
    );
  4. Deploy the chain: Use createRollup to 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);
  5. Post-deployment (if using AnyTrust): If DataAvailabilityCommittee is true, set the DAC keyset in the SequencerInbox.

    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 stakeToken is a compliant ERC-20 on 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 wei for ETH).
  • 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

  1. Prepare the chain configuration: Generate the base chain config using prepareChainConfig. Set the chainId and 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)
    },
    });
  2. 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
    });
  3. Prepare deployment parameters, including stakeToken: Use createRollupPrepareDeploymentParamsConfig to define the rollout config. This is where you configure the stakeToken (the ERC-20 address on the parent chain) and baseStake (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.
    },
    );
  4. Deploy the chain: Use createRollup to 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);
  5. Post-deployment (if using AnyTrust): If DataAvailabilityCommittee is true, set the DAC keyset in the SequencerInbox.

    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 baseStake of the stakeToken into the Rollup contract. This can be done via contract calls (e.g., using the RollupUserLogic interface).
  • Warnings:
    • Ensure the stakeToken is a compliant ERC-20 on 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).

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

  1. 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
    },
    });
  2. 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'),
    });
  3. Prepare deployment params, including loserStakeEscrow: Set loserStakeEscrow as 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.
    },
    );
  4. 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);
  5. For AnyTrust chains: If DataAvailabilityCommittee is true, configure the DAC keyset post-deployment using setValidKeyset from 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 loserStakeEscrow to 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=false to 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.
  • 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:

  1. Identify the upgradeExecutor contract address for the chain.
  2. Call the executeCall method on it:
    • Set target to the Rollup contract address.
    • Set targetCalldata to 0xa3ffb772 followed by your validator address (this is the encoded signature for setValidator(address[],bool[])).
  3. Verify by calling isValidator(your_address) on the Rollup contract, which should return true.

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.

Additional information

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.