As decentralized finance and Web3 applications are rapidly growing in popularity, there is also a rise in investors building their crypto asset portfolios across different blockchains. Keeping track of these assets as they spread across these protocols can be daunting, especially without using the right tools. This is where a Web3 portfolio tracker comes in handy.
In this guide, you’ll learn how to build your own Web3 portfolio tracker using the GoldRush Unified API. You can check out and interact with the Portfolio Tracker application we will build here!
GoldRush provides a unified API to easily access on-chain data across multiple blockchains like Ethereum, Binance Chain (BSC), Polygon and many more. We will be using the GoldRush API to build a portfolio tracker that can provide a list of all the assets (ERC-20 tokens and NFTs) a wallet address is holding and also displays the market value of these assets which gives the users a clear overview of how much they have invested across multiple blockchains.
A good understanding of JavaScript.
Knowledge of React.js.
Knowledge of how to build front-end applications using React.js.
Knowledge of how to interact with APIs.
Have Node.js and NPM or Yarn installed on your computer.
Have VSCode installed.
Interacting with the GoldRush Unified API requires that you have an account on the GoldRush platform which gives you access to generating an API key for interacting with the API endpoints. Use the following guide to generate an API key:
Navigate to the GoldRush Website.
Click on the Signup for a free API key button on the main page to create an account.
On the signup page, fill out the required input fields, check the Terms of Use checkbox, and click the Sign me up button.
After signing up, verify your email and log in to your GoldRush account. You will be asked to provide some information about yourself in the input fields.
Once you have completed all the necessary onboarding/signup steps, go to GoldRush to create your own API key. An API key will be created for you automatically, else, click the dropdown beside the Manage API keys button and click the Create button.
Now you have created an account on GoldRush, and you also have an API key to interact with the GoldRush unified API, let’s proceed to building the Portfolio tracker.
Let’s start building the portfolio tracker application.
1
npx create-react-app .
2
3
4
import React, { useState, Fragment } from "react";
import { CovalentClient } from "@covalenthq/client-sdk";
import LoadingSpinner from "./UI/LoadingSpinner";
import { ethers } from 'ethers';
function App() {
const [address, setAddress] = useState("");
const [network, setNetwork] = useState("eth-mainnet");
const [tokenData, setTokenData] = useState(null);
const [nftData, setNftData] = useState(null);
const [nativeCurrencyData, setNativeCurrencyData] = useState(null);
const [loading, setLoading] = useState(false);
const client = new CovalentClient(process.env.REACT_APP_API_KEY);
const getWalletTokenBalance = async () => {
try {
const resp = await client.BalanceService.getTokenBalancesForWalletAddress(network, address);
setTokenData(resp.data);
} catch (error) {
console.error(error);
}
}
const getWalletNfts = async () => {
try {
const resp = await client.NftService.getNftsForAddress(network, address);
setNftData(resp.data);
} catch (error) {
console.error(error);
}
}
const getNativeTokenBalance = async () => {
try {
const resp = await client.BalanceService.getNativeTokenBalance(network, address);
setNativeCurrencyData(resp.data);
} catch (error) {
console.error(error);
}
}
const addressChangeHandler = (e) => {
e.preventDefault();
setAddress(e.target.value);
}
const networkChangeHandler = (e) => {
e.preventDefault();
setNetwork(e.target.value);
}
const fetchWalletTokens = async (e) => {
e.preventDefault();
if(address === "" || network === "") {
alert("input an address and select a blockchain network");
} else {
setLoading(true);
await getNativeTokenBalance();
await getWalletTokenBalance();
await getWalletNfts();
setLoading(false);
}
}
return (
<div className="App">
<div className="container">
<nav className="navbar navbar-expand-lg navbar-light bg-primary mb-1">
<a className="navbar-brand text-light font-weight-bold" href="!#">Portfolio Tracker</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarText">
<ul className="navbar-nav mr-auto"></ul>
<span className="navbar-text text-light">
Track your crypto assets across multiple chains
</span>
</div>
</nav>
<div className="row">
<div className="col-md-12">
<form>
<div className="input-group mb-3 input-group-lg">
<input type="text" className="form-control" placeholder="Wallet Address" onChange={addressChangeHandler} />
<select className="form-control" value={network} onChange={networkChangeHandler}>
<option value="eth-mainnet">Ethereum</option>
<option value="bsc-mainnet">Binance</option>
<option value="matic-mainnet">Polygon</option>
<option value="fantom-mainnet">Fantom</option>
</select>
<div className="input-group-append">
<button className="btn btn-primary" onClick={fetchWalletTokens}>Explore</button>
</div>
</div>
</form>
</div>
</div>
{loading ?
<div className="text-center mt-5">
<LoadingSpinner />
<h3 className="text-center text-black mt-3">Loading...</h3>
</div>
:
<Fragment>
<div className="row mt-4">
<div className="col-md-4">
<div className="card bg-primary text-white">
<div className="card-body">
<span>
Currency: {nativeCurrencyData !== null && nativeCurrencyData.items.length > 0 && ethers.utils.formatUnits(nativeCurrencyData.items[0].balance, nativeCurrencyData.items[0].contract_decimals)}
<strong>{nativeCurrencyData !== null && nativeCurrencyData.items.length > 0 && nativeCurrencyData.items[0].contract_ticker_symbol}</strong>
</span>
<h3>{nativeCurrencyData !== null && nativeCurrencyData.items.length > 0 && nativeCurrencyData.items[0].pretty_quote}</h3>
</div>
</div>
</div>
<div className="col-md-4">
<div className="card bg-primary text-white">
<div className="card-body">
<span>Tokens</span>
<h3>{tokenData !== null && tokenData.items.length}</h3>
</div>
</div>
</div>
<div className="col-md-4">
<div className="card bg-primary text-white">
<div className="card-body">
<span>NFTs:</span>
<h3>{nftData !== null && nftData.items.length}</h3>
</div>
</div>
</div>
</div>
<div className="row mt-5">
<div className="col-md-12">
<ul className="nav nav-tabs nav-justified">
<li className="nav-item">
<a className="nav-link active" data-toggle="tab" href="#tokens">Tokens</a>
</li>
<li className="nav-item">
<a className="nav-link" data-toggle="tab" href="#nfts">NFTs</a>
</li>
</ul>
<div className="tab-content">
<div className="tab-pane container active" id="tokens">
<div className="table-responsive my-4">
<table className="table table-striped table-light">
<thead>
<tr>
<th scope="col">SN</th>
<th scope="col">Token</th>
<th scope="col">Symbol</th>
<th scope="col">Amount</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
{tokenData !== null && tokenData.items.length > 0 &&
tokenData.items.map((item, index) => (
<tr key={item.contract_address}>
<th scope="row">{index + 1}</th>
<td><img style={{ width: "30px" }} src={`${item.logo_url}`} alt="" /> {item.contract_name}</td>
<td>{item.contract_ticker_symbol}</td>
<td>{ethers.utils.formatUnits(item.balance_24h, item.contract_decimal)}</td>
<td>{item.pretty_quote_24h === null ? "$0.00" : item.pretty_quote_24h}</td>
</tr>
))
}
</tbody>
</table>
</div>
</div>
<div className="tab-pane container fade" id="nfts">
<div className="table-responsive my-4">
<table className="table table-striped table-light">
<thead>
<tr>
<th scope="col">SN</th>
<th scope="col">NFT</th>
<th scope="col">Symbol</th>
<th scope="col">Contract Address</th>
<th scope="col">Floor Price</th>
</tr>
</thead>
<tbody>
{nftData !== null && nftData.items.length > 0 &&
nftData.items.map((item, index) => (
<tr key={item.contract_address}>
<th scope="row">{index + 1}</th>
<td>{item.contract_name}</td>
<td>{item.contract_ticker_symbol}</td>
<td>{item.contract_address}</td>
<td>{item.pretty_floor_price_quote === null ? "$0.00" : item.pretty_floor_price_quote}</td>
</tr>
))
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</Fragment>
}
</div>
</div>
);
}
export default App;
5
6
7
npm start
. This will open the application on http://localhost:3000.Let’s now interact with the portfolio tracker application we have created! To interact with the application:
Get any Ethereum wallet address and paste it inside the Wallet Address input field.
Select any blockchain you wish to see the portfolio and click the Explore button.
This will list out all the tokens (ERC-20 and NFTs) that the specified Ethereum address is holding.
The portfolio tracker shows all the ERC-20 tokens the given address is holding on Binance Smart Chain. In our example, it shows that the wallet address has thirty-one (31) ERC-20 tokens and one NFT on the Binance chain while also showing the value of these token assets.
The tracker also shows the NFT a given address is holding on Binance Smart Chain. You can switch chains and click the Explore button to check the wallet assets on other chains. This portfolio tracker supports four different blockchains; Ethereum, Binance, Polygon and Fantom.
The final step of building the portfolio tracker is to deploy the application, making it accessible to every other person on the internet.
We will be deploying this application on Vercel using the following steps:
1
2
3
Your application will deploy successfully and you will be provided a link to access your own portfolio tracker application. Access the full codebase for the portfolio tracker here.
The portfolio tracker application is a utility application that comes in very handy in the Web3 and DeFi ecosystem, making it easy for investors to keep track of their investments across multiple chains. Of course, the application we have built can be improved, you can explore other options to make it better by adding support for more blockchains and also showing on-chain transactions of a wallet address. Cheers, and Happy Building!