Troubleshooting Tron
The failures a Tron install actually produces — the service refusing to start, deposits that stop after a day, TRC-20 payouts stuck in PENDING, reverted transfers and addresses that look wrong.
Tron produces a small number of failures repeatedly. Work down this page in order — the first two sections cover most of what an install will ever show you.
Everything Tron logs is tagged TRON, and the deposit monitors log under
TRON_DEPOSIT. Start there:
pm2 logs backend --lines 500 | grep -i "TRON"The service will not start at all
Every one of these presents identically as "Tron service not available" or "TRON service not available. Please ensure your license is activated and the blockchain is enabled." Four different causes, one message.
The most common cause and the least obvious. The host is resolved inside the constructor, so an unrecognised value throws before the singleton exists. Every Tron code path then fails — deposits, withdrawals, master wallet creation, admin balance reads.
Check the exact string in .env, including case and quoting. mainnet-beta,
main, MAINNET and an empty value all fail. The Requirements page reports this
one explicitly; nothing else does.
Fix it and restart the backend — the service is constructed once.
The chain service lives at backend/src/blockchains/tron.ts with a compiled
counterpart in backend/dist. Ecosystem loads it through a safe import, so a
missing module is reported rather than fatal.
Requirements → Tron shows Chain service installed: no with the module path it
looked for. Extract the addon and run pnpm updator.
Activation writes lic/54577641.lic relative to the project root. Without it the
chain cannot be enabled — the status endpoint returns 403 before it writes
anything.
If you enabled the chain on another machine or restored a backup, note that the file is machine-bound. It must be re-activated on this box.
Admin → Ecosystem → Blockchains. The Tron row ships with status: false and
stays that way until you enable it.
Both gates are cached for five minutes. A chain you just enabled may report inactive briefly; the service re-checks its own status on the next call once it has seen an inactive result, so this clears without a restart.
Deposits stopped arriving
Search the log for Max consecutive errors. After ten consecutive polling
errors the monitor deletes its own loop for that address permanently.
On mainnet without TRON_API_KEY this is close to inevitable under any real
user count: the anonymous TronGrid quota produces 429s, the monitor backs off to
five minutes, and ten failures still arrive.
Set TRON_API_KEY, restart, and re-run the Requirements test — the REST check
reports the key as valid, invalid or absent.
Rate limited for <address>, backing off to Ns is the monitor working as
designed, not a fault, but it is a warning sign at volume. Each poll is one
TronGrid call per watched address every 30 seconds, plus the background scanner
at 0.5 address-scans per second.
If you are hitting limits with a valid key, reduce load with
ECOSYSTEM_SCAN_RATE_TRON, or upgrade the TronGrid plan.
An invalid, expired or revoked API key. The monitor triples its interval on a 403
rather than doubling, so it degrades faster than plain rate limiting. Regenerate
the key in TronGrid, update .env, restart.
Dust filtering. Native transfers below 0.001 TRX and TRC-20 transfers below
1 / 10^(decimals − 3) — 0.001 for a 6-decimal token — are skipped, counted and
logged at debug level as "Skipped N dust transaction(s)".
This exists because airdropped spam tokens on Tron would otherwise create a transaction row and a notification each. It is not adjustable.
Check TRON_NETWORK against where the coins actually went. Tron addresses are
textually identical across mainnet, Shasta and Nile, and Ecosystem's
token-network guard treats Tron as network-agnostic — so nothing rejects the
mismatch. A perfectly valid address will be watched on a chain the coins are not
on.
Also confirm the token's contract address on the row matches the contract the sender actually used. TRC-20 monitoring filters by contract, so a lookalike token is invisible.
The two use different TronGrid endpoints. Confirm the token row has the correct contract address and decimals, and that the token is enabled. Then check whether the transfer appears on the TRC-20 endpoint at all — TronGrid is queried for confirmed records only on that path, so a very recent transfer may simply not be indexed yet.
Withdrawals
An empty or underfunded master wallet. Search for WITHDRAWAL_REQUEUED — the
message names the master address, the TRX it holds and the TRX it needs.
Tron has no fee-payer primitive, so the platform must physically transfer TRX to whichever address is about to sign. Native TRX withdrawals do not need this, which is exactly why the failure looks selective and reads like a token configuration problem.
Fund the master wallet. The reconciliation watchdog drains the backlog; nothing needs re-submitting and nobody was refunded.
The transfer was mined and reverted. No tokens moved, the row is FAILED, and
the user was refunded.
The platform simulates each transfer for its real energy cost and funds the signer with 20% headroom, so this means the estimate was wrong or the signer lost resources between funding and broadcast. Recurring cases on one token usually mean recipients who have never held it, which costs more energy than the simulation of an existing holder.
There is no per-token energy override. Keep the master wallet comfortably funded so the headroom is not the only margin.
A wallet carrying a Tron address whose key row was never written — a state an earlier release could produce, because the address was recorded before the key. The current code writes the key first, so a failure leaves neither and the next wallet fetch retries cleanly.
For TRC-20 this is survivable. The platform logs a warning and sources the tokens from an alternative custodial wallet; it only fails when no funded alternative exists, and then the user is refunded with nothing moved. For native TRX it is fatal at validation, before any debit, because the platform signs native transfers only from the user's own address.
Broadcast succeeded; no on-chain receipt appeared within 60 seconds. This is not a failure and it is deliberately not refunded — the coins may well be on-chain.
Take the transaction hash from the row and look it up on Tronscan. If it succeeded, the debit was correct and the row can be resolved as completed. If it reverted, refund manually. The state exists because the platform could not tell which.
Look for "Suppressed FAILED write … row already carries a tx hash". A row with a
transaction hash is never relabelled FAILED, because the refund path keys off
that hash and refunding would mint funds against coins that genuinely left.
That log line means the guard worked. Verify the hash on Tronscan before doing anything by hand.
Check the private ledger. When a Tron withdrawal is sourced from an alternative
custodial wallet, an offchainDifference is recorded against that wallet and the
withdrawal path subtracts it before deciding what is available. That is
deliberate — it is what stops one wallet being selected and drained repeatedly.
Browse it at Admin → Ecosystem → Wallets → Ledgers.
Addresses and balances
Wallet fetches regenerate an address entry that is missing, has no address, or
does not start with T. A 0x… value is a leftover from an old release that
derived Tron addresses through the EVM path and cannot receive TRC-20 tokens at
all, so replacing it is correct.
Regeneration is a silent, correct self-heal. If a user deposited to the old
0x… value, those coins were never on Tron in a usable form and this is a
support case, not a platform fault.
Expected. An ECO wallet is per currency and each one generates its own independent Tron address from its own mnemonic. A user's TRX address and their USDT address are unrelated keys.
Also expected, and it is the custody model rather than a discrepancy. Balances credited from internal transfers, P2P trades and trading profits never move on-chain, and TRC-20 payouts are pooled — one custodial wallet's on-chain balance can fund another user's withdrawal, with the draw recorded in the private ledger.
An address's on-chain balance is not a statement about who owns those coins. Read balances from the platform.
The Tron figure is refreshed by the master-wallet list route,
GET /api/admin/ecosystem/wallet/master. It calls
getEcosystemMasterWalletBalance in wallet/master/utils.ts — the only balance
function with a TRON branch — and caches each wallet in Redis for one minute, so
a top-up made inside that minute will not appear. Reload the list.
Do not reach for the endpoint that sounds like the right one.
GET /api/admin/ecosystem/wallet/master/balance carries a second copy of the
refresh logic that handles UTXO chains and then sends everything else through
getProvider(), which builds an ethers JSON-RPC provider. chainConfigs.TRON
has no chainId, so that call throws "Chain ID not found for TRON on mainnet",
the error is swallowed to the console, and the row keeps its old balance. It has
never been able to read a Tron balance, and its cache is five minutes rather than
one.
When nothing above matches
Turn on verbose initialisation logging, restart, and read what the service says about the host it resolved and whether it found an API key:
DEBUG_TRON="true"Then re-run Admin → Ecosystem → Blockchains → Requirements → Tron and read the readiness rows rather than the individual green ticks. Readiness is downgraded by signing prerequisites the probes do not cover — a locked vault, or a missing or disabled master wallet — so every probe can pass while a customer still cannot withdraw.