Twitter Account Sales - Irul Media
Back to Blog

Building a Telegram Bot that Tracks Ethereum Wallet Transactions in Real Time (2025 Guide)

Building a Telegram Bot that Tracks Ethereum Wallet Transactions in Real Time (2025 Guide)

Introduction

Tracking Ethereum wallet activity manually wastes precious time and opportunities. Whether you're monitoring whale movements, tracking your DeFi positions, or watching for specific transactions, constant manual checking simply doesn't scale. This guide teaches you to build an Ethereum wallet tracker bot that sends instant Telegram notifications whenever ETH moves to or from any address you specify.

By the end of this tutorial, you'll deploy a production-ready bot that monitors wallets 24/7, alerts you within seconds of any transaction, and runs reliably on minimal infrastructure. We'll use modern Web3 tools and best practices to ensure your bot stays online and responsive.

Prerequisites

Before diving in, ensure you have:

  • Node.js 20+ installed (check with node --version)
  • An Infura or Alchemy API key (free tier works fine)
  • A Telegram Bot token from @BotFather
  • Basic familiarity with JavaScript and async/await patterns
  • An Ethereum wallet address to monitor (any public address works)

Overview of the Workflow

Our bot architecture follows five core steps:

  1. Environment Setup - Initialize the project and install dependencies
  2. Web3 Connection - Connect to Ethereum via WebSocket for real-time events
  3. Telegram Integration - Build the bot logic to handle commands and send alerts
  4. Testing Pipeline - Verify functionality on testnet before mainnet
  5. Production Deployment - Deploy to a VPS or serverless platform

Step 1 — Environment Setup

Start by creating a new project directory and initializing it:

bash
mkdir eth-wallet-tracker-bot
cd eth-wallet-tracker-bot
npm init -y

Install the required dependencies:

bash
npm install web3 node-telegram-bot-api dotenv winston
npm install --save-dev nodemon jest

Create a .env file to store sensitive configuration:

bash
touch .env

Add your credentials to .env:

env
INFURA_WSS_URL=wss://mainnet.infura.io/ws/v3/YOUR_PROJECT_ID
TELEGRAM_BOT_TOKEN=YOUR_BOT_TOKEN_FROM_BOTFATHER
MONITORED_WALLET=0x742d35Cc6634C0532925a3b844Bc9e7595f6E3D

Tip: Never commit .env files to version control. Add it to .gitignore immediately.

Create the main application file:

bash
touch index.js

Step 2 — Web3 Provider & Event Filters

Setting up the Web3 connection requires careful attention to WebSocket stability. Here's our core monitoring logic:

javascript
const Web3 = require('web3');
const TelegramBot = require('node-telegram-bot-api');
const winston = require('winston');
require('dotenv').config();

// Configure logger
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
    new winston.transports.Console({ format: winston.format.simple() })
  ]
});

// Initialize Web3 with WebSocket provider
const web3 = new Web3(new Web3.providers.WebsocketProvider(
  process.env.INFURA_WSS_URL,
  {
    reconnect: {
      auto: true,
      delay: 5000,
      maxAttempts: 10,
      onTimeout: false
    }
  }
));

// Initialize Telegram bot
const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { polling: true });

// Store active chat IDs
const activeChatIds = new Set();

// Monitor wallet function
async function monitorWallet(address) {
  const normalizedAddress = address.toLowerCase();
  
  // Subscribe to pending transactions
  const subscription = web3.eth.subscribe('pendingTransactions', (error, txHash) => {
    if (error) {
      logger.error('Subscription error:', error);
      return;
    }
  });

  subscription.on('data', async (txHash) => {
    try {
      const tx = await web3.eth.getTransaction(txHash);
      
      if (!tx || !tx.to) return;
      
      const fromAddress = tx.from.toLowerCase();
      const toAddress = tx.to.toLowerCase();
      
      if (fromAddress === normalizedAddress || toAddress === normalizedAddress) {
        const valueInEth = web3.utils.fromWei(tx.value, 'ether');
        const direction = fromAddress === normalizedAddress ? 'OUT' : 'IN';
        
        const message = formatTransactionMessage(tx, valueInEth, direction);
        
        // Send to all active chats
        activeChatIds.forEach(chatId => {
          bot.sendMessage(chatId, message, { parse_mode: 'Markdown' });
        });
        
        logger.info(`Transaction detected: ${txHash}`);
      }
    } catch (error) {
      logger.error('Error processing transaction:', error);
    }
  });
}

function formatTransactionMessage(tx, valueInEth, direction) {
  const directionEmoji = direction === 'IN' ? '📥' : '📤';
  
  return `${directionEmoji} *${direction} Transaction Detected*\n\n` +
         `💰 *Amount:* ${valueInEth} ETH\n` +
         `📊 *Gas Price:* ${web3.utils.fromWei(tx.gasPrice, 'gwei')} Gwei\n` +
         `🔗 *Hash:* \`${tx.hash}\`\n` +
         `👤 *From:* \`${tx.from}\`\n` +
         `📍 *To:* \`${tx.to}\`\n\n` +
         `[View on Etherscan](https://etherscan.io/tx/${tx.hash})`;
}

Note: WebSocket connections can drop unexpectedly. The reconnect configuration ensures your bot recovers automatically.

Step 3 — Building the Telegram Bot Logic

Now let's implement the Telegram bot commands and interaction logic:

javascript
// Bot command handlers
bot.onText(/\/start/, (msg) => {
  const chatId = msg.chat.id;
  activeChatIds.add(chatId);
  
  const welcomeMessage = 
    `🤖 *Ethereum Wallet Tracker Bot*\n\n` +
    `I'll monitor wallet transactions and notify you instantly!\n\n` +
    `Currently watching: \`${process.env.MONITORED_WALLET}\`\n\n` +
    `Commands:\n` +
    `/status - Check bot status\n` +
    `/wallet - View monitored wallet\n` +
    `/stop - Stop notifications\n` +
    `/help - Show this message`;
  
  bot.sendMessage(chatId, welcomeMessage, { parse_mode: 'Markdown' });
  logger.info(`New user started bot: ${chatId}`);
});

bot.onText(/\/status/, async (msg) => {
  const chatId = msg.chat.id;
  
  try {
    const latestBlock = await web3.eth.getBlockNumber();
    const balance = await web3.eth.getBalance(process.env.MONITORED_WALLET);
    const balanceInEth = web3.utils.fromWei(balance, 'ether');
    
    const statusMessage = 
      `✅ *Bot Status: Online*\n\n` +
      `📦 *Latest Block:* ${latestBlock}\n` +
      `💎 *Wallet Balance:* ${parseFloat(balanceInEth).toFixed(4)} ETH\n` +
      `👥 *Active Users:* ${activeChatIds.size}`;
    
    bot.sendMessage(chatId, statusMessage, { parse_mode: 'Markdown' });
  } catch (error) {
    bot.sendMessage(chatId, '❌ Error fetching status. Please try again.');
    logger.error('Status command error:', error);
  }
});

bot.onText(/\/wallet/, (msg) => {
  const chatId = msg.chat.id;
  const walletMessage = 
    `🔍 *Monitored Wallet*\n\n` +
    `Address: \`${process.env.MONITORED_WALLET}\`\n\n` +
    `[View on Etherscan](https://etherscan.io/address/${process.env.MONITORED_WALLET})`;
  
  bot.sendMessage(chatId, walletMessage, { parse_mode: 'Markdown' });
});

bot.onText(/\/stop/, (msg) => {
  const chatId = msg.chat.id;
  activeChatIds.delete(chatId);
  bot.sendMessage(chatId, '🛑 Notifications stopped. Use /start to resume.');
  logger.info(`User stopped notifications: ${chatId}`);
});

bot.onText(/\/help/, (msg) => {
  const chatId = msg.chat.id;
  bot.sendMessage(chatId, 'Use /start to begin tracking wallet transactions.');
});

// Error handling for polling errors
bot.on('polling_error', (error) => {
  logger.error('Telegram polling error:', error);
});

Step 4 — Testing & Debugging

Testing blockchain applications requires a methodical approach. Create a test file:

javascript
// __tests__/bot.test.js
const Web3 = require('web3');

describe('Ethereum Wallet Tracker Bot', () => {
  let web3;
  
  beforeAll(() => {
    web3 = new Web3(process.env.INFURA_WSS_URL);
  });
  
  test('should connect to Ethereum network', async () => {
    const isConnected = await web3.eth.net.isListening();
    expect(isConnected).toBe(true);
  });
  
  test('should validate wallet address format', () => {
    const validAddress = '0x742d35Cc6634C0532925a3b844Bc9e7595f6E3D';
    expect(Web3.utils.isAddress(validAddress)).toBe(true);
  });
  
  test('should format transaction message correctly', () => {
    const mockTx = {
      hash: '0x123...',
      from: '0xabc...',
      to: '0xdef...',
      value: '1000000000000000000',
      gasPrice: '20000000000'
    };
    
    const message = formatTransactionMessage(mockTx, '1.0', 'IN');
    expect(message).toContain('📥');
    expect(message).toContain('1.0 ETH');
  });
});

Common debugging scenarios and solutions:

WebSocket Connection Drops:

javascript
web3.currentProvider.on('error', e => {
  logger.error('WebSocket error:', e);
});

web3.currentProvider.on('end', e => {
  logger.error('WebSocket connection ended:', e);
  // Implement reconnection logic
  setTimeout(() => {
    web3.setProvider(new Web3.providers.WebsocketProvider(process.env.INFURA_WSS_URL));
    monitorWallet(process.env.MONITORED_WALLET);
  }, 5000);
});

Rate Limiting Issues:

javascript
const rateLimiter = {
  requests: 0,
  resetTime: Date.now() + 60000,
  
  canMakeRequest() {
    if (Date.now() > this.resetTime) {
      this.requests = 0;
      this.resetTime = Date.now() + 60000;
    }
    
    if (this.requests < 100) {
      this.requests++;
      return true;
    }
    return false;
  }
};

Step 5 — Deploying to a VPS or Serverless Function

For production deployment, create a Dockerfile:

dockerfile
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs

CMD ["node", "index.js"]

Add a health check endpoint:

javascript
const http = require('http');

const healthServer = http.createServer((req, res) => {
  if (req.url === '/health') {
    res.writeHead(200);
    res.end('OK');
  }
});

healthServer.listen(3000);

Deploy using PM2 for process management:

bash
npm install -g pm2
pm2 start index.js --name eth-wallet-bot
pm2 save
pm2 startup

Best Practices & Optimizations

Implement these optimizations for production reliability:

1. Transaction Confirmation Handling:

javascript
async function waitForConfirmation(txHash, confirmations = 12) {
  let currentBlock = await web3.eth.getBlockNumber();
  const targetBlock = currentBlock + confirmations;
  
  return new Promise((resolve, reject) => {
    const checkConfirmation = setInterval(async () => {
      const receipt = await web3.eth.getTransactionReceipt(txHash);
      currentBlock = await web3.eth.getBlockNumber();
      
      if (receipt && currentBlock >= targetBlock) {
        clearInterval(checkConfirmation);
        resolve(receipt);
      }
    }, 15000); // Check every 15 seconds
  });
}

2. Gas Price Optimization:

javascript
async function getOptimalGasPrice() {
  const history = await web3.eth.getFeeHistory(20, 'latest', [25, 50, 75]);
  const baseFee = history.baseFeePerGas[history.baseFeePerGas.length - 1];
  const priorityFee = history.reward[history.reward.length - 1][1]; // 50th percentile
  
  return {
    maxFeePerGas: baseFee + priorityFee,
    maxPriorityFeePerGas: priorityFee
  };
}

3. Memory Leak Prevention:

javascript
setInterval(() => {
  // Clean up inactive chats
  activeChatIds.forEach(chatId => {
    bot.getChat(chatId).catch(() => {
      activeChatIds.delete(chatId);
      logger.info(`Removed inactive chat: ${chatId}`);
    });
  });
}, 3600000); // Every hour

Security Considerations

Protecting your bot requires multiple security layers:

API Key Protection:

  • Store all sensitive data in environment variables
  • Use AWS Secrets Manager or similar for production
  • Rotate API keys regularly

Wallet Security:

javascript
// Never store private keys in code
// Use read-only operations only
const SAFE_METHODS = ['eth_getBalance', 'eth_getTransaction', 'eth_blockNumber'];

function validateMethod(method) {
  if (!SAFE_METHODS.includes(method)) {
    throw new Error('Unsafe method attempted');
  }
}

Re-org Protection:

javascript
async function handleReorg(blockNumber) {
  logger.warn(`Potential re-org detected at block ${blockNumber}`);
  // Re-validate recent transactions
  const recentTxs = await getRecentTransactions(blockNumber - 10);
  recentTxs.forEach(tx => validateTransaction(tx));
}

Input Validation:

javascript
function sanitizeAddress(address) {
  if (!Web3.utils.isAddress(address)) {
    throw new Error('Invalid Ethereum address');
  }
  return address.toLowerCase();
}

Visual Diagram Walk-through

Imagine a flowchart with four main components:

  1. Ethereum Node (Infura WebSocket) continuously streams blockchain data
  2. Your Bot Server filters transactions matching your wallet address
  3. Telegram Bot API receives formatted alerts from your server
  4. User's Telegram App displays notifications instantly

The data flows left to right: when a transaction occurs on Ethereum, it triggers your WebSocket subscription, your bot processes it, formats a message, and sends it through Telegram's API to the user's device. This entire process typically completes in under 3 seconds.

Final Checklist

Before deploying your Ethereum wallet tracker bot, verify:

  • Environment variables properly configured in .env
  • WebSocket reconnection logic implemented
  • Error handling covers network failures
  • Telegram bot commands respond correctly
  • Rate limiting prevents API exhaustion
  • Logs capture errors for debugging
  • Health check endpoint returns 200 OK
  • Process manager (PM2) configured for auto-restart

Frequently Asked Questions

Q: How much does running this bot cost? A: Using free tiers from Infura and running on a $5/month VPS covers most use cases. Heavy usage might require paid API plans.

Q: Can I monitor multiple wallets simultaneously? A: Yes! Modify the monitorWallet function to accept an array of addresses and check each transaction against all monitored wallets.

Q: How do I track ERC-20 token transfers? A: Subscribe to contract events using web3.eth.subscribe('logs') with the Transfer event signature and token contract address.

Q: What about tracking internal transactions? A: Internal transactions require a different approach. Consider using Etherscan's API or running your own archive node with trace capabilities.

Q: How can I reduce notification spam? A: Implement filters for minimum transaction amounts, specific addresses, or time-based rate limiting.

Conclusion

You've built a powerful Ethereum wallet tracker bot that provides real-time transaction notifications through Telegram. This foundation opens doors to advanced features like portfolio tracking, DeFi position monitoring, or whale watching systems. The WebSocket approach ensures minimal latency while the modular architecture supports easy expansion.

For deeper Telegram bot capabilities, explore the official Telegram Bot API documentation. Ready to go further? Explore more Web3 development resources at IrulMedia.

Launch your bot and never miss a wallet movement again!

About admin

The author is a member of the Irul Media team, specializing in Twitter account sales and social media marketing.

Related Posts

Interested in Purchasing a Twitter Account?

We offer a wide selection of high-quality Twitter accounts for various needs.