Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 51 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ To install the latest release version:
forge install succinctlabs/sp1-contracts
```

Add `@sp1-contracts/=lib/sp1-contracts/contracts/src/` in `remappings.txt.`
Add `@sp1-contracts/=lib/sp1-contracts/contracts/src/` in `remappings.txt`.

### Usage

Expand All @@ -22,7 +22,8 @@ pragma solidity ^0.8.20;
import {ISP1Verifier} from "@sp1-contracts/ISP1Verifier.sol";

contract MyContract {
address public constant SP1_VERIFIER = 0x3B6041173B80E77f038f3F2C0f9744f04837185e;
/// @dev Use the gateway address for your chain from the deployments/ folder
address public SP1_VERIFIER;

bytes32 public constant PROGRAM_VKEY = ...;

Expand All @@ -32,9 +33,9 @@ contract MyContract {
}
```

You can obtain the correct `SP1_VERIFIER` address for your chain by looking in the [deployments](./contracts/deployments) directory, it's recommended to use the `SP1_VERIFIER_GATEWAY` address which will automatically route proofs to the correct verifier based on their version.
You can obtain the correct `SP1_VERIFIER` address for your chain by looking in the [deployments](./contracts/deployments) directory. Use the `SP1_VERIFIER_GATEWAY` address which automatically routes proofs to the correct verifier based on their version.

You can obtain the correct `PROGRAM_VKEY` for your program calling the `setup` function for your ELF:
You can obtain the correct `PROGRAM_VKEY` for your program by calling the `setup` function for your ELF:

```rs
let client = ProverClient::new();
Expand All @@ -44,49 +45,77 @@ You can obtain the correct `PROGRAM_VKEY` for your program calling the `setup` f

### Deployments

To deploy the contracts, ensure your [.env](./contracts/.env.example) file is configured with all the chains you want to deploy to.

Then you can use the `forge script` command and specify the specific contract you want to deploy. For example, to deploy the SP1 Verifier Gateway for PLONK you can run:
To deploy contracts, configure your [.env](./contracts/.env.example) file:

```bash
FOUNDRY_PROFILE=deploy forge script ./script/deploy/SP1VerifierGatewayPlonk.s.sol:SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast
cd sp1-contracts/contracts

# Create .env with your values
PRIVATE_KEY=0x...
RPC_MAINNET=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY # Use Alchemy/Infura, not public RPCs
ETHERSCAN_API_KEY=...
```

or to deploy the SP1 Verifier Gateway for Groth16 you can run:
> **Important**:
> - Run `source .env` before any forge commands (the scripts read env vars directly)
> - The `--rpc-url` CLI flag is ignored; scripts read `RPC_<CHAIN>` from environment
> - Specify target chain with `CHAINS=` env var (e.g., `CHAINS=MAINNET`)

Deploy the SP1 Verifier Gateway:

```bash
FOUNDRY_PROFILE=deploy forge script ./script/deploy/SP1VerifierGatewayGroth16.s.sol:SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast
# Groth16 gateway
CHAINS=MAINNET FOUNDRY_PROFILE=deploy forge script \
./script/deploy/SP1VerifierGatewayGroth16.s.sol:SP1VerifierGatewayScript \
--private-key $PRIVATE_KEY --verify --verifier etherscan --broadcast

# Plonk gateway (use different CREATE2_SALT to avoid collision)
CHAINS=MAINNET FOUNDRY_PROFILE=deploy forge script \
./script/deploy/SP1VerifierGatewayPlonk.s.sol:SP1VerifierGatewayScript \
--private-key $PRIVATE_KEY --verify --verifier etherscan --broadcast
```

### Adding Verifiers

You can use the `forge script` command to specify which verifier you want to deploy and add to the gateway. For example to deploy the PLONK verifier and add it to the PLONK gateway you can run:
Deploy verifiers and optionally register them with the gateway:

```bash
FOUNDRY_PROFILE=deploy forge script ./script/deploy/v3.0.0/SP1VerifierPlonk.s.sol:SP1VerifierScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast
# Deploy Groth16 verifier
CHAINS=MAINNET FOUNDRY_PROFILE=deploy forge script \
./script/deploy/v6.0.0-beta.1/SP1VerifierGroth16.s.sol:SP1VerifierScript \
--private-key $PRIVATE_KEY --verify --verifier etherscan --broadcast

# Deploy Plonk verifier
CHAINS=MAINNET FOUNDRY_PROFILE=deploy forge script \
./script/deploy/v6.0.0-beta.1/SP1VerifierPlonk.s.sol:SP1VerifierScript \
--private-key $PRIVATE_KEY --verify --verifier etherscan --broadcast
```

or to deploy the Groth16 verifier and add it to the Groth16 gateway you can run:
By default, `REGISTER_ROUTE=false` so verifiers are deployed without registering routes. This is intended for multisig-owned gateways where route registration requires a separate multisig transaction.

To deploy AND register routes in one step (only works if deployer owns the gateway):

```bash
FOUNDRY_PROFILE=deploy forge script ./script/deploy/v3.0.0/SP1VerifierGroth16.s.sol:SP1VerifierScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast
REGISTER_ROUTE=true CHAINS=MAINNET FOUNDRY_PROFILE=deploy forge script ...
```

Change `v3.0.0` to the desired version to add.
For multisig gateways, use the Safe Transaction Builder JSON generator after deploying:

### Freezing Verifiers
```bash
node script/utils/generate-safe-batch.js --chain=1 --version=v6.0.0-beta.1
```

> [!WARNING]
> **BE CAREFUL** When a freezing a verifier. Once it is frozen, it cannot be unfrozen, and it can no longer be routed to.
### Freezing Verifiers

To freeze a verifier on the gateway, run:
> [!WARNING]
> Once frozen, a verifier cannot be unfrozen and can no longer be routed to.

```bash
FOUNDRY_PROFILE=deploy forge script ./script/deploy/v3.0.0/SP1VerifierPlonk.s.sol:SP1VerifierScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast --sig "freeze()"
CHAINS=MAINNET FOUNDRY_PROFILE=deploy forge script \
./script/deploy/v6.0.0-beta.1/SP1VerifierPlonk.s.sol:SP1VerifierScript \
--private-key $PRIVATE_KEY --broadcast --sig "freeze()"
```

Change `v3.0.0` to the desired version to freeze.

## For Developers: Integrate SP1 Contracts

This repository contains the EVM contracts for verifying SP1 PLONK EVM proofs.
Expand Down
41 changes: 11 additions & 30 deletions contracts/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ CHAINS=
### RPCs for each chain ID
RPC_MAINNET=
RPC_SEPOLIA=
RPC_HOLESKY=
RPC_ARBITRUM=
RPC_ARBITRUM_SEPOLIA=
RPC_BASE=
Expand All @@ -19,37 +18,19 @@ RPC_OPTIMISM=
RPC_OPTIMISM_SEPOLIA=
RPC_SCROLL=
RPC_SCROLL_SEPOLIA=
RPC_HOODI=
RPC_HYPEREVM=
RPC_BSC=
RPC_MONAD=
RPC_XLAYER=
RPC_MEGA=
RPC_PLASMA=
RPC_TEMPO=

# Etherscan API keys for each chain ID
ETHERSCAN_API_KEY_MAINNET=
ETHERSCAN_API_KEY_SEPOLIA=
ETHERSCAN_API_KEY_HOLESKY=
ETHERSCAN_API_KEY_ARBITRUM=
ETHERSCAN_API_KEY_ARBITRUM_SEPOLIA=
ETHERSCAN_API_KEY_BASE=
ETHERSCAN_API_KEY_BASE_SEPOLIA=
ETHERSCAN_API_KEY_OPTIMISM=
ETHERSCAN_API_KEY_OPTIMISM_SEPOLIA=
ETHERSCAN_API_KEY_SCROLL=
ETHERSCAN_API_KEY_SCROLL_SEPOLIA=
ETHERSCAN_API_KEY_HOODI=
ETHERSCAN_API_KEY_HYPEREVM=

# Etherscan API URLs for each chain ID
ETHERSCAN_API_URL_MAINNET=https://api.etherscan.io/api
ETHERSCAN_API_URL_HOLESKY=https://api-holesky.etherscan.io/api
ETHERSCAN_API_URL_SEPOLIA=https://api-sepolia.etherscan.io/api
ETHERSCAN_API_URL_ARBITRUM=https://api.arbiscan.io/api
ETHERSCAN_API_URL_ARBITRUM_SEPOLIA=https://api-sepolia.arbiscan.io/api
ETHERSCAN_API_URL_BASE=https://api.basescan.org/api
ETHERSCAN_API_URL_BASE_SEPOLIA=https://api-sepolia.basescan.org/api
ETHERSCAN_API_URL_OPTIMISM=https://api-optimistic.etherscan.io/api
ETHERSCAN_API_URL_OPTIMISM_SEPOLIA=https://api-sepolia-optimistic.etherscan.io/api
ETHERSCAN_API_URL_SCROLL=https://api.scrollscan.com/api
ETHERSCAN_API_URL_SCROLL_SEPOLIA=https://api-sepolia.scrollscan.com/api
ETHERSCAN_API_URL_HOODI=https://api.etherscan.io/v2/api?chainid=560048
ETHERSCAN_API_URL_HYPEREVM=https://api.etherscan.io/v2/api?chainid=999
# Etherscan V2 API key (single key works for 16/18 chains via etherscan.io)
ETHERSCAN_API_KEY=
# OKLink API key (for X Layer verification only, from oklink.com)
OKLINK_API_KEY=

## Contract Deployer Private Key
PRIVATE_KEY=
8 changes: 7 additions & 1 deletion contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ broadcast/
docs/

# Dotenv file
.env
.env

# macOS
.DS_Store

# Safe Transaction Builder generated files (keep committed for handoff)
# Regenerate with: node script/utils/generate-safe-batch.js
190 changes: 190 additions & 0 deletions contracts/check-routes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#!/usr/bin/env bash
# Check v6.0.0 route registration status on all 18 chains.
# Usage: ./check-routes.sh [path-to-.env]
#
# Reads gateway addresses from deployments/*.json and calls routes(bytes4)
# on each gateway to determine if the v6 verifiers are registered.

set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${1:-$SCRIPT_DIR/.env}"

if [[ ! -f "$ENV_FILE" ]]; then
echo "Error: .env file not found at $ENV_FILE"
echo "Usage: $0 [path-to-.env]"
exit 1
fi

# Source env for RPC URLs
set -a
source "$ENV_FILE"
set +a

# V6 verifier selectors (first 4 bytes of VERIFIER_HASH)
V6_GROTH16_SEL="0x0e78f4db"
V6_PLONK_SEL="0xbb1a6f29"

# Expected v6 verifier addresses (CREATE2-deterministic, same on all chains)
V6_GROTH16="0x99A74A05a0FaBEB217C1A329b0dac59a1FA52508"
V6_PLONK="0x8a0fd5e825D14368d90Fe68F31fceAe3E17AFc5C"

# Chain definitions: CHAIN_ID:NAME:RPC_VAR
CHAINS=(
# Testnets (run these first)
"11155111:Sepolia:RPC_SEPOLIA"
"421614:Arbitrum Sepolia:RPC_ARBITRUM_SEPOLIA"
"84532:Base Sepolia:RPC_BASE_SEPOLIA"
"11155420:OP Sepolia:RPC_OPTIMISM_SEPOLIA"
"534351:Scroll Sepolia:RPC_SCROLL_SEPOLIA"
"560048:Hoodi:RPC_HOODI"
# Mainnets
"1:Ethereum:RPC_MAINNET"
"10:Optimism:RPC_OPTIMISM"
"42161:Arbitrum:RPC_ARBITRUM"
"8453:Base:RPC_BASE"
"534352:Scroll:RPC_SCROLL"
"56:BSC:RPC_BSC"
"196:X Layer:RPC_XLAYER"
"143:Monad:RPC_MONAD"
"4326:MegaETH:RPC_MEGA"
"9745:Plasma:RPC_PLASMA"
"999:HyperEVM:RPC_HYPEREVM"
"4217:Tempo:RPC_TEMPO"
)

# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

DEPLOY_DIR="$SCRIPT_DIR/deployments"
TIMEOUT=15

# Counters
total=0
registered=0
not_registered=0
errors=0

printf "\n${BOLD}SP1 v6.0.0 Route Registration Status${NC}\n"
printf "Groth16 verifier: ${CYAN}%s${NC}\n" "$V6_GROTH16"
printf "Plonk verifier: ${CYAN}%s${NC}\n" "$V6_PLONK"
printf "Groth16 selector: ${CYAN}%s${NC} Plonk selector: ${CYAN}%s${NC}\n\n" "$V6_GROTH16_SEL" "$V6_PLONK_SEL"

printf "${BOLD}%-20s %-10s %-15s %-15s${NC}\n" "CHAIN" "ID" "GROTH16" "PLONK"
printf "%-20s %-10s %-15s %-15s\n" "--------------------" "----------" "---------------" "---------------"

check_route() {
local gateway="$1"
local selector="$2"
local rpc="$3"
local expected_verifier="$4"

local result
result=$(cast call "$gateway" "routes(bytes4)(address,bool)" "$selector" --rpc-url "$rpc" 2>&1) || {
echo "ERROR"
return
}

# cast returns two lines: address and bool
local addr
local frozen
addr=$(echo "$result" | head -1 | tr -d '[:space:]')
frozen=$(echo "$result" | tail -1 | tr -d '[:space:]')

# Zero address means not registered
if [[ "$addr" == "0x0000000000000000000000000000000000000000" ]]; then
echo "NOT_REGISTERED"
return
fi

# Check if correct verifier is registered
local addr_lower
local expected_lower
addr_lower=$(echo "$addr" | tr '[:upper:]' '[:lower:]')
expected_lower=$(echo "$expected_verifier" | tr '[:upper:]' '[:lower:]')

if [[ "$addr_lower" == "$expected_lower" ]]; then
if [[ "$frozen" == "true" ]]; then
echo "FROZEN"
else
echo "REGISTERED"
fi
else
echo "WRONG:${addr}"
fi
}

for chain_def in "${CHAINS[@]}"; do
IFS=':' read -r chain_id chain_name rpc_var <<< "$chain_def"

rpc_url="${!rpc_var:-}"
if [[ -z "$rpc_url" ]]; then
printf "${YELLOW}%-20s %-10s %-15s %-15s${NC}\n" "$chain_name" "$chain_id" "NO RPC" "NO RPC"
errors=$((errors + 2))
total=$((total + 2))
continue
fi

deploy_file="$DEPLOY_DIR/${chain_id}.json"
if [[ ! -f "$deploy_file" ]]; then
printf "${YELLOW}%-20s %-10s %-15s %-15s${NC}\n" "$chain_name" "$chain_id" "NO DEPLOY" "NO DEPLOY"
errors=$((errors + 2))
total=$((total + 2))
continue
fi

# Read gateway addresses from deployment JSON
gw_groth16=$(python3 -c "import json; print(json.load(open('$deploy_file'))['SP1_VERIFIER_GATEWAY_GROTH16'])")
gw_plonk=$(python3 -c "import json; print(json.load(open('$deploy_file'))['SP1_VERIFIER_GATEWAY_PLONK'])")

# Check Groth16 route
g16_status=$(check_route "$gw_groth16" "$V6_GROTH16_SEL" "$rpc_url" "$V6_GROTH16")
# Check Plonk route
plonk_status=$(check_route "$gw_plonk" "$V6_PLONK_SEL" "$rpc_url" "$V6_PLONK")

# Format output
format_status() {
local status="$1"
case "$status" in
REGISTERED) printf "${GREEN}%-15s${NC}" "REGISTERED" ;;
NOT_REGISTERED) printf "${RED}%-15s${NC}" "NOT REGISTERED" ;;
FROZEN) printf "${YELLOW}%-15s${NC}" "FROZEN" ;;
WRONG:*) printf "${RED}%-15s${NC}" "WRONG ADDR" ;;
ERROR) printf "${YELLOW}%-15s${NC}" "ERROR" ;;
esac
}

printf "%-20s %-10s " "$chain_name" "$chain_id"
format_status "$g16_status"
printf " "
format_status "$plonk_status"
printf "\n"

# Count
total=$((total + 2))
for s in "$g16_status" "$plonk_status"; do
case "$s" in
REGISTERED) registered=$((registered + 1)) ;;
NOT_REGISTERED) not_registered=$((not_registered + 1)) ;;
*) errors=$((errors + 1)) ;;
esac
done
done

printf "\n${BOLD}Summary${NC}\n"
printf "Total routes checked: %d\n" "$total"
printf "${GREEN}Registered: %d${NC}\n" "$registered"
printf "${RED}Not registered: %d${NC}\n" "$not_registered"
if [[ $errors -gt 0 ]]; then
printf "${YELLOW}Errors/Other: %d${NC}\n" "$errors"
fi

if [[ $not_registered -eq 0 && $errors -eq 0 ]]; then
printf "\n${GREEN}${BOLD}All v6.0.0 routes are registered.${NC}\n"
elif [[ $registered -eq 0 ]]; then
printf "\n${RED}${BOLD}No v6.0.0 routes are registered yet (clean slate).${NC}\n"
fi
4 changes: 3 additions & 1 deletion contracts/deployments/1.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@
"V4_0_0_RC_3_SP1_VERIFIER_GROTH16": "0xa27A057CAb1a4798c6242F6eE5b2416B7Cd45E5D",
"V4_0_0_RC_3_SP1_VERIFIER_PLONK": "0xE00a3cBFC45241b33c0A44C78e26168CBc55EC63",
"V5_0_0_SP1_VERIFIER_GROTH16": "0x50ACFBEdecf4cbe350E1a86fC6f03a821772f1e5",
"V5_0_0_SP1_VERIFIER_PLONK": "0x0459d576A6223fEeA177Fb3DF53C9c77BF84C459"
"V5_0_0_SP1_VERIFIER_PLONK": "0x0459d576A6223fEeA177Fb3DF53C9c77BF84C459",
"V6_0_0_SP1_VERIFIER_GROTH16": "0x99A74A05a0FaBEB217C1A329b0dac59a1FA52508",
"V6_0_0_SP1_VERIFIER_PLONK": "0x8a0fd5e825D14368d90Fe68F31fceAe3E17AFc5C"
}
Loading