Integrate with a front-end
Now that you have built your Equito App smart contract, it's time for you to integrate it with your front-end application. This tutorial explains how to implement a Ping Pong flow in a React frontend using Equito and various blockchain interactions.
Let's start from understanding the process, its actors and its flow. The process involves four actors:
- the Client, your front-end application;
- chain A, from which the ping messages start;
- chain B, which receives a ping and answers with a pong to chain A;
- equito Network, which is responsible for message confirmation and proof generation.
Prerequisites
After you created your React project, make sure you have installed the following packages:
- @tanstack/react-query
- wagmi + @wagmi/core
- @rainbow-me/rainbowkit
- viem
- @equito-sdk/evm
- @equito-sdk/viem
- @equito-sdk/core
- @equito-sdk/client
App Configuration
Now you need to wrap your app with AppProvider that shares app state across the app, it's composed by wallet connect provider and our custom providers:
- EquitoProvider to share equito evm source and destination chain states;
- PingPongProvider to manage ping pong flow state.
Then configure your app by editing ping-pong.abi.ts with the ping pong abi you generated in the past steps and specify your deployed ping pong contract address in the file chains.ts.
Step-by-Step guide
The ping pong flow is implemented by the useExecutePingPong hook. Let's breakdown the process in steps.
Step 1: Request PING Message Send (Client to Chain A)
Client Submits a Transaction to Chain A
- The client initiates the process by sending a PING message request to Chain A.
- Chain A processes this request and emits a "MessageSentRequest" event, containing the sent PING message.
const sendPingReceipt = await sendPing.execute();
The process is handled by the useSendPing hook: switches to the right chain, sends the transaction that invokes the sendPing
function, passing destination chain selector and ping message, and, finally, waits for the transaction to complete.
await switchChainAsync({ chainId: from.chain.definition.id });
const hash = await writeContractAsync({
address: from.chain.pingPongContract,
abi: pingPongAbi,
functionName: "sendPing",
value: pingFee.fee,
chainId: from.chain.definition.id,
args: [BigInt(to.chain.chainSelector), pingMessage],
});
return await waitForTransactionReceipt(config, {
hash,
chainId: from.chain.definition.id,
});
Step 2: Listen to Events and Approve PING Message (Client and Equito Chain)
Chain A Emits Event and Equito Chain Listens
- The client and Equito Chain listen for the "MessageSentRequest" event from Chain A.
- The client then checks on the Equito Chain to see if the sent PING message is approved by its hash, this is implemented in useApprove hook.
const sentPingMessage = parseEventLogs({
abi: routerAbi,
logs: sendPingReceipt.logs,
}).flatMap(({ eventName, args }) =>
eventName === "MessageSendRequested" ? [args] : []
)[0];
const { timestamp: sentPingTimestamp } = await getBlock(config, {
chainId: from.chain.definition.id,
blockNumber: sendPingReceipt.blockNumber,
});
const { proof: sentPingProof } = await approve.execute({
messageHash: generateHash(sentPingMessage.message),
fromTimestamp: Number(sentPingTimestamp) * 1000,
chainSelector: from.chain.chainSelector,
});
Step 3: Deliver PING and Send PONG (Client to Chain B)
Client Submits a Transaction to Chain B
- The client submits the proof and the PING message to Chain B, requesting the delivery of the PING message and sending a PONG message. The transaction is triggered and listened by the useDeliver hook.
- Chain B processes this request and emits two events: "MessageExecuted" with the sent PING message and "MessageSentRequest" with the sent PONG message.
const deliverPingAndSendPongReceipt = await deliverPingAndSendPong.execute(
sentPingProof,
sentPingMessage.message,
sentPingMessage.messageData,
pongFee.fee
);
const sentPongMessage = parseEventLogs({
abi: routerAbi,
logs: deliverPingAndSendPongReceipt.logs,
}).flatMap(({ eventName, args }) =>
eventName === "MessageSendRequested" ? [args] : []
)[0];
Step 4: Approve PONG Message (Client and Equito Chain)
Chain B Emits Event and Equito Chain Listens
- The client checks on the Equito Chain to see if the sent PONG message is approved by its hash.
- The Equito Chain replies with the approval proof of the sent PONG message.
const { timestamp: sentPongTimestamp } = await getBlock(config, {
chainId: to.chain.definition.id,
blockNumber: deliverPingAndSendPongReceipt.blockNumber,
});
const { proof: sentPongProof } = await approve.execute({
messageHash: generateHash(sentPongMessage.message),
fromTimestamp: Number(sentPongTimestamp) * 1000,
chainSelector: to.chain.chainSelector,
});
Step 5: Deliver PONG (Client to Chain A)
Client Submits a Transaction to Chain A
- The client submits the proof and the PONG message to Chain A, requesting the delivery of the PONG message.
- Chain A processes this request and emits a "MessageExecuted" event with the sent PONG message.
await deliverPong.execute(
sentPongProof,
sentPongMessage.message,
sentPongMessage.messageData
);