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 key | Recommended value | Why |
|---|---|---|
vm.supportConstant | true | Required to call balanceOf and other read-only contract methods. Without this, TRC-20 balance queries fail |
vm.saveInternalTx | true | Stores 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.maxTimeRatio | 20.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 = truemakes 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
| Network | Base URL |
|---|---|
| Mainnet | https://api.trongrid.io |
| Shasta testnet | https://api.shasta.trongrid.io |
| Nile testnet | https://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:
| Endpoint | Returns |
|---|---|
v1/accounts/{address}/transactions | TRX and TRC-10 history for an address |
v1/accounts/{address}/transactions/trc20 | TRC-20 history for an address |
v1/contracts/{address}/events | Event 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
| Asset | API | Example |
|---|---|---|
| TRX | /wallet/getaccount | TRX balance |
| TRC-10 | /wallet/getaccount | TRC-10 balance |
| TRC-20 | /wallet/triggerconstantcontract | TRC-20 balance |
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.
| Asset | API | Example |
|---|---|---|
| TRX | /wallet/createtransaction | Transfer TRX |
| TRC-10 | /wallet/transferasset | Transfer TRC-10 |
| TRC-20 | /wallet/triggersmartcontract | Transfer TRC-20 |
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:
| API | Returns |
|---|---|
v1/accounts/{address}/transactions?only_confirmed=true | TRX and TRC-10 history (type field distinguishes TransferContract from TransferAssetContract) |
v1/accounts/{address}/transactions/trc20?only_confirmed=true | TRC-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
- Poll the latest solidified block number (
/walletsolidity/getnowblock) - For each new block, fetch by number (
/walletsolidity/getblockbynum) - For each transaction, read
raw_data.contract[0].typeand dispatch - 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].type | Represents | Fields to read |
|---|---|---|
TransferContract | Plain TRX transfer | owner_address, to_address, amount |
TransferAssetContract | TRC-10 transfer | owner_address, to_address, asset_name, amount |
TriggerSmartContract | Smart-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 range | What asset_name contains | Example |
|---|---|---|
| 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_nameas 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:
-
Verify success — call
/walletsolidity/gettransactioninfobyidand checkreceipt.result == SUCCESS. Skip failed transactions -
TRC-20 transfers — parse
log[]forTransferevents. Each event yields the contract address, the from address, the to address, and the amount. A single transaction may emit multipleTransferevents. See Event log decoding -
Internal transactions — walk
internal_transactions[]. Skip entries whererejected == true, because their asset transfers did not take effect. For each remaining entry, iterate overcallValueInfo[]and apply these rules:- Empty
tokenIdandcallValue > 0→ internal TRX transfer. Read the sender fromcaller_address, the recipient fromtransferTo_address, and the amount in sun from the currentcallValueInfo.callValue. - Non-empty
tokenIdandcallValue > 0→ internal TRC-10 transfer. Read the sender fromcaller_address, the recipient fromtransferTo_address, and the token ID and amount from the currentcallValueInfoentry.
- Empty
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
| Operation | API |
|---|---|
| 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
getUnfreezeDelayDaysvia/wallet/getchainparametersinstead of hard-coding 14 days, and show the resulting "withdrawable on YYYY-MM-DD" time in the UI.
6.2 Resource delegation
| Operation | API |
|---|---|
| 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
| Operation | API |
|---|---|
| Vote for Super Representatives | /wallet/votewitnessaccount |
| Query unwithdrawn rewards | /wallet/getReward |
| Withdraw voting rewards | /wallet/withdrawbalance |
6.4 Query staking state
| Operation | API |
|---|---|
| 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
- Exchange and custodial wallet integration overview — architecture decisions and pre-launch checklist
- Wallet developer guide — non-custodial wallet development
- Deploy a node — self-hosted node setup
- TronGrid — TronGrid node service and extension API introduction
- TronGrid V1 API overview — account history, TRC-20 transfers, event logs, and internal transaction queries
- API Key — create API keys, choose network endpoints, and configure request headers
- Resource model — Bandwidth, Energy,
fee_limit - Staking, voting, and rewards — full staking semantics
- Consensus and DPoS — block solidification and finality
- FAQ — common contract-call Energy issues
Updated 11 days ago