Wait for Transaction Receipt to Send a Second Transaction
This recipe shows how to create a multi-step transaction process where you wait for the receipt of the first transaction before sending a second transaction.
Before You Beginโ
Before you proceed with this recipe, make sure you have the required dependencies installed, and you're familiar with setting up your Ethereum development environment.
In this recipe you will use a few hooks:
- usePublicClient and useContractWrite from wagmi.
- useScaffoldContractWrite and useDeployedContractInfo Scaffold ETH-2 hooks.
You'll also use AddressInput and InputBase Scaffold ETH-2 components, ParseEther viem utility and erc721ABI from wagmi. We recommend checking out the details of these hooks, components and utils before start implementing this recipe.
Implementationโ
Step 1: Create Your Componentโ
Begin by creating a new component, which we'll name "SellNFT.tsx". This component will allow users to perform a multi-step ERC721 approve+listing process for selling a NFT, similar to the ERC20 approve pattern.
Import the necessary libraries and components:
import { useState } from "react";
import type { NextPage } from "next";
import { parseEther } from "viem";
import { erc721ABI, useContractWrite, usePublicClient } from "wagmi";
import { AddressInput, InputBase } from "~~/components/scaffold-eth/Input";
import { useDeployedContractInfo, useScaffoldContractWrite } from "~~/hooks/scaffold-eth";
Define the functional component "SellNFT" which will be used to create the user interface for the multi-step transaction process:
const SellNFT: NextPage = () => {
// Your component code will go here.
};
Step 2: Initialize Hooks for Contract Interactionโ
Initialize the necessary hooks for interacting with the smart contract. In this example, we'll use two hooks: useContractWrite
and useScaffoldContractWrite
.
const [address, setAddress] = useState("");
const [tokenId, setTokenId] = useState("");
const [price, setPrice] = useState("");
const publicClient = usePublicClient();
const { data: NftMarketplace } = useDeployedContractInfo("NftMarketplace");
const approveTx = useContractWrite({
address: address,
abi: erc721ABI,
functionName: "approve",
args: [NftMarketplace?.address as string, BigInt(tokenId)],
});
const listNftTx = useScaffoldContractWrite({
contractName: "NftMarketplace",
functionName: "listItem",
args: [address, BigInt(tokenId), parseEther(price)],
});
Step 3: Create the Multi-Step Transaction Processโ
Design the user interface to allow users to perform a multi-step transaction process. In this example, we've created a form for selling a NFT:
return (
<div>
<h2 className="p-8 text-xl font-bold">Sell Your NFT</h2>
<div className="flex w-1/3">
<form className="w-full pl-8" onSubmit={handleSubmit}>
<div className="mb-3">
<label>NFT Address</label>
<AddressInput value={address} onChange={val => setAddress(val)} />
</div>
<div className="mb-3">
<label>NFT Token ID</label>
<InputBase value={tokenId} onChange={val => setTokenId(val)} />
</div>
<div className="mb-3">
<label>Price (ETH)</label>
<InputBase value={price} onChange={val => setPrice(val)} />
</div>
<div className="flex justify-end">
<button className="btn btn-primary" type="submit">
Submit
</button>
</div>
</form>
</div>
</div>
);
Step 4: Implement Multi-Step Transaction Logicโ
Add logic to handle the multi-step transaction process. In this example, we submit the first transaction to approve the marketplace contract address to transfer our NFT. Then we wait for its receipt before proceeding to list the NFT for sale:
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const approveHash = await approveTx?.writeAsync();
await publicClient.waitForTransactionReceipt(approveHash);
await listNftTx?.writeAsync();
};
Step 5: Test and Deployโ
Test your multi-step transaction process to ensure that it correctly waits for the receipt of the first transaction before proceeding with the second.
Conclusionโ
By following these steps, you've created a multi-step transaction process that waits for the receipt of the first transaction before sending the second. This behaviour will be useful for many different types of smart contract interactions, like approving a contract to spend your tokens, or approving a contract to transfer your NFTs.
Full Recipe Codeโ
Here's the complete code for the "SellNFT" component:
import { useState } from "react";
import type { NextPage } from "next";
import { parseEther } from "viem";
import { erc721ABI, useContractWrite, usePublicClient } from "wagmi";
import { AddressInput, InputBase } from "~~/components/scaffold-eth/Input";
import { useDeployedContractInfo, useScaffoldContractWrite } from "~~/hooks/scaffold-eth";
const SellNFT: NextPage = () => {
const [address, setAddress] = useState("");
const [tokenId, setTokenId] = useState("");
const [price, setPrice] = useState("");
const publicClient = usePublicClient();
const { data: NftMarketplace } = useDeployedContractInfo("NftMarketplace");
const approveTx = useContractWrite({
address: address,
abi: erc721ABI,
functionName: "approve",
args: [NftMarketplace?.address as string, BigInt(tokenId)],
});
const listNftTx = useScaffoldContractWrite({
contractName: "NftMarketplace",
functionName: "listItem",
args: [address, BigInt(tokenId), parseEther(price)],
});
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const approveHash = await approveTx?.writeAsync();
await publicClient.waitForTransactionReceipt(approveHash);
await listNftTx?.writeAsync();
};
return (
<div>
<h2 className="p-8 text-xl font-bold">Sell Your NFT</h2>
<div className="flex w-1/3">
<form className="w-full pl-8" onSubmit={handleSubmit}>
<div className="mb-3">
<label>NFT Address</label>
<AddressInput value={address} onChange={val => setAddress(val)} />
</div>
<div className="mb-3">
<label>NFT Token ID</label>
<InputBase value={tokenId} onChange={val => setTokenId(val)} />
</div>
<div className="mb-3">
<label>Price (eth)</label>
<InputBase value={price} onChange={val => setPrice(val)} />
</div>
<div className="flex justify-end">
<button className="btn btn-primary" type="submit">
Submit
</button>
</div>
</form>
</div>
</div>
);
};
export default SellNFT;