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

# Sponsor gas on Tron with Transatron

> Use Transatron to cover Tron transaction fees so users can transact without holding TRX.

[Transatron](https://transatron.io) is a Tron RPC provider that delegates the energy and bandwidth required to process a transaction, so end users can transact on Tron without holding TRX. Apps integrate Transatron as a drop-in replacement for the Tron full node and select a fee-payment mode that fits their UX.

This recipe combines Privy's [Tron wallet support](/recipes/use-tier-2#tron) with Transatron's gas sponsorship to broadcast TRX-less transfers from a user's embedded wallet.

## Resources

<CardGroup cols={2}>
  <Card title="Transatron docs" icon="arrow-up-right-from-square" href="https://docs.transatron.io" arrow>
    Official Transatron integration documentation.
  </Card>

  <Card title="Tron raw signing" icon="key" href="/recipes/use-tier-2#tron" arrow>
    How Privy signs Tron transactions via `raw_sign`.
  </Card>
</CardGroup>

***

## Prerequisites

* A funded [Transatron account](https://te.transatron.io) and a **Spender** API key issued from the dashboard. The Spender key authorizes fee deduction from the app's prepaid TFN/TFU balance on every broadcast.

<Warning>
  Keep the Transatron API key on the server. Exposing it to a browser or mobile binary lets any
  caller spend the app's Transatron balance.
</Warning>

## How fee payment works

Transatron supports four fee-payment modes:

| Mode                 | When to use                                                                                                                                                                            | User holds TRX? |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| **Internal account** | Default, simplest integration. Broadcasting through the Transatron RPC with a Spender key auto-deducts fees from the app's prepaid TFN/TFU balance. No per-transaction setup required. | No              |
| **Instant payment**  | The user wallet holds a small TRX or USDT balance to cover Transatron's per-tx fee. Primarily for non-custodial wallet integrations.                                                   | Yes (small)     |
| **Coupon**           | Per-transaction spend cap. The backend issues a coupon backed by its Transatron balance. Typically used for promos and individual discounts.                                           | No              |
| **Bypass**           | Fallback mode. The sender's wallet burns TRX directly for fees as on a vanilla Tron node.                                                                                              | Yes             |

All modes except bypass rely on broadcasting through the Transatron RPC. Submitting the same signed transaction to a vanilla Tron node bypasses Transatron's resource delegation logic, and Tron charges fees as usual.

This recipe focuses on the **internal account** mode — the simplest integration path. Broadcasting through the RPC with a Spender key is all that's needed to sponsor fees. For other modes, see the [Transatron integration guide](https://docs.transatron.io/category/integration-guidelines).

***

## 1. Create a Tron wallet for the user

Privy supports Tron at the Tier 2 level. Create a wallet with `chain_type: 'tron'`. The wallet
address can receive TRX, USDT, and other TRC-20 assets.

<View title="React" icon="react">
  Use `useCreateWallet` from the extended-chains entrypoint to provision a Tron wallet for a logged-in user.

  ```tsx theme={"system"}
  import {useCreateWallet} from '@privy-io/react-auth/extended-chains';

  function CreateTronWallet() {
    const {createWallet} = useCreateWallet();

    const handleCreate = async () => {
      const {wallet} = await createWallet({chainType: 'tron'});
      console.log('Tron address:', wallet.address);
    };

    return <button onClick={handleCreate}>Create Tron wallet</button>;
  }
  ```
</View>

<View title="NodeJS" icon="node-js">
  ```typescript theme={"system"}
  import {PrivyClient} from '@privy-io/node';

  const privy = new PrivyClient({
    appId: process.env.PRIVY_APP_ID,
    appSecret: process.env.PRIVY_APP_SECRET
  });

  const wallet = await privy.wallets().create({
    chain_type: 'tron'
  });

  console.log('Wallet ID:', wallet.id);
  console.log('Tron address:', wallet.address);
  ```
</View>

<View title="REST API" icon="terminal">
  ```bash theme={"system"}
  curl --request POST https://api.privy.io/v1/wallets \
    -u "<your-privy-app-id>:<your-privy-app-secret>" \
    -H "privy-app-id: <your-privy-app-id>" \
    -H 'Content-Type: application/json' \
    -d '{
      "owner": {"user_id": "did:privy:xxxxxx"},
      "chain_type": "tron"
    }'
  ```
</View>

<Note>
  Newly created Tron addresses may require on-chain activation before some sending flows work
  reliably. Fund the address with a small TRX amount first if activation related errors appear.
</Note>

## 2. Configure TronWeb to use the Transatron RPC

Point TronWeb at `https://api.transatron.io` and attach the Spender API key as a header. All subsequent operations — fee estimation, simulation, and broadcasts — flow through Transatron. The Spender key on the connection authorizes automatic fee deduction from the app's prepaid balance on every broadcast.

```typescript theme={"system"}
import {TronWeb, providers} from 'tronweb';

const TRANSATRON_RPC = 'https://api.transatron.io';
const TRANSATRON_TIMEOUT = 60_000;
const headers = {'TRANSATRON-API-KEY': process.env.TRANSATRON_API_KEY!};

const tronWeb = new TronWeb({
  fullNode: new providers.HttpProvider(TRANSATRON_RPC, TRANSATRON_TIMEOUT, '', '', headers),
  solidityNode: new providers.HttpProvider(TRANSATRON_RPC, TRANSATRON_TIMEOUT, '', '', headers),
  eventServer: new providers.HttpProvider(TRANSATRON_RPC, TRANSATRON_TIMEOUT, '', '', headers)
});
```

## 3. Sign Tron transactions with Privy

Privy's `raw_sign` returns a 64-byte ECDSA signature, but Tron expects 65 bytes — the trailing recovery byte is either `0x1b` or `0x1c`. Signing is the only step in the flow that can run on the client; building and broadcasting still happen on the server, since both require the Transatron API key.

<View title="React" icon="react">
  Call `useSignRawHash` with `chainType: 'tron'` to have the user's embedded wallet sign the transaction's `txID`. Send the returned 64-byte signature back to your server, which will attach the recovery byte and broadcast.

  ```tsx theme={"system"}
  import {useSignRawHash} from '@privy-io/react-auth/extended-chains';

  function useSignTronTxId() {
    const {signRawHash} = useSignRawHash();

    return async ({address, txId}: {address: string; txId: string}) => {
      const txIdHex = txId.startsWith('0x') ? txId : `0x${txId}`;

      const {signature} = await signRawHash({
        address,
        chainType: 'tron',
        hash: txIdHex as `0x${string}`
      });

      return signature;
    };
  }
  ```

  The corresponding server helper attaches the recovery byte after receiving the signature from the client:

  ```typescript theme={"system"}
  import type {TronWeb, Types} from 'tronweb';

  function attachTronSignature({
    tronWeb,
    walletAddress,
    transaction,
    signature
  }: {
    tronWeb: TronWeb;
    walletAddress: string;
    transaction: Types.SignedTransaction;
    signature: string;
  }): Types.SignedTransaction {
    const baseSig = signature.replace(/^0x/, '');
    transaction.signature = [`${baseSig}1b`];

    if (tronWeb.trx.ecRecover(transaction) !== walletAddress) {
      transaction.signature = [`${baseSig}1c`];
    }

    return transaction;
  }
  ```
</View>

<View title="NodeJS" icon="node-js">
  When signing happens entirely on the server (e.g. for backend bots or schedulers), call `privy.wallets().rawSign()` directly and probe both recovery bytes inline.

  ```typescript theme={"system"}
  import {PrivyClient} from '@privy-io/node';
  import type {TronWeb, Types} from 'tronweb';

  const privy = new PrivyClient({
    appId: process.env.PRIVY_APP_ID,
    appSecret: process.env.PRIVY_APP_SECRET
  });

  async function signTronTransaction({
    tronWeb,
    walletId,
    walletAddress,
    transaction
  }: {
    tronWeb: TronWeb;
    walletId: string;
    walletAddress: string;
    transaction: Types.SignedTransaction;
  }): Promise<Types.SignedTransaction> {
    const txIdHex = transaction.txID.startsWith('0x') ? transaction.txID : `0x${transaction.txID}`;

    const {signature} = await privy.wallets().rawSign(walletId, {
      params: {hash: txIdHex}
    });

    const baseSig = (signature as string).replace(/^0x/, '');
    transaction.signature = [`${baseSig}1b`];

    if (tronWeb.trx.ecRecover(transaction) !== walletAddress) {
      transaction.signature = [`${baseSig}1c`];
    }

    return transaction;
  }
  ```
</View>

## 4. Broadcast a sponsored transaction

With the internal-account mode, broadcasting through the Transatron RPC is all that's needed to sponsor fees. The Spender key on the connection authorizes the charge — no coupon creation or additional per-transaction setup is required.

The code below runs entirely on the server. If signing happens in a React client (per step 3), replace the inline `signTronTransaction(...)` call with a roundtrip — return the unsigned transaction's `txID` to the client, receive the 64-byte signature back, and finalize via `attachTronSignature(...)` before broadcasting.

<Steps>
  <Step title="Compute the Tron fee limit">
    Tron rejects transactions whose `fee_limit` is lower than the energy required to execute them. Use `triggerConstantContract` to estimate `energy_used` and multiply by the live `energy_fee` from chain parameters. This is the Tron-side fee limit that gets baked into the signed transaction.
    This example uses USDT on Tron mainnet.

    ```typescript theme={"system"}
    const USDT_CONTRACT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'; // USDT on Tron mainnet

    async function estimateFeeLimit({
      ownerAddress,
      recipientAddress,
      amountBaseUnits
    }: {
      ownerAddress: string;
      recipientAddress: string;
      amountBaseUnits: string;
    }): Promise<number> {
      const ownerHex = tronWeb.address.toHex(ownerAddress);
      const contractHex = tronWeb.address.toHex(USDT_CONTRACT);

      const constant = await tronWeb.transactionBuilder.triggerConstantContract(
        contractHex,
        'transfer(address,uint256)',
        {},
        [
          {type: 'address', value: recipientAddress},
          {type: 'uint256', value: amountBaseUnits}
        ],
        ownerHex
      );

      const params = await tronWeb.trx.getChainParameters();
      const energyFee = params.find((p) => p.key === 'getEnergyFee')?.value ?? 420;
      const energyUsed = constant.energy_used ?? 0;

      return Math.ceil(energyUsed * energyFee * 1.1); // 10% buffer
    }
    ```
  </Step>

  <Step title="Build, sign, and broadcast the transfer">
    Build the TRC-20 transfer locally, sign the `txID` with Privy, and broadcast through the Transatron RPC. The Spender key on the connection handles fee payment automatically.

    ```typescript theme={"system"}
    async function sendSponsoredTransfer({
      walletId,
      walletAddress,
      recipientAddress,
      amountBaseUnits,
      feeLimit
    }: {
      walletId: string;
      walletAddress: string;
      recipientAddress: string;
      amountBaseUnits: string;
      feeLimit: number;
    }) {
      const ownerHex = tronWeb.address.toHex(walletAddress);
      const contractHex = tronWeb.address.toHex(USDT_CONTRACT);

      const built = await tronWeb.transactionBuilder.triggerSmartContract(
        contractHex,
        'transfer(address,uint256)',
        {feeLimit, callValue: 0},
        [
          {type: 'address', value: recipientAddress},
          {type: 'uint256', value: amountBaseUnits}
        ],
        ownerHex
      );

      if (!built.transaction) {
        throw new Error('Failed to build transfer transaction');
      }

      const signed = await signTronTransaction({
        tronWeb,
        walletId,
        walletAddress,
        transaction: built.transaction as Types.SignedTransaction<Types.TriggerSmartContract>
      });

      return tronWeb.fullNode.request('wallet/broadcasttransaction', signed, 'post');
    }
    ```

    The broadcast response includes a nested `transatron` object. Fields can vary by mode and API
    version (for example `tx_fee_rtrx_account`, `code`, or zero-fee counters), but the key signal
    is a sponsored/free-fee outcome instead of a Tron burn from the sender wallet.
  </Step>

  <Step title="Orchestrate the full flow">
    Chain the helpers from the previous steps to send a sponsored TRC-20 transfer. With
    internal-account sponsorship, transaction fees can be covered by Transatron instead of burning
    TRX from the sender wallet.

    ```typescript theme={"system"}
    const feeLimit = await estimateFeeLimit({
      ownerAddress,
      recipientAddress,
      amountBaseUnits
    });

    const result = await sendSponsoredTransfer({
      walletId,
      walletAddress: ownerAddress,
      recipientAddress,
      amountBaseUnits,
      feeLimit
    });
    ```
  </Step>
</Steps>

***

## Operating the business account

Use the following Transatron API endpoints to check account balance, review spending history, and fetch current pricing.

<AccordionGroup>
  <Accordion title="Check account balance">
    Query the current TFN/TFU balance available for sponsoring transactions:

    ```typescript theme={"system"}
    const config = await tronWeb.fullNode.request<{
      balance_rtrx: number;
      balance_rusdt: number;
    }>('api/v1/config', {}, 'get');

    console.log('TFN balance:', config.balance_rtrx);
    console.log('TFU balance:', config.balance_rusdt);
    ```
  </Accordion>

  <Accordion title="View spending history">
    Retrieve recent fee charges against the account to audit per-transaction costs:

    ```typescript theme={"system"}
    const ordersResponse = await tronWeb.fullNode.request<{
      orders?: Array<{
        order_id: string;
        order_date: string;
        amount_trx: number;
        charge_token: 'RTRX' | 'RUSDT';
      }>;
      pagination?: {limit: number; offset: number; total: number};
    }>(
      'api/v1/orders',
      {
        limit: 50,
        offset: 0,
        from_date: '2026-01-01T00:00:00Z',
        to_date: '2026-03-01T00:00:00Z'
      },
      'get'
    );
    const orders = ordersResponse.orders ?? [];

    for (const order of orders) {
      console.log(
        `Order ${order.order_id}: ${order.amount_trx} sun (${order.charge_token}) at ${order.order_date}`
      );
    }
    ```
  </Accordion>

  <Accordion title="Check current prices">
    Read pricing from the same `GET /api/v1/config` response:

    ```typescript theme={"system"}
    const config = await tronWeb.fullNode.request<{
      activation_price: number;
      energy_price_per_unit: number;
      bandwidth_price_per_unit: number;
    }>('api/v1/config', {}, 'get');

    console.log('Activation price:', config.activation_price, 'sun');
    console.log('Energy price:', config.energy_price_per_unit, 'sun per unit');
    console.log('Bandwidth price:', config.bandwidth_price_per_unit, 'sun per unit');
    ```
  </Accordion>
</AccordionGroup>

<Info>
  These account-management endpoints require the ADMIN (spender) API key. For full endpoint details,
  see the [Transatron account management
  docs](https://docs.transatron.io/category/account-management).
</Info>
