Setting Up Payment Acceptance in Tron (TRX)
Tron is popular for payment systems for one reason: transaction fees for TRC-20 transfers (USDT) are close to zero if the sender has sufficient Energy and Bandwidth. This makes it the preferred channel for small payments and high-volume transactions in developing markets. Setting up payment acceptance is a straightforward task, but there are specifics to working with Tron's resource model.
Resource Model: Energy and Bandwidth
Tron has no familiar gas. Instead — two resources:
Bandwidth — for regular TRX transfers and account activation. Each account gets 600 Bandwidth per day for free. If insufficient — TRX is deducted (0.1 TRX per 1000 Bandwidth).
Energy — for interacting with smart contracts, including TRC-20 transfers. Not given for free. Sources:
- Freeze TRX → get Energy
- Buy Energy directly from delegators (cheaper than freezing for one-off operations)
- If no Energy — transaction burns TRX directly
For USDT TRC-20 receipt: the sender spends Energy from their account. Your receiving address does not spend Energy. But there's a nuance: first interaction with a smart contract on a new account requires activation (1 TRX). If a user transfers from a new wallet — make sure their account is activated.
Setup via TronWeb
const TronWeb = require('tronweb');
const tronWeb = new TronWeb({
fullHost: 'https://api.trongrid.io', // or own node
headers: { 'TRON-PRO-API-KEY': process.env.TRONGRID_API_KEY },
privateKey: process.env.TRON_PRIVATE_KEY,
});
// Get TRC-20 balance (USDT)
const USDT_CONTRACT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
async function getUsdtBalance(address: string): Promise<bigint> {
const contract = await tronWeb.contract().at(USDT_CONTRACT);
const balance = await contract.balanceOf(address).call();
return BigInt(balance.toString());
}
TronGrid — official public API. Limits without API key: 15 req/sec, with key (free): 1000 req/sec.
Payment Address Generation
HD Wallet for Tron uses coin type 195 per BIP-44:
import { ethers } from 'ethers';
import TronWeb from 'tronweb';
function deriveTronAddress(mnemonic: string, index: number): string {
const hdNode = ethers.HDNodeWallet.fromMnemonic(
ethers.Mnemonic.fromPhrase(mnemonic)
).derivePath(`m/44'/195'/0'/0/${index}`);
// Tron address — Ethereum address with different prefix (T instead of 0x)
return TronWeb.address.fromPrivateKey(hdNode.privateKey.slice(2));
}
Tron address technically matches Ethereum address, only displayed in Base58Check with prefix T.
Monitoring Incoming Transactions
async function pollTrc20Transactions(address: string, fromTimestamp: number) {
const response = await fetch(
`https://api.trongrid.io/v1/accounts/${address}/transactions/trc20` +
`?min_timestamp=${fromTimestamp}&contract_address=${USDT_CONTRACT}&limit=200`,
{ headers: { 'TRON-PRO-API-KEY': process.env.TRONGRID_API_KEY! } }
);
const data = await response.json();
for (const tx of data.data) {
if (tx.to === address && tx.type === 'Transfer') {
await processPayment({
txId: tx.transaction_id,
amount: BigInt(tx.value), // in sun, 1 USDT = 1_000_000 sun
confirmations: tx.confirmed ? 20 : 0,
});
}
}
}
Tron finalizes blocks through DPOS consensus. A transaction is considered confirmed at approximately 19 blocks (~57 sec). For payments: wait for confirmed: true from the API — this is sufficient, the probability of reorg in Tron is extremely low.
Notification Configuration
TronGrid supports webhook subscriptions via TronWatch or third-party services. Alternatively — Moralis Streams with Tron support:
// Example with Moralis Streams
const stream = await Moralis.Streams.add({
chains: [EvmChain.TRON_MAINNET],
description: "USDT TRC-20 payments",
tag: "payments",
webhookUrl: "https://your-api.com/tron-webhook",
includeContractLogs: true,
abi: ERC20_ABI,
topic0: ["Transfer(address,address,uint256)"],
advancedOptions: [
{ topic0: "Transfer(address,address,uint256)", filter: { "eq": ["to", receivingAddress] } }
],
});
Setup Checklist
- TronGrid API key registered and added to config
- Addresses generated from HD Wallet with coin type 195
- TRC-20 Transfer event monitoring on USDT contract
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t - Amounts stored in bigint (avoid float — loss of precision)
- Confirmation:
confirmed: trueor 20+ blocks - Test with real transaction on small amount before production







