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:
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.
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>
);
};