I'm new to coding. Trying to fetch hashes and amounts of USDT transactions on TRC20 and ERC20 systems, without binding to single wallet.
Have been trying to use TRX and ETH native API's and third party API as well, such as QuickNode - which gave me better experience, but still can't get a proper result:(
Using QuickNode API managed to fetch some blocks, and TRX transactions, but have no idea how to get their amounts and properly filter them by contract - in another words, by token, such as USDT.
Expecting to be able to fetch hashes and amounts of latest USDT transactions on TRC20 or ERC20 ecosystems without binding fetch just to a single wallet
👋🏼 Head of DevRel at QuickNode here
Instead of having to repeatedly query the blockchain or an index (polling), you can use a service to feed you the data you're looking for. Our QuickAlerts product is one such service.
In our Expression Library, we have an example expression for ERC-20/TRC-20 transfers. The Expression Library explains exactly how the expression works. All you need to do is:
To make it easy, we have a video guide on how to use the QuickAlerts interface.
Finding a company that provides an indexed API will always be easiest here, though there aren't many that offer the ability to query by ERC-20 token or a large set of chains. If you're set on this, find a company that supports the chains you're looking for, and reach out asking if they can expose this new method.
The manual way may be where you land here since with someone like QuickNode, you can get access to all the chains you need. Just keep in mind it's going to take quite a few RPC calls and processing as you'll be iterating over many blocks.
I've written some code for you below showing how it can be done on Ethereum Mainnet. You'll want to make sure that you:
startingBlock
definition in fetchAllTransfers
. Adjust this value based on the block the USDT contract was deployed on each chain you're working with.const { Web3 } = require('web3');
// QuickNode API URL (replace with your own)
const quickNodeApiUrl = 'YOUR_QUICKNODE_API_URL';
// Initialize Web3
const web3 = new Web3(quickNodeApiUrl);
// Ethereum MainNet USDT Token Contract Address, adjust for other chains
const usdtContractAddress = '0xdAC17F958D2ee523a2206206994597C13D831ec7';
// Define the ABI for the USDT token transfer event
const usdtAbi = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_upgradedAddress","type":"address"}],"name":"deprecate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"deprecated","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_evilUser","type":"address"}],"name":"addBlackList","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"upgradedAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maximumFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_maker","type":"address"}],"name":"getBlackListStatus","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newBasisPoints","type":"uint256"},{"name":"newMaxFee","type":"uint256"}],"name":"setParams","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"issue","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"redeem","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"basisPointsRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBlackListed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_clearedUser","type":"address"}],"name":"removeBlackList","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_UINT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_blackListedUser","type":"address"}],"name":"destroyBlackFunds","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_initialSupply","type":"uint256"},{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"Issue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newAddress","type":"address"}],"name":"Deprecate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"feeBasisPoints","type":"uint256"},{"indexed":false,"name":"maxFee","type":"uint256"}],"name":"Params","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_blackListedUser","type":"address"},{"indexed":false,"name":"_balance","type":"uint256"}],"name":"DestroyedBlackFunds","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_user","type":"address"}],"name":"AddedBlackList","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_user","type":"address"}],"name":"RemovedBlackList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"}];
// Initialize the USDT Token Contract
const usdtToken = new web3.eth.Contract(usdtAbi, usdtContractAddress);
// Function to fetch transfers with pagination
const fetchTransfersPaginated = async (fromBlock, toBlock, accumulatedEvents = []) => {
try {
// Maximum block range to fetch in a single call, QuickNode limit for getlogs is 10,000
const MAX_BLOCK_RANGE = 10000;
const remainingBlocks = toBlock - fromBlock;
const batchSize = Math.min(remainingBlocks, MAX_BLOCK_RANGE);
const batchToBlock = fromBlock + batchSize - 1; // Inclusive range
console.log(`Fetching transfers batch (block ${fromBlock} to ${batchToBlock})`);
const pageEvents = await usdtToken.getPastEvents('Transfer', {
fromBlock,
toBlock: batchToBlock,
});
accumulatedEvents = accumulatedEvents.concat(pageEvents);
if (remainingBlocks > batchSize) {
// Fetch next batch
return await fetchTransfersPaginated(fromBlock + batchSize, toBlock, accumulatedEvents);
}
return accumulatedEvents;
} catch (error) {
console.error('Error fetching transfers:', error);
return accumulatedEvents; // Return accumulated events so far
}
};
const fetchAllTransfers = async () => {
try {
const latestBlock = await web3.eth.getBlockNumber();
// Switch to first definition for full run, second for testing with smaller ranges
//const startingBlock = BigInt(4634748); // block where USDT contract was deployed on Ethereum Mainnet
const startingBlock = latestBlock - BigInt(100);
console.log(`Fetching transfers form block ${startingBlock} to ${latestBlock}`);
// Fetch transfers in batches of 10,000
const events = await fetchTransfersPaginated(Number(startingBlock), Number(latestBlock));
for (const event of events) {
// Extract transaction hash and transferred amount
const transactionHash = event.transactionHash;
let amount = event.returnValues.value;
if (typeof amount === 'bigint') {
// Convert to number for division
amount = Number(amount);
}
// Convert based on decimals (USDT has 6 decimals)
amount = amount / 10 ** 6;
// Log information, write this to a database or file if needed
console.log(`Transaction Hash: ${transactionHash}`);
console.log(`Amount Transferred: ${amount}`);
console.log('---');
}
console.log('Fetched', events.length, 'transfers');
} catch (error) {
console.error('Error fetching transfers:', error);
}
};
fetchAllTransfers().then(() => {
console.log('All transfers fetched');
}).catch((error) => {
console.error('Error:', error);
});