Build An ERC20 Token Claim App in React
⚠️ Warning: This guide currently uses v4 of the Connect SDK. For v5 (latest) code snippets, please check out our documentation while this guide is being updated. ⚠️
In this guide, we will build a web application that allows users to claim ERC20 tokens in a Next.js app, and also show all the holders of our token!
Just like our NFT Drop contract, the Token Drop contract makes the token available for users to claim.
Check out our example repository with the project built out on Github and click here to see a live example of the app.
Flow
The user flow of this app is as follows, a user enters the app, connects their wallet and enters the number of tokens they want to claim. Once the claim button is clicked, the transaction is processed and the user receives their tokens!
Setup
To build this app, we need to walk through the following steps.
- Deploy a Token Drop contract and define the claim phases for your NFTs using the dashboard
- Build the Next.js project (we will be using the starter template thirdweb has made available!)
- Build the front end, including a
Claim
button for the token - Show a list of token holders in our front end
Build
First, deploy a Token Drop
contract from the dashboard by clicking on + Deploy new contract
.
Once deployed, set your claim conditions under the Claim Phases
which outline the rules of how tokens can be claimed.
To set up a project with our Web3 SDK installed, run the following command to get the Next.js JavaScript starter template from the CLI:
npx thirdweb create --next --js
Inside the _app.js
file, change the chain ID to the chain you deployed the smart contract to.
import { ChainId, ThirdwebProvider } from "@thirdweb-dev/react";
// This is the chainId your dApp will work on.
const activeChainId = ChainId.Mainnet;
function MyApp({ Component, pageProps }) {
return (
<ThirdwebProvider desiredChainId={activeChainId}>
<Component {...pageProps} />
</ThirdwebProvider>
);
}
export default MyApp;
Connect our smart contract
Inside the index.js
file import the hook to connect to our Token Drop
contract and pass your contract address.
import {useTokenDrop} from "@thirdweb-dev/react";
//add your own contract address below
const tokenDropContract = useTokenDrop(<CONTRACT_ADDRESS>);
Claim component
We’re going to use the useClaimToken
hook from the React SDK to allow the connected wallet to claim tokens from the drop.
Because we’re allowing the user to enter the amount, we need to make use of the react hook useState
to allow our app to handle the dynamic input.
const address = useAddress();
const [amountToClaim, setAmountToClaim] = useState("");
const { mutate: claimTokens } = useClaimToken(tokenDropContract);
async function claim() {
if (!amountToClaim || !address) {
return;
}
try {
claimTokens(
{
to: address,
amount: amountToClaim,
},
{
onSuccess: (data) => {
console.log("Claimed", data);
alert("Successfully claimed!");
},
},
);
} catch (e) {
console.error(e);
alert(e);
}
}
Finally, we bind the method via a function to a button
<button onClick={claim}>
The full code looks something like this 👇
import { useAddress, useClaimToken } from "@thirdweb-dev/react";
import React, { useState } from "react";
export default function Claim({ tokenDropContract }) {
const address = useAddress();
const [amountToClaim, setAmountToClaim] = useState("");
const { mutate: claimTokens } = useClaimToken(tokenDropContract);
async function claim() {
if (!amountToClaim || !address) {
return;
}
try {
claimTokens(
{
to: address,
amount: amountToClaim,
},
{
onSuccess: (data) => {
console.log("Claimed", data);
alert("Successfully claimed!");
},
},
);
} catch (e) {
console.error(e);
alert(e);
}
}
return (
<div>
<input
type="text"
placeholder="Enter amount to claim"
onChange={(e) => setAmountToClaim(e.target.value)}
/>
<button onClick={claim}>Claim</button>
</div>
);
}
Token Holders
Now to render out a list of our token holders, we’ll use the getAllHolderBalances
method.
It’s straightforward, build the function and render it out inside the component like this.
Let’s build out the function first:
const [holders, setHolders] = useState([]);
async function checkHolders() {
//define the method
const balances = await token.history.getAllHolderBalances();
//assign the data to the variable balances
setHolders(balances);
}
Next, we’ll trigger the function upon loading the page. We can do that with the hook useEffect
useEffect(() => {
checkHolders();
}, []);
Now we need to render out the data inside our page. We can use a map
method to render out all the holders and their balances like this.
{
holders?.map((holder) => (
<div key={holder.holder}>
<p>{holder.holder}</p>
<p>
{holder.balance.displayValue} {holder.balance.symbol}
</p>
</div>
));
}
Here is the full page, including a loading view and sorting of the balances 👇
export default function TokenHolders() {
const [loading, setLoading] = useState(true);
const [holders, setHolders] = useState([]);
async function checkHolders() {
const sdk = new ThirdwebSDK("mumbai"); // configure this to your network
const token = sdk.getToken("0xCFbB61aF7f8F39dc946086c378D8cd997C72e2F3");
const balances = await token.history.getAllHolderBalances();
setHolders(balances);
setLoading(false);
}
useEffect(() => {
checkHolders();
}, []);
if (loading) {
return <div>Loading...</div>;
}
return (
<>
<div>
{holders
.sort(
(a, b) =>
parseInt(b.balance.displayValue) -
parseInt(a.balance.displayValue),
)
.map((holder) => (
<div key={holder.holder}>
<p>{truncateAddress(holder.holder)}</p>
<p>
{holder.balance.displayValue} {holder.balance.symbol}
</p>
</div>
))}
</div>
</>
);
}
That’s it!
That’s how you integrate the Token Drop
contract with a NextJS
app.
This guide is just an example of how to do it. Again we went over the core logic of the app.
Feel free to go to the repo to get a full copy and start building!