Send USDT Programmatically on TRON with TronWeb — TRON Wiki

Send USDT Programmatically on TRON with TronWeb

11 min read · ⌘K search

Automating USDT payouts on TRON — payroll, withdrawals, merchant refunds — requires building signed TRC-20 transfer transactions via TronWeb or raw HTTP APIs. TRON's ~3 second finality makes programmatic USDT ideal for high-throughput settlement when you handle Energy and security correctly.

This guide covers end-to-end USDT sends from a backend service.

Prerequisites

  • TronWeb + TronGrid API key
  • Hot wallet with USDT balance + TRX or frozen Energy
  • Official USDT contract: TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
  • Address validation before every send
Hot wallet risk
Automated signing keys are theft targets. Use HSM, MPC, or dedicated payout server with minimal balance.

Amount conversion

USDT uses 6 decimals:

Code
function toUsdtBaseUnits(humanAmount) {
  return Math.round(Number(humanAmount) * 1e6);
}

function fromUsdtBaseUnits(base) {
  return Number(base) / 1e6;
}

Prefer BigInt for large values.

Basic transfer with TronWeb

Code
const TronWeb = require('tronweb');

const tronWeb = new TronWeb({
  fullHost: 'https://api.trongrid.io',
  privateKey: process.env.PAYOUT_PRIVATE_KEY,
  headers: { 'TRON-PRO-API-KEY': process.env.TRONGRID_KEY },
});

const USDT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';

async function sendUsdt(toAddress, amountHuman) {
  if (!tronWeb.isAddress(toAddress)) {
    throw new Error('Invalid TRON address');
  }

  const contract = await tronWeb.contract().at(USDT);
  const amount = toUsdtBaseUnits(amountHuman);

  const txid = await contract.transfer(toAddress, amount).send({
    feeLimit: 100_000_000,
    callValue: 0,
    shouldPollResponse: true,
  });

  return txid;
}

Energy and feeLimit

  • Set feeLimit high enough to avoid OUT_OF_ENERGY (100 TRX sun units common starting point — tune empirically).
  • Monitor sender Energy: tronWeb.trx.getAccountResources(address)
  • Freeze TRX on payout wallet for predictable costs

User-facing fee context: how to send USDT TRC-20.

Idempotency and safety

Production payout systems need:

ControlPurpose
Idempotency keysPrevent double-send on retry
Balance pre-checkQuery balance before transfer
AllowlistOptional recipient validation
Max daily volumeLimit hot wallet exposure
TX logStore txid per payout request

Confirm delivery

After broadcast:

Code
const info = await tronWeb.trx.getTransactionInfo(txid);
// info.receipt.result === 'SUCCESS'

Full patterns: verify transaction API.

transferFrom pattern (custody)

If user approved your contract/spender:

Code
await contract.transferFrom(userAddress, toAddress, amount).send({ feeLimit: 100_000_000 });

Requires prior approve from user — common in DeFi, rare in simple payouts.

Error handling

ErrorAction
OUT_OF_ENERGYTop up TRX or freeze more TRX
REVERTCheck balance, contract pause, blacklist
DUPLICATEIdempotency — tx may already exist
Invalid addressReject at API validation layer

Testing on Shasta

Deploy mock TRC-20 with 6 decimals on Shasta or use test tokens. Never test with mainnet private keys in repos.

Compliance note

Automated USDT transfers may trigger AML obligations depending on jurisdiction. This is technical documentation, not legal advice.

Error handling in production

Wrap .send() in try/catch. On failure, fetch getTransactionInfo before retry — partial broadcasts are rare on TRON but idempotency matters. Log Energy consumed to tune frozen TRX amounts on hot wallets.

TronLink vs server signing

Browser apps should use TronLink window.tronWeb for user-controlled keys. Server payment sweeps use private key in TronWeb constructor — isolate in VPC, never expose to frontend. Multi-sig treasury requires account permissions workflow instead of single-key TronWeb.

FAQ

How much Energy does a USDT transfer use?

Approximately 65,000 Energy on mainnet for a standard TRC-20 transfer when recipient already holds USDT. First-time recipients may cost more.

Can I send USDT without TRX in the same wallet?

The sending account needs Energy or TRX to burn for fees. USDT alone cannot pay network resources.

How do I batch many payouts?

Loop with rate limiting — TRON does not have native ERC-4337 batching. Consider queue worker with concurrency cap.

No. Server uses private key directly via TronWeb.

Wrong address sent programmatically?

Irreversible. Validate tronWeb.isAddress() and optionally confirm checksum patterns.