Exchange wallet integration

Full implementation guide for integrating an exchange or custodial wallet with TRON. Covers node configuration, TronGrid usage, balance and transfer APIs, block parsing for deposit detection, and the Stake 2.0 staking APIs.

This guide is the implementation-level reference for integrating an exchange, custodial wallet, or payment platform with the TRON network. It covers two access paths (TronGrid versus a self-hosted full node), the core operation APIs (balances, transfers, history), the block-parsing recipe for deposit detection, and the Stake 2.0 staking APIs.

For the architecture-level overview and pre-launch checklist, see Exchange and custodial wallet integration.

📘

Prerequisites


1. Choosing your access layer

Two paths exist:

  • TronGrid (widely used hosted service in the TRON ecosystem) — fastest to integrate; rate-limited by plan
  • Self-hosted full node — no rate limits; you operate the hardware

If you have not yet decided, see the architecture comparison at Exchange and custodial wallet integration § 2. When switching providers for the same API family, business code usually only needs a Base URL change. However, JSON-RPC and node HTTP APIs use different request and response formats and cannot be used interchangeably.

Both paths can coexist: many integrators run a self-hosted node for hot-path operations and use TronGrid for less-frequent extension-API queries (account history, contract events).


2. Running your own full node

2.1 Deploy the node

See Deploy a node for the full setup procedure.

2.2 Configure for exchange workloads

Three configuration options in config.conf matter for exchange integration:

Config keyRecommended valueWhy
vm.supportConstanttrueRequired to call balanceOf and other read-only contract methods. Without this, TRC-20 balance queries fail
vm.saveInternalTxtrueStores internal transactions — TRX or TRC-10 moved by smart contracts. Required for accurate deposit detection when users receive through routers, multi-call contracts, or batched flows
vm.maxTimeRatio20.0 or higher (on lower-spec hardware)Per-transaction timeout tolerance during smart-contract verification. Too low on slow hardware causes the node to stop syncing on heavy contract calls

What internal transactions are: when a smart contract invokes another contract or transfers TRX or TRC-10 to an address, those movements are recorded inside the parent transaction's execution trace rather than as separate top-level transactions. A user whose deposit arrives via a swap router would not be credited if you only inspected top-level transactions. vm.saveInternalTx = true makes these movements queryable.

Other configuration options not specific to exchange workloads are documented in Deploy a node.


3. Using TronGrid

TronGrid is TRON's first-party hosted RPC service. It exposes every full-node endpoint plus extension APIs that simplify common integration flows (account history, TRC-20 history, contract events).

3.1 Endpoints

NetworkBase URL
Mainnethttps://api.trongrid.io
Shasta testnethttps://api.shasta.trongrid.io
Nile testnethttps://nile.trongrid.io

3.2 API key and rate limits

Production traffic requires an API key in the TRON-PRO-API-KEY header. See API Key for the request flow and TronGrid rate limits for current rate limits and tier details — limits change over time, so always check the plan page rather than hard-coding limits into your operational runbook.

3.3 Extension APIs

Beyond the standard full-node endpoints, TronGrid exposes APIs that would otherwise require block parsing:

EndpointReturns
v1/accounts/{address}/transactionsTRX and TRC-10 history for an address
v1/accounts/{address}/transactions/trc20TRC-20 history for an address
v1/contracts/{address}/eventsEvent logs emitted by a smart contract

Full reference: TronGrid V1 API overview.


4. Core operations

The API tables below apply to both access paths. For TronGrid, prefix the URL with the network base URL from §3.1; for a self-hosted node, prefix with your node's address (typically http://127.0.0.1:8090).

4.1 Query balances

TRC-20 balances require calling balanceOf on the token contract, which is a constant call. This is why vm.supportConstant = true is required on a self-hosted node.

4.2 Send transfers

A transfer transaction has three steps: construct, sign, broadcast. See Transactions for the full lifecycle. SDKs in JavaScript (TronWeb), Java (Trident), Go (gotron-sdk), and others include offline signing.

fee_limit settings apply to TRC-20 and any other contract-based transfer. Set fee_limit based on your expected Energy cost; see Resource model.

4.3 Get transaction history (TronGrid only)

Self-hosted nodes do not expose a "history for an address" endpoint — you either parse blocks (§5) or rely on TronGrid:

APIReturns
v1/accounts/{address}/transactions?only_confirmed=trueTRX and TRC-10 history (type field distinguishes TransferContract from TransferAssetContract)
v1/accounts/{address}/transactions/trc20?only_confirmed=trueTRC-20 history (decoded from Transfer events)

5. Parsing blocks for deposit detection

If you run a self-hosted node, deposit detection is implemented by walking each new solidified block and dispatching by transaction type.

5.1 The pipeline

  1. Poll the latest solidified block number (/walletsolidity/getnowblock)
  2. For each new block, fetch by number (/walletsolidity/getblockbynum)
  3. For each transaction, read raw_data.contract[0].type and dispatch
  4. For smart-contract transactions, also inspect events and internal transactions

Parse from solidified height (not chain head) — solidification lags head by about 1 minute and guarantees no fork rollback. See Consensus and DPoS § Block solidification.

5.2 Dispatch by contract type

contract[0].typeRepresentsFields to read
TransferContractPlain TRX transferowner_address, to_address, amount
TransferAssetContractTRC-10 transferowner_address, to_address, asset_name, amount
TriggerSmartContractSmart-contract call (may contain TRC-20, TRX, or TRC-10 movements)See §5.4

5.3 TRC-10 token identification

When you decode a TRC-10 transfer (TransferAssetContract), the asset_name field means different things depending on when the transaction landed:

Block rangeWhat asset_name containsExample
Block 5537806 onward (the vast majority of blocks)The numeric token ID"1002000"
Before block 5537806 (early-2019 and earlier)The token's name string"USDJ"

Why the change? Early TRON required every TRC-10 token name to be unique, so the name itself worked as an identifier. A governance proposal (TRONSCAN #14) later allowed duplicate names — after that point, only the numeric ID stays unique per token.

What this means for your code:

  • Most exchanges and wallets only credit deposits from recent blocks — just treat asset_name as a numeric token ID and reject any transfer from before block 5537806. You will almost never need pre-cutoff history.
  • Full-chain scanners (block explorers, historical indexers) must branch on block number before parsing — apply name-lookup for pre-cutoff blocks and ID-lookup for everything after.

5.4 Parsing smart-contract transactions

A single TriggerSmartContract transaction can carry a TRC-20 transfer, internal TRX transfers, and internal TRC-10 transfers — sometimes all in one. Procedure:

  1. Verify success — call /walletsolidity/gettransactioninfobyid and check receipt.result == SUCCESS. Skip failed transactions

  2. TRC-20 transfers — parse log[] for Transfer events. Each event yields the contract address, the from address, the to address, and the amount. A single transaction may emit multiple Transfer events. See Event log decoding

  3. Internal transactions — walk internal_transactions[]. Skip entries where rejected == true, because their asset transfers did not take effect. For each remaining entry, iterate over callValueInfo[] and apply these rules:

    • Empty tokenId and callValue > 0 → internal TRX transfer. Read the sender from caller_address, the recipient from transferTo_address, and the amount in sun from the current callValueInfo.callValue.
    • Non-empty tokenId and callValue > 0 → internal TRC-10 transfer. Read the sender from caller_address, the recipient from transferTo_address, and the token ID and amount from the current callValueInfo entry.

Example transactions for reference:


6. Staking services

If your platform offers staking to users, implement the Stake 2.0 API. The Stake 1.0 unstake API is still operational for users who previously staked through the older path, but new staking flows should use 2.0. Full semantics — including the unstake delay, voting rewards, and resource delegation — are at Staking, voting, and rewards.

6.1 Stake, unstake, withdraw

OperationAPI
Stake TRX (Stake 2.0)/wallet/freezebalancev2
Unstake TRX (Stake 2.0)/wallet/unfreezebalancev2
Withdraw unstaked TRX after the unstake delay/wallet/withdrawexpireunfreeze
Cancel a pending unstake/wallet/cancelallunfreezev2
Unstake Stake 1.0 deposits/wallet/unfreezebalance

The unstake delay is user-visible. Between the unstake call and the corresponding withdrawal, TRX is locked. The delay is controlled by chain parameter #70, currently 14 days on Mainnet and configurable in the range 1–365. Query getUnfreezeDelayDays via /wallet/getchainparameters instead of hard-coding 14 days, and show the resulting "withdrawable on YYYY-MM-DD" time in the UI.

6.2 Resource delegation

OperationAPI
Delegate resources to another address/wallet/delegateresource
Undelegate resources/wallet/undelegateresource
Maximum delegatable resource amount/wallet/getcandelegatedmaxsize
Resources delegated from one address to another/wallet/getdelegatedresourcev2
Delegation index for an account/wallet/getdelegatedresourceaccountindexv2

6.3 Voting and rewards

OperationAPI
Vote for Super Representatives/wallet/votewitnessaccount
Query unwithdrawn rewards/wallet/getReward
Withdraw voting rewards/wallet/withdrawbalance

6.4 Query staking state

OperationAPI
Full account state — staking, resources, unstake queue, voting/wallet/getaccount
Resource quotas and usage/wallet/getaccountresource
Currently withdrawable balance/wallet/getcanwithdrawunfreezeamount
Remaining unstake operations allowed/wallet/getavailableunfreezecount

Related resources