Token Balances, Web3 Woes, and React Rejoicing: A Trifecta of Tech Triumph
Aghasi Gasparyan
Co-founder at Galileo Figaro. Solidity & Web3 - Smart Contract Developer - galileofigaro.io
Step-by-step tutorial on creating a React application that gets the Ethereum address balances of the tokens that the address has in their balance. Here are the steps:
Before we start, you will need to set up your development environment. Ensure that you have Node.js and npm installed on your computer. You can check if you have these installed by running the following commands in your terminal:
node -
npm -v
If you don't have Node.js or npm installed, you can download them from the official website at https://nodejs.org.
Step 2: Create a new React project
The first step is to create a new React project. To do this, you can use the create-react-app command. Open your terminal and navigate to the directory where you want to create your project, and run the following command:
npx create-react-app my-app
This will create a new React project called my-app. You can replace my-app with the name of your choice.
Step 3: Install required packages
Next, you need to install the required packages for this project. In your terminal, navigate to the root directory of your project and run the following command:
npm install web3 --save
This will install the web3 library, which we will use to interact with the Ethereum blockchain.
Step 4: Create a new file for your code
Now that you have your project set up, you can start writing your code. In the root directory of your project, create a new file called App.js. This will be the main component for your application.
Step 5: Import necessary modules and define variables
In your App.js file, start by importing the necessary modules:
import React, { useState, useEffect } from 'react'
import Web3 from 'web3';
import './App.css';
Next, define the following variables
const infuraEndpoint = ''; // Replace YOUR_INFURA_ENDPOINT with your Infura endpoin
const etherscanApiKey = ''; // Replace YOUR_ETHERSCAN_API_KEY with your Etherscan API key
const abi = [
{
"constant": true,
"inputs": [{ "name": "_owner", "type": "address" }],
"name": "balanceOf",
"outputs": [{ "name": "balance", "type": "uint256" }],
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [{ "name": "", "type": "string" }],
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [{ "name": "", "type": "string" }],
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{ "name": "", "type": "uint8" }],
"type": "function"
}
];
The infuraEndpoint variable should contain your Infura endpoint. You can sign up for a free account at https://infura.io. The etherscanApiKey variable should contain your Etherscan API key.
Step 7: The rest of the code
Update the fetchBalances function in the useEffect hook to fetch the token transfer events using the etherscanApiKey and the address state variable.
async function fetchBalances() { if (address === '') { return; } const web3 = new Web3(infuraEndpoint); const tokenTransferEventsUrl = `https://api.etherscan.io/api?module=account&action=tokentx&address=${address}&sort=desc&apikey=${etherscanApiKey}`; const response = await fetch(tokenTransferEventsUrl); const data = await response.json(); // ... }
Extract the token addresses from the transfer events data by using a Set.
领英推荐
const tokenAddresses = new Set(data.result.map(tx => tx.contractAddress));
Use web3.eth.getBalance to get the balance of the address in Ether.
const balanceEth = await web3.eth.getBalance(address);
Create an empty array to store the token balances.
const balances = [];
Add the Ethereum balance to the balances array.
balances.push({ tokenAddress: 'Native token', symbol: 'ETH', name: 'Ethereum', balance: balanceEth / 10 ** 18 });
Loop through the token addresses and get the balance, symbol, name, and decimals of each token using the token contract ABI.
for (const tokenAddress of tokenAddresses) { const tokenContract = new web3.eth.Contract(abi, tokenAddress); const balance = await tokenContract.methods.balanceOf(address).call(); const symbol = await tokenContract.methods.symbol().call(); const name = await tokenContract.methods.name().call(); const decimals = await tokenContract.methods.decimals().call(); balances.push({ tokenAddress: tokenAddress, symbol: symbol, name: name, balance: balance / 10 ** decimals, }); }
Set the balances state variable to the array of balances.
setBalances(balances);
In the handleSubmit function, prevent the default form submission behavior using event.preventDefault(). Then, clear the balances state variable and set the address state variable to the value of the input field.
const handleSubmit = (event) => { event.preventDefault(); setBalances([]); setAddress(event.target.elements.address.value); }
Step 8: Update App.css file
import React, { useState, useEffect } from 'react'
import Web3 from 'web3';
import './App.css';
const infuraEndpoint = ''; // Replace YOUR_INFURA_ENDPOINT with your Infura endpoint
const etherscanApiKey = ''; // Replace YOUR_ETHERSCAN_API_KEY with your Etherscan API key
const abi = [
? {
? ? "constant": true,
? ? "inputs": [{ "name": "_owner", "type": "address" }],
? ? "name": "balanceOf",
? ? "outputs": [{ "name": "balance", "type": "uint256" }],
? ? "type": "function"
? },
? {
? ? "constant": true,
? ? "inputs": [],
? ? "name": "name",
? ? "outputs": [{ "name": "", "type": "string" }],
? ? "type": "function"
? },
? {
? ? "constant": true,
? ? "inputs": [],
? ? "name": "symbol",
? ? "outputs": [{ "name": "", "type": "string" }],
? ? "type": "function"
? },
? {
? ? "constant": true,
? ? "inputs": [],
? ? "name": "decimals",
? ? "outputs": [{ "name": "", "type": "uint8" }],
? ? "type": "function"
? }
];
function App() {
? const [balances, setBalances] = useState([]);
? const [address, setAddress] = useState('');
? useEffect(() => {
? ? async function fetchBalances() {
? ? ? if (address === '') {
? ? ? ? return;
? ? ? }
? ? ?
? ? ? const web3 = new Web3(infuraEndpoint);
? ? ? const tokenTransferEventsUrl = `https://api.etherscan.io/api?module=account&action=tokentx&address=${address}&sort=desc&apikey=${etherscanApiKey}`;
? ? ? const response = await fetch(tokenTransferEventsUrl);
? ? ? const data = await response.json();
? ? ? const tokenAddresses = new Set(data.result.map(tx => tx.contractAddress));
? ? ? const balanceEth = await web3.eth.getBalance(address);
? ? ? const balances = [];
? ? ? balances.push({
? ? ? ? tokenAddress: 'Native token',
? ? ? ? symbol: 'ETH',
? ? ? ? name: 'Ethereum',
? ? ? ? balance: balanceEth / 10 ** 18
? ? ? });
? ? ? for (const tokenAddress of tokenAddresses) {
? ? ? ? const tokenContract = new web3.eth.Contract(abi, tokenAddress);
? ? ? ? const balance = await tokenContract.methods.balanceOf(address).call();
? ? ? ? const symbol = await tokenContract.methods.symbol().call();
? ? ? ? const name = await tokenContract.methods.name().call();
? ? ? ? const decimals = await tokenContract.methods.decimals().call();
? ? ? ? balances.push({
? ? ? ? ? tokenAddress: tokenAddress,
? ? ? ? ? symbol: symbol,
? ? ? ? ? name: name,
? ? ? ? ? balance: balance / 10 ** decimals,
? ? ? ? });
? ? ? }
? ? ? setBalances(balances);
? ? }
? ? fetchBalances();
? }, [address]);
? const handleSubmit = (event) => {
? ? event.preventDefault();
? ? setBalances([]);
? ? setAddress(event.target.elements.address.value);
? }
? return (
? ? <div className="App">
? ? ? <h1>Address Balance Tracker</h1>
? ? ? <form onSubmit={handleSubmit}>
? ? ? ? ? <input type="text" name="address" placeholder='for ex. 0x000...000' />
? ? ? ? <button type="submit">Get Balances</button>
? ? ? </form>
? ? ? <div className="balances">
? ? ? ? {balances.length > 0 ? (
? ? ? ? ? balances.map((balance, index) => (
? ? ? ? ? ? <div className="balance" key={index}>
? ? ? ? ? ? ? <div className="token-name">{balance.name} ({balance.symbol})</div>
? ? ? ? ? ? ? <div className="token-balance">{balance.balance}</div>
? ? ? ? ? ? </div>
? ? ? ? ? ))
? ? ? ? ) : (
? ? ? ? ? <div className="no-balances">Enter an address to fetch token balances</div>
? ? ? ? )}
? ? ? </div>
? ? </div>
? );
}
export default App;
Step 9: Make some styling.
Update App.css file
.App
? background-color: black;
? color: white;
? font-family: Arial, sans-serif;
? display: flex;
? flex-direction: column;
? align-items: center;
}
.title {
? text-align: center;
? margin-top: 20px;
}
.balance {
? padding: 20px;
? margin: 20px;
? border: 1px solid white;
? border-radius: 5px;
? display: flex;
? flex-direction: column;
? align-items: center;
}
.token-name {
? font-weight: bold;
? color: yellow
}
.token-balance {
? color: pink
}
form {
? display: flex;
? flex-direction: column;
? align-items: center;
}
input[type="text"] {
? padding: 20px;
? border: 2px solid yellow
? border-radius: 5px;
? margin-top: 10px;
}
button {
? background-color: pink
? color: white;
? padding: 10px 20px;
? border: none;
? border-radius: 5px;
? margin-top: 10px;
}
.result {
? display: flex;
? flex-direction: column;
? align-items: center;
? margin-top: 20px;
}
Run the application by typing the following command in your terminal:
npm start
This should open up a browser window with the React application running. If you navigate to the address balance tracker page, you should see the form for entering an Ethereum address and a "Get Balances" button.
In this tutorial, we covered the following steps:
Feel free to modify this application to fit your needs, such as adding additional functionality to track other Ethereum data or creating a different user interface. Happy coding!