Learn how to set up and configure an OP Stack proposer to post L2 state roots.
After you have spun up your sequencer and batcher, 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.
Step 4 of 5: This tutorial is designed to be followed step-by-step. Each step builds on the previous one.
Automated Setup AvailableFor a complete working setup with all components, check out the automated approach in the code directory.
This guide assumes you already have a functioning sequencer, batcher, and the necessary L1 contracts deployed using op-deployer. If you haven’t set up your sequencer and batcher yet, please refer to the sequencer guide and batcher guide first.To see configuration info for the proposer, check out the configuration page.
Before setting up your proposer, ensure you have:Running infrastructure:
An operational sequencer node
Access to a L1 RPC endpoint
Network information:
Your L2 chain ID and network configuration
L1 network details (chain ID, RPC endpoints)
For setting up the proposer, we recommend using Docker as it provides a consistent and isolated environment. Building from source is also available as an option.
Use docker
Build from source
If you prefer containerized deployment, you can use the official Docker images and do the following:
1
Set up directory structure and copy configuration files
Report incorrect code
Copy
Ask AI
# Create a proposer directory inside rollupcd ../ # Go back to rollup directory if you're in batchermkdir proposercd proposer# inside the proposer directory, copy the state.json file from the op-deployer setup# Copy configuration files from deployercp ../deployer/.deployer/state.json .# Extract the DisputeGameFactory addressGAME_FACTORY_ADDRESS=$(cat state.json | jq -r '.opChainDeployments[0].disputeGameFactoryProxyAddress')echo "DisputeGameFactory Address: $GAME_FACTORY_ADDRESS"
2
Create environment variables file
OP Stack Standard VariablesThe proposer uses OP Stack standard environment variables following the OP Stack conventions. These are prefixed with OP_PROPOSER_ for proposer-specific settings.
Report incorrect code
Copy
Ask AI
# Create .env file with your actual valuescat > .env << 'EOF'# L1 Configuration - Replace with your actual RPC URLsOP_PROPOSER_L1_RPC_URL=https://sepolia.infura.io/v3/YOUR_ACTUAL_INFURA_KEY# L2 Configuration - Should match your sequencer setupOP_PROPOSER_ROLLUP_RPC=http://op-node:8547# Contract addresses - Extract from your op-deployer outputOP_PROPOSER_GAME_FACTORY_ADDRESS=YOUR_ACTUAL_GAME_FACTORY_ADDRESS# Private key - Replace with your actual private keyOP_PROPOSER_PRIVATE_KEY=YOUR_ACTUAL_PRIVATE_KEY# OP Stack proposer configuration (optional - defaults provided)OP_PROPOSER_PROPOSAL_INTERVAL=3600sOP_PROPOSER_GAME_TYPE=0OP_PROPOSER_POLL_INTERVAL=20sOP_PROPOSER_ALLOW_NON_FINALIZED=trueOP_PROPOSER_WAIT_NODE_SYNC=trueEOF
Important: Replace ALL placeholder values (YOUR_ACTUAL_*) with your real configuration values.
3
Create docker-compose.yml
If you get “failed to dial address” errors, ensure your proposer is in the same Docker network as your sequencer.Common fixes:
Add networks: - sequencer-node_default to your proposer’s docker-compose.yml
Use service names like op-geth:8545 and op-node:8547 in your .env file
Verify your sequencer network name with docker network ls
To ensure you’re using the latest compatible versions of OP Stack components, always check the official releases page.Look for the latest op-proposer/v* release that’s compatible with your sequencer setup.
This guide uses op-proposer/v1.10.0 which is compatible with op-node/v1.13.3 and op-geth/v1.101511.1 from the sequencer setup.
Always check the release notes for compatibility information.
Building from source gives you full control over the binaries.
1
Clone and build op-proposer
Report incorrect code
Copy
Ask AI
# If you don't already have the optimism repository from the sequencer setupgit clone https://github.com/ethereum-optimism/optimism.gitcd optimism# Checkout the latest release taggit checkout op-proposer/v1.10.0# Build op-proposercd op-proposerjust# Binary will be available at ./bin/op-proposer
The rest of this guide assumes you’re using the build-from-source approach.
If you chose Docker, all the necessary configuration was covered in the Docker tab above.
1
Organize your workspace
Create your proposer working directory at the same level as your sequencer:
Report incorrect code
Copy
Ask AI
# Create proposer directory inside rollupcd ../ # Go back to rollup directorymkdir proposercd proposer# Create scripts directorymkdir scripts
2
Extract DisputeGameFactory address
Extract the DisputeGameFactory contract address from your op-deployer output:
Report incorrect code
Copy
Ask AI
# Make sure you're in the rollup/proposer directorycd rollup/proposer# Copy the state.json from deployercp ../deployer/.deployer/state.json .# Extract the DisputeGameFactory addressGAME_FACTORY_ADDRESS=$(cat state.json | jq -r '.opChainDeployments[0].disputeGameFactoryProxyAddress')echo "DisputeGameFactory Address: $GAME_FACTORY_ADDRESS"
The proposer only needs the DisputeGameFactory address to submit proposals.
The GAME_TYPE=0 represents the standard fault proof game type.
3
Set up environment variables
Create your .env file with the actual values:
Report incorrect code
Copy
Ask AI
# Create .env file with your actual values# L1 Configuration - Replace with your actual RPC URLL1_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 outputGAME_FACTORY_ADDRESS=YOUR_ACTUAL_GAME_FACTORY_ADDRESS# Private key - Replace with your actual private keyPRIVATE_KEY=YOUR_ACTUAL_PRIVATE_KEY# Proposer configurationPROPOSAL_INTERVAL=3600sGAME_TYPE=0POLL_INTERVAL=20s# RPC configurationPROPOSER_RPC_PORT=8560
Important: Replace ALL placeholder values (YOUR_ACTUAL_*) with your real configuration values!
4
Get your private key
Get a private key from your wallet that will be used for submitting proposals to L1. This account needs sufficient ETH to pay for L1 gas costs.
The proposer account needs to be funded with ETH on L1 to pay for proposal submission transactions. Monitor this account’s balance regularly as it will consume ETH for each proposal submission.