Overview
To retrieve the balance of an ERC20 token, you need to interact with the token's smart contract. This requires the contract's address, ABI, and access to a blockchain node for data retrieval.
Fetching token balances across multiple blockchain networks can be time-consuming and complex. For a faster and more efficient approach, we've created this Function example. Here, we'll use QuickNode RPC to obtain the USDC balances of a wallet address on several EVM blockchain networks.
Sample Function
We will be using the ethers.js library to interact with the EVM blockchains. Thus, we will need to create a zip file and upload it to our Functions dashboard.
We will need to install ethers
and dotenv
with the following command:
npm i ethers dotenv
Create a .env
file to store RPC endpoint URLs from QuickNode. Create new endpoints on QuickNode.
ARBITRUM_RPC_URL=REPLACE_WITH_RPC_URL
AVALANCHE_RPC_URL=REPLACE_WITH_RPC_URL
BASE_RPC_URL=REPLACE_WITH_RPC_URL
BSC_RPC_URL=REPLACE_WITH_RPC_URL
CELO_RPC_URL=REPLACE_WITH_RPC_URL
ETHEREUM_RPC_URL=REPLACE_WITH_RPC_URL
OPTIMISM_RPC_URL=REPLACE_WITH_RPC_URL
POLYGON_RPC_URL=REPLACE_WITH_RPC_URL
ZKSYNC_RPC_URL=REPLACE_WITH_RPC_URL
Replace the REPLACE_WITH_RPC_URL placeholder with a URL for the specified chain.
Now, create an index.js
file and paste the following code into it:
// Import dotenv and ethers packages
require('dotenv').config();
const { ethers } = require("ethers");
// Environment variable names for various blockchains
const envVarNames = {
arbitrum: "ARBITRUM_RPC_URL",
avalanche: "AVALANCHE_RPC_URL",
base: "BASE_RPC_URL",
bsc: "BSC_RPC_URL",
celo: "CELO_RPC_URL",
ethereum: "ETHEREUM_RPC_URL",
optimism: "OPTIMISM_RPC_URL",
polygon: "POLYGON_RPC_URL",
zksync: "ZKSYNC_RPC_URL"
};
// USDC contract addresses on various blockchains
const usdcAddresses = {
arbitrum: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
avalanche: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
bsc: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
celo: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
optimism: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
polygon: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
zksync: "0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4"
};
// ABI for balanceOf of ERC20 tokens to interact with USDC contract
const abi = [
"function balanceOf(address owner) view returns (uint256)"
];
async function getUSDCBalance(providerUrl, contractAddress, walletAddress) {
const provider = new ethers.JsonRpcProvider(providerUrl); // Create new ethers JsonRpcProvider provider
const contract = new ethers.Contract(contractAddress, abi, provider); // Create new contract intance using contract address, abi, provider
const balance = await contract.balanceOf(walletAddress); // Fetch balance for specified wallet
return ethers.formatUnits(balance, 6); // USDC has 6 decimal places
}
async function main(params) {
console.log('Starting main function with params:', params); // Log input params
const walletAddress = params.user_data.wallet_address; // Get wallet address from user
try {
const balances = {}; // Empty balances object
let missingRpcUrls = false; // variable flag for missing URLs
/* - A function to iterate over mentioned chains in the envVarNames
- Fetch RPC URLs from .env file and check if the URL is set
- If URL is set run getUSDCBalance function
- Handle missing URLs by setting missingRpcUrls true
*/
for (const [chain, envVarName] of Object.entries(envVarNames)) {
const rpcUrl = process.env[envVarName];
if (rpcUrl) {
const usdcAddress = usdcAddresses[chain];
const balance = await getUSDCBalance(rpcUrl, usdcAddress, walletAddress);
balances[chain] = balance;
} else {
balances[chain] = `Add ${envVarName} in the .env file.`;
missingRpcUrls = true;
}
}
//Log final results and return the balances with a message
console.log('Final balances:', balances); // Log final results
return {
message: "USDC Balances across EVM blockchains.",
balances: balances,
};
} catch (error) {
console.error('Error in main function:', error); // Log any errors encountered in the main function
throw error;
}
}
module.exports = { main };
Zip the Files
QuickNode Functions supports several Node.js v20 core modules, but since we've used ethers.js and dotenv which are third-party libraries, we will need to upload a .zip file of index.js
, .env
, package.json
, and package-lock.json
along with node_modules
folder so that our function can work on the QuickNode Functions platform.
Learn more about QuickNode Functions Code Editor and .Zip file uploading.
Request
We will invoke the function with the following cURL command. Be sure to replace the YOUR_API_KEY with your own QuickNode API key and the POST URL with the URL of your Function. You can send the request with any wallet address to check the balance.
curl -X POST "https://api.quicknode.com/functions/rest/v1/namespaces/0f6812dd-a17f-4cbc-9ab4-7a529eb33940/functions/usdc-balances/call" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"network": "ethereum-mainnet",
"dataset": "block",
"block": "latest",
"user_data": {
"wallet_address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
}
}'
Response
Resulting in the following response:
{
"response" : {
"activationId" : "28b6912a79f74ad8b6912a79f77ad866",
"annotations" : [
{
"key" : "path",
"value" : "ca43f206-1786-4d85-8521-952f2a528f49/usdc-balances"
},
{
"key" : "waitTime",
"value" : 142
},
{
"key" : "kind",
"value" : "nodejs:20"
},
{
"key" : "timeout",
"value" : false
},
{
"key" : "limits",
"value" : {
"concurrency" : 1,
"logs" : 10,
"memory" : 256,
"timeout" : 60000
}
},
{
"key" : "initTime",
"value" : 313
}
],
"duration" : 406,
"end" : 1719555581845,
"logs" : [],
"name" : "usdc-balances",
"namespace" : "ca43f206-1786-4d85-8521-952f2a528f49",
"publish" : false,
"response" : {
"result" : {
"balances" : {
"arbitrum" : "Add ARBITRUM_RPC_URL in the .env file.",
"avalanche" : "Add AVALANCHE_RPC_URL in the .env file.",
"base" : "Add BASE_RPC_URL in the .env file.",
"bsc" : "Add BSC_RPC_URL in the .env file.",
"celo" : "Add CELO_RPC_URL in the .env file.",
"ethereum" : "7065.377042",
"optimism" : "Add OPTIMISM_RPC_URL in the .env file.",
"polygon" : "70.052053",
"zksync" : "Add ZKSYNC_RPC_URL in the .env file."
},
"message" : "USDC Balances across EVM blockchains."
},
"size" : 447,
"status" : "success",
"success" : true
},
"start" : 1719555581439,
"subject" : "0f6812dd-a17f-4cbc-9ab4-7a529eb33940",
"version" : "0.0.3"
}
}
Learn more about QuickNode Functions.
We ❤️ Feedback!
Let us know if you have any feedback or requests for new topics. We'd love to hear from you.