Liquidity Layer API - Front end

Liquidity Layer API - Front end

Hey Developers!
We will be building a simple frontend application which would be a useful template to call the Liquidity Layer contract that was developed in the previous blog. In a gist, you will be transferring USDC through CIRCLE bridge into other chains. All of this happens under the hood of Hyperlane. So let's get started:

We will be walking through the following series:

  • Setup

  • Connect Wallet

  • Contracts Integration

  • Additional JSX

  • Calling Contract

💡 Liquidity Layer mainly deals with the transfer of USDC and is active on Goerli and Fuji testnets. We have deployed contracts on Fuji networks in the previous blog as getting Fuji faucet is much easier than Goerli. Hence we will be sending USDC from Fuji to Goerli.


We have set up a basic react app on Replit. You can fork this repository and start working with it. It is a simple create-react-app command implemented application.

Replit Instance :

(If you are using Replit, you can skip till here)

So if you don’t want to use the replit then open the terminal in the desired directory for your frontend application and enter the following command (hoping you have npm installed, else install npm and then continue)

npx create-react-app messaging-api-frontend

If you are using the above command and not the replit instance, then I would recommend navigating to the ./src folder and open the App.js file. Inside the App.js file, in the return section, remove all of the JSX except the div with className as “App”.

Connect Wallet:

Now that our application is ready to be set up, let's start building the following components:

  • Connect Wallet button: we need a signer to sign the transaction right.

  • Button to call the contract on a remote chain which will in turn fetch the votes from another remote chain.

Since we have an idea of what we are building with the components lets get started with the functionality.

Initially, let's install ethers library using the following command:

npm i ethers

We use Ethers library to interact with contracts.

The first step is to get the user to connect his wallet to our webapp/frontend.

So, let's create a button. Inside the div as follows:

<div className="App">
    <button>Connect Wallet</button>

If you wish to see your app changes, for:

Once using Replit, just press the Run button.

Once using the local setup, run the following command:

npm run start

You can see a button in the top middle of the screen.

Let's add some functionality to it so that it pops up a wallet and return the account address as well as the provider. We need the provider as it contains the signer object.

Let's add the following piece of code to our codebase. In the App.js :

import { useState } from "react";
import { ethers } from "ethers";

function App(){
    const [wallet, setWallet] = useState();
  const [provider, setProvider] = useState();
const connectWallet = () => {
    const { ethereum } = window;
    if (!ethereum) {
      alert("Get metamask!");
      .request({ method: "eth_requestAccounts" })
      .then(async (accounts) => {
        let newProvider = new ethers.BrowserProvider(window.ethereum);
        console.log(await newProvider.getNetwork());
        if ((await newProvider.getNetwork()).chainId !== 80001n) {
          await window.ethereum.request({
            method: "wallet_switchEthereumChain",
            params: [{ chainId: "0xa869" }],      // fuji network
          setProvider(new ethers.BrowserProvider(window.ethereum));
    <div className="App">
        <button onClick={connectWallet}>Connect Wallet</button>

export default App;

Add the code accordingly or just copy-paste your App.js file with the above code.

Here we are using useState hook, to set the wallet address, and the provider.

In the connectWallet function, we are checking for an object destructuring named as ethereum from the window library. Usually, EVM wallets are named as Ethereum and the most common one, Metamask pops up for the same. If you want to use any other wallet, recommend using a wallet hook from walletConnect SDK.

Now, if there is an Ethereum object or a wallet available, then it pops up whenever a button is clicked and then asks for permission to grant the user details to the web app. If you press connect, then your web app will have access to the account address as well as the provider.

And here, since we have our Router contract deployed in Mumbai, we will be telling the user to shift to Mumbai network if he is not connected to the Mumbai network. We do this by checking the provider.getNetwork() method and comparing the chain ID retrieved. Once we have the chain changed, we will be setting the new Provider through which we will be retrieving the signer object.

Once you have added this piece of code, just try to hit the button as you will see a wallet pop up and if no wallet exists, then, it will throw an alert as ”Get Metamask!”

And finally let's get the signer object from the provider. Let's write a simple function after connectWallet(). Add this piece of code under connectWallet() function.

const getSigner = async () => {
    const signer = await provider.getSigner();
    return signer;

This above piece of code will return the signer object which will be needed later.


Now, let's construct the contracts.

Inside your ./src directory, create a folder named ./utils . Under ./utils Create 3 files named contracts.js , liquidityLayer.json and erc20ABI.json

contracts.js file will have the Router contract instantiations.

liquidityLayer.json will have the ABI of the router contract.

erc20ABI.json will have the ABI of ERC20 which is our USDC contract.

You can find the ABI by copy pasting the contract into remix → Compile → Copy ABI and then paste it into respective JSON files. It will usually be an array object.

Now let's deep dive into creating contract connections.

Inside contracts.js add the following piece of code:

 * Instantiate contracts
import { ethers } from "ethers";
import abiLayer from "./liquidityLayer.json";
import abiERC from "./ERC20.json"

const contractAddress = "0xdEB0F354CC542Cb6d4E86836850505782E3Df3C0";
const usdcAddress = "0x5425890298aed601595a70AB815c96711a31Bc65";

export const fetchContract = async (signer) => {
  return new ethers.Contract(contractAddress, abiLayer, signer);

export const fetchERCContract = async(signer) => {
    return new ethers.Contract(usdcAddress, abiERC, signer);

You can change the address as per your deployment addresses.

Now there is 1 function that takes the signer as an argument and returns a Contract interface. The ethers.Contract takes in 3 arguments, the address of the contract, ABI of the contract, and signer instance. The signer argument will be passed during the function call which we will see down the line.

Now that we have the provider, contracts ready let's start calling the contract from our main file i.e. App.js .

Let's import our contracts.js into our App.js file. We will include the import statement at the top of the file under the import statements.

Additional JSX:

We now need things like

  • Amount : Amount of USDC

  • Message : Message for the transaction

  • Destination chain

  • Address of our contract

We now will set up input elements with useState hooks to cater to the above needs. Just paste the code below at the start ,i.e. right above the App() function where we used the useState hook for storing wallet and provider.

  const [amount, setAmount] = useState(0);
  const [message, setMessage] = useState("");

Now add the following JSX below the connect wallet button in the return statement.

      <input type="text" onChange={(e) => setMessage(} />
      <input type="number" min={0} onChange={(e) => setAmount(} />

Hence here we are storing the states of Message and Amount.

We will set the destination chain to Goerli with the domain ID 5 (you will find it here).

We will set the contract address to our contract address.

Add the below lines to your codebase below the useState hooks.

  const address = '0xdEB0F354CC542Cb6d4E86836850505782E3Df3C0';   // contract address
  const destination = 5;   // destination chain domain id

Now with these values, we can move ahead to call the contract.

Call Contract:

Now let's import the contract instances created in the ./utils/contracts.js as follows which will enable us to use the contract in the App.js file. We import as follows, in App.js :

import { fetchContract, fetchERCContract } from "./utils/contracts";

We will now be coding a button to call the contract. Just add a button below the last input element as follows:

<button onClick={contractCall}>Send</button>

Let us finally define the contractCall function right below getSigner() function, which would include the following functionality:

  • getting signer from the provider

  • checking if the address provided is the right address

  • getting contract instances

  • Getting approval of USDC contract

  • Calling the contract with required parameters

The code for the following is shown below:

const contractCall = async () => {
    if (provider !== null) {
      const signer = await getSigner();
      const checkAddress = ethers.isAddress(address); // address variable was set above

      if (checkAddress === false) {
        alert("Invalid address");
      const usdcContract = await fetchERCContract(signer);
      const approval = await usdcContract.approve("", amount);

      if (approval === false) {
        alert("Approval failed");
      const contract = await fetchContract(signer);
      const tx = await contract.send(destination, address, amount, message, {
        value: "0.01",
        gasLimit: 1000000,

The only aspect where a normal contract interaction changes with respect to a contract integrating Hyperlane is while calling the function that enables Hyperlane APIs. Hence we see that we attach value to the function while calling which will be used InterchainGasPaymaster as it enables us to bridge tokens to the other chain.


Hence, with this simple frontend and implementation, you can design a web app that will deal with contracts that have Hyperlane integration to them. As in the beginning, the change in building an application from a normal application to that which has implemented Hyperlane is simple with just adding a value parameter to the function that will send to another contract on a remote chain via Hyperlane.

The link to the GitHub repository is given below:

Happy Building!