This guide covers how to run and test the Set Chain L2 locally using Anvil (Foundry's local Ethereum node).
- Docker installed and running, or a valid local Foundry install
- curl and jq for command-line interactions
./scripts/dev.sh and ./scripts/start-local-anvil.sh auto-detect a usable
Foundry backend. They prefer a real local forge/cast/anvil install, and
fall back to the official Docker image when the local binary is missing or is
not actually Foundry (for example, Electron Forge on PATH).
Set FOUNDRY_USE_DOCKER=1 to force the Docker backend. Set
FOUNDRY_DOCKER_IMAGE if you want to pin a specific GHCR tag or digest.
Pinned tool versions for reproducible builds live in docs/toolchain.md.
# 1. Start the local node
./scripts/dev.sh start
# 2. In another terminal, deploy contracts
./scripts/dev.sh deploy
# 3. (Optional) Validate config
./scripts/dev.sh validate
# 4. Check status
./scripts/dev.sh statusStart Anvil with Set Chain configuration:
./scripts/dev.sh start
# Or directly:
./scripts/start-local-anvil.shThe script reads chain parameters from config/chain-config.toml to keep the
local devnet in sync with the repository defaults.
This starts Anvil with:
- Chain ID: 84532001
- Block Time: 2 seconds
- Gas Limit: 30M per block
- RPC URL: http://localhost:8545
- 10 pre-funded accounts with 10,000 ETH each
With Anvil running, deploy the contracts:
./scripts/dev.sh deployThis deploys:
- SetRegistry - Merkle root anchoring for commerce events
- SetPaymaster - Gas sponsorship for merchants
| Contract | Address |
|---|---|
| SetRegistry (proxy) | 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 |
| SetRegistry (impl) | 0x5FbDB2315678afecb367f032d93F642f64180aa3 |
| SetPaymaster (proxy) | 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 |
| SetPaymaster (impl) | 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 |
These addresses are deterministic on a fresh Anvil instance; redeploying will produce new addresses.
Anvil provides pre-funded accounts for testing:
| Role | Address | Private Key |
|---|---|---|
| Admin/Deployer | 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 |
0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 |
| Sequencer | 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 |
0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d |
| Batcher | 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC |
0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a |
| Proposer | 0x90F79bf6EB2c4f870365E785982E1f101E93b906 |
0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6 |
| User 5 | 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 |
0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a |
View all accounts:
./scripts/dev.sh accountsThe dev.sh script provides convenient commands:
./scripts/dev.sh start # Start Anvil node
./scripts/dev.sh deploy # Deploy all contracts
./scripts/dev.sh test # Run Foundry tests
./scripts/dev.sh status # Check node status
./scripts/dev.sh validate # Validate config vs live node
./scripts/dev.sh smoke # Deploy + commit batch + verify multiproof
./scripts/dev.sh anchor-start # Run anchor service with mock sequencer
./scripts/dev.sh anchor-smoke # Anchor service smoke test
./scripts/dev.sh reset # Reset devnet and restart Anvil
./scripts/dev.sh accounts # Show test accounts
./scripts/dev.sh fund <addr> # Send 100 ETH to address
./scripts/dev.sh console # Open cast shellSmoke overrides (optional):
EVENT_LEAF_0=0x... EVENT_LEAF_1=0x... TENANT_ID=0x... STORE_ID=0x... \
NEW_STATE_ROOT=0x... ./scripts/dev.sh smokeCheck chain ID:
curl -s http://localhost:8545 -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' | jqGet block number:
curl -s http://localhost:8545 -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' | jqRead SetRegistry owner:
docker run --rm --entrypoint cast --network=host ghcr.io/foundry-rs/foundry:nightly \
call 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 "owner()" \
--rpc-url http://localhost:8545Check if sequencer is authorized:
docker run --rm --entrypoint cast --network=host ghcr.io/foundry-rs/foundry:nightly \
call 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 \
"authorizedSequencers(address)" 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \
--rpc-url http://localhost:8545# Using the sequencer's private key
docker run --rm --entrypoint cast --network=host ghcr.io/foundry-rs/foundry:nightly \
send 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 \
"commitBatch(bytes32,bytes32,bytes32,bytes32,bytes32,bytes32,uint64,uint64,uint32)" \
0x0000000000000000000000000000000000000000000000000000000000000001 \
0x0000000000000000000000000000000000000000000000000000000000000000 \
0x0000000000000000000000000000000000000000000000000000000000000000 \
0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef \
0x0000000000000000000000000000000000000000000000000000000000000000 \
0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890 \
1 100 100 \
--private-key 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d \
--rpc-url http://localhost:8545Run the Foundry test suite:
./scripts/dev.sh test
# Or with specific options
./scripts/dev.sh test --match-test testCommitBatch
./scripts/dev.sh test -vvvv # Extra verbosityRun tests directly via Docker:
docker run --rm --entrypoint forge -v $(pwd)/contracts:/app -w /app \
ghcr.io/foundry-rs/foundry:nightly test -vvvSDK tests require Node 20+. The SDK Vitest wrapper will use a local Node 20+
binary when available, or fall back to Docker if docker is installed.
To wipe local artifacts and restart Anvil cleanly:
./scripts/dev.sh resetTo skip the confirmation prompt:
./scripts/dev.sh reset --forceTo reset without restarting:
./scripts/reset-devnet.sh --no-startFor local devnet, start from config/local.env.example:
cp config/local.env.example config/local.env
# Then source it when needed:
source config/local.envCreate a .env file for custom configuration (optional):
# .env
RPC_URL=http://localhost:8545
DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
SEQUENCER_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8
TREASURY_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266To connect the anchor service to the local node:
# Run anchor service against local devnet (includes mock sequencer)
./scripts/dev.sh anchor-start
# Smoke test the anchor service
./scripts/dev.sh anchor-smokeEnvironment variables are loaded from config/local.env when present. The mock
sequencer listens on http://localhost:3001 by default.
anchor-smoke starts a mock sequencer, runs the anchor service, and waits for
the on-chain commitment to be recorded.
The mock sequencer requires python3 (or python) to be available locally.
# Check if Anvil is running
curl -s http://localhost:8545 -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
# Restart the node
./scripts/dev.sh start# Ensure output directories exist with correct permissions
mkdir -p contracts/out contracts/cache contracts/broadcast
chmod 777 contracts/out contracts/cache contracts/broadcastFoundry binaries require newer GLIBC. Use Docker-based commands instead:
# Instead of: forge build
docker run --rm --entrypoint forge -v $(pwd)/contracts:/app -w /app \
ghcr.io/foundry-rs/foundry:nightly build
# Instead of: cast call ...
docker run --rm --entrypoint cast --network=host ghcr.io/foundry-rs/foundry:nightly \
call ...If forge --version resolves to Electron Forge or another non-Foundry tool,
the repo wrappers will ignore it and use Docker automatically. To force that
behavior explicitly:
FOUNDRY_USE_DOCKER=1 ./scripts/dev.sh testRestart Anvil to reset all blockchain state:
# Reset local artifacts and restart the node
./scripts/dev.sh reset --force┌─────────────────────────────────────────────────────────────┐
│ Local Development │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────────────────────┐ │
│ │ Anvil │ │ Deployed Contracts │ │
│ │ (Local L2) │────▶│ - SetRegistry (proxy) │ │
│ │ │ │ - SetPaymaster (proxy) │ │
│ │ Chain: 84532001 │ │ │
│ │ RPC: :8545 │ └──────────────────────────────┘ │
│ └──────────────┘ │
│ ▲ │
│ │ │
│ ┌──────┴───────┐ │
│ │ dev.sh │ │
│ │ Helper │ │
│ │ Scripts │ │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
- Run the test suite to verify everything works
- Connect the anchor service to submit batch commitments
- Integrate with stateset-sequencer for end-to-end testing
- Deploy to Base Sepolia when ready for testnet