Chaingateway
10 min read
|
7/4/2026

What Is a TRC20 Address?

What a TRC20 address is, why it starts with T, hex vs Base58 forms, contract vs wallet addresses, and how to create TRON addresses via API.

Chapters
C
Chaingateway Team
Blockchain Experts

A TRC20 address is a TRON account address used to send and receive TRC20 tokens, most prominently USDT. It starts with the letter T and is 34 characters long, for example TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t. Strictly speaking there is no separate “TRC20 address type”: every TRON address can hold TRX, TRC10 tokens and TRC20 tokens alike. When an exchange asks for your “USDT TRC20 address”, it means your normal TRON address.

The format looks nothing like the 0x addresses you know from Ethereum or BSC, and TRON adds a twist that confuses developers more than anything else: the same address has a second, hexadecimal spelling that starts with 41. This guide walks through the encoding, the hex-vs-Base58 question, the difference between wallet and contract addresses, validation code, and how to create TRON addresses at scale through an API.

The format: T plus 33 characters, Base58Check

A TRON address in its human-readable form is Base58Check-encoded. Base58 is the same alphabet Bitcoin uses: all digits and letters except 0, O, I and l, which were dropped because they are easy to confuse in print. So a valid TRON address:

  • starts with T
  • is exactly 34 characters long
  • contains only characters from the Base58 alphabet

The “Check” part means the encoding carries a built-in checksum. Under the Base58 surface, an address is 25 bytes: a prefix byte 0x41, a 20-byte account identifier, and 4 checksum bytes. A single mistyped character breaks the checksum, so a wallet can reject the typo before any transaction is signed. This matters because unlike Ethereum’s optional EIP-55 casing, the checksum in a TRON address is not optional. Every valid address has one.

TRC-20 vs ERC-20 vs BEP-20 side by side

USDT alone lives on all three of these networks, so the formats meet each other daily in withdrawal forms and support tickets. What separates them, with numbers as of mid-2026:

TRC-20 (TRON)ERC-20 (Ethereum)BEP-20 (BNB Smart Chain)
Address formatT + 33 Base58 characters0x + 40 hex characters0x + 40 hex characters
ChecksumBase58Check, always presentEIP-55 letter casing, optionalEIP-55 letter casing, optional
Cross-paste risk with the othersNone, format fails validationHigh: identical to BEP-20 stringsHigh: identical to ERC-20 strings
Block time3 s12 s0.45 s
Time to finality~57 s (19 blocks)~13–16 min (two epochs)1–2 s
Typical USDT transfer fee~6.4–13.4 TRX without staked energytens of cents in ETH, more under loada few cents in BNB

The middle rows explain why this article keeps warning about EVM chains rather than about TRON itself. An ERC-20 and a BEP-20 address are the same string, so nothing in the address tells sender or validator which chain is meant; a TRON address can never be mistaken for either. The mistake TRON users actually make sits in the fee and network rows: picking the wrong network in an exchange dropdown, or underestimating what moving deposits onward costs.

The timing rows are worth a date stamp. BSC reached its 0.45-second block time with the Fermi hard fork in January 2026, its second block-time cut within a year, while TRON has produced a block every 3 seconds since launch and Ethereum has held 12-second slots since its move to Proof of Stake. TRON’s 19-block finality window, about 57 seconds, is what most exchanges wait out before crediting a TRC20 deposit, which is why “sent” and “credited” sit a minute apart even when everything works.

How a TRON address is derived

The derivation is closer to Ethereum than the look of the result suggests:

  1. Generate a private key on the secp256k1 curve, the same curve Bitcoin and Ethereum use.
  2. Compute the public key and hash it with Keccak-256.
  3. Keep the last 20 bytes of the hash. Up to here this is exactly the Ethereum procedure.
  4. Prepend the byte 0x41 (TRON’s mainnet prefix), giving 21 bytes.
  5. Hash those 21 bytes twice with SHA-256 and take the first 4 bytes as checksum.
  6. Append the checksum and encode the 25 bytes in Base58.

The leading T is no convention that someone picked for branding; it falls out of the math. Any 25-byte string beginning with 0x41 encodes to Base58 text beginning with T.

Base58Check with real numbers

Encodings stick better when you can re-run them, so here is the USDT contract address assembled from its parts. The 21-byte payload is the prefix 41 plus the 20-byte account identifier:

payload: 41a614f803b6fd780986a42c78ec9c7f77e6ded13c
sha256(payload): 3a42512dd4f64e4d9dad3d5e6aa0ecf55adbd8a85979135cf211ba347278d029
sha256(again): 710277f5d80b170d90e2ec6c52caa368bc74b9226e3befe5c09825a0eedbfb68
checksum: 710277f5

The checksum is the first 4 bytes of the second hash. Append it to the payload and you hold 25 bytes: 41a614f803b6fd780986a42c78ec9c7f77e6ded13c710277f5. Treat those bytes as one big number, divide by 58 repeatedly, and map each remainder to the Base58 alphabet. The result is TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t, the address every wallet displays.

Decoding runs the same path backwards: Base58 to 25 bytes, split off the last 4, hash the first 21 twice with SHA-256, and compare. If the comparison fails, some character was mangled in transit and the address must not be used. That check is the reason a mistyped TRON address gets rejected by any decent wallet: the odds that a random typo still produces a matching 4-byte checksum are 1 in 2^32, about one in four billion. Ethereum’s EIP-55, for comparison, catches a typo with roughly 15 check bits, so TRON’s scheme is the stricter of the two by a wide margin.

Hex form (41…) vs Base58 form (T…): one address, two spellings

Here is the part almost no explainer covers. When you work with TRON’s own node API or low-level libraries, addresses come back in hexadecimal, starting with 41. The USDT token contract, for example:

Base58: TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
Hex: 41a614f803b6fd780986a42c78ec9c7f77e6ded13c

Both spellings identify the same account. The hex form is the raw 21 bytes (prefix plus identifier) without the checksum; the Base58 form wraps those bytes with the checksum for human use. Wallets and explorers show T..., raw node responses and signed transaction payloads use 41....

Two practical consequences. First, never compare addresses as strings without normalizing the encoding, or TR7N... and 41a6... will look like different accounts to your code. Second, if you strip the 41 prefix from the hex form, the remaining 20 bytes have the same shape as an Ethereum address, which is why some libraries can convert between TRON and EVM representations. Same shape does not mean same account: a key used on both networks produces unrelated addresses, because the derivation differs from step 4 on.

Converting between the two forms

The conversion is short enough to write yourself instead of pulling in a TRON SDK. Going from Base58 to hex means decoding and dropping the checksum; the other direction recomputes it:

import bs58 from "bs58";
import { createHash } from "node:crypto";
const sha256 = (b) => createHash("sha256").update(b).digest();
function base58ToHex(address) {
const raw = Buffer.from(bs58.decode(address));
return raw.subarray(0, 21).toString("hex"); // drop 4 checksum bytes
}
function hexToBase58(hex) {
const payload = Buffer.from(hex, "hex"); // 21 bytes, starts with 41
const checksum = sha256(sha256(payload)).subarray(0, 4);
return bs58.encode(Buffer.concat([payload, checksum]));
}

Pick one canonical form for storage and convert at the boundaries. Storing Base58 keeps your database matching what users and explorers show; storing hex matches what raw node payloads contain. Either works, but mixing both in the same table is how the “same address, no match” bug is born, usually discovered while reconciling deposits at 2 a.m.

Contract address vs wallet address

Both wallets and smart contracts live at T... addresses, and nothing in the string tells them apart. The distinction matters most for USDT:

  • Your wallet address is where you receive USDT. It belongs to your private key.
  • The TRC20 contract address is where the token’s code lives. For USDT on TRON that is TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t.

You will meet the contract address when you add a custom token to a wallet, when you verify on Tronscan that a token is the real USDT and not a copycat with the same name, and when you call the token from code. What you must never do is send tokens to the contract address. Token contracts have no owner who could send them back, so transfers to the contract are gone for good in almost all cases. Explorers label contract accounts as “Contract”, which is the quickest way to check what kind of address you are looking at.

Verifying an address on Tronscan, step by step

Tronscan is TRON’s block explorer, and it settles most doubts in under a minute.

Paste the address into the search bar. The account page shows the TRX balance, TRC20 holdings, and every transfer in and out. A brand-new deposit address shows an empty page, which is fine; TRON accounts get activated by their first incoming transaction, so “no data yet” means unused, not invalid.

Check the account type next. Contract accounts carry a visible “Contract” tag near the top of the page. Money goes to plain accounts; if the destination someone gave you turns out to be tagged as a contract, do not send.

For tokens, search by contract address rather than by name. Typing “USDT” into Tronscan returns the genuine token surrounded by imitations using the same ticker, and on TRON anyone can deploy a token called USDT. The real one lives at TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t, has six decimals, and its Tronscan page shows an issuer label and a holder count in the tens of millions. An unfamiliar token whose page shows a few hundred holders and no verified issuer is not the asset you think it is.

Last step after any transfer: search the transaction ID. The detail page lists sender, recipient, token contract, amount, and the confirmation status. Once the transaction shows as confirmed past the 19-block window, it is final.

Don’t mix up networks

USDT exists on several chains at once: as a TRC20 token on TRON, as an ERC-20 token on Ethereum, as a BEP-20 token on BSC. Same ticker, separate contracts, incompatible networks.

TRON is actually the friendly case here, because the formats differ visibly: a T... address cannot be pasted into an Ethereum withdrawal form without failing validation, and a 0x... address fails on TRON. The risk sits in exchange withdrawal menus, where you pick the network from a dropdown next to the address field. Pick “ERC20” and paste a TRON address, and a good exchange blocks it. But if you own addresses on several networks and paste the wrong one that happens to match the selected network, no validator can save you. State the network explicitly everywhere you display or accept an address. For the EVM side of this problem, where one address exists on many chains at once, see What is a BEP20 address?.

Address poisoning: the attack that checksums can’t catch

Every check described so far catches accidents. Address poisoning is deliberate, and it works precisely because the poisoned address is perfectly valid.

The setup: an attacker watches your on-chain activity, then grinds out a vanity address whose first and last characters match an address you transact with regularly. Generating a TRON address that matches, say, four leading and four trailing characters of a target is a matter of compute time, not of breaking any cryptography. The attacker then plants that lookalike in your transaction history, either by sending a dust amount of TRX or, more elegantly, by abusing the fact that TRC20 contracts allow zero-value transfers: a transferFrom of 0 USDT costs the attacker little and shows up in your history as if you had interacted with the address.

The payoff comes weeks later, when you copy “the usual address” from your transfer history instead of from your own records. Wallets and explorers abbreviate addresses to their first and last characters, the exact characters the attacker matched, so the fake looks right at a glance. Losses from this pattern are not theoretical; in one widely reported 2024 case, an Ethereum user sent about 68 million dollars in WBTC to a poisoned address. TRON’s cheap transactions make the seeding step even cheaper there.

The defenses are unglamorous and work. Never copy addresses out of your transaction history; copy them from your own address book, your database, or the recipient’s receive screen. When you verify, compare more than eight characters, or better, compare the whole string once and then rely on an allowlist. For platforms, the rule is structural: payout destinations come from your database, entered and verified once, and are never derived from on-chain history. And the test-transaction habit applies here too: a small transfer, confirmed on Tronscan as received by the party you meant, costs a few TRX and beats any visual inspection.

Validating a TRC20 address in code

A first-pass check is a regular expression against the Base58 alphabet:

const TRON_FORMAT = /^T[1-9A-HJ-NP-Za-km-z]{33}$/;

The regex catches wrong length and forbidden characters, but not typos within the alphabet. For that you verify the checksum:

import bs58 from "bs58";
import { createHash } from "node:crypto";
const sha256 = (buf) => createHash("sha256").update(buf).digest();
function isValidTronAddress(address) {
if (!/^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address)) return false;
const decoded = Buffer.from(bs58.decode(address));
if (decoded.length !== 25 || decoded[0] !== 0x41) return false;
const payload = decoded.subarray(0, 21);
const checksum = decoded.subarray(21);
const expected = sha256(sha256(payload)).subarray(0, 4);
return expected.equals(checksum);
}

A passing check proves the string is a well-formed TRON address. It does not prove the account exists on-chain or that anyone holds its key, and it cannot distinguish a wallet from a contract. For the contract question, query the chain or check the explorer.

Where wallet addresses come from: HD derivation on TRON

When a wallet app hands you a TRON address seconds after you wrote down twelve words, this is the machinery behind it. The words are a BIP-39 mnemonic that expands into a seed. From the seed, BIP-32 derives a tree of key pairs, always the same tree for the same words. BIP-44 then assigns each blockchain its own branch via a registered coin type, and TRON’s is 195, so the standard path for your first TRON address reads m/44'/195'/0'/0/0.

The coin type is the detail worth remembering. Ethereum sits at coin type 60, TRON at 195, and both numbers appear at a hardened level of the path. Same seed words, entirely different key trees. This is why the same recovery phrase gives you a 0x address in an EVM wallet and an unrelated T address in a TRON wallet, and why neither wallet can see the other’s funds. If you restore a phrase in an Ethereum-only wallet and your TRON balance seems gone, nothing is lost; the wallet simply never derived the 195 branch. Restore the same words in a wallet with TRON support and the address, and the balance, reappear.

The index at the end of the path increments: .../0/1 is your second TRON address from the same words, .../0/2 the third. Personal wallets rarely go past a handful. Platforms are the opposite case, and for them per-customer keys generated independently and registered over an API are the safer construction, because no single phrase then controls every deposit address in the system.

How to get a TRC20 wallet address

There are two routes, and which one you need depends on whether you want one address for yourself or many addresses for an application.

The wallet route (one address, personal use)

Install a wallet that supports TRON: TronLink is the network’s standard browser wallet, Trust Wallet covers mobile, Ledger covers hardware. Create an account, write down the recovery phrase offline, and open the receive screen. The T... address shown there is your TRC20 wallet address for USDT and every other TRON token. There is no fee for creating it and no registration step; the address works the moment the wallet generates it.

The API route (many addresses, for applications)

A platform that credits deposits per customer needs one address per customer, and clicking through a wallet UI a thousand times is not an option. With the Chaingateway REST API you generate keys in your own environment and register them for the TRON network. Authentication is a Bearer token:

Terminal window
curl -X POST https://app.chaingateway.io/api/v2/tron/addresses/import \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"address": "TYourGeneratedTronAddress",
"privatekey": "<generated secp256k1 private key, hex>",
"password": "<encryption password for this key>"
}'

With the header X-Network: testnet the same call targets the TRON testnet, so the whole flow can be tested with free test TRX first. Sending tokens later runs through POST /api/v2/tron/transactions/trc20. The quickstart walks through both calls; a 7-day trial without KYC onboarding covers the testing phase, and plans are on the pricing page.

Accepting TRC20 deposits: the webhook flow

Once each customer has an address, the remaining problem is noticing deposits without polling Tronscan. The pattern:

  1. Assign a dedicated TRON address per customer and store the mapping in your database.
  2. Register a webhook. When a TRC20 transfer reaches the address, your endpoint gets called with the transaction data.
  3. Verify the HMAC signature on the webhook, then credit the customer.

Deliveries are signed, and a delivery your endpoint missed is not lost: GET /api/v2/tron/webhooks/notifications/failed lists what never got through, and POST /api/v2/tron/webhooks/notifications/{id}/retry re-sends it. After an outage, GET /api/v2/tron/webhooks/notifications lists past notifications for reconciliation. Details and signature code are in the webhooks guide.

One TRON-specific point: receiving is free, but moving deposits onward costs energy and bandwidth. Since proposal #104 lowered the energy price from 210 to 100 Sun, a USDT transfer costs roughly 6.4 TRX to an active address and roughly 13.4 TRX to an empty one, if you pay without staked energy. Plan the consolidation costs with the TRON fee calculator; steady senders cut the ongoing burn by staking TRX for energy, which the API covers with POST /api/v2/tron/freeze.

FAQ

Is a TRON address the same as a TRC20 address?

Yes. A TRC20 address is simply a TRON account address used in the context of TRC20 tokens. The same T... address holds TRX, TRC10 tokens and TRC20 tokens. The “TRC20” label on exchange withdrawal pages refers to the token standard and network, not to a special address type.

How many characters does a TRC20 address have?

34 characters, starting with T. Under the Base58Check encoding sit 25 bytes: the prefix byte 0x41, a 20-byte account identifier, and a 4-byte checksum.

What is the USDT TRC20 contract address?

TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t. That is where the token’s code lives, not an address to send funds to. Use it to verify tokens on Tronscan or to add USDT to a wallet manually; transfers sent to the contract itself are unrecoverable.

Why does the API show my address starting with 41 instead of T?

That is the hexadecimal spelling of the same address. Raw TRON node responses and transaction payloads use hex with the 41 prefix; wallets and explorers use the Base58 form starting with T. Convert between the two before comparing addresses in code.

Can I send TRC20 USDT to an Ethereum (0x) address?

No, and the formats protect you here: a 0x address fails TRON’s validation and a T... address fails Ethereum’s. The real risk is picking the wrong network in an exchange withdrawal menu while pasting an address you own on another chain. Always match the network label to the address format.

Do testnet addresses look different?

No. TRON testnets (Shasta, Nile) use the same 0x41 prefix, so testnet addresses also start with T and pass the same validation. Keep testnet and mainnet keys strictly separated in your configuration; the address format won’t warn you if you mix them up.

How much does it cost to send USDT to a TRC20 address?

Receiving is free; sending costs energy and bandwidth. Without staked energy, a USDT transfer runs roughly 6.4 TRX to an active recipient address and roughly 13.4 TRX to an empty one, at the 100 Sun energy price set by proposal #104. The TRON fee calculator computes the current numbers for your case, and staking TRX for energy cuts what you actually pay.

How long does a TRC20 transfer take?

The transaction lands in a block within about 3 seconds and is treated as final after 19 blocks, roughly 57 seconds, once two-thirds of the Super Representatives have built on top of it. Most exchanges credit TRC20 deposits after that window, so about a minute between sending and crediting is normal behavior, not a stuck transaction.

C
Chaingateway Team
Blockchain Experts

The Chaingateway team is dedicated to simplifying blockchain integration for developers worldwide.