Now that we know about the Token API and have a QuickNode RPC with Token API let us create a small React app to show balances of ERC20 tokens for a given address. To do so, let us install some libraries/dependencies we will use.
Copy-paste the following into your terminal/command prompt
how to create an erc20 token balance app with quicknode token api
npx create-react-app token_wallet
npx create-react-app token_wallet
npx create-react-app token_wallet
This will create a new directory, token_wallet, with the required React components.
We will need ethers.js to connect our QuickNode node to the React app and Tailwind CSS for styling our React app. Go to the recently created directly and copy-paste the following in your terminal/command prompt to install ethers and Tailwind CSS:
how to create an erc20 token balance app with quicknode token api
cd token_wallet
npm install ethers
npx tailwindcss init -p
cd token_wallet
npm install ethers
npx tailwindcss init -p
cd token_wallet
npm install ethers
npx tailwindcss init -p
We need to let Tailwind know about the file's path, which needs styling, so open your project directory in a code editor, open the tailwind.config.js file, and modify the config to look like this:
how to create an erc20 token balance app with quicknode token api
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
}
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
}
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
}
After saving the above file, navigate to src/index.js in your code editor and add the following line between lines 9 and 10:
how to create an erc20 token balance app with quicknode token api
<link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet" />
This will import Tailwind's utility classes.
The app we are creating will have a form input field and a submit button. Once an address is input and the button is clicked, using the Token API, ERC20 token data will be retrieved for the imputed address and displayed
Start building by going to src/app.js in your code editor and pasting the following code:
how to create an erc20 token balance app with quicknode token api
import { useState } from 'react';
import { ethers, utils } from "ethers";
function App() {
//State variables
const [tokens, setTokens] = useState([])
const [address, setAddress] = useState('')
}
export default App;
import { useState } from 'react';
import { ethers, utils } from "ethers";
function App() {
//State variables
const [tokens, setTokens] = useState([])
const [address, setAddress] = useState('')
}
export default App;
import { useState } from 'react';
import { ethers, utils } from "ethers";
function App() {
//State variables
const [tokens, setTokens] = useState([])
const [address, setAddress] = useState('')
}
export default App;
In the above code, on
Line 1-2: we're importing the React useState hook to track our input wallet address and ethers library to connect our app with the Ethereum blockchain using QuickNode.
Line 4-7: Starting our React app named App and declaring two state variables to store token data and another to store input address, respectively.
Now add the following on line 9:
how to create an erc20 token balance app with quicknode token api
//Function to handle form submission
const handleSubmit = (e) => {
e.preventDefault();
setAddress(address);
fetchTokens()
.then(data => {
setTokens(data.assets)
})
.catch(err => setTokens([]))
}
//Function to fetch tokens
const fetchTokens = async () => {
if (!utils.isAddress(address)){
alert('Please enter a valid Ethereum wallet address')
return;
}
const provider = new ethers.providers.JsonRpcProvider("YOUR-QUICKNODE-HTTP-URL-HERE");
const tokens = await provider.send("qn_getWalletTokenBalance", {
wallet: address,
contracts: [
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', //WETH
'0xdAC17F958D2ee523a2206206994597C13D831ec7', //USDT
'0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', //MATIC
'0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72', //ENS
]
});
return tokens
}
//Function to handle form submission
const handleSubmit = (e) => {
e.preventDefault();
setAddress(address);
fetchTokens()
.then(data => {
setTokens(data.assets)
})
.catch(err => setTokens([]))
}
//Function to fetch tokens
const fetchTokens = async () => {
if (!utils.isAddress(address)){
alert('Please enter a valid Ethereum wallet address')
return;
}
const provider = new ethers.providers.JsonRpcProvider("YOUR-QUICKNODE-HTTP-URL-HERE");
const tokens = await provider.send("qn_getWalletTokenBalance", {
wallet: address,
contracts: [
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', //WETH
'0xdAC17F958D2ee523a2206206994597C13D831ec7', //USDT
'0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', //MATIC
'0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72', //ENS
]
});
return tokens
}
//Function to handle form submission
const handleSubmit = (e) => {
e.preventDefault();
setAddress(address);
fetchTokens()
.then(data => {
setTokens(data.assets)
})
.catch(err => setTokens([]))
}
//Function to fetch tokens
const fetchTokens = async () => {
if (!utils.isAddress(address)){
alert('Please enter a valid Ethereum wallet address')
return;
}
const provider = new ethers.providers.JsonRpcProvider("YOUR-QUICKNODE-HTTP-URL-HERE");
const tokens = await provider.send("qn_getWalletTokenBalance", {
wallet: address,
contracts: [
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', //WETH
'0xdAC17F958D2ee523a2206206994597C13D831ec7', //USDT
'0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', //MATIC
'0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72', //ENS
]
});
return tokens
}
Replace YOUR-QUICKNODE-HTTP-URL-HERE with QuickNode's HTTP Provider link we got in the last step.
In the above code, on
Line 10-18: We're writing a function handleSubmit to handle the form submission event; basically, it is changing the state of the address variable to store the imputed address's value, then calling the function fetchTokens, then changing the state of the tokens variable to store token data.
Line 20-37: A function fetchTokens to fetch tokens. First, an if statement to check if the input address is a valid Ethereum address or not using the ethers isAddress function. Then connecting to QuickNode Ethereum node. Making a Token API call to get wallet tokens by passing the address as a parameter along contract addresses of WETH, USDT, MATIC, and ENS to get these specific token's data. Then Returning tokens
On line 38, paste the following code:
how to create an erc20 token balance app with quicknode token api
//JSX code
return(
<div className="h-screen w-screen justify-center space-x-3">
<div className="flex justify-center space-x-3 w-screen h-14 mt-10">
<form
onSubmit={handleSubmit}
className="w-4/12 h-15 relative block overflow-hidden rounded-md border border-gray-200 px-3 pt-3 shadow-sm focus-within:border-blue-600 focus-within:ring-1 focus-within:ring-blue-600 dark:border-gray-700 dark:bg-gray-800">
<input
onChange={e => setAddress(e.target.value)}
type="text"
placeholder="Enter your Address here 🎯"
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
<button
type='submit'
className="rounded-lg top-1 right-1 bottom-1 border absolute w-12 justify-center bg-blue-400 text-white p-3 font-bold uppercase"
>
GO
</button>
</form>
</div>
<div className="relative top-4/12 left-1/4 overflow-x-auto justify-center space-x-3 w-6/12 h-140 m-10">
<table className="min-w-full divide-y-2 divide-gray-200 text-sm">
<thead>
<tr>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-1000">
Name
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Symbol
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Balance
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{tokens.map((token, index) => (
<tr key={index}>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.name}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.symbol}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{utils.formatUnits(token.amount, token.decimals)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
//JSX code
return(
<div className="h-screen w-screen justify-center space-x-3">
<div className="flex justify-center space-x-3 w-screen h-14 mt-10">
<form
onSubmit={handleSubmit}
className="w-4/12 h-15 relative block overflow-hidden rounded-md border border-gray-200 px-3 pt-3 shadow-sm focus-within:border-blue-600 focus-within:ring-1 focus-within:ring-blue-600 dark:border-gray-700 dark:bg-gray-800">
<input
onChange={e => setAddress(e.target.value)}
type="text"
placeholder="Enter your Address here 🎯"
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
<button
type='submit'
className="rounded-lg top-1 right-1 bottom-1 border absolute w-12 justify-center bg-blue-400 text-white p-3 font-bold uppercase"
>
GO
</button>
</form>
</div>
<div className="relative top-4/12 left-1/4 overflow-x-auto justify-center space-x-3 w-6/12 h-140 m-10">
<table className="min-w-full divide-y-2 divide-gray-200 text-sm">
<thead>
<tr>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-1000">
Name
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Symbol
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Balance
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{tokens.map((token, index) => (
<tr key={index}>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.name}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.symbol}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{utils.formatUnits(token.amount, token.decimals)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
//JSX code
return(
<div className="h-screen w-screen justify-center space-x-3">
<div className="flex justify-center space-x-3 w-screen h-14 mt-10">
<form
onSubmit={handleSubmit}
className="w-4/12 h-15 relative block overflow-hidden rounded-md border border-gray-200 px-3 pt-3 shadow-sm focus-within:border-blue-600 focus-within:ring-1 focus-within:ring-blue-600 dark:border-gray-700 dark:bg-gray-800">
<input
onChange={e => setAddress(e.target.value)}
type="text"
placeholder="Enter your Address here 🎯"
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
<button
type='submit'
className="rounded-lg top-1 right-1 bottom-1 border absolute w-12 justify-center bg-blue-400 text-white p-3 font-bold uppercase"
>
GO
</button>
</form>
</div>
<div className="relative top-4/12 left-1/4 overflow-x-auto justify-center space-x-3 w-6/12 h-140 m-10">
<table className="min-w-full divide-y-2 divide-gray-200 text-sm">
<thead>
<tr>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-1000">
Name
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Symbol
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Balance
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{tokens.map((token, index) => (
<tr key={index}>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.name}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.symbol}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{utils.formatUnits(token.amount, token.decimals)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
The above code is basic HTML in JS (JSX) and Tailwind styling. The few worth noting lines are:
Line 44: Calling the handleSubmit function on form submission.
Line 47: Getting the current value of the input field and setting it to setAddress.
Line 79: Mapping the token data in the tokens array.
Line 81: Printing the token's name, symbol, and balance (formatting the units based on that token's decimal values).
Note: The app will only display balances of tokens which me added in the contracts section of the RPC.
Save the file, and the final file should look like this:
how to create an erc20 token balance app with quicknode token api
import { useState } from 'react';
import { ethers, utils } from "ethers";
function App() {
//State variables
const [tokens, setTokens] = useState([])
const [address, setAddress] = useState('')
//Function to handle form submission
const handleSubmit = (e) => {
e.preventDefault();
setAddress(address);
fetchTokens()
.then(data => {
setTokens(data.assets)
})
.catch(err => setTokens([]))
}
//Function to fetch tokens
const fetchTokens = async () => {
if (!utils.isAddress(address)){
alert('Please enter a valid Ethereum wallet address')
return;
}
const provider = new ethers.providers.JsonRpcProvider("YOUR-QUICKNODE-HTTP-URL-HERE");
const tokens = await provider.send("qn_getWalletTokenBalance", {
wallet: address,
contracts: [
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', //WETH
'0xdAC17F958D2ee523a2206206994597C13D831ec7', //USDT
'0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', //MATIC
'0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72', //ENS
]
});
return tokens
}
//JSX code
return(
<div className="h-screen w-screen justify-center space-x-3">
<div className="flex justify-center space-x-3 w-screen h-14 mt-10">
<form
onSubmit={handleSubmit}
className="w-4/12 h-15 relative block overflow-hidden rounded-md border border-gray-200 px-3 pt-3 shadow-sm focus-within:border-blue-600 focus-within:ring-1 focus-within:ring-blue-600 dark:border-gray-700 dark:bg-gray-800">
<input
onChange={e => setAddress(e.target.value)}
type="text"
placeholder="Enter your Address here 🎯"
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
<button
type='submit'
className="rounded-lg top-1 right-1 bottom-1 border absolute w-12 justify-center bg-blue-400 text-white p-3 font-bold uppercase"
>
GO
</button>
</form>
</div>
<div className="relative top-4/12 left-1/4 overflow-x-auto justify-center space-x-3 w-6/12 h-140 m-10">
<table className="min-w-full divide-y-2 divide-gray-200 text-sm">
<thead>
<tr>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-1000">
Name
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Symbol
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Balance
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{tokens.map((token, index) => (
<tr key={index}>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.name}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.symbol}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{utils.formatUnits(token.amount, token.decimals)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
export default App;
import { useState } from 'react';
import { ethers, utils } from "ethers";
function App() {
//State variables
const [tokens, setTokens] = useState([])
const [address, setAddress] = useState('')
//Function to handle form submission
const handleSubmit = (e) => {
e.preventDefault();
setAddress(address);
fetchTokens()
.then(data => {
setTokens(data.assets)
})
.catch(err => setTokens([]))
}
//Function to fetch tokens
const fetchTokens = async () => {
if (!utils.isAddress(address)){
alert('Please enter a valid Ethereum wallet address')
return;
}
const provider = new ethers.providers.JsonRpcProvider("YOUR-QUICKNODE-HTTP-URL-HERE");
const tokens = await provider.send("qn_getWalletTokenBalance", {
wallet: address,
contracts: [
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', //WETH
'0xdAC17F958D2ee523a2206206994597C13D831ec7', //USDT
'0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', //MATIC
'0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72', //ENS
]
});
return tokens
}
//JSX code
return(
<div className="h-screen w-screen justify-center space-x-3">
<div className="flex justify-center space-x-3 w-screen h-14 mt-10">
<form
onSubmit={handleSubmit}
className="w-4/12 h-15 relative block overflow-hidden rounded-md border border-gray-200 px-3 pt-3 shadow-sm focus-within:border-blue-600 focus-within:ring-1 focus-within:ring-blue-600 dark:border-gray-700 dark:bg-gray-800">
<input
onChange={e => setAddress(e.target.value)}
type="text"
placeholder="Enter your Address here 🎯"
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
<button
type='submit'
className="rounded-lg top-1 right-1 bottom-1 border absolute w-12 justify-center bg-blue-400 text-white p-3 font-bold uppercase"
>
GO
</button>
</form>
</div>
<div className="relative top-4/12 left-1/4 overflow-x-auto justify-center space-x-3 w-6/12 h-140 m-10">
<table className="min-w-full divide-y-2 divide-gray-200 text-sm">
<thead>
<tr>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-1000">
Name
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Symbol
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Balance
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{tokens.map((token, index) => (
<tr key={index}>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.name}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.symbol}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{utils.formatUnits(token.amount, token.decimals)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
export default App;
import { useState } from 'react';
import { ethers, utils } from "ethers";
function App() {
//State variables
const [tokens, setTokens] = useState([])
const [address, setAddress] = useState('')
//Function to handle form submission
const handleSubmit = (e) => {
e.preventDefault();
setAddress(address);
fetchTokens()
.then(data => {
setTokens(data.assets)
})
.catch(err => setTokens([]))
}
//Function to fetch tokens
const fetchTokens = async () => {
if (!utils.isAddress(address)){
alert('Please enter a valid Ethereum wallet address')
return;
}
const provider = new ethers.providers.JsonRpcProvider("YOUR-QUICKNODE-HTTP-URL-HERE");
const tokens = await provider.send("qn_getWalletTokenBalance", {
wallet: address,
contracts: [
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', //WETH
'0xdAC17F958D2ee523a2206206994597C13D831ec7', //USDT
'0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', //MATIC
'0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72', //ENS
]
});
return tokens
}
//JSX code
return(
<div className="h-screen w-screen justify-center space-x-3">
<div className="flex justify-center space-x-3 w-screen h-14 mt-10">
<form
onSubmit={handleSubmit}
className="w-4/12 h-15 relative block overflow-hidden rounded-md border border-gray-200 px-3 pt-3 shadow-sm focus-within:border-blue-600 focus-within:ring-1 focus-within:ring-blue-600 dark:border-gray-700 dark:bg-gray-800">
<input
onChange={e => setAddress(e.target.value)}
type="text"
placeholder="Enter your Address here 🎯"
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
<button
type='submit'
className="rounded-lg top-1 right-1 bottom-1 border absolute w-12 justify-center bg-blue-400 text-white p-3 font-bold uppercase"
>
GO
</button>
</form>
</div>
<div className="relative top-4/12 left-1/4 overflow-x-auto justify-center space-x-3 w-6/12 h-140 m-10">
<table className="min-w-full divide-y-2 divide-gray-200 text-sm">
<thead>
<tr>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-1000">
Name
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Symbol
</th>
<th
className="whitespace-nowrap px-4 py-2 text-left font-medium text-gray-900">
Balance
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{tokens.map((token, index) => (
<tr key={index}>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.name}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{token.symbol}</td>
<td className="whitespace-nowrap px-4 py-2 text-gray-900">{utils.formatUnits(token.amount, token.decimals)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
export default App;
Start the application by running the following:
how to create an erc20 token balance app with quicknode token api
It should look something like this.
Paste an address in the input field and click on GO; you should see the balances of tokens.