Skip to Content
DocsBuild with AvailDeploy a Rollup on Avail DAOptimiumOP StackDeploy an Avail-Powered OP Stack Alt-DA Optimium

Deploy OP Stack chain with Avail DA

Introduction

Embark on setting up your own OP stack chain in alt-da mode using Avail as Data Availabilty layer . This guide targets Ethereum’s Sepolia testnet and Avail Turing testnet. For a detailed understanding, review the Optimism Documentation.

Prerequisites

Ensure you have installed the following software.

Installation commands are based on Ubuntu 24.04 LTS:

SoftwareVersionVersion Check Command
git^2git --version
go^1.21go version
node^20node --version
pnpm^8pnpm --version
foundry^0.2.0forge --version
make^3make --version
jq^1.6jq --version
direnv^2direnv --version
Just1.35.0just --version
Use the following releases while following the guide:
ComponentVersion
op-nodev1.13.3
op-gethv1.101511.0
op-batcherv1.13.2
op-deployerv0.3.2
op-proposerv1.10.0
Get Access to a Sepolia Node

For deploying to Sepolia, access an L1 node using a provider like Alchemy or run your own Sepolia node.

Setup Avail DA server

This introduces a sidecar DA Server for Optimism that interacts with Avail DA for posting and retrieving data.

Run avail-da server(binary).

  1. Build Avail DA Server

    git clone https://github.com/availproject/avail-alt-da-server.git cd avail-alt-da-server make da-server
  2. Run your DA sidecar:

    ./bin/avail-da-server ./cmd/avail --addr=localhost --port=8000 --avail.rpc=<WS url to an avail node> --avail.seed=<> --avail.appid=<>

Run using docker

  • Copy .env.example to .env. Fill the values inside.

  • Run the following commands:

    docker-compose build docker-compose up

Configure op-stack

  1. Clone the optimism repositroy:

    git clone https://github.com/ethereum-optimism/optimism.git cd optimism git fetch --tag --all git submodule update --init --recursive
  2. Add env variables at root of the optimism directly

    file-name: .envrc

    ################################################## # Getting Started # ################################################## # Admin account export GS_ADMIN_ADDRESS= export GS_ADMIN_PRIVATE_KEY= # Batcher account export GS_BATCHER_ADDRESS= export GS_BATCHER_PRIVATE_KEY= # Proposer account export GS_PROPOSER_ADDRESS= export GS_PROPOSER_PRIVATE_KEY= # Sequencer account export GS_SEQUENCER_ADDRESS= export GS_SEQUENCER_PRIVATE_KEY= ################################################## # op-node Configuration # ################################################## # The kind of RPC provider, used to inform optimal transactions receipts # fetching. Valid options: alchemy, quicknode, infura, parity, nethermind, # debug_geth, erigon, basic, any. export L1_RPC_KIND= export L1_CHAIN_ID= export L2_CHAIN_ID= export L1_BLOCK_TIME=12 export L2_BLOCK_TIME=5 export DA_SERVER_HTTP_URL=http://localhost:8000 export GAME_FACTORY_ADDRESS="" ################################################## # Contract Deployment # ################################################## # RPC URL for the L1 network to interact with export L1_RPC_URL=

    Fill out the required details

    Variable NameDescription
    L1_RPC_URLURL for your L1 node (a Sepolia node in this case).
    L1_RPC_KINDKind of L1 RPC you’re connecting to, used to inform optimal transactions receipts fetching. Valid options: alchemy, quicknode, infura, parity, nethermind, debug_geth, erigon, basic, any.
    L1_BLOCK_TIMEBlock time of your respective settlement layer for this L2 layer
    L2_BLOCK_TIMEBlock time of your L2 layer
    L1_CHAIN_IDChain id of your respective settlement layer for this L2 layer
    L2_CHAIN_IDChain id of your L2 layer
  3. Generate and Secure Keys

    Create and fill these four essential accounts with private keys to above created .envrc file:

    • Admin (contract upgrade authority)

    • Batcher (publishes Sequencer data to L1)

    • Proposer (publishes L2 results to L1)

    • Sequencer (signs blocks on the p2p network)

      echo "Admin:" cast wallet new echo "Proposer:" cast wallet new echo "Batcher:" cast wallet new echo "Sequencer:" cast wallet new

      You should see an output similar to:

      Admin: Successfully created new keypair. Address: 0xc4A01194958DE0D90A876e8A5fc9D7B530072148 Private key: 0xb8e39bd94a210e410c4024e1cc91014de45a5eb1e42f3aa99a368b5a5ac19b45 Proposer: Successfully created new keypair. Address: 0xFC0374Ae658e46cA4022acA179d3cb6D8e1A4934 Private key: 0xa9bc1b3f5deb1e00251df68bf86e3493b25bc5430665433546f2f9aacc748d1a Batcher: Successfully created new keypair. Address: 0xD6857B5BE9468Be67d64ABaB48459378d5329b96 Private key: 0xe9cd8960fc7984a301d567b819e0c62871eb2c7239c2e66b8f319eaa45c3cbd5 Sequencer: Successfully created new keypair. Address: 0x33348817E4B1192D576C4f157e9a5EC93dc5392D Private key: 0xd98b49e11e4e0be9931017831395e6644a50c36285d08e14d1a479af5ee08675

      You’ll need to fund Admin, Proposer, and Batcher with Sepolia ETH (0.5 ETH for Admin, 0.2 ETH for Proposer, 0.1 ETH for Batcher).

    ⚠️

    NOTE FOR PRODUCTION
    Use secure hardware for key management in production environments. cast wallet is not designed for production deployments.

  4. Generate a jwt.txt file, which is crucial for the secure interaction between op-node and op-geth client:

    openssl rand -hex 32 > jwt.txt

Contract Deployment using op-deployer

Deploy essential L1 contracts for the chain’s functionality:

  1. Build op-deplyer.

    ## op-deployer cd op-deployer git checkout op-deployer/v0.3.2 just build
  2. Generate intent.toml and state.json file for deployment configuration:

    ## Intent and state file intialization ./bin/op-deployer init --l1-chain-id <YOUR_L1_CHAIN_ID> --l2-chain-ids <YOUR_L2_CHAIN_ID> --workdir .deployer --intent-type custom

    Edit intent.toml file

    configType = "custom" l1ChainID = 11155111 fundDevAccounts = false useInterop = false l1ContractsLocator = "tag://op-contracts/v3.0.0" l2ContractsLocator = "tag://op-contracts/v3.0.0" [superchainRoles] proxyAdminOwner = "ENTER_ADMIN_ADDRESS" protocolVersionsOwner = "ENTER_ADMIN_ADDRESS" guardian = "ENTER_ADMIN_ADDRESS" [[chains]] id = "ENTER_CHAIN_ID" baseFeeVaultRecipient = "ENTER_ADMIN_ADDRESS" l1FeeVaultRecipient = "ENTER_ADMIN_ADDRESS" sequencerFeeVaultRecipient = "ENTER_ADMIN_ADDRESS" eip1559DenominatorCanyon = 250 eip1559Denominator = 50 eip1559Elasticity = 6 operatorFeeScalar = 0 operatorFeeConstant = 0 [chains.roles] l1ProxyAdminOwner = "ENTER_ADMIN_ADDRESS" l2ProxyAdminOwner = "ENTER_ADMIN_ADDRESS" systemConfigOwner = "ENTER_ADMIN_ADDRESS" unsafeBlockSigner = "ENTER_SEQUENCER_ADDRESS" batcher = "ENTER_BATCHER_ADDRESS" proposer = "ENTER_PROPOSER_ADDRESS" challenger = "ENTER_ADMIN_ADDRESS" [chains.dangerousAltDAConfig] useAltDA = true daCommitmentType = "GenericCommitment" daChallengeWindow = 160 daResolveWindow = 160 daBondSize = 1000000 daResolverRefundPercentage = 0
  3. Deploy contracts:

    ## L1 contract deployment ./bin/op-deployer apply --workdir .deployer \ --l1-rpc-url <RPC_URL_FOR_L1> \ --private-key <DEPLOYER_PRIVATE_KEY_HEX>
  4. Generate genesis.json, rollup.json and l1-addresses.json:

    ./bin/op-deployer inspect genesis --workdir .deployer <L2_CHAIN_ID> > .deployer/genesis.json ./bin/op-deployer inspect rollup --workdir .deployer <L2_CHAIN_ID> > .deployer/rollup.json ./bin/op-deployer inspect l1 --workdir .deployer <L2_CHAIN_ID> > .deployer/l1-addresses.json

Build and Run the op-stack components

op-geth

  1. Clone op-geth:

    git clone https://github.com/ethereum-optimism/op-geth.git cd op-geth git checkout v1.101511.0 make geth
  2. Move the genesis.json and jwt.txt files into its directory:

    cp genesis.json ~/op-geth cp jwt.txt ~/op-geth
  3. Create and configure data directory

    mkdir datadir ./build/bin/geth init --state.scheme=hash --datadir=datadir genesis.json
  4. Run op-geth

    # Set environment variables export L2_CHAIN_ID= ## Run op-geth ./build/bin/geth \ --datadir ./datadir \ --http \ --http.corsdomain="*" \ --http.vhosts="*" \ --http.addr=0.0.0.0 \ --http.port=8545 \ --http.api=web3,debug,eth,txpool,net,engine \ --ws \ --ws.addr=0.0.0.0 \ --ws.port=8546 \ --ws.origins="*" \ --ws.api=debug,eth,txpool,net,engine \ --syncmode=full \ --gcmode=archive \ --nodiscover \ --maxpeers=0 \ --networkid=$L2_CHAIN_ID \ --authrpc.vhosts="*" \ --authrpc.addr=0.0.0.0 \ --authrpc.port=8551 \ --authrpc.jwtsecret=./jwt.txt \ --rollup.disabletxpoolgossip=true \ --state.scheme=hash

    op-geth is now active, but block creation will begin once op-node is operational.

op-node

  1. Build op-node from optimism

    cd op-node git checkout op-node/v1.13.3 make op-node
  2. Move the rollup.json and jwt.txt files into its directory:

    cp rollup.json ~/op-node cp jwt.txt ~/op-node
  3. Run op-node

    # Set env variable from `.envrc` direnv allow . ## Run op-node ./bin/op-node \ --l2=http://localhost:8551 \ --l2.jwt-secret=./jwt.txt \ --sequencer.enabled \ --sequencer.l1-confs=5 \ --verifier.l1-confs=4 \ --rollup.config=./rollup.json \ --rpc.addr=0.0.0.0 \ --rpc.port=8547 \ --p2p.disable \ --rpc.enable-admin \ --p2p.sequencer.key=$GS_SEQUENCER_PRIVATE_KEY \ --l1=$L1_RPC_URL \ --l1.rpckind=$L1_RPC_KIND \ --altda.enabled=true \ --altda.da-server=$DA_SERVER_HTTP_URL \ --altda.da-service=true \ --l1.beacon.ignore=true

    Block creation will commence once op-node starts processing L1 information and interfaces with op-geth.

    To optimize synchronization and avoid network resource waste:

    • Disable p2p sync (--p2p.disable) by default.
    • Use specific command line parameters for synchronization among multiple nodes.

op-batcher

  1. Build op-batcher from optimism

    cd op-batcher git checkout op-batcher/v1.13.2 make op-batcher
  2. Run op-batcher

    # Set env variable from `.envrc` direnv allow . ## Run op-batcher ./bin/op-batcher \ --l2-eth-rpc=http://localhost:8545 \ --rollup-rpc=http://localhost:8547 \ --poll-interval=1s \ --sub-safety-margin=6 \ --num-confirmations=1 \ --safe-abort-nonce-too-low-count=3 \ --resubmission-timeout=30s \ --rpc.addr=0.0.0.0 \ --rpc.port=8548 \ --rpc.enable-admin \ --max-channel-duration=25 \ --l1-eth-rpc=$L1_RPC_URL \ --private-key=$GS_BATCHER_PRIVATE_KEY \ --altda.enabled=true \ --altda.da-service=true \ --altda.da-server=$DA_SERVER_HTTP_URL \ --throttle-threshold=0

    Adjust the --max-channel-duration=n setting to balance transaction frequency on L1 and the operational costs of the batcher. Recommended is a minumum of 2 since avail block time is 20s and ethereum’s 12sec.

op-proposer

  1. Build op-proposer from optimism

    cd op-proposer git checkout op-proposer/v1.10.0 make op-proposer
  2. Run op-proposer

    # Set env variable from `.envrc` direnv allow . ## Run op-proposer ./bin/op-proposer \ --poll-interval=10s \ --rpc.port=8560 \ --rollup-rpc=http://localhost:8547 \ --game-factory-address=$GAME_FACTORY_ADDRESS \ --game-type 1 \ --private-key=$GS_PROPOSER_PRIVATE_KEY \ --l1-eth-rpc=$L1_RPC_URL \ --allow-non-finalized \ --num-confirmations=1 \ --proposal-interval=10s \ --wait-node-sync=true \ --log.level=debug

Appendix

Acquire Sepolia ETH for Layer 2

To obtain ETH on your Rollup:

  1. Go to contracts-bedrock:

    cd ~/optimism/packages/contracts-bedrock
  2. Find the L1 standard bridge contract address:

    cat deployments/avail-optimism/L1StandardBridgeProxy.json | jq -r .address
  3. Send Sepolia ETH to the bridge contract address.

Why Archive Mode?

Archive mode on op-geth, requiring more disk space than full mode, is essential for:

  1. op-proposer to access the full state history.
  2. The explorer’s functionality.

Reinitializing op-geth

In cases of database corruption indicated by op-node errors or failure to find L2 heads, follow these steps:

  1. Stop op-geth.
  2. Remove the existing data:
    cd ~/op-geth rm -rf datadir/geth
  3. Reinitialize:
    build/bin/geth init --datadir=datadir genesis.json
  4. Restart op-geth and then op-node.

See your rollup in action

You now have a fully operational Avail-Powered Optimism-based EVM Rollup. You can interact with your Rollup the same way you’d interact with any other EVM chain. Send some transactions, deploy some contracts, and see what happens!

Last updated on