Monitoring blockchain transactions in real-time is essential for applications like cryptocurrency wallets, exchanges, and payment gateways. The TRON network, known for its high throughput and low transaction fees, supports seamless tracking of native TRX and USDT (TRC20) transfers. This guide walks you through a robust, production-ready Java implementation to monitor TRON blockchain transactions by polling blocks and parsing transfer events—ideal for integrating deposit and withdrawal logic into your system.
Core Workflow Overview
The solution follows a block-by-block polling mechanism using TRON’s public HTTP API (https://api.trongrid.io). Since TRON does not support WebSocket-based event streaming for all use cases, we rely on periodic polling to detect new blocks and extract relevant transaction data.
Key Steps in the Process:
- Fetch the latest block number from the TRON network.
- Compare it with the last processed block stored in Redis.
- Iterate through each unprocessed block, retrieving transaction data.
- Parse transactions to identify TRX and USDT (TRC20) transfers.
- Update Redis with the latest block number after processing.
- Handle rate limiting and ensure system stability.
This method ensures reliable detection of on-chain activity with minimal latency.
🔧 Technical Prerequisites
Before implementing the solution, ensure you have the following:
1. TRON API Key
To avoid rate-limiting (RQS), obtain a free API key from TronGrid.
- Free tier allows up to 500,000 API calls per day.
- Include the key in your HTTP headers as
TRON-PRO-API-KEY.
👉 Generate your free TRON API access and start monitoring transactions securely.
2. Required Dependencies (Maven)
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.33</version>
</dependency>
</dependencies>These libraries handle HTTP communication and JSON parsing efficiently.
🧱 System Architecture & Components
The implementation consists of several modular components:
- Scheduled Task: Triggers every 10 seconds to check for new blocks.
- Redis Storage: Caches the last processed block number for persistence.
- Block Data Fetcher: Retrieves block data via TRON’s
/wallet/getblockbynumendpoint. - Transaction Parser: Extracts and decodes TRX and USDT transfer events.
- Base58 Address Decoder: Converts hexadecimal addresses to user-friendly Base58 format.
💻 Core Implementation Code
1. Scheduled Task Runner
package com.app.web.task;
import com.app.web.trx.TrxEventDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Slf4j
@Component
public class Web3Task {
@Resource
private TrxEventDataService trxEventDataService;
/**
* Executes every 10 seconds after an initial 20s delay
*/
@Scheduled(initialDelay = 20_000, fixedDelay = 10_000)
public void exec() {
String contractUsdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; // USDT on TRON
String url = "https://api.trongrid.io";
String apiKey = "your_trongrid_api_key"; // Replace with your key
trxEventDataService.exec(contractUsdt, url, apiKey);
System.out.println("Sync completed -------------");
}
}This task runs periodically, fetching new blocks and processing transactions without missing any.
2. Transaction Processing Service
package com.app.web.trx;
import com.alibaba.fastjson.*;
import com.app.common.util.RedisUtil;
import org.springframework.stereotype.Service;
import tron_scan.ScanBlock;
import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
@Service
public class TrxEventDataService {
private static final ScanBlock scan = new ScanBlock();
public static final String REDIS_BLOCK_NUM = "REDIS_BLOCK_NUM";
public void exec(String contractUsdt, String url, String apiKey) {
scan.set_api_key(apiKey);
scan.set_uri(url);
String endNumStr = scan.GetNowBlockNum();
String startNumber = RedisUtil.get(REDIS_BLOCK_NUM);
if (startNumber == null) {
RedisUtil.set(REDIS_BLOCK_NUM, endNumStr);
startNumber = endNumStr;
}
int start = Integer.parseInt(startNumber);
int end = Integer.parseInt(endNumStr);
for (int i = start + 1; i < end; i++) {
final int index = i;
String returnStr = sendPost(url + "/walletsolidity/getblockbynum", "{\"num\":" + i + "}", apiKey);
if (JSON.isValid(returnStr)) {
JSONObject json = JSON.parseObject(returnStr);
if (json.size() == 0) {
try {
System.out.println("No data, retrying in 30s...");
Thread.sleep(30000);
returnStr = sendPost(url + "/walletsolidity/getblockbynum", "{\"num\":" + i + "}", apiKey);
json = JSON.parseObject(returnStr);
} catch (InterruptedException e) { /* ignored */ }
}
if (json.containsKey("blockID") && json.containsKey("transactions")) {
String value = AnalysisOt.getTransferEvent(json.getJSONArray("transactions").toJSONString(), String.valueOf(i));
processTransferData(value, contractUsdt);
}
}
RedisUtil.set(REDIS_BLOCK_NUM, index + "");
System.out.println("Processed block: " + i + " - Sleeping 1s...");
try { Thread.sleep(1000); } catch (InterruptedException e) { }
}
}
private void processTransferData(String allData, String contractUsdt) {
JSONArray jsonArray = JSON.parseArray(allData);
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject transfer = jsonArray.getJSONObject(i);
if (!"SUCCESS".equals(transfer.getString("contractRet"))) continue;
String type = transfer.getString("type");
BigDecimal amount = new BigDecimal(transfer.getString("amount"));
BigDecimal divisor = new BigDecimal("1000000");
String formattedAmount = amount.divide(divisor, 6, BigDecimal.ROUND_HALF_UP).toString();
if ("TriggerSmartContract".equals(type) &&
contractUsdt.equalsIgnoreCase(transfer.getString("contract_address"))) {
System.out.println("USDT Transfer: " +
transfer.getString("from_address") + " → " +
transfer.getString("to_address") + " | " +
formattedAmount + " USDT | Tx: " +
transfer.getString("txID"));
} else if ("TransferContract".equals(type)) {
System.out.println("TRX Transfer: " +
transfer.getString("from_address") + " → " +
transfer.getString("to_address") + " | " +
formattedAmount + " TRX | Tx: " +
transfer.getString("txID"));
}
}
}
public String sendPost(String url, String json, String apiKey) {
StringBuilder response = new StringBuilder();
try {
URL requestUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) requestUrl.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.setRequestProperty("accept", "application/json");
conn.setRequestProperty("TRON-PRO-API-KEY", apiKey);
conn.setRequestProperty("User-Agent", "Mozilla/5.0");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
try (OutputStream os = conn.getOutputStream()) {
byte[] data = json.getBytes(StandardCharsets.UTF_8);
os.write(data, 0, data.length);
}
if (conn.getResponseCode() == 200) {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return response.toString();
}
}🔍 Understanding Transfer Event Parsing
The AnalysisOt.getTransferEvent() method decodes raw transaction data:
- TRX Transfers: Use
TransferContracttype. - USDT (TRC20) Transfers: Identified by
TriggerSmartContractwith function selectora9059cbb(ERC20transfer()). - Hex to Base58 Conversion: Ensures addresses are human-readable.
- Amount Scaling: Divides by 1e6 since USDT uses 6 decimal places.
⚠️ Best Practices & Considerations
1. Rate Limiting
- Always use an API key to avoid being throttled.
- Add a 1-second delay between requests to stay under limits.
2. Error Handling
- Implement retry logic for failed block fetches.
- Use multiple fallback nodes (
tronstack.io, etc.) for redundancy.
3. Data Consistency
- Store processed block numbers in Redis or database.
- Avoid stopping the service for long periods—catch-up may take time.
👉 Discover advanced tools to enhance your blockchain monitoring infrastructure.
❓ Frequently Asked Questions (FAQ)
Q1: Why can’t I use WebSockets for real-time TRON event listening?
A: While TRON supports some WebSocket endpoints, they are unstable or limited in production. Polling via HTTP APIs remains the most reliable method for mission-critical systems.
Q2: How do I handle service downtime?
If the service stops, it resumes from the last saved block in Redis. Since TRON produces a block every ~3 seconds and our system processes one per second, it will catch up quickly once restarted.
Q3: Can this monitor other TRC20 tokens besides USDT?
Yes! Just replace the contract address with any valid TRC20 token address (e.g., BTT, JST). The parsing logic works universally for standard TRC20 transfers.
Q4: Is Redis mandatory?
While not strictly required, Redis provides fast, persistent storage for block numbers. Alternatives include MySQL, PostgreSQL, or even file-based storage—but Redis is optimal for performance.
Q5: How accurate is the transaction parsing?
The parser accurately identifies successful transfers by checking contractRet=SUCCESS and validates input data length and method signatures (a9059cbb). Failed or reverted transactions are filtered out.
Q6: Can I deploy this in a clustered environment?
Yes, but ensure only one instance processes blocks at a time to prevent duplication. Use distributed locking (e.g., Redis SETNX) or run the task on a single node.
🔑 Core Keywords
- TRON blockchain monitoring
- TRX transaction listener
- USDT on TRON
- Java blockchain integration
- Parse TRC20 transfers
- Real-time crypto tracking
- Block polling system
- Smart contract event parsing
By following this guide, you can build a scalable and resilient system to monitor TRON-based assets. Whether you're building a wallet, exchange, or payment processor, this foundation ensures timely detection of deposits and withdrawals.
👉 Enhance your blockchain application with secure, real-time data tools today.