Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
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.
What we have:
A ERC20 Transfer
Event emitted in Sepolia USDC contract.
What we need:
The verification on Chiado chain of the actual Sepolia Transfer
event.
How Hashi enables the Event verification:
With HashiAPI: a offchain component used to retrieve the Event Proof for the specific Sepolia Event emitted.
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 (Chiado in this case) and can be used in a custom contract which can use the Event proof verification provided by HashiProver itself.
Steps followed in this example:
Fetch the event proof for Transfer
event from Sepolia.
Deploy a custom contract on Chiado that will verify the event proof using HashiProver.
Submitting the proof to the custom contract.
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 Hashi explorer).
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.
The Quick Start guide will walk you through two core use cases of Hashi: verifying storage proofs or event proofs against a block header and pushing messages across chains. We’ll go through the core use cases with three examples:
verify a foreign event
read a foreign state via storage proofs
push a message to foreign chain.
The code can be found in Hashi template. This repository serves as a template for developers to kickstart a Hashi project using Hardhat/Foundry.
In this example, we’ll send a message from a contract on Sepolia to another contract on Chiado, using Hashi to relay and execute the message. The callback will be triggered and validated on Chiado using the provided onMessage
function.
Here’s how you write a contract on Sepolia that dispatches a message using Hashi’s Yaho contract. This message will be relayed to Chiado and trigger the callback.
Explanation:
The contract interacts with the Yaho contract on Sepolia to dispatch a message to Chiado using dispatchMessageToAdapters
.
The sendMessageToChiado
function takes the target chain ID(10200 for Chiado), target address (ChiadoReceiver contract on Chiado), and the message to be sent.
The reporters, adapters, and threshold are passed into the Yaho contract for cross-chain message validation and relaying.
Deployment:
Deploy this contract on Sepolia, passing the reporters, adapters, and threshold into the constructor.
A list of reporters and adapters contract can be found in: Oracles
Threshold
must equal to expectedThreshold
in Receiver contract.
Yaho contract address can be found in
The first step is to fetch the Event Proof from Sepolia. An important parameter is the block number which ensures that Hashi can verify the proof against the block header stored on Chiado. This is possible if the block header related the emitted event on the origin chain (Sepolia in this case) has already been propagated to the target chain (Chiado in this case). To check the block propagation status is possible to use the Hashi Explorer.
Fetching the Proof
Use the following curl
command to fetch the account and storage proof for the following ERC20 Transaction
: tx-example.
chainId: 11155111 (Sepolia).
txHash: The tx-hash that emitted the Transfer Event on Sepolia: tx-example.
logIndex: The log index for the event.
This curl
command / script returns the Event Proof for the Transfer
Event on Sepolia, which will be used for verification on Chiado.
Once the proof has been fetched, we can verify it using a custom contract on Chiado. The contract will use the HashiProver helper library to check the proof against the block header stored on Chiado.
Deploying the Custom Verification Contract on Chiado
Here is a custom contract that deployed on Chiado can be used to read and verify the Transfer event on Sepolia:
In this example, pass to the custom verification contract constructor <ShoyuBashi contract address on Chiado, the ERC20 contract address on Sepolia, from which the Transfer event is emitted, chain ID of the ERC20Contract (Sepolia: 11155111)>
verifyForeignEvent
explanation:
Inputs:
proof
: the HashiAPI retreived proof
Logic:
check that the proof chainID matches the origin chain chainId (Sepolia in this case).
The verifyForeignEvent
function validates the event proof against the block header stored on Chiado, which was relayed from Sepolia.
check that rlpEncodedEvent
returned from HashiProver.verifyForeignEvent
matches the expectedRlpEncodedEvent that we passed as the function argument. This is useful to check that the Event infos encoded in the proof just verified actually match the Event we expect.
If the proof is valid, the contract emits the TransferEventVerified
event. You may define your own logic after the proof has been verified.
Submitting the Proof
Once the contract is deployed on Chiado, 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 Sepolia. If the proof is valid, the rplEncodedEvent will be emitted.
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.
expectedRlpEncodedEvent
: an bytes encoded format with the Event information: check out the in order to generate it starting from the Event topics and data.
Check verifyForeignEvent
implementation .
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.
Use the following curl
command or javascript script to fetch the account and storage proof for the USDC total supply on Optimism:
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.
Hashi support SP1 zkVM to generate storage proofs.
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:
HashiProver & ShoyuBashi are already deployed on a list of networks 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.
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.
Check verifyForeignStorage
implementation here.
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.
This example demonstrates the validation of a Transfer
Event from an ERC-20 contract on the Sepolia network using an event proof on the Chiado network. The proof is retrieved via an API call and provided to a custom contract to be deployed on Chiado. The contract utilizes the HashiProver
helper library to verify the proof against the latest block header recorded in Hashi.
Now, we’ll write the callback function on Chiado using the onMessage
implementation you provided. This contract will handle the message sent from Sepolia.
Explanation:
The onMessage
function implements the callback, which is triggered when the Yaru contract on Chiado relays the message.
The function checks that the Yaru contract on Chiado is the sender, that the chainId, sender address, and threshold match what was expected, and that the list of adapters matches the expected hash.
After the validations pass, the message is decoded and stored in the contract.
Deployment:
Deploy this contract on Chiado. Ensure that the values like SOURCE_SENDER_ADDRESS (Sepolia sender contract), EXPECTED_THRESHOLD, and ADAPTERS are correctly configured.
Yaru address with respect to the source chain can be found in
A list of adapters on the target chain can be found in
Here’s what happens after you send the message:
Message Dispatch on Sepolia:
When sendMessageToChiado
is called on the SepoliaSender contract, the message is dispatched through Yaho on Sepolia.
The message is sent with the targetChainId, threshold, reporters, and adapters, and the data (in this case, the message string).
Oracles and Relaying:
Hashi’s oracles pick up the message and relay it to Chiado.
The oracles validate the message and check the block headers for consensus.
Message Receipt and Callback on Chiado:
On Chiado, the Yaru contract receives the message and triggers the onMessage
function on the ChiadoReceiver contract.
The callback validates the message by ensuring that the message is from the expected Yaru contract, the source chain, and the correct sender.
After validation, the message is decoded and stored in the contract.
You deploy SepoliaSender on Sepolia and configure it with the appropriate Yaho contract and oracle parameters.
You deploy ChiadoReceiver on Chiado to handle the incoming message and validate it against Hashi’s oracle system.
When you call sendMessageToChiado
from SepoliaSender, the message is dispatched to Chiado, where the callback function processes it and updates the state.