Interact with the Front-end
Creating a user-friendly web interface for smart contracts on the Rootstock network enhances user interaction. Here, we'll focus on using Wagmi and RainbowKit, some popular libraries for connecting your smart contracts to a web front-end.
Project Setup
-
Create a new web project. In this case, we'll be using Next.js as our web framework.
npx create-next-app@latest -
Go to the root of your Next.js project and, using your preferred package manager, install these dependencies:
yarn add @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query -
Create an
.envfile at the root of your project and add the following content. You can get your Wallet Connet ID from WalletConnect Dashboard.touch .env.local
echo "NEXT_PUBLIC_WC_PROJECT_ID=<YOUR_PROJECT_ID>" >> .env.local -
Create a
providers.tsxfile inside theappdirectory and add the following content:"use client";
import {
getDefaultConfig,
RainbowKitProvider,
} from "@rainbow-me/rainbowkit";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { WagmiProvider } from "wagmi";
import { rootstock, rootstockTestnet } from "wagmi/chains";
const config = getDefaultConfig({
appName: "Rootstock Wagmi Starter",
projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID as string,
chains: [rootstockTestnet, rootstock],
ssr: true,
});
const queryClient = new QueryClient();
export default function Providers({
children,
}: {
children: React.ReactNode;
}) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider>{children}</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
} -
And now import and use the
Providerscomponent to wrap your application in thelayout.tsxfile inside theappdirectory:import type { Metadata } from "next";
import "./globals.css";
import localFont from "next/font/local";
import Providers from "./providers";
import "@rainbow-me/rainbowkit/styles.css";
export const metadata: Metadata = {
title: "Rootstock Wagmi Starter",
description:
"Interact with contracts on Rootstock Network with Wagmi and RainbowKit",
};
const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<Providers>{children}</Providers>
</body>
</html>
);
} -
Finally, start the web server.
yarn dev
If everything went well, you should be able to access your web app by navigating to http://localhost:3000 in your browser.
Congrats!
You're all set up. Let's get to the smart contract interaction.
Call Smart Contract using Connect Button and Wagmi hooks
We're going to be editing the page.tsx file inside the app directory. Follow these steps to interact with the smart contract:
-
Delete the default content and add the
<ConnectButton />component to check if it's working fine.import { ConnectButton } from "@rainbow-me/rainbowkit";
export default function Home() {
return (
<main className="flex flex-col justify-center items-center min-h-screen">
<ConnectButton /> <!-- RainbowKit Connect Button component -->
</main>
);
}And you should see something like this in the browser:

Please try connecting your wallet.
-
Now we're going to use our first hook from Wagmi to check if a user wallet is connected and, if so, get the connected address. The hook is
useAccountand is used like this:const {
address, // Connected address
isConnected, // true if a wallet is connected
} = useAccount();Note: As we're using react hooks in a Next.js project, don't forget to add the
'use client'directive at the top of yourpage.tsxfile.Now that we know if a wallet is connected, we can add some conditional rendering content and show the connected address.
{
isConnected && <p>Connected address: {address}</p>;
}So the full code on the
page.tsxfile should be something like this:"use client";
import { ConnectButton } from "@rainbow-me/rainbowkit";
import { useAccount } from "wagmi";
export default function Home() {
const { isConnected, address } = useAccount();
return (
<main className="flex flex-col justify-center items-center min-h-screen gap-3">
<ConnectButton />
{isConnected && <p>Connected address: {address}</p>}
</main>
);
}And the browser should look something like this:

-
We're now going to make our first read from the blockchain. The hook we're using for that is
useReadContractand is used like this:const {
data: balance, // Retrieved data from the function
isLoading, // Check if the data is still being fetched
error, // Check if an error occurred
} = useReadContract({
address: "<YOUR_CONTRACT_ADDRESS>", // Your deployed contract address
abi: [
// Contract abi
],
functionName: "balanceOf", // The name of the function you want to call
args: [address], // Function arguments if they are required
});Given this, we need to bring the contract abi that should be available at the Hardhat project we've been working on. Once you compile a contract, a file is generated at
artifacts/contracts/YourContract.sol/YourContract.jsonwhich contains the abi of the contract.In this case, we're going to copy the abi array and paste it in a new file called
MyContractAbi.tsinside a newassetsfolder. the file should look like this:// assets/MyContractAbi.ts
export const abi = [
{
inputs: [
{
internalType: "uint256",
name: "initialSupply",
type: "uint256",
},
],
stateMutability: "nonpayable",
type: "constructor",
},
...
];Now, lets compose our
useReadContracthook with our contract information and show the balance of the connected address:"use client";
import { abi } from "@/assets/MyTokenAbi";
import { ConnectButton } from "@rainbow-me/rainbowkit";
import { useAccount, useReadContract } from "wagmi";
const CONTRACT_ADDRESS = "0x543ba9fc0ade6f222bd8c7bf50a0cd9923faf569"; // Replace with your contract address
export default function Home() {
const { isConnected, address } = useAccount();
const {
data: balance,
isLoading,
error,
} = useReadContract({
// Once the component is mounted, useReadContract is called
address: CONTRACT_ADDRESS,
abi,
functionName: "balanceOf",
args: [address], // Replace with the address you want to check
});
return (
<main className="flex flex-col justify-center items-center min-h-screen gap-3">
<ConnectButton />
{isConnected && (
<>
<p>Connected address: {address}</p>
<p>
<span className="font-bold">Balance:</span>{" "}
{
isLoading // Check if the data is still being fetched
? "Loading..."
: error // Check if there was an error
? "Error retrieving balance"
: balance?.toString() // If the data is ready, display the balance
}
</p>
</>
)}
</main>
);
}And the browser should look something like this:

Well done!