Withdrawals

How a Tron payout is sourced, funded, signed and confirmed — pooled custodial sourcing, the master-wallet gas top-up, on-chain receipt verification, and every terminal state including the two that must never be refunded.

7 min readUpdated 3 August 2026withdrawals, trc20, trx, queue, refunds

A Tron withdrawal takes the standard Ecosystem route — validate, debit under a row lock, queue — and then diverges sharply at execution. Two things make it different from every other chain: the coins may not be at the user's own address, and the signer must be funded before it can sign.

If you have not read Energy and bandwidth, start there. This page assumes it.

The shared front half

Before anything Tron-specific happens, the withdrawal route does what it does on every chain:

  1. Validate. The destination must be a Tron address beginning with T. Decimal precision is checked against the token's precision. The withdrawal 2FA policy applies if you have enabled it.

  2. Short-circuit internal transfers. If the destination belongs to another user on this platform it is settled internally, with no on-chain transaction. Withdrawing to your own address is rejected.

  3. Estimate — informationally. For a new recipient a 1 TRX account-activation figure is recorded. A network cost is estimated: energy-based for TRC-20, bandwidth-based for native TRX. Neither is charged.

  4. Debit under a row lock. The wallet row is locked FOR UPDATE and amount + the token's platform fee is deducted in one transaction alongside a PENDING transaction row.

  5. Queue it. The transaction ID goes onto an in-memory FIFO which moves the row through PENDINGPROCESSING → a terminal state.

One pre-flight check is Tron-specific and worth knowing, because it decides whether a user is rejected early or debited and refunded later. For a native TRX withdrawal the platform signs only from the user's own address, so a missing key row is fatal and the request is rejected outright with the balance untouched. For a TRC-20 withdrawal the key row is optional at this stage, because execution can source the tokens from elsewhere.

Pooled sourcing

The awkward truth of a custodial exchange is that a user's platform balance and the coins at their own address are not the same thing. Balances arrive from internal transfers, P2P trades and trading profits without any on-chain movement, so a user can legitimately hold 500 USDT on the platform while their own Tron address holds nothing.

Tron handles this by pooling. Before signing, the service reads the signer's actual on-chain balance — for TRC-20 by calling the contract's balanceOf view directly, not by trusting an indexer — and if it is short, looks for an alternative.

The selector picks the single custodial wallet with the highest available balance for that currency and chain, where "available" is the greater of the wallet's recorded chain balance and its canonical platform balance, minus anything the private ledger says is already spoken for. Only wallets with usable key material are eligible.

When an alternative is used, the platform records the draw in ecosystem_private_ledger as an offchainDifference against that wallet and decrements its recorded balance — inside a transaction that locks the row, so concurrent withdrawals sharing a source serialise rather than race. That ledger entry is what stops the same wallet being selected and drained repeatedly.

It is deliberate, it is bounded by the private ledger, and it is the mechanism that lets an exchange settle a balance that never existed on-chain under the user's own key. It also means a Tron address's on-chain balance is not a statement about who owns those coins. Read balances from the platform, not from Tronscan.

Native TRX withdrawals use the same fallback with one difference: a missing key on the user's own wallet cannot be worked around, because the pre-flight check already rejected the request.

Funding the signer

Once the signing address is settled, the TRC-20 path sizes and funds it from the master wallet — simulate the transfer for its real energy cost, read the signer's live resources and the current burn prices, compute only the shortfall with 20% headroom, and top the address up to that target.

Topping up to a target rather than sending a fixed amount is what makes a re-queued withdrawal safe: the second attempt re-reads the balance, finds it already funded, and sends nothing.

If the master wallet cannot cover the top-up, the withdrawal is requeued, not failed. The row stays PENDING, the reconciliation watchdog picks it up later, and no refund is issued because nothing was attempted on-chain.

Native TRX withdrawals skip this entirely. The address being drained is the one paying the bandwidth.

Signing, broadcasting and confirming

The transfer is built with triggerSmartContract calling transfer(address,uint256) under a 100 TRX feeLimit, then signed.

Before it is broadcast, the platform writes the signed transaction's hash to the row as a pending hash. Tron transaction IDs are deterministic before broadcast — signing only appends a signature — so persisting the hash first means a crash or an HTTP timeout during broadcast leaves a recoverable row instead of one that looks like a clean failure and gets wrongly refunded.

TronWeb returning result: true means the node accepted the transaction, nothing more. A TRC-20 transfer can be mined and still revert or run out of energy, burning resources without moving a single token.

Marking that COMPLETED would write a transaction hash, and the refund path refuses to refund a row that carries one — a permanent, silent customer loss. The platform therefore polls the on-chain receipt for up to 60 seconds and only accepts SUCCESS.

Every terminal state

State Meaning Refunded What you do
COMPLETED Receipt confirmed SUCCESS on-chain No Nothing
FAILED Reverted, out of energy, or failed before broadcast Yes Read the description; usually resources or a bad recipient
TIMEOUT Broadcast but no receipt inside 60 seconds No Look the hash up on Tronscan and resolve manually
PENDING (requeued) Master wallet short of TRX, or a top-up not yet confirmed No Fund the master wallet; the watchdog drains the backlog

Two guards protect the money, and both are worth understanding because they explain behaviour that otherwise looks wrong.

A row that already carries a transaction hash is never relabelled FAILED. The failure write is conditional on the hash being null. If it is suppressed, the log says so explicitly. The coins left the platform; refunding would mint funds.

TIMEOUT is not a failure. The transfer may well be on-chain. It is routed to manual review rather than refunded, and the reconciliation watchdog will resolve it if the receipt appears.

What to do when a payout stalls

Almost always an empty master wallet. Search the backend log for WITHDRAWAL_REQUEUED — the message names the master address, the balance it has and the amount it needs. Fund it; the backlog drains without intervention.

Confirm the shape by checking whether native TRX withdrawals are completing at the same time. If they are, it is gas, not Tron.

The transfer was mined and reverted, so nothing moved and the user was refunded. The energy simulation under-estimated, or the signer lost resources between funding and broadcast.

If it recurs on one token, that token's real cost exceeds the simulation — check whether the recipients are addresses that have never held it, which costs more energy. There is no per-token energy override; the practical response is to ensure the master wallet is comfortably funded so the 20% headroom is not the only margin.

An old wallet that carries a Tron address but whose key row was never written — a state an earlier release could produce because the address was recorded before the key.

For TRC-20 this is survivable: the platform logs a warning and sources from an alternative custodial wallet. It only becomes fatal when no funded alternative exists, and then the queue refunds the user with no tokens moved. For native TRX it is fatal at validation, before any debit.

Wallet fetches self-heal a missing address, but not a missing key on an address that already looks valid. Affected wallets are a data-repair job, not a configuration change.

Broadcast succeeded, the receipt did not appear within 60 seconds. Take the hash from the transaction row and look it up on Tronscan.

If it succeeded, the coins are gone and the user's debit was correct — resolve the row as completed. If it reverted, the user must be refunded manually. Do not assume either; the state exists precisely because the platform could not tell.

The energy simulation executes the real transfer against real state, so it reverts when the signing address does not itself hold the tokens — which is the normal case under pooled sourcing. The service catches that, logs it, and falls back to 65,000 energy.

A REVERT opcode executed warning from the estimator is expected and harmless. It never becomes a user-facing charge.