Skip to content

Use a Proof

Prerequisite Reading: Request a Proof, Proof Lifecycle

After requesting a proof, the next step is to use that proof in the application's workflow. The exact way proofs are used will vary depending on the architecture of the application. However, there is a common pattern; once a proof is received from the Boundless market, the next step will be to verify that proof on-chain.

Use a Proof

It is recommended that the application contract calls the RiscZeroVerifierRouter for verification. This allows handling many types of proofs, and proof system versions seamlessly. In Boundless, the seal, which is often a zk-STARK or SNARK, will usually be Merkle inclusion proof into an aggregated proof. These Merkle inclusion proofs are cheap to verify, and reuse a cached verification result from a batch of proofs verified with a single SNARK.

Proof Verification

The Boundless Foundry Template, walks through a simple application which, with an input number, x:

  1. Uses a simple guest program to check if x is even.
  2. Requests, and receives, a proof of x being even from the Boundless Market.
  3. Calls the set function on the EvenNumber smart contract with the arguments: x and the seal (the proof bytes).
  4. The set function verifies the proof that x is even; if the proof is valid, the number variable (in smart contract state) is set to equal x.

Concretely, receiving the proof (see code) from the Boundless Market returns a journal and a seal:









let (journal, seal) = boundless_client
    .wait_for_request_fulfillment(request_id, Duration::from_secs(5), expires_at)
    .await?;

Using Alloys sol! Macro, the rust types/bindings are generated for the EvenNumber.sol contract. To create an EvenNumber contract instance:
















let even_number = IEvenNumber::new(
    args.even_number_address,
    boundless_client.provider().clone(),
);

To call the set function on the EvenNumber contract, a “set number” transaction is created:





















let set_number = even_number
    .set(U256::from(args.number), seal)
    .from(boundless_client.caller());

Finally, this transaction is broadcasted with:

























let pending_tx = set_number.send().await.context("failed to broadcast tx")?;
let tx_hash = pending_tx
    .with_timeout(Some(TX_TIMEOUT))
    .watch()
    .await
    .context("failed to confirm tx")?;
tracing::info!("Tx {:?} confirmed", tx_hash);

Definition of the set function on EvenNumber.sol:

EvenNumber.sol
/// @notice Set the even number stored on the contract. Requires a RISC  Zero proof that the number is even.
function set(uint256 x, bytes calldata seal) public {
  bytes memory journal = abi.encode(x);
  verifier.verify(seal, imageId, sha256(journal));
  number = x;
}

Calling the set function will verify the proof via the RISC Zero verifier contract. The verify call will revert if the proof is invalid, otherwise the number variable will be updated to x, which is now certainly even.

Each application will have its own requirements and flows, but this is a common pattern and a good starting point for building your own application.

Relevant links: Boundless Foundry Template, Journal, Seal