Skip to main content

Wagmi useWriteContract with transaction status

This recipe demonstrates how to create a button for contract interaction using the "useTransactor" and "useWriteContract" hooks from the "wagmi" library. The interaction includes the capability to provide feedback on the transaction status when using wagmi useWriteContract.

Here is the full code, which we will be implementing in the guide below:
components/ContractInteraction.tsx
import * as React from "react";
import { parseEther } from "viem";
import { useWriteContract } from "wagmi";
import DeployedContracts from "~~/contracts/deployedContracts";
import { useTransactor } from "~~/hooks/scaffold-eth";

export const ContractInteraction = () => {
const { writeContractAsync, isPending } = useWriteContract();

const writeContractAsyncWithParams = () =>
writeContractAsync({
address: DeployedContracts[31337].YourContract.address,
abi: DeployedContracts[31337].YourContract.abi,
functionName: "setGreeting",
value: parseEther("0.01"),
args: ["Hello world!"],
});

const writeTx = useTransactor();

const handleSetGreeting = async () => {
try {
await writeTx(writeContractAsyncWithParams, { blockConfirmations: 1 });
} catch (e) {
console.log("Unexpected error in writeTx", e);
}
};

return (
<button className="btn btn-primary" onClick={handleSetGreeting} disabled={isPending}>
{isPending ? <span className="loading loading-spinner loading-sm"></span> : "Send"}
</button>
);
};

Implementationโ€‹

Step 1: Set Up Your Componentโ€‹

Create a new component in the "components" folder. The component will show a button that will allow users to interact with your smart contract.

components/ContractInteraction.tsx
import * as React from "react";

export const ContractInteraction = () => {
return <button>Send</button>;
};

Step 2: Configure wagmi's useWriteContract hookโ€‹

Add wagmi's useWriteContract hook and configure writeContractAsync with the parameters: abi, address, functionName, value and args. Get the ABI and address of your smart contract from the DeployedContracts or you can grab it from ExternalContracts object, those will be used to set up the contract interaction.

import * as React from "react";
import { parseEther } from "viem";
import { useWriteContract } from "wagmi";
import DeployedContracts from "~~/contracts/deployedContracts";

export const ContractInteraction = () => {
const { writeContractAsync } = useWriteContract();

const writeContractAsyncWithParams = () =>
writeContractAsync({
address: DeployedContracts[31337].YourContract.address,
abi: DeployedContracts[31337].YourContract.abi,
functionName: "setGreeting",
value: parseEther("0.01"),
args: ["Hello world!"],
});
return <button>Send</button>;
};

Step 3: Initialize useTransactor hook and send transactionโ€‹

Initialize the useTransactor hook, and use it to wrap writeContractAsyncWithParams function which we got from useWriteContract to show feedback transaction status to user.

import * as React from "react";
import { parseEther } from "viem";
import { useWriteContract } from "wagmi";
import DeployedContracts from "~~/contracts/deployedContracts";
import { useTransactor } from "~~/hooks/scaffold-eth";

export const ContractInteraction = () => {
const { writeContractAsync } = useWriteContract();

const writeContractAsyncWithParams = () =>
writeContractAsync({
address: DeployedContracts[31337].YourContract.address,
abi: DeployedContracts[31337].YourContract.abi,
functionName: "setGreeting",
value: parseEther("0.01"),
args: ["Hello world!"],
});

const writeTx = useTransactor();

return <button onClick={() => writeTx(writeContractAsyncWithParams, { blockConfirmations: 1 })}>Send</button>;
};

Step 4: Wrap useTransactor in a handler async functionโ€‹

Wrap the writeTx function in a handler function to start the transaction when the user clicks the button.

import * as React from "react";
import { parseEther } from "viem";
import { useWriteContract } from "wagmi";
import DeployedContracts from "~~/contracts/deployedContracts";
import { useTransactor } from "~~/hooks/scaffold-eth";

export const ContractInteraction = () => {
const { writeContractAsync, isPending } = useWriteContract();

const writeContractAsyncWithParams = () =>
writeContractAsync({
address: DeployedContracts[31337].YourContract.address,
abi: DeployedContracts[31337].YourContract.abi,
functionName: "setGreeting",
value: parseEther("0.01"),
args: ["Hello world!"],
});

const writeTx = useTransactor();

const handleSetGreeting = async () => {
try {
await writeTx(writeContractAsyncWithParams, { blockConfirmations: 1 });
} catch (e) {
console.log("Unexpected error in writeTx", e);
}
};


return (
<button className="btn btn-primary" onClick={handleSetGreeting}>
Send
</button>
);

Step 5: Bonus adding loading stateโ€‹

We can use isPending returned from useWriteContract while the transaction is being mined and also disable the button.

import * as React from "react";
import { parseEther } from "viem";
import { useWriteContract } from "wagmi";
import DeployedContracts from "~~/contracts/deployedContracts";
import { useTransactor } from "~~/hooks/scaffold-eth";

export const ContractInteraction = () => {
const { writeContractAsync, isPending } = useWriteContract();

const writeContractAsyncWithParams = () =>
writeContractAsync({
address: DeployedContracts[31337].YourContract.address,
abi: DeployedContracts[31337].YourContract.abi,
functionName: "setGreeting",
value: parseEther("0.01"),
args: ["Hello world!"],
});

const writeTx = useTransactor();

const handleSetGreeting = async () => {
try {
await writeTx(writeContractAsyncWithParams, { blockConfirmations: 1 });
} catch (e) {
console.log("Unexpected error in writeTx", e);
}
};

return (
<button className="btn btn-primary" onClick={handleSetGreeting} disabled={isPending}>
{isPending ? <span className="loading loading-spinner loading-sm"></span> : "Send"}
</button>
);
};