Skip to main content

Write to a Contract with writeAsync button

This recipe shows how to implement a button that allows users to interact with a smart contract by executing the writeAsync function returned by useScaffoldContractWrite. 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:
components/Greetings.tsx
import { useState } from "react";
import { parseEther } from "viem";
import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth";

export const Greetings = () => {
const [newGreeting, setNewGreeting] = useState("");

const { writeAsync, isLoading } = useScaffoldContractWrite({
contractName: "YourContract",
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={() => writeAsync()} disabled={isLoading}>
{isLoading ? <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.

components/Greetings.tsx
export const Greetings = () => {
return (
<>
<input type="text" placeholder="Write your greeting" className="input border border-primary" />
<button>Send</button>
</>
);
};

Step 2: Initialize useScaffoldContractWrite hookโ€‹

Initialize the useScaffoldContractWrite hook to set up the contract interaction. This hook provides the writeAsync function for sending transactions.

import { useState } from "react";
import { parseEther } from "viem";
import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth";

export const Greetings = () => {
const [newGreeting, setNewGreeting] = useState("");

const { writeAsync } = useScaffoldContractWrite({
contractName: "YourContract",
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 { useScaffoldContractWrite } from "~~/hooks/scaffold-eth";

export const Greetings = () => {
const [newGreeting, setNewGreeting] = useState("");

const { writeAsync } = useScaffoldContractWrite({
contractName: "YourContract",
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={() => writeAsync()}
>
Send
</button>
</>
);
};

Step 4: Bonus adding loading stateโ€‹

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

import { useState } from "react";
import { parseEther } from "viem";
import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth";

export const Greetings = () => {
const [newGreeting, setNewGreeting] = useState("");
const { writeAsync, isLoading } = useScaffoldContractWrite({
contractName: "YourContract",
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={() => writeAsync()}
disabled={isLoading}
>
{isLoading ? <span className="loading loading-spinner loading-sm"></span> : <>Send</>}
</button>
</>
);
};
Hint

You can also create a writeAsync button sending args imperatively, here is an example:

<button className="btn btn-primary" onClick={() => writeAsync({ args: ["Hello"], value: parseEther("0.02") })}>
Send imperatively
</button>