0

I have been trying to get the token balances for an address at the first of the month for every month in the past year. There is an incredible article about how to do this with chainstack: https://chainstack.com/the-ultimate-guide-to-getting-multiple-token-balances-on-ethereum/. This example won't work for Alchemy APIs. The batch.js file that is supposed to get token balances looks like this.

const Web3 = require('web3')
const { convertToNumber, getTokens } = require('./utils')
const { abi, bathEndpoint, walletAddress } = require('./constant.js')

const web3 = new Web3(new Web3.providers.HttpProvider(bathEndpoint))

const generateContractFunctionList = ({ tokens, blockNumber }) => {
  const batch = new web3.BatchRequest()

  tokens.map(async ({ address: tokenAddress, symbol, decimals }) => {
    // console.log('tokenAddress :>> ', tokenAddress)
    if (tokenAddress != null && tokenAddress != '') {
      const contract = new web3.eth.Contract(abi)
      contract.options.address = tokenAddress
      try {
        batch.add(
          contract.methods
            .balanceOf(walletAddress)
            .call.request({}, blockNumber)
        )
      } catch (error) {
        console.error('Error adding request to batch for token ', tokenAddress)
      }
    }
  })

  return batch
}

const main = async () => {
  const { tokens } = await getTokens()
  console.log('tokens retrieved :>> ', tokens.length)

  const batch = generateContractFunctionList({ tokens })
  // query block number
  // const batch = generateContractFunctionList({ tokens, blockNumber: 11633038 })
  const tokenBalances = {}
  const tokensIgnored = []
  let batchData
  try {
    batchData = await batch.execute()
  } catch (error) {
    console.error('Error retrieving balances for some tokens')
    batchData = error
  }

  batchData.response.forEach((res, index) => {
    const { name, decimals, symbol } = tokens[index]
    if (res && res._hex) {
      tokenBalances[name] = `${convertToNumber(res._hex, decimals)} ${symbol}`
    } else {
      tokensIgnored.push(name)
    }
  })

  console.log(
    'The following tokens returned an error when checking balance:',
    tokensIgnored
  )
  console.log('----------')
  console.log(
    `Balance checked for ${Object.keys(tokenBalances).length} tokens:`
  )
  console.log(tokenBalances)
}

main()

The utils.js file referenced in the above file looks like this:

const { toBN } = require('web3-utils');
require('isomorphic-fetch');

const tokenSource = 'https://tokens.coingecko.com/uniswap/all.json';
const getTokens = () => {
  return fetch(tokenSource, {
    methods: 'GET',
    headers: { 'Content-Type': 'application/json', },
  }).then(data => data.json());
};

const convertToNumber = (hex, decimals) => {
  const balance = toBN(hex);
  let balanceDecimal = balance;
  if (decimals && (balance.toLocaleString() === '0' && decimals < 20)) {
    balanceDecimal = balance.div(toBN(10 ** decimals));
  }

  return balanceDecimal.toLocaleString();
};

module.exports = {
  convertToNumber,
  getTokens,
};

and the constant.js file looks like this:

const abi = [
 {
    constant: true,
    inputs: [
      {
        name: '_owner',
        type: 'address',
      },
    ],
    name: 'balanceOf',
    outputs: [
      {
        name: 'balance',
        type: 'uint256',
      },
    ],
    payable: false,
    stateMutability: 'view',
    type: 'function',
  },
];

const username = 'username'; // replace with your Ethereum node RPC username
const password = 'password'; // replace with your Ethereum node RPC password
const rpcEndpoint = 'https://nd-123-456-789.p2pify.com'; // replace with your Ethereum node RPC endpoint
const bathEndpoint = `https://${username}:${password}@nd-123-456-789.p2pify.com`; // replace with your Ethereum node RPC endpoint
const walletAddress = '0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be'; // replace with the address you want to query

module.exports = {
  abi,
  bathEndpoint,
  password,
  rpcEndpoint,
  username,
  walletAddress,
};

It won't work with Alchemy APIs. I get this as my error:

<myusername>@<mycomputername> token-balance-through-time % node batch2.js
tokens retrieved :>>  5146
Error retrieving balances for some tokens
/Users/<myusername>/Desktop/Dev/token-balance-through-time/batch2.js:47
  batchData.response.forEach((res, index) => {
                     ^

TypeError: Cannot read properties of undefined (reading 'forEach')
    at main (/Users/<myusername>/Desktop/Dev/token-balance-through-time/batch2.js:47:22)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
<myusername>@<mycomputername> token-balance-through-time % 

And when I add a try catch to stop the forEach from failing I get this:

<myusername>@<mycomputername> token-balance-through-time % node batch2.js
tokens retrieved :>>  5146
Error retrieving balances for some tokens
Error retrieving balances for some tokens
The following tokens returned an error when checking balance: []
----------
Balance checked for 0 tokens:
{}
/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-eth-abi/lib/index.js:297
        throw new Error('Returned values aren\'t valid, did it run Out of Gas? ' +
              ^

Error: Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced.
    at ABICoder.decodeParametersWith (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-eth-abi/lib/index.js:297:15)
    at ABICoder.decodeParameters (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-eth-abi/lib/index.js:284:17)
    at Contract._decodeMethodReturn (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-eth-contract/lib/index.js:493:22)
    at Method.outputFormatter (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-eth-contract/lib/index.js:800:42)
    at Method.formatOutput (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-core-method/lib/index.js:148:54)
    at sendTxCallback (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-core-method/lib/index.js:531:33)
    at /Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-core-requestmanager/lib/index.js:307:9
    at XMLHttpRequest.request.onreadystatechange (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/web3-providers-http/lib/index.js:98:13)
    at XMLHttpRequestEventTarget.dispatchEvent (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:22)
    at XMLHttpRequest._setReadyState (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/xhr2-cookies/dist/xml-http-request.js:208:14)
    at XMLHttpRequest._onHttpResponseEnd (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/xhr2-cookies/dist/xml-http-request.js:318:14)
    at IncomingMessage.<anonymous> (/Users/<myusername>/Desktop/Dev/token-balance-through-time/node_modules/xhr2-cookies/dist/xml-http-request.js:289:61)
    at IncomingMessage.emit (node:events:402:35)
    at endReadableNT (node:internal/streams/readable:1343:12)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
<myusername>@<mycomputername> token-balance-through-time % 

How do I find token balances with Alchemy? If someone can fix the batch.js file code to work with Alchemy I could add a block number to the .call.request({}, blockNumber) function and get an address's token balance backwards in time.

1 Answer 1

1

We've released a new and updated version on how to get token balances that specifically includes the block number parameter as well. Check it out https://chainstack.com/ultimate-guide-erc20-token-balance/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.