> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bungee.exchange/llms.txt
> Use this file to discover all available pages before exploring further.

# Swap across EVM & Tron

> Bridge and swap assets between EVM chains and Tron using Bungee

Bungee also supports swap assets from any EVM chain to Tron and from Tron to any EVM chain.

This guide walks you through swapping assets between EVM chains and Tron using Bungee. It covers transaction options, and current limitations.

## Quick Start

### Key differences

Here's what's different when integrating Tron Bungee API compared to standard EVM chains:

**Transaction Flow**

* User sends funds to a Deposit contract with the quote ID
* Bungee indexes the transaction and delivers the funds on the destination chain

**API Behavior**

* Both `userAddress` and `receiverAddress` required for quotes
* The only asset supported from/to Tron is [Tron USDT](https://tronscan.io/#/token20/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t)
* There is no Fee collection at the moment

## Integration Steps

The ["Auto routing onchain" guide from Bungee Docs](/integrate/integration-guides/auto-onchain-requests) outlines how to integrate the deposit flow as it is a quite similar process.

1. **Select Chains:** Users choose the source and destination chains, which determine the tokens available for bridging.
2. **Fetch Routes:** After selecting the tokens, retrieve the deposit route if available using the `/quote` endpoint.
3. **Submitting a deposit via the Deposit contract:** Obtain the transaction data from the `/quote` endpoint. Use this data to execute the deposit transaction on the source chain.
4. **Track Transaction Status:** Monitor the transaction's progress by polling the `/status` endpoint until the bridging process is complete.

<Info>
  For Tron quotes, please ensure both `userAddress` and `receiverAddress` are defined.

  Also, note that this is a separate route from `autoRoute` since it is under `depositRoute`.
</Info>

## Examples

### Queries

<Accordion title="Quote from Polygon USDC to Tron USDT">
  ```
  https://public-backend.bungee.exchange/api/v1/bungee/quote?userAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&receiverAddress=TMe1CS54poswoTTrPAtqsKhry4RM7nvSF4&originChainId=137&destinationChainId=728126428&inputToken=0x3c499c542cef5e3811e1192ce70d8cc03d5c3359&outputToken=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t&inputAmount=5000000
  ```
</Accordion>

<Accordion title="Quote from Tron USDT to Base USDC">
  ```
  https://public-backend.bungee.exchange/api/v1/bungee/quote?userAddress=TMe1CS54poswoTTrPAtqsKhry4RM7nvSF4&receiverAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&originChainId=728126428&destinationChainId=8453&inputToken=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t&outputToken=0x833589fcd6edb6e08f4c7c32d4f71b54bda02913&inputAmount=9681288
  ```
</Accordion>

### Scripts

<Accordion title="Quote and swap from Tron USDT to Polygon USDC (tronweb)">
  ```tsx theme={null}
  import { privateKeyToAccount } from "viem/accounts";
  import { TronWeb } from "tronweb";

  // Check if PRIVATE_KEY is set, set in console, i.e.: export PRIVATE_KEY=<PRIVATE_KEY_HEX_64>
  if (!process.env.PRIVATE_KEY) {
    console.error("Error: PRIVATE_KEY environment variable is not set");
    console.error(
      "Example: PRIVATE_KEY=<PRIVATE_KEY_HEX_64>"
    );
    process.exit(1);
  }

  // Check if TRON_PRIVATE_KEY is set
  if (!process.env.TRON_PRIVATE_KEY) {
    console.error("Error: TRON_PRIVATE_KEY environment variable is not set");
    console.error(
      "Example: TRON_PRIVATE_KEY=<PRIVATE_KEY_HEX_64>"
    );
    process.exit(1);
  }

  // Create account from private key
  const account = privateKeyToAccount(`${process.env.PRIVATE_KEY}`);

  // Derive TRON address from TRON private key
  const tronPrivateKey = process.env.TRON_PRIVATE_KEY.replace(/^0x/, "");
  const tronWeb = new TronWeb({
    fullHost: "https://api.trongrid.io",
    privateKey: tronPrivateKey,
  });
  const tronAddress = tronWeb.address.fromPrivateKey(tronPrivateKey);

  // API and token parameters
  const BUNGEE_API_BASE_URL = "https://public-backend.bungee.exchange";

  // USDT contract address on Tron
  const USDT_CONTRACT = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";

  // Function to get USDT balance
  async function getUSDTBalance() {
    try {
      const contract = await tronWeb.contract().at(USDT_CONTRACT);
      const balance = await contract.balanceOf(tronAddress).call();
      return balance.toString();
    } catch (error) {
      console.error("Failed to get USDT balance:", error);
      throw error;
    }
  }

  // Function to get a quote
  async function getQuote(params) {
    try {
      const url = `${BUNGEE_API_BASE_URL}/api/v1/bungee/quote`;
      const queryParams = new URLSearchParams(params);
      const fullUrl = `${url}?${queryParams}`;

      const response = await fetch(fullUrl);
      console.log(fullUrl);
      const data = await response.json();
      const serverReqId = response.headers.get("server-req-id");

      // Check for errors
      if (!data.success) {
        throw new Error(
          `Quote error: ${data.statusCode}: ${data.message}. server-req-id: ${serverReqId}`
        );
      }

      // Check if autoRoute exists
      if (!data.result.depositRoute) {
        throw new Error(`No depositRoute available. server-req-id: ${serverReqId}`);
      }

      const quoteId = data.result.depositRoute.quoteId;
      console.log("- Quote ID:", quoteId);

      // Extract data based on the response structure
      const txData = data.result.depositRoute.txData;

      return {
        quoteId,
        txData,
        fullResponse: data,
      };
    } catch (error) {
      console.error("Failed to get quote:", error);
      throw error;
    }
  }

  // Function to submit the transaction to the Deposit contract
  async function submitNativeTransaction(txData) {
    try {
      console.log("- Submitting transaction to Deposit contract...");
      console.log("  To:", txData.to);
      console.log("  Value:", txData.value);
      console.log("  Data:", txData.data);

      const transaction = await tronWeb.transactionBuilder.triggerSmartContract(
        tronWeb.address.fromHex(txData.to),
        "", // Empty functionSelector when using 'input'
        {
          input: txData.data.replace(/^0x/, ""), // Full data including function selector + params + quoteId
          callValue: parseInt(txData.value) || 0,
          feeLimit: 100000000,
        },
        [], // Empty parameters since we're using 'input'
        tronAddress
      );

      // Sign and send
      const signed = await tronWeb.trx.sign(transaction.transaction);
      const result = await tronWeb.trx.sendRawTransaction(signed);

      if (!result.result) {
        throw new Error(`Transaction failed: ${JSON.stringify(result)}`);
      }

      const hash = result.txid;
      console.log("- Transaction sent:", hash);

      // Wait for confirmation
      let receipt;
      while (!receipt) {
        await new Promise((resolve) => setTimeout(resolve, 3000));
        try {
          const txInfo = await tronWeb.trx.getTransactionInfo(hash);
          if (txInfo?.blockNumber) {
            receipt = { blockNumber: txInfo.blockNumber, status: txInfo.receipt ? "success" : "failed", transactionHash: hash };
          }
        } catch { }
      }

      console.log("- Transaction confirmed in block:", receipt.blockNumber);

      return { hash, receipt };
    } catch (error) {
      console.error("Failed to submit transaction:", error);
      throw error;
    }
  }

  // Function to check the status of a request
  async function checkStatus(requestHash) {
    try {

      const response = await fetch(
        `${BUNGEE_API_BASE_URL}/api/v1/bungee/status?requestHash=${requestHash}`
      );
      const data = await response.json();

      if (!data.success) {
        throw new Error(
          `Status error: ${data.error?.message || "Unknown error"}`
        );
      }

      return data.result[0];
    } catch (error) {
      console.error("Failed to check status:", error);
      throw error;
    }
  }

  // Main function to handle the flow
  async function main() {
    try {
      console.log("Starting Bungee Tron to EVM Token Test (Deposit Route)...");

      // Get USDT balance
      console.log("\n1. Getting USDT balance...");
      const usdtBalance = await getUSDTBalance();
      console.log("- USDT Balance:", usdtBalance);

      // Quote parameters for ERC20 token test
      const quoteParamsERC20 = {
        userAddress: tronAddress,
        receiverAddress: account.address,
        originChainId: 728126428, // Tron
        destinationChainId: 137, // Polygon
        inputToken: USDT_CONTRACT, // USDT on Tron
        outputToken: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", // USDC on Polygon
        inputAmount: usdtBalance, // Use actual USDT balance
      };

      // Get the quote and extract data
      console.log("\n2. Getting quote...");
      const quoteResponse = await getQuote(quoteParamsERC20);
      //console.log(JSON.stringify(quoteResponse.fullResponse, null, 2));

      if (quoteResponse.txData) {
        // Submit the transaction
        console.log("\n3. Submitting transaction...");
        const { hash, receipt } = await submitNativeTransaction(quoteResponse.txData);

        console.log(
          "\n4. Transaction submitted:",
          "\n- Hash:",
          hash,
          "\n- Status:",
          receipt.status
        );

        // Wait for 5 seconds before checking status
        console.log("\n5. Waiting for 5 seconds...");
        let status;
        do {
          await new Promise((resolve) => setTimeout(resolve, 5000));
          console.log("\n6. Checking status...");
          try {
            status = await checkStatus(quoteResponse.quoteId);
            console.log("- Status details:", status.bungeeStatusCode);
          } catch (error) {
            console.error(
              "Failed to check status:",
              error?.message || "Unknown error"
            );
          }
        } while (status?.bungeeStatusCode !== 3 && status?.bungeeStatusCode !== 4);

        console.log(
          "\n7. Transaction complete:",
          "\n- Hash:",
          status.destinationData?.txHash || "Transaction hash not available"
        );
      } else {
        console.log("No transaction data available in the quote response");
      }
    } catch (error) {
      console.error("Error in processing:", error?.shortMessage || error.message);
      throw error;
    }
  }

  // Execute the main function
  main();
  ```
</Accordion>

<Accordion title="Quote and swap from Polygon USDC to Tron USDT (Viem)">
  ```tsx theme={null}
  import { privateKeyToAccount } from "viem/accounts";
  import { createPublicClient, http, createWalletClient } from "viem";
  import { polygon } from "viem/chains";
  import { TronWeb } from "tronweb";

  // Check if PRIVATE_KEY is set, set in console, i.e.: export PRIVATE_KEY=<PRIVATE_KEY_HEX_64>
  if (!process.env.PRIVATE_KEY) {
    console.error("Error: PRIVATE_KEY environment variable is not set");
    console.error(
      "Example: PRIVATE_KEY=<PRIVATE_KEY_HEX_64>"
    );
    process.exit(1);
  }

  // Create account from private key
  // Normalize private key to ensure it has 0x prefix (required by viem)
  const rawPrivateKey = process.env.PRIVATE_KEY;
  const normalizedPrivateKey = rawPrivateKey.startsWith('0x') 
    ? rawPrivateKey 
    : `0x${rawPrivateKey}`;
  const account = privateKeyToAccount(normalizedPrivateKey);

  // Create Viem clients
  const publicClient = createPublicClient({
    chain: polygon,
    transport: http(),
  });

  const walletClient = createWalletClient({
    account,
    chain: polygon,
    transport: http(),
  });

  // Check if TRON_PRIVATE_KEY is set
  if (!process.env.TRON_PRIVATE_KEY) {
    console.error("Error: TRON_PRIVATE_KEY environment variable is not set");
    console.error(
      "Example: TRON_PRIVATE_KEY=<PRIVATE_KEY_HEX_64>"
    );
    process.exit(1);
  }

  // Derive TRON address from TRON private key
  const tronPrivateKey = process.env.TRON_PRIVATE_KEY.replace(/^0x/, "");
  const tronWeb = new TronWeb({
    fullHost: "https://api.trongrid.io",
    privateKey: tronPrivateKey,
  });
  const tronAddress = tronWeb.address.fromPrivateKey(tronPrivateKey);

  // API and token parameters
  const BUNGEE_API_BASE_URL = "https://public-backend.bungee.exchange";

  // Quote parameters for ERC20 token test (USDC from Optimism to Arbitrum)
  const quoteParamsERC20 = {
    userAddress: account.address,
    receiverAddress: tronAddress,
    originChainId: 137, // Polygon
    destinationChainId: 728126428, // Tron
    inputToken: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", // USDC on Polygon
    outputToken: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", // USDT
    inputAmount: "10000000", // 10 USDC (6 decimals)
  };

  // Function to get a quote
  async function getQuote(params) {
    try {
      const url = `${BUNGEE_API_BASE_URL}/api/v1/bungee/quote`;
      const queryParams = new URLSearchParams(params);
      const fullUrl = `${url}?${queryParams}`;

      const response = await fetch(fullUrl);
      console.log(fullUrl);
      const data = await response.json();
      const serverReqId = response.headers.get("server-req-id");

      // Check for errors
      if (!data.success) {
        throw new Error(
          `Quote error: ${data.statusCode}: ${data.message}. server-req-id: ${serverReqId}`
        );
      }

      // Check if autoRoute exists
      if (!data.result.depositRoute) {
        throw new Error(`No depositRoute available. server-req-id: ${serverReqId}`);
      }

      const quoteId = data.result.depositRoute.quoteId;
      console.log("- Quote ID:", quoteId);

      // Extract data based on the response structure
      const txData = data.result.depositRoute.txData;

      return {
        quoteId,
        txData,
        fullResponse: data,
      };
    } catch (error) {
      console.error("Failed to get quote:", error);
      throw error;
    }
  }

  // Function to submit the transaction to the inbox contract
  async function submitNativeTransaction(txData) {
    try {
      console.log("- Submitting transaction to inbox contract...");
      console.log("  To:", txData.to);
      console.log("  Value:", txData.value);
      console.log("  Data:", txData.data);

      // Send the transaction
      const hash = await walletClient.sendTransaction({
        to: txData.to,
        value: BigInt(txData.value),
        data: txData.data,
      });

      console.log("- Transaction sent:", hash);

      // Wait for transaction to be mined
      const receipt = await publicClient.waitForTransactionReceipt({ hash });
      console.log("- Transaction confirmed in block:", receipt.blockNumber);

      return {
        hash,
        receipt,
      };
    } catch (error) {
      console.error("Failed to submit transaction:", error);
      throw error;
    }
  }

  // Function to check the status of a request
  async function checkStatus(requestHash) {
    try {

      const response = await fetch(
        `${BUNGEE_API_BASE_URL}/api/v1/bungee/status?requestHash=${requestHash}`
      );
      const data = await response.json();

      if (!data.success) {
        throw new Error(
          `Status error: ${data.error?.message || "Unknown error"}`
        );
      }

      return data.result[0];
    } catch (error) {
      console.error("Failed to check status:", error);
      throw error;
    }
  }

  // Main function to handle the flow
  async function main() {
    try {
      console.log("Starting Bungee EVM to Tron Test (Deposit Route)...");

      // Get the quote and extract data
      console.log("\n1. Getting quote...");
      const quoteResponse = await getQuote(quoteParamsERC20);
      //console.log(JSON.stringify(quoteResponse.fullResponse, null, 2));

      if (quoteResponse.txData) {
        // Submit the transaction
        console.log("\n2. Submitting transaction...");
        const { hash, receipt } = await submitNativeTransaction(quoteResponse.txData);

        console.log(
          "\n3. Transaction submitted:",
          "\n- Hash:",
          hash,
          "\n- Status:",
          receipt.status
        );

        // Wait for 5 seconds before checking status
        console.log("\n4. Waiting for 5 seconds...");
        let status;
        do {
          await new Promise((resolve) => setTimeout(resolve, 5000));
          console.log("\n5. Checking status...");
          try {
            status = await checkStatus(quoteResponse.quoteId);
            console.log("- Status details:", status.bungeeStatusCode);
          } catch (error) {
            console.error(
              "Failed to check status:",
              error?.message || "Unknown error"
            );
          }
        } while (status?.bungeeStatusCode !== 3 && status?.bungeeStatusCode !== 4);

        console.log(
          "\n6. Transaction complete:",
          "\n- Hash:",
          status.destinationData?.txHash || "Transaction hash not available"
        );
      } else {
        console.log("No transaction data available in the quote response");
      }
    } catch (error) {
      console.error("Error in processing:", error?.shortMessage || error.message);
      throw error;
    }
  }

  // Execute the main function
  main();
  ```
</Accordion>
