Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/risc0/risc0/llms.txt

Use this file to discover all available pages before exploring further.

Ethereum Integration

Integrate RISC Zero zkVM proofs with Ethereum smart contracts for verifiable off-chain computation.

Overview

RISC Zero provides deployed verifier contracts on Ethereum mainnet and testnets. Your smart contracts can call these verifiers to validate zkVM proofs on-chain.
All examples and contracts are available in the risc0-ethereum repository.

Verifier Contracts

The RiscZeroVerifierRouter automatically routes verification requests to the appropriate verifier based on the proof type and zkVM version. Benefits:
  • Supports multiple proof types (Groth16, composite receipts)
  • Automatically updated with new zkVM releases
  • Future-proof your application
  • Managed by RISC Zero

Contract Interface

All verifiers implement the IRiscZeroVerifier interface:
interface IRiscZeroVerifier {
    /// @notice Verify a RISC Zero proof
    /// @param seal The encoded cryptographic proof
    /// @param imageId The identifier for the guest program
    /// @param journalDigest The SHA-256 digest of the journal
    function verify(
        bytes calldata seal,
        bytes32 imageId,
        bytes32 journalDigest
    ) external view;
}

Quick Start with Foundry Template

The fastest way to start is using the Boundless Foundry Template:
1

Initialize Project

forge init my-zkvm-app --template boundless-xyz/boundless-foundry-template
cd my-zkvm-app
2

Install Dependencies

forge install
cargo build
3

Run Tests

forge test

Building Your Application

Step 1: Write Your Guest Program

Create your zkVM guest program in Rust:
// methods/guest/src/main.rs
use risc0_zkvm::guest::env;

fn main() {
    // Read inputs
    let number: u64 = env::read();
    
    // Perform computation
    let is_even = number % 2 == 0;
    
    // Commit results to journal
    env::commit(&number);
    env::commit(&is_even);
}

Step 2: Generate Proof (Host)

Generate a Groth16 proof from your Rust host application:
use risc0_zkvm::{default_prover, ExecutorEnv, ProverOpts, VerifierContext};
use risc0_zkvm::Receipt;
use methods::{EVEN_CHECK_ELF, EVEN_CHECK_ID};

fn prove_even(number: u64) -> Receipt {
    let env = ExecutorEnv::builder()
        .write(&number)
        .unwrap()
        .build()
        .unwrap();
    
    // Generate Groth16 proof for on-chain verification
    let prover = default_prover();
    let opts = ProverOpts::groth16();
    
    let prove_info = prover
        .prove_with_ctx(
            env,
            &VerifierContext::default(),
            EVEN_CHECK_ELF,
            &opts,
        )
        .unwrap();
    
    prove_info.receipt
}

Step 3: Create Solidity Contract

Implement verification in your smart contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IRiscZeroVerifier} from "risc0-ethereum/contracts/src/IRiscZeroVerifier.sol";

contract EvenNumber {
    IRiscZeroVerifier public immutable verifier;
    bytes32 public constant EVEN_CHECK_ID = /* your image ID */;
    
    uint256 public number;
    
    constructor(IRiscZeroVerifier _verifier) {
        verifier = _verifier;
    }
    
    /// @notice Set an even number with proof verification
    function set(uint256 x, bytes calldata seal) public {
        // Construct expected journal
        bytes memory journal = abi.encode(x);
        
        // Verify the proof
        verifier.verify(
            seal,
            EVEN_CHECK_ID,
            sha256(journal)
        );
        
        // Update state only if proof is valid
        number = x;
    }
    
    function get() public view returns (uint256) {
        return number;
    }
}
The verify() function will revert if the proof is invalid. Always handle this in your contract logic.

Deployed Verifier Addresses

RISC Zero maintains deployed verifier routers on multiple networks:
For the latest contract addresses, see the risc0-ethereum repository or check the official documentation.

Using the Router

In your contract constructor:
import {IRiscZeroVerifier} from "risc0-ethereum/contracts/src/IRiscZeroVerifier.sol";

contract MyContract {
    IRiscZeroVerifier public immutable verifier;
    
    constructor(address _verifierRouter) {
        verifier = IRiscZeroVerifier(_verifierRouter);
    }
}

Steel: Ethereum State Proofs

Steel is a library for creating view call proofs, enabling you to prove Ethereum state within zkVM applications.

Use Cases

  • Prove ERC-20 balances
  • Verify storage slots
  • Access historical state
  • Cross-chain state verification

Example

use risc0_steel::{Contract, SolCommit};
use alloy_sol_types::sol;

sol! {
    interface IERC20 {
        function balanceOf(address account) external view returns (uint256);
    }
}

// In your guest code
let contract = Contract::new(token_address, &provider);
let balance = contract.call_builder(&IERC20::balanceOfCall { 
    account: user_address 
}).call().await?;

// Commit to journal
env::commit(&balance._0);

Steel Documentation

Complete Steel API reference

Steel Blog Post

Introducing Steel 2.0

Gas Costs

Typical verification costs:
  • Groth16 Verification: ~280,000 gas
  • Fixed cost: Independent of computation complexity
  • Proof size: ~200-300 bytes
Verification cost is constant regardless of the computation proven. A 1-second computation costs the same to verify as a 1-hour computation.

Testing Your Integration

Local Testing with Dev Mode

For faster iteration during development:
RISC0_DEV_MODE=1 forge test
DEV_MODE skips proof generation. Never use in production!

Testing with Real Proofs

// test/EvenNumber.t.sol
import {Test} from "forge-std/Test.sol";
import {EvenNumber} from "../src/EvenNumber.sol";

contract EvenNumberTest is Test {
    EvenNumber public evenNumber;
    
    function setUp() public {
        // Deploy with verifier router
        evenNumber = new EvenNumber(VERIFIER_ROUTER_ADDRESS);
    }
    
    function testSetEvenNumber() public {
        // Generate proof in Rust, then:
        bytes memory seal = /* proof from Rust */;
        
        evenNumber.set(42, seal);
        assertEq(evenNumber.get(), 42);
    }
    
    function testRejectOddNumber() public {
        bytes memory seal = /* proof for odd number */;
        
        vm.expectRevert();
        evenNumber.set(43, seal);
    }
}

Remote Proving with Bonsai

For production, use Bonsai for remote proving:
use bonsai_sdk::alpha as bonsai_sdk;

let client = bonsai_sdk::Client::from_env()?;

// Upload ELF
let img_id = client.upload_img(EVEN_CHECK_ELF)?;

// Upload input
let input_id = client.upload_input(input_data)?;

// Start proving session
let session = client.create_session(img_id, input_id)?;

// Wait for proof
let receipt = session.wait()?;

Best Practices

1

Use the Verifier Router

Always use RiscZeroVerifierRouter for future compatibility
2

Validate Journal Data

Construct the expected journal in your contract and verify its hash
3

Handle Reverts

The verify() function reverts on invalid proofs - plan your error handling
4

Cache Image IDs

Store image IDs as constants to save gas
5

Test Thoroughly

Test both valid and invalid proof scenarios

Next Steps

Contract Verification Details

Deep dive into proof verification

Blockchain Examples

Browse example projects