Operators
Spin up your proposer

Spin up your proposer

After you have spun up your sequencer, you need to attach a proposer to post your L2 state roots data back onto L1 so we can prove withdrawal validity. The proposer is a critical component that enables trustless L2-to-L1 messaging and creates the authoritative view of L2 state from L1's perspective.

This guide assumes you already have a functioning sequencer and the necessary L1 contracts deployed using op-deployer. If you haven't set up your sequencer yet, please refer to the [sequencer guide](Todo - Add this link when it's live) first.

Understanding the proposer's role

The proposer (op-proposer) serves as a crucial bridge between your L2 chain and L1. Its primary responsibilities include:

  • State commitment: Proposing L2 state roots to L1 at regular intervals
  • Withdrawal enablement: Providing the necessary commitments for users to prove and finalize withdrawals

The proposer creates dispute games via the DisputeGameFactory contract, enabling permissionless challenging of proposed state roots.

Prerequisites

Before setting up your proposer, ensure you have:

Running infrastructure:

  • A synced and operational sequencer node
  • Access to a L1 RPC endpoint
  • The rollup node (op-node) running and accessible

From op-deployer deployment:

  • Successfully deployed L1 contracts using op-deployer apply
  • Access to your deployment output (genesis and rollup) files.

Security setup:

  • A dedicated proposer private key

Network information:

  • Your L2 chain ID and network configuration
  • L1 network details (chain ID, RPC endpoints)

Software installation

Build from source

Clone and build op-proposer

# If you don't already have the optimism repository from the sequencer setup
git clone https://github.com/ethereum-optimism/optimism.git
cd optimism
 
# Checkout the latest release tag
git checkout op-proposer/v1.10.0
 
# Build op-proposer
cd op-proposer
make op-proposer
 
# Binary will be available at ./bin/op-proposer

Verify installation

./bin/op-proposer --version

Configuration setup

1. Organize your workspace

Create your proposer working directory:

# Create proposer directory at the same level as your sequencer
mkdir proposer-node
cd proposer-node
 
# Create scripts directory
mkdir scripts

2. Extract contract addresses

Extract the necessary contract addresses from your op-deployer output:

# Extract L1 contract addresses from your op-deployer deployment
cd proposer-node
 
# Copy the L1 contracts file from op-deployer, update to paths to wherever your `.deployer/state.json` live
cp ../.deployer/state.json .
 
# Extract the DisputeGameFactory address
GAME_FACTORY_ADDRESS=$(cat state.json | jq -r '.opChainDeployments[0].disputeGameFactoryProxyAddress')
echo "DisputeGameFactory Address: $GAME_FACTORY_ADDRESS"

3. Set up environment variables

Create your .env file with the actual values:

# Create .env file with your actual values
# L1 Configuration - Replace with your actual RPC URL
L1_RPC_URL=https://sepolia.infura.io/v3/YOUR_ACTUAL_INFURA_KEY
 
# L2 Configuration - Should match your sequencer setup
L2_RPC_URL=http://localhost:8545
ROLLUP_RPC_URL=http://localhost:8547
 
# Contract addresses - Extract from your op-deployer output
GAME_FACTORY_ADDRESS=YOUR_ACTUAL_GAME_FACTORY_ADDRESS
 
# Private key - Replace with your actual proposer private key
PROPOSER_PRIVATE_KEY=0xYOUR_ACTUAL_PROPOSER_PRIVATE_KEY
 
# Proposer configuration
PROPOSAL_INTERVAL=3600s
GAME_TYPE=0
POLL_INTERVAL=20s
 
# RPC configuration
PROPOSER_RPC_PORT=8560

Important: Replace ALL placeholder values (YOUR_ACTUAL_*) with your real configuration values!

4. Get your proposer private key

Extract the proposer private key from your op-deployer intent file:

# View your intent file to find the proposer private key
cat ~/.deployer/intent.toml
 
# Look for a section like:
# [chains.my-chain.roles.proposer]
# privateKey = "0x..."

Proposer configuration

Create scripts/start-proposer.sh:

#!/bin/bash
 
source .env
 
# Path to the op-proposer binary we built
../optimism/op-proposer/bin/op-proposer \
  --poll-interval=$POLL_INTERVAL \
  --rpc.port=$PROPOSER_RPC_PORT \
  --rpc.enable-admin \
  --rollup-rpc=$ROLLUP_RPC_URL \
  --l1-eth-rpc=$L1_RPC_URL \
  --private-key=$PROPOSER_PRIVATE_KEY \
  --game-factory-address=$GAME_FACTORY_ADDRESS \
  --game-type=$GAME_TYPE \
  --proposal-interval=$PROPOSAL_INTERVAL \
  --num-confirmations=1 \
  --resubmission-timeout=30s \
  --wait-node-sync=true \
  --log.level=info

Starting the proposer

1. Verify prerequisites

Ensure your sequencer and op-node are running:

# Test L1 connectivity
curl -X POST -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
  $L1_RPC_URL
 
# Test L2 connectivity  
curl -X POST -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
  $L2_RPC_URL
 
# Test rollup node connectivity
curl -X POST -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"optimism_syncStatus","params":[],"id":1}' \
  $ROLLUP_RPC_URL

2. Start the proposer

# Make the script executable
chmod +x scripts/start-proposer.sh
 
# Start the proposer
./scripts/start-proposer.sh

Verification

Verify your proposer is working correctly:

Check proposer status

# Verify proposer is responsive
curl -X POST -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"optimism_version","params":[],"id":1}' \
  http://localhost:8560

Monitor dispute games on L1

# Check latest dispute game count (requires cast from foundry)
cast call $GAME_FACTORY_ADDRESS "gameCount()" --rpc-url $L1_RPC_URL

Monitor your proposer's ETH balance and gas usage to ensure continuous operation. Your proposer is now operational!

Next steps