All pages
Powered by GitBook
1 of 4

Loading...

Loading...

Loading...

Loading...

Setting the scene

What we have:

------

What we need:

  • The verification on Gnosis Chain chain of USDC total supply on Optimism.

How Hashi enables the Event verification:

  • With HashiAPI: a offchain component used to retrieve the Storage Proof of a specific storage slot (the one holding the USDC total supply).

  • With HashiProver: a onchain contract provided by Hashi which can verify the block proof provided by HashiAPI. HashiProver must be deployed on the target chain (Gnosis Chain in this case) and can be used in a custom contract which can use the Event proof verification provided by HashiProver itself.

  • The proof will be verified using the latest block header that was relayed to Hashi from Optimism to Gnosis Chain (check the ).

Steps followed in this example:

  • Fetch the storage proof for the USDC total supply on Optimism.

  • Deploy a custom contract on Gnosis Chain that will verify the event proof using HashiProver.

  • Submitting the proof to the custom contract.

Hashi explorer

Reading foreign state

This example demonstrates the verification of the total supply of USDC on Optimism from Gnosis Chain using a storage proof. The proof will be fetched via HashiAPI and fed to a contract deployed on Gnosis Chain. The contract will use HashiProver to validate the proof against the last Optimism reported block header stored in Hashi.

Validating your statements

Once the proof has been fetched, we can now verify it using a custom contract on Gnosis Chain. The contract will use the HashiProver helper library to check the proof against the block header stored on Gnosis Chain.

Deploying the custom Verification Contract on Gnosis Chain

Here is a contract that deployed on Gnosis Chain can be used to read and verify the USDC total supply on Optimism:

Deployment:

  • HashiProver & ShoyuBashi are already deployed on a that can be called from the custom verification contract (this contract). In order to configure a custom oracle set for it is possible to deploy a new ShoyuBashi contract, inherit HashiProver contract and configure your oracle set in the ShoyuBashi contract.

  • In this example, pass to the custom verification contract constructor <ShoyuBashi contract address on Gnosis Chain>

readTotalSupply explanation:

Inputs:

  • proof: the HashiAPI retreived storage proof.

Logic:

  • check that the proof chainID matches the origin chain chainID (Optimism in this case).

  • check that the USDC contract address.

  • check that storage key match the expected value.

  • This contract uses HashiProver to verify on Gnosis Chain the storage proof coming from Optimism fetched earlier.

Check verifyForeignStorage implementation .

Submitting the Proof

Once the contract is deployed on Gnosis Chain, you can submit the proof fetched from the API using the following command:

This will trigger the contract to verify the proof using the block header stored in Hashi from Optimism. If the proof is valid, the total supply of USDC will be emitted.

Getting the Storage Proof

First we need to fetch the storage proof from Optimism using the block number of a Optimism block which header that has already been propagated to Hashi. This block number ensures that Hashi can verify the proof against the just mentioned block header propagated to Gnosis Chain.

Fetching the Proof

Use the following curl command or javascript script to fetch the account and storage proof for the USDC total supply on Optimism:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { HashiProver } from "https://github.com/gnosis/hashi/blob/feat/hashi-prover/packages/evm/contracts/prover/HashiProver.sol";

contract UsdcTotalSupplyReader is HashiProver {
    event UsdcTotalSupply(uint256 totalSupply);

    constructor(address shoyuBashi) HashiProver(shoyuBashi) {}

    function readTotalSupply(HashiProver.AccountAndStorageProof calldata proof) external {
        require(proof.chainId == 10, "Invalid chain id"); // Optimism
        require(proof.account == 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85, "Invalid account");
        require(proof.storageKeys.length == 1 && proof.storageKeys[0] == bytes32(uint256(0xb)), "Invalid storage key");

        // Verify the proof against the latest block header stored in Hashi
        uint256 totalSupply = uint256(bytes32(verifyForeignStorage(proof)[0]));
        emit UsdcTotalSupply(totalSupply);
    }
}

It checks that the proof is from Optimism (chain ID 10), and that the USDC contract address and storage key match the expected values.

  • The verifyForeignStorage function validates the proof against the block header stored on Gnosis Chain, which was relayed from Optimism.

  • If the proof is valid, the contract emits the total supply of USDC on Optimism.

  • list of networks
    here
    // Example of submitting the proof in Solidity (or via web3 call)
    HashiProver.AccountAndStorageProof memory proof = { /* fetched proof from curl command */ };
    usdcTotalSupplyReader.readTotalSupply(proof);
    import axios from "axios";
    
    const main = async () => {
    
      // fetch storage proof from Hashi Prover
      console.log("Fetching storage proof from Hashi prover...");
      const result = await
    
    • chainId: 10 (Optimism).

    • address: The address of the USDC contract on Optimism: 0x0b2c639c533813f4aa9d7837caf62653d097ff85.

    • storageKeys: The storage key for the USDC total supply: 0xb.

    • blockNumber: The block number on Optimism, 126086800, which must have already been propagated to Gnosis Chain via Hashi for the proof to be valid.

    This curl command returns the account and storage proof for the USDC total supply, which will be used for verification on Gnosis Chain.

    Use the SP1 storage proof generator

    Hashi support SP1 zkVM to generate storage proofs.

    curl --location --request POST 'https://jsonrpc.hashi-explorer.xyz/v1' \
    --header 'Content-Type: application/json' \
    --data-raw '{
      "id": 1,
      "jsonrpc": "2.0",
      "method": "hashi_getAccountAndStorageProof",
      "params": {
          "chainId": 10,
          "address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
          "storageKeys": ["0x00000000000000000000000000000000000000000000000000000000000000b"],
          "blockNumber": 126086800
      }
    }'
    RUST_LOG=info cargo run --release -- --execute \
        --rpc-url https://mainnet.infura.io/v3/YOUR-PROJECT-ID \
        --reference-block-number 0x7B60EA3 \
        --proof-block-number 0x783EE90 \
        --account 0x0b2c639c533813f4aa9d7837caf62653d097ff85 \
        --storage-key 0x000000000000000000000000000000000000000000000000000000000000000b
    axios
    .
    post
    (
    "http://jsonrpc.hashi-explorer.xyz:3000/v1",
    {
    jsonrpc: "2.0",
    method: "hashi_getAccountAndStorageProof",
    params: {
    chainId: 10,
    address: "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
    storageKeys: ["0x00000000000000000000000000000000000000000000000000000000000000b"],
    blockNumber: 126086800
    },
    id: 1,
    },
    {
    headers: {
    "Content-Type": "application/json",
    },
    }
    );
    console.log("Storage proof result", result.data.result.proof);
    };
    main();