Retrieve ETH and ERC-20 Token Balances at a Specific Timestamp Using Ethers.js

·

In blockchain development, checking an address's balance at a specific historical moment is a common requirement, whether for auditing, tax reporting, or transaction analysis. This guide explains how to use the Ethers.js library to retrieve the balance of Ethereum (ETH) and ERC-20 tokens for any address at a particular Unix timestamp.

Prerequisites

Before starting, ensure you have the following:

Setting Up the Environment

To begin, initialize your project and install the necessary dependencies. Create a new JavaScript file and import the Ethers.js library.

const { ethers } = require('ethers');

Configuring the Infura Provider

Ethers.js requires a provider to interact with the Ethereum blockchain. Infura offers a reliable JSON-RPC endpoint. Replace the placeholder with your Infura project ID.

const infuraUrl = 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID';
const provider = new ethers.providers.JsonRpcProvider(infuraUrl);

Defining the Target Address and Timestamp

Specify the Ethereum address you want to query and the Unix timestamp representing the exact moment for the balance check. Unix time is the number of seconds since January 1, 1970.

const address = 'TARGET_ETHEREUM_ADDRESS';
const timestamp = SPECIFIC_UNIX_TIMESTAMP;

Retrieving the ETH Balance

Use the getBalance method provided by Ethers.js. This function accepts the address and an optional block parameter, which can be a block number or timestamp.

const ethBalancePromise = provider.getBalance(address, timestamp);

Checking ERC-20 Token Balances

For ERC-20 tokens, you need the token's contract address and its Application Binary Interface (ABI). The ABI defines the balanceOf function needed to query holdings.

const tokenAddress = 'ERC20_TOKEN_CONTRACT_ADDRESS';
const tokenAbi = ['function balanceOf(address) view returns (uint256)'];
const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, provider);
const tokenBalancePromise = tokenContract.balanceOf(address, timestamp);

Executing the Balance Queries

Combine both balance queries using Promise.all to handle asynchronous operations efficiently. This approach ensures both balances are retrieved simultaneously.

Promise.all([ethBalancePromise, tokenBalancePromise])
  .then(([ethBalance, tokenBalance]) => {
    console.log(`ETH balance at timestamp ${timestamp}: ${ethers.utils.formatEther(ethBalance)} ETH`);
    console.log(`Token balance at timestamp ${timestamp}: ${ethers.utils.formatUnits(tokenBalance, TOKEN_DECIMALS)}`);
  })
  .catch((error) => {
    console.error('Error retrieving balances:', error);
  });

Handling Results and Errors

After obtaining the balances, format them for readability. ETH balances are in wei by default; use formatEther to convert to ETH. Token balances may require specifying decimals, which vary per token.

Error handling is crucial for debugging issues like invalid addresses, provider errors, or incorrect timestamps.

Important Considerations

👉 Explore advanced blockchain query techniques

Frequently Asked Questions

Can I retrieve balances for multiple tokens simultaneously?
Yes, create multiple contract instances and use Promise.all to handle multiple balanceOf calls. Ensure your provider can handle the increased load.

What if the timestamp is before the address existed?
The balance will be zero. If the timestamp predates the token's deployment, the query might fail unless the contract handles historical calls.

How do I convert a human-readable date to Unix timestamp?
Use JavaScript's Date.getTime() method, dividing by 1000 to get seconds. For example: Math.floor(new Date('2023-01-01').getTime() / 1000).

Why is my balance query returning zero?
Possible reasons include incorrect address, timestamp outside the blockchain history, or the address having no balance at that time.

Is this method compatible with testnets?
Yes, replace the Infura endpoint with the testnet URL (e.g., Ropsten or Goerli) and use testnet addresses.

Can I use this for other EVM-compatible blockchains?
Absolutely. Adjust the Infura URL to the respective blockchain (e.g., Polygon or BNB Smart Chain) and use the correct network settings.

Conclusion

Querying historical balances is straightforward with Ethers.js and a provider like Infura. This method is essential for developers building analytics tools, financial reports, or audit systems. Always test with known addresses and timestamps to verify accuracy. For more complex queries, consider using specialized indexers or archives.