Cross Chain Swaps from Tron

View as Markdown

The 0x Cross-Chain API is in private beta. Request access to start building.

Quick link with examples: 0x-examples

Steps to Cross Chain Swap Tokens from Tron

This guide will walk you through using the /quotes and /status endpoints on 0x’s Cross Chain API to:

  • Fetch a quote
  • Build and sign a transaction
  • Broadcast and confirm the transaction
  • Monitor the status of the cross chain swap

In our example, we will be swapping USDT on Tron to USDC on Arbitrum. For understanding how to deal with the native TRX, please refer to Native Tokens Handling.

0. Prerequisites

Make sure you have:

  • A funded Tron wallet (with TRX for energy/bandwidth fees)
  • 0x API key
  • A connection to a Tron full-node HTTP API (see details below)
  • TronWeb library (used for address conversion, signing, and broadcasting)

Tron provides a public full-node HTTP API, but for production use, it’s strongly recommended to use a dedicated provider such as TronGrid or run your own Tron full node.

Tron transactions are built via the HTTP API endpoint wallet/triggersmartcontract, then signed and broadcast using TronWeb helpers.

1import TronWeb from "tronweb";
2
3const TRON_RPC_URL = "https://api.trongrid.io";
4
5const tronWeb = new TronWeb({
6 fullHost: TRON_RPC_URL,
7 privateKey: "your_private_key_hex",
8});

Unlike EVM chains, Tron uses an energy and bandwidth model instead of gas:

  • Bandwidth — consumed when broadcasting any transaction. Free bandwidth is available from staking TRX.
  • Energy — consumed when executing smart contract calls. Can be obtained by staking TRX or paid in TRX directly.

If your wallet does not have enough staked TRX to cover energy/bandwidth, the required TRX will be burned from your balance automatically. For cross-chain swaps, make sure your wallet has sufficient TRX to cover these fees.

1. Fetch a Quote

Start by sending a GET request to the 0x /quotes endpoint to get a quote for a specific token and chain pair with a selected amount. You may use 'tron' or '999999999993' as the originChain. Tron addresses use the Base58Check format (e.g. TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t).

1const quotesParams = new URLSearchParams({
2 originChain: 'tron', // Tron mainnet
3 destinationChain: '42161', // Arbitrum mainnet
4 sellToken: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT on Tron
5 buyToken: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', // USDC on Arbitrum
6 sellAmount: '10000000', // 10 USDT (6 decimals)
7 originAddress: '$USER_TRON_ADDRESS', // Tron address (Base58Check) that will make the trade
8 destinationAddress: '$USER_EVM_ADDRESS', // Arbitrum address that will receive the output
9 sortQuotesBy: 'price', // Prefer the quote that will result in the best price / output
10 maxNumQuotes: '1', // only the best quote
11});
12
13const headers = {
14 '0x-api-key': '[api-key]', // Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps)
15};
16
17const response = await fetch('https://api.0x.org/cross-chain/quotes?' + quotesParams.toString(), { headers });
18const quoteResponse = await response.json();
19
20console.log(quoteResponse);
1{
2 "liquidityAvailable": true,
3 "allowanceTarget": null,
4 "originChainId": 999999999993,
5 "originChain": "tron",
6 "destinationChainId": 42161,
7 "destinationChain": "arbitrum",
8 "sellToken": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
9 "buyToken": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
10 "issues": {
11 "allowance": null,
12 "balance": null,
13 "simulationIncomplete": false,
14 "invalidSwapSourcesPassed": [],
15 "invalidBridgesPassed": []
16 },
17 "zid": "0x16eccbf6d178cd46304ae903",
18 "quotes": [
19 {
20 "sellAmount": "10000000",
21 "buyAmount": "9984660",
22 "minBuyAmount": "9884813",
23 "quoteId": "0x16eccbf6d178cd46304ae903eb6e3703",
24 "fees": {
25 "integratorFee": null,
26 "zeroExFee": null,
27 "bridgeNativeFee": null
28 },
29 "gasCosts": {
30 "chainType": "tvm",
31 "energyFee": "27300000",
32 "bandwidthFee": "350000",
33 "total": "27650000"
34 },
35 "steps": [
36 {
37 "type": "bridge",
38 "originChainId": 999999999993,
39 "destinationChainId": 42161,
40 "sellToken": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
41 "buyToken": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
42 "sellAmount": "10000000",
43 "buyAmount": "9984660",
44 "minBuyAmount": "9884813",
45 "provider": "near_intents",
46 "estimatedTimeSeconds": 79
47 }
48 ],
49 "transaction": {
50 "chainType": "tvm",
51 "details": {
52 "to": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
53 "data": "a9059cbb00000000000000000000000000000069ed03...0000000000",
54 "value": "0",
55 "ownerAddress": "T...",
56 "memo": ""
57 }
58 },
59 "estimatedTimeSeconds": 79,
60 "issues": {
61 "allowance": null,
62 "balance": null,
63 "simulationIncomplete": false
64 }
65 }
66 ]
67}

Unlike EVM chains, Tron cross-chain swaps do not require token approvals. The API uses deposit-only transactions, so there is no allowanceTarget and no approval step needed.

2. Build and sign the transaction

Tron transactions follow a different flow than EVM or Solana. Instead of receiving a pre-built serialized transaction, you receive the contract call parameters (to, data, value, ownerAddress) and use them to build a transaction via the Tron HTTP API.

The recommended approach is to call the Tron HTTP API directly for building the transaction, then use TronWeb for signing:

1const quote = quoteResponse.quotes[0];
2const txDetails = quote.transaction.details;
3
4// Build unsigned transaction via Tron HTTP API
5const triggerResponse = await fetch(`${TRON_RPC_URL}/wallet/triggersmartcontract`, {
6 method: "POST",
7 headers: { "Content-Type": "application/json" },
8 body: JSON.stringify({
9 owner_address: tronWeb.address.toHex(txDetails.ownerAddress),
10 contract_address: tronWeb.address.toHex(txDetails.to),
11 function_selector: "",
12 parameter: "",
13 data: txDetails.data.replace(/^0x/, ""),
14 call_value: Number(txDetails.value) || 0,
15 fee_limit: 150_000_000, // 150 TRX fee limit
16 }),
17});
18
19const { transaction } = await triggerResponse.json();
20
21// Sign the transaction
22const signedTx = await tronWeb.trx.sign(transaction);

3. Broadcast and confirm the transaction

After signing, broadcast the transaction and poll for confirmation. Tron blocks are produced approximately every 3 seconds.

1// Broadcast the signed transaction
2const broadcastResult = await tronWeb.trx.sendRawTransaction(signedTx);
3
4if (!broadcastResult.result) {
5 throw new Error(`Broadcast failed: ${broadcastResult.message}`);
6}
7
8const txHash = broadcastResult.txid;
9console.log(`Transaction broadcast: ${txHash}`);
10
11// Poll for confirmation
12async function waitForConfirmation(txHash: string, maxAttempts = 40) {
13 for (let i = 0; i < maxAttempts; i++) {
14 await new Promise((r) => setTimeout(r, 3000)); // Wait 3 seconds between polls
15
16 const info = await tronWeb.trx.getTransactionInfo(txHash);
17
18 if (info && info.id) {
19 if (info.receipt?.result === "SUCCESS") {
20 console.log("Transaction confirmed!");
21 return info;
22 }
23 if (info.receipt?.result && info.receipt.result !== "SUCCESS") {
24 throw new Error(`Transaction failed: ${info.receipt.result}`);
25 }
26 }
27 }
28 throw new Error("Transaction not confirmed within timeout");
29}
30
31const confirmation = await waitForConfirmation(txHash);

4. Monitor the cross chain execution

The last step is to monitor the execution of the cross chain transaction, including the fill on the destination chain. For that, we will use the /status endpoint.

When using the /status endpoint for Tron-originated transactions, pass the transaction hash as a hex string prefixed with 0x (64 hex characters).

1const statusParams = new URLSearchParams({
2 originChain: "tron", // origin chain
3 originTxHash: `0x${txHash}`, // Tron tx hash, 0x-prefixed
4});
5
6const headers = {
7 '0x-api-key': '[api-key]', // Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps)
8};
9
10const statusResponse = await fetch('https://api.0x.org/cross-chain/status?' + statusParams.toString(), { headers });
11
12console.log(await statusResponse.json());

In your application, you might need to monitor the status repeatedly, as bridging operation might take several seconds or minutes to complete.

1{
2 "status": "bridge_filled",
3 "bridge": "relay",
4 "steps": [
5 {
6 "type": "bridge",
7 "bridge": "relay",
8 "originChainId": 999999999993,
9 "destinationChainId": 42161,
10 "sellToken": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
11 "buyToken": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
12 "sellAmount": "10000000",
13 "minBuyAmount": "9884813",
14 "quotedBuyAmount": "9984660",
15 "settledBuyAmount": null,
16 "estimatedTimeSeconds": 5,
17 "transactions": [
18 {
19 "chainId": 999999999993,
20 "chain": "tron",
21 "txHash": "0xabc123...64hex",
22 "timestamp": 1755200803
23 },
24 {
25 "chainId": 42161,
26 "chain": "arbitrum",
27 "txHash": "0xdef456...64hex",
28 "timestamp": 1755200810
29 }
30 ]
31 }
32 ],
33 "failure": null,
34 "transactions": [
35 {
36 "chainId": 999999999993,
37 "chain": "tron",
38 "txHash": "0xabc123...64hex",
39 "timestamp": 1755200803
40 },
41 {
42 "chainId": 42161,
43 "chain": "arbitrum",
44 "txHash": "0xdef456...64hex",
45 "timestamp": 1755200810
46 }
47 ],
48 "zid": "0xabc123def456789012345678"
49}

Key differences from EVM and Solana

EVMSolanaTron
Chain identifierNumeric chain ID (e.g. 8453)solana or 999999999991tron or 999999999993
Address formatHex, 0x-prefixedBase58 (Pubkey)Base58Check (e.g. T...)
Transaction formatPre-built to, data, valueSerialized VersionedTransactionContract call params (to, data, value, ownerAddress)
Gas modelGas price × gas limitBase fee + priority feeEnergy fee + bandwidth fee
Token allowanceERC-20 approve to AllowanceHolderNot needed (handled in transaction)Not needed (deposit-only transactions)
ConfirmationwaitForTransactionReceiptconfirmTransactionPoll getTransactionInfo (~3s blocks)
chainType discriminator"evm""svm""tvm"