Now that you have a running OP Stack rollup with fault proofs enabled, it’s critical to monitor your dispute games to ensure your chain operates securely. This tutorial will guide you through setting up op-dispute-mon to track all dispute game activity on your rollup.
This tutorial continues from the previous steps in Creating Your Own L2 Rollup. Make sure you have completed the op-challenger setup and have dispute games running before proceeding.
What you’ll set up
By the end of this tutorial, you’ll have:
- op-dispute-mon monitoring all dispute games on your chain
- Real-time tracking of game status, agreements, and honest actor participation
- Understanding of key metrics for dispute game health
Prerequisites
Before you begin, ensure you have:
- Completed all previous steps in the Creating Your Own L2 Rollup tutorial
- op-challenger running and participating in dispute games
- op-proposer running and creating proposals
- L1 RPC endpoint with sufficient rate limits
- Rollup RPC endpoint (op-node)
- Docker installed (for Docker setup) OR Go 1.21+ (for build from source)
What is op-dispute-mon?
op-dispute-mon is a specialized monitoring tool that tracks dispute games in the fault proof system. It provides visibility into:
- Game status: Track games from creation to resolution
- Honest actor participation: Monitor your proposer and challenger activity
- Agreement tracking: See which games agree/disagree with your honest actors
- Game completion: Monitor in-progress vs completed games
- Ignored games: Track games that don’t meet monitoring criteria
The monitor exposes Prometheus metrics that can be used to build dashboards and alerts.
Use Docker (Recommended)
Build from Source
Create dispute-mon directory
# From your project root
cd rollup
mkdir dispute-mon
cd dispute-mon
Get required addresses
# Get DisputeGameFactory address from deployment
export GAME_FACTORY_ADDRESS=$(jq -r '.opChainDeployments[0].DisputeGameFactoryProxy' ../deployer/.deployer/state.json)
echo "Game Factory Address: $GAME_FACTORY_ADDRESS"
# Get Proposer address
export PROPOSER_ADDRESS=$(jq -r '.appliedIntent.chains[0].roles.proposer' ../deployer/.deployer/state.json)
echo "Proposer Address: $PROPOSER_ADDRESS"
# Get Challenger address
export CHALLENGER_ADDRESS=$(jq -r '.appliedIntent.chains[0].roles.challenger' ../deployer/.deployer/state.json)
echo "Challenger Address: $CHALLENGER_ADDRESS"
Create environment file
Create a .env file with the addresses:cat > .env << EOF
# Contract Addresses
GAME_FACTORY_ADDRESS=$GAME_FACTORY_ADDRESS
# Honest Actors
PROPOSER_ADDRESS=$PROPOSER_ADDRESS
CHALLENGER_ADDRESS=$CHALLENGER_ADDRESS
# L1 RPC (replace with your actual RPC)
L1_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY
EOF
Create docker-compose.yml
version: "3.8"
services:
op-dispute-mon:
image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-dispute-mon:v1.4.2-rc.1
container_name: dispute-mon
ports:
- "7300:7300" # Metrics port
env_file:
- .env
environment:
# L1 RPC Configuration
- OP_DISPUTE_MON_L1_ETH_RPC=${L1_RPC_URL}
# Rollup RPC Configuration (your op-node)
- OP_DISPUTE_MON_ROLLUP_RPC=http://op-node:8547
# Honest Actors (your proposer and challenger addresses)
- OP_DISPUTE_MON_HONEST_ACTORS=${PROPOSER_ADDRESS},${CHALLENGER_ADDRESS}
# DisputeGameFactory Contract Address
- OP_DISPUTE_MON_GAME_FACTORY_ADDRESS=${GAME_FACTORY_ADDRESS}
# Monitoring Configuration
- OP_DISPUTE_MON_MONITOR_INTERVAL=10s
- OP_DISPUTE_MON_NETWORK=op-sepolia
# Logging Configuration
- OP_DISPUTE_MON_LOG_FORMAT=logfmt
- OP_DISPUTE_MON_LOG_LEVEL=debug
# Metrics Configuration
- OP_DISPUTE_MON_METRICS_ADDR=0.0.0.0
- OP_DISPUTE_MON_METRICS_ENABLED=true
- OP_DISPUTE_MON_METRICS_PORT=7300
restart: unless-stopped
networks:
- dispute-mon-network
networks:
dispute-mon-network:
driver: bridge
Replace YOUR_INFURA_KEY with your actual Infura API key or use another L1 RPC provider. Make sure your L1 RPC has sufficient rate limits.
Start op-dispute-mon
# Start the container (variables loaded from .env)
docker-compose up -d
# View logs
docker-compose logs -f op-dispute-mon
Verify it's running
# Query metrics endpoint
curl http://localhost:7300/metrics
# View recent logs
docker-compose logs --tail=50 op-dispute-mon
You should see logs indicating the monitor is tracking games:t=2025-10-21T05:58:52+0100 lvl=info msg="Starting op-dispute-mon"
t=2025-10-21T05:58:52+0100 lvl=info msg="started metrics server" addr=[::]:7300
t=2025-10-21T05:59:03+0100 lvl=info msg="Completed monitoring update" games=0 ignored=0 failed=0
Understanding key metrics
Once op-dispute-mon is running, it exposes several important metrics that help you monitor dispute game health.
Core metrics
Monitor health:
# Monitor is running (1 = up, 0 = down)
op_dispute_mon_up 1
Game agreement metrics:
The primary metric for tracking dispute games, with labels for completion status, result correctness, root agreement, and game status:
# Games by completion and status
op_dispute_mon_games_agreement{completion="complete",result_correctness="correct",root_agreement="agree",status="agree_defender_wins"} 0
op_dispute_mon_games_agreement{completion="in_progress",result_correctness="correct",root_agreement="agree",status="agree_defender_ahead"} 0
op_dispute_mon_games_agreement{completion="complete",result_correctness="incorrect",status="challenger_wins"} 0
Label meanings:
completion: "complete" or "in_progress"
result_correctness: "correct" or "incorrect" (based on your honest actors)
root_agreement: "agree" or "disagree" (whether game agrees with honest actors)
status: Game outcome like "agree_defender_wins", "challenger_wins", etc.
What to monitor:
- Any non-zero
root_agreement="disagree" requires immediate investigation
result_correctness="incorrect" games should resolve to "challenger_wins"
- For new testnets, all values being
0 is normal until games are created
Ignored games:
# Games not meeting monitoring criteria
op_dispute_mon_ignored_games 0
Games may be ignored if they don’t involve your honest actors or fall outside the monitoring window.
Honest actor bonds:
# Bond status for your proposer and challenger
op_dispute_mon_honest_actor_bonds{honest_actor_address="0xYourProposer",state="won"} 0
op_dispute_mon_honest_actor_bonds{honest_actor_address="0xYourProposer",state="lost"} 0
op_dispute_mon_honest_actor_bonds{honest_actor_address="0xYourProposer",state="pending"} 0
Tracks bonds posted by your honest actors and their outcomes (won, lost, or pending).
Honest actor claims:
# Claims made by your honest actors
op_dispute_mon_honest_actor_claims{honest_actor_address="0xYourProposer",state="valid"} 0
op_dispute_mon_honest_actor_claims{honest_actor_address="0xYourProposer",state="invalid"} 0
Monitors whether your honest actors have sufficient collateral for bonds.
For a new testnet, it’s normal for all game metrics to show 0 values. Games will only appear after your proposer creates proposals (typically every ~1 hour).
Next steps
Now that you have dispute game monitoring set up, you can:
Congratulations! You now have a fully operational OP Stack L2 rollup with comprehensive monitoring.