Skip to main content

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.

Ping pong flow

Prerequisites

After you created your React project, make sure you have installed the following packages:

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:

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

  1. The client initiates the process by sending a PING message request to Chain A.
  2. 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

  1. The client and Equito Chain listen for the "MessageSentRequest" event from Chain A.
  2. 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

  1. 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.
  2. 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

  1. The client checks on the Equito Chain to see if the sent PONG message is approved by its hash.
  2. 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

  1. The client submits the proof and the PONG message to Chain A, requesting the delivery of the PONG message.
  2. Chain A processes this request and emits a "MessageExecuted" event with the sent PONG message.
await deliverPong.execute(
sentPongProof,
sentPongMessage.message,
sentPongMessage.messageData
);