const axios = require('axios');
const fs = require('fs').promises;
const path = require('path');
// Configuration
const USDT_CONTRACT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'; // USDT TRC20 contract
const CHAINSTACK_ENDPOINT = 'CHAINSTACK_NODE_ENDPOINT';
const POLLING_INTERVAL = 3000; // 3 seconds which is the TRON block time
const STATE_FILE = path.join(__dirname, 'usdt-listener-state.json'); // Persistence file
const MAX_RETRY_ATTEMPTS = 5;
const BASE_RETRY_DELAY = 1000; // 1 second base delay for exponential backoff
class PureUSDTListener {
constructor() {
this.isRunning = false;
this.lastProcessedBlock = 0;
this.retryAttempts = 0;
console.log('π Pure USDT Listener Started (Zero Dependencies!)');
console.log(`π Contract: ${USDT_CONTRACT}`);
console.log(`π Endpoint: ${CHAINSTACK_ENDPOINT}`);
console.log(`πΎ State file: ${STATE_FILE}`);
console.log('β'.repeat(80));
}
// Save state to file for persistence
async saveState() {
try {
const state = {
lastProcessedBlock: this.lastProcessedBlock,
timestamp: new Date().toISOString()
};
await fs.writeFile(STATE_FILE, JSON.stringify(state, null, 2));
} catch (error) {
console.error('β Failed to save state:', error.message);
}
}
// Load state from file
async loadState() {
try {
const data = await fs.readFile(STATE_FILE, 'utf8');
const state = JSON.parse(data);
this.lastProcessedBlock = state.lastProcessedBlock || 0;
console.log(`π Loaded state: last processed block ${this.lastProcessedBlock}`);
return true;
} catch (error) {
console.log('π No previous state found, starting fresh');
return false;
}
}
// Calculate exponential backoff delay
getBackoffDelay() {
return Math.min(BASE_RETRY_DELAY * Math.pow(2, this.retryAttempts), 30000); // Max 30 seconds
}
// Reset retry counter on success
resetRetries() {
if (this.retryAttempts > 0) {
console.log(`β
Connection recovered after ${this.retryAttempts} attempts`);
this.retryAttempts = 0;
}
}
// Convert hex to decimal (BigInt for large numbers)
hexToDecimal(hex) {
try {
if (!hex) return '0';
const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
return BigInt('0x' + cleanHex).toString();
} catch (error) {
return '0';
}
}
// Format USDT amount (6 decimals)
formatUSDTAmount(amount) {
try {
const divisor = BigInt(10 ** 6); // USDT has 6 decimals
const amountBig = BigInt(amount);
const wholePart = amountBig / divisor;
const fractionalPart = amountBig % divisor;
const formatted = wholePart.toString() + '.' + fractionalPart.toString().padStart(6, '0');
return parseFloat(formatted).toString();
} catch (error) {
return amount.toString();
}
}
// Get current block number via HTTP
async getCurrentBlock() {
try {
const response = await axios.post(`${CHAINSTACK_ENDPOINT}/wallet/getnowblock`, {}, {
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
});
this.resetRetries();
return response.data.block_header.raw_data.number;
} catch (error) {
console.error('β Failed to get current block:', error.message);
throw error;
}
}
// Get block data via HTTP
async getBlockData(blockNumber) {
try {
const response = await axios.post(`${CHAINSTACK_ENDPOINT}/wallet/getblockbynum`, {
num: blockNumber,
visible: true,
}, {
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
});
return response.data;
} catch (error) {
console.error(`β Failed to get block ${blockNumber}:`, error.message);
return null;
}
}
// Check if transaction is USDT transfer
isUSDTTransfer(transaction) {
try {
if (!transaction.raw_data?.contract || !transaction.ret?.[0]) {
return false;
}
if (transaction.ret[0].contractRet !== 'SUCCESS') {
return false;
}
const contract = transaction.raw_data.contract[0];
if (contract.type !== 'TriggerSmartContract') {
return false;
}
const contractAddress = contract.parameter?.value?.contract_address;
const data = contract.parameter?.value?.data;
// Check if it's calling USDT contract with transfer function
const isUSDTContract = contractAddress === USDT_CONTRACT;
const isTransferFunction = data && data.startsWith('a9059cbb'); // transfer(address,uint256)
return isUSDTContract && isTransferFunction;
} catch (error) {
return false;
}
}
// Parse USDT transfer transaction
parseUSDTTransfer(transaction, blockNumber) {
try {
const contract = transaction.raw_data.contract[0];
const data = contract.parameter.value.data;
const fromAddress = contract.parameter.value.owner_address;
const valueHex = data.slice(72, 136);
const toHex = data.slice(32, 72);
let toAddress = 'T' + toHex;
const amount = this.hexToDecimal(valueHex);
const formattedAmount = this.formatUSDTAmount(amount);
return {
txHash: transaction.txID,
blockNumber: blockNumber,
timestamp: new Date(transaction.raw_data.timestamp).toISOString(),
from: fromAddress,
to: toAddress,
amount: amount,
formattedAmount: formattedAmount,
};
} catch (error) {
console.error('β Failed to parse USDT transfer:', error.message);
return null;
}
}
// Process a single block
async processBlock(blockNumber) {
const blockData = await this.getBlockData(blockNumber);
if (!blockData?.transactions) {
return [];
}
const transfers = [];
for (const transaction of blockData.transactions) {
if (this.isUSDTTransfer(transaction)) {
const transfer = this.parseUSDTTransfer(transaction, blockNumber);
if (transfer) {
transfers.push(transfer);
}
}
}
return transfers;
}
// Log transfer event
logTransfer(transfer) {
const amount = parseFloat(transfer.formattedAmount);
const emoji = amount >= 100000 ? 'π¨' : amount >= 10000 ? 'π°' : amount >= 1000 ? 'π΅' : 'π³';
console.log(`${emoji} USDT Transfer Detected:`);
console.log(` π Amount: ${transfer.formattedAmount} USDT`);
console.log(` π€ From: ${transfer.from}`);
console.log(` π₯ To: ${transfer.to}`);
console.log(` π TX: ${transfer.txHash}`);
console.log(` π¦ Block: ${transfer.blockNumber}`);
console.log(` β° Time: ${transfer.timestamp}`);
if (amount >= 100000) {
console.log(` π¨ LARGE TRANSFER ALERT: $${amount.toLocaleString()}`);
}
console.log('β'.repeat(80));
}
// Main listening loop
async start() {
this.isRunning = true;
try {
// Load previous state or get initial block
const hasState = await this.loadState();
if (!hasState) {
this.lastProcessedBlock = await this.getCurrentBlock() - 1;
await this.saveState();
}
console.log(`π Starting from block: ${this.lastProcessedBlock}`);
console.log('π Listening for USDT transfers (Zero Dependencies!)...\n');
while (this.isRunning) {
try {
const currentBlock = await this.getCurrentBlock();
// Process all blocks from last processed + 1 to current (NO SKIPPING)
const fromBlock = this.lastProcessedBlock + 1;
if (fromBlock <= currentBlock) {
const blocksToProcess = currentBlock - fromBlock + 1;
if (blocksToProcess > 10) {
console.log(`β οΈ Processing ${blocksToProcess} blocks (catching up from block ${fromBlock} to ${currentBlock})`);
}
// Process blocks sequentially to maintain order
for (let blockNum = fromBlock; blockNum <= currentBlock; blockNum++) {
const transfers = await this.processBlock(blockNum);
for (const transfer of transfers) {
this.logTransfer(transfer);
}
if (transfers.length > 0) {
console.log(`β
Processed block ${blockNum}: ${transfers.length} USDT transfers found`);
}
// Update and save state after each block
this.lastProcessedBlock = blockNum;
await this.saveState();
}
}
// Reset retry counter on successful iteration
this.resetRetries();
// Wait before next iteration
await this.sleep(POLLING_INTERVAL);
} catch (error) {
this.retryAttempts++;
const backoffDelay = this.getBackoffDelay();
console.error(`β Error in main loop (attempt ${this.retryAttempts}/${MAX_RETRY_ATTEMPTS}):`, error.message);
if (this.retryAttempts >= MAX_RETRY_ATTEMPTS) {
console.error('π¨ Max retry attempts reached. Resetting retry counter.');
this.retryAttempts = 0;
}
console.log(`β³ Retrying in ${backoffDelay}ms (exponential backoff)...`);
await this.sleep(backoffDelay);
}
}
} catch (error) {
console.error('β Failed to start listener:', error.message);
}
}
// Stop listening
stop() {
this.isRunning = false;
console.log('\nπ Stopping Pure USDT listener...');
}
// Sleep utility
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Create and start the listener
const listener = new PureUSDTListener();
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('\nπ Received SIGINT. Shutting down gracefully...');
listener.stop();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\nπ Received SIGTERM. Shutting down gracefully...');
listener.stop();
process.exit(0);
});
// Start listening
listener.start().catch(error => {
console.error('β Failed to start Pure USDT listener:', error);
process.exit(1);
});