Write to a Contract with writeContractAsync
button
This recipe shows how to implement a button that allows users to interact with a smart contract by executing the writeContractAsync
function returned by useScaffoldWriteContract. By following this guide, you can create a user interface for writing data to a contract.
Here is the full code, which we will be implementing in the guide below:
import { useState } from "react";
import { parseEther } from "viem";
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
export const Greetings = () => {
const [newGreeting, setNewGreeting] = useState("");
const { writeContractAsync, isPending } = useScaffoldWriteContract("YourContract");
const writeContractAsyncWithParams = () =>
writeContractAsync(
{
functionName: "setGreeting",
args: [newGreeting],
value: parseEther("0.01"),
},
{
onBlockConfirmation: txnReceipt => {
console.log("๐ฆ Transaction blockHash", txnReceipt.blockHash);
},
},
);
return (
<>
<input
type="text"
placeholder="Write your greeting"
className="input border border-primary"
onChange={e => setNewGreeting(e.target.value)}
/>
<button
className="btn btn-primary"
onClick={async () => await writeContractAsyncWithParams()}
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. This component will enable users to write data to a smart contract.
export const Greetings = () => {
return (
<>
<input type="text" placeholder="Write your greeting" className="input border border-primary" />
<button>Send</button>
</>
);
};
Step 2: Initialize useScaffoldWriteContract
hookโ
Initialize the useScaffoldWriteContract
hook to set up the contract interaction. This hook provides the writeContractAsync
function for sending transactions, and we'll create writeContractAsyncWithParams
to pass the parameters to it.
import { useState } from "react";
import { parseEther } from "viem";
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
export const Greetings = () => {
const { writeContractAsync } = useScaffoldWriteContract("YourContract");
const writeContractAsyncWithParams = () =>
writeContractAsync(
{
functionName: "setGreeting",
args: [newGreeting],
value: parseEther("0.01"),
},
{
onBlockConfirmation: txnReceipt => {
console.log("๐ฆ Transaction blockHash", txnReceipt.blockHash);
},
},
);
return (
<>
<input type="text" placeholder="Write your greeting" className="input border border-primary" />
<button>Send</button>
</>
);
};
Step 3: Add input change logic and send transaction when users click the buttonโ
Design the user interface to allow users to input data and trigger the contract interaction. The example below demonstrates a simple form:
import { useState } from "react";
import { parseEther } from "viem";
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
export const Greetings = () => {
const [newGreeting, setNewGreeting] = useState("");
const { writeContractAsync } = useScaffoldWriteContract("YourContract");
const writeContractAsyncWithParams = () =>
writeContractAsync(
{
functionName: "setGreeting",
args: [newGreeting],
value: parseEther("0.01"),
},
{
onBlockConfirmation: txnReceipt => {
console.log("๐ฆ Transaction blockHash", txnReceipt.blockHash);
},
},
);
return (
<>
<input
type="text"
placeholder="Write your greeting"
className="input border border-primary"
onChange={e => setNewGreeting(e.target.value)}
/>
<button
className="btn btn-primary"
onClick={async () => await writeContractAsyncWithParams()}
>
Send
</button>
</>
);
};
Step 4: Bonus adding loading stateโ
We can use isPending
returned from useScaffoldWriteContract
while the transaction is being mined and also disable the button.
import { useState } from "react";
import { parseEther } from "viem";
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
export const Greetings = () => {
const [newGreeting, setNewGreeting] = useState("");
const { writeContractAsync, isPending } = useScaffoldWriteContract("YourContract");
const writeContractAsyncWithParams = () =>
writeContractAsync(
{
functionName: "setGreeting",
args: [newGreeting],
value: parseEther("0.01"),
},
{
onBlockConfirmation: txnReceipt => {
console.log("๐ฆ Transaction blockHash", txnReceipt.blockHash);
},
},
);
return (
<>
<input
type="text"
placeholder="Write your greeting"
className="input border border-primary"
onChange={e => setNewGreeting(e.target.value)}
/>
<button
className="btn btn-primary"
onClick={async () => await writeContractAsyncWithParams()}
disabled={isPending}
>
{isPending ? <span className="loading loading-spinner loading-sm"></span> : <>Send</>}
</button>
</>
);
};
You can also create a writeContractAsync
button sending args imperatively, here is an example:
<button
className="btn btn-primary"
onClick={async () =>
await writeContractAsync(
{
contractName: "YourContract",
functionName: "setGreeting",
args: [newGreeting],
value: parseEther("0.01"),
},
{
onBlockConfirmation: txnReceipt => {
console.log("๐ฆ Transaction blockHash", txnReceipt.blockHash);
},
},
)
}
disabled={isPending}
>
{isPending ? <span className="loading loading-spinner loading-sm"></span> : <>Send Imperatively</>}
</button>