Skip to main content
On TRON, every transaction costs gas, and users normally pay it in TRX. This recipe shows how your app can cover that gas instead, so users can send tokens like USDT from a wallet that holds no TRX. Dynamic doesn’t sponsor TRON gas the way it does for Solana and EVM. Instead, your backend uses a sponsor wallet to stake TRX and lend the resulting gas to your users’ wallets. TRON calls this gas Energy and Bandwidth, and calls lending it delegating. This recipe uses two wallets:
  • A sponsor wallet: a TRON server wallet that your backend controls with the Node SDK. It stakes TRX and lends gas to users.
  • A user wallet: the user’s TRON embedded wallet, which signs TRC-20 transfers in the browser without holding TRX.

Prerequisites

Dynamic environment
  • TRON enabled in two places in the Dynamic Console: Chains & Networks, with the same network active as your backend (for example, Nile), and Embedded Wallets.
  • A frontend using the JavaScript SDK (@dynamic-labs-sdk/*) with the TRON extension. The legacy React SDK’s @dynamic-labs/tron supports only external wallets.
Sponsor wallet (backend)
  • A TRON server wallet from Create a TRON wallet. Save the full walletMetadata that createWalletAccount returns, and the wallet’s password.
  • A Dynamic API token and your environment ID.
  • Enough TRX in the sponsor wallet to stake. Step 3 shows how much.
TRON network
  • A TRON full node, such as TronGrid (https://api.trongrid.io on mainnet, https://nile.trongrid.io on Nile). Use a TronGrid API key in production, because TronGrid rate-limits requests without one.

How it works

  1. The sponsor wallet stakes TRX for Energy, which contract calls such as TRC-20 transfers consume.
  2. Your frontend sends the user’s address to your backend, which activates the address if it’s new and delegates Energy to it.
  3. Once the delegation confirms, the user’s wallet sends transfers as usual, and TRON spends the delegated Energy instead of TRX. Without delegated Bandwidth, free Bandwidth covers about one transfer a day.
  4. When the user no longer needs coverage, the sponsor reclaims the Energy and can unstake the TRX.

1. Set up the sponsor client (backend)

Install @dynamic-labs-wallet/node-tron. The examples read these environment variables:
  • DYNAMIC_ENVIRONMENT_ID and DYNAMIC_AUTH_TOKEN
  • SPONSOR_WALLET_METADATA (the saved walletMetadata as JSON) and SPONSOR_WALLET_PASSWORD
  • TRON_NODE, and optionally TRONGRID_API_KEY
Only delegate to wallets of authenticated users, or anyone can drain the sponsor. Verify the user’s Dynamic JWT, check that its environment_id matches yours, and check that userAddress is one of the user’s wallets. If the token omits verified_credentials, look up the user (the token’s sub) with Get a user by ID and your API token.
In production:
  • Keep DYNAMIC_AUTH_TOKEN, SPONSOR_WALLET_PASSWORD, and the wallet metadata in a secrets manager.
  • A long-lived client’s session expires, and signTransaction then fails with HTTP 403. Call authenticateApiToken again and retry, or create the client per request.
  • Load @dynamic-labs-wallet/node-tron as an ES module (the CommonJS build fails wallet creation with bs58.encode is not a function). In Next.js, also add @dynamic-labs-wallet/node and @dynamic-labs-wallet/node-tron to serverExternalPackages, because Turbopack can’t bundle their native addon.

2. Add full-node helpers

The full node builds the sponsor’s transactions, and your backend signs and broadcasts them as in Sign TRON Transactions. The node reports invalid requests as HTTP 200 with an Error field, so the helper checks for it before signing.

3. Stake TRX on the sponsor wallet

Staked TRX earns a share of the network’s daily Energy, and spent Energy recovers over 24 hours. Size the stake as the Energy your users need per day divided by the Energy per TRX, which is TotalEnergyLimit / TotalEnergyWeight from /wallet/getaccountresource. Estimate a transfer’s Energy with /wallet/triggerconstantcontract (energy_used); transfers to an address that doesn’t hold the token yet cost more.
Recent mainnet USDT transfers used about 65,000 Energy to an existing holder and 130,000 to a new one.1 At 10 Energy per TRX, one 65,000-Energy transfer a day needs 6,500 TRX of stake.

4. Lend gas to the user’s wallet

A new embedded wallet doesn’t exist on-chain, and receiving TRC-20 tokens doesn’t activate it. Delegating to it fails with Account[…] not exists, so the sponsor activates it first, for a one-time fee of about 1.1 TRX.2
delegateresource takes staked TRX in SUN, not Energy, so convert with the Energy per TRX. Stake whose Energy is still recovering can’t be delegated, so check /wallet/getcandelegatedmaxsize first and stake more or reclaim when it runs low.
Delegate, and wait for confirmation before responding to the frontend. A transfer sent before the delegation confirms doesn’t get the Energy.
Repeated delegations to one address add up. Before delegating, call /wallet/getaccountresource for the user’s address, and skip the delegation when EnergyLimit minus EnergyUsed already covers ENERGY_PER_TRANSFER.
A delegation with lock: true can’t be reclaimed until its lock period expires.

Cover more than one transfer a day

Every transaction also uses Bandwidth, which delegated Energy doesn’t cover. An activated account gets 600 free Bandwidth a day and a TRC-20 transfer uses about 345, so a user with no TRX can send about one transfer a day.3 Another transfer that day fails with BANDWITH_ERROR (TRON’s spelling). To cover more than one transfer a day per user, repeat steps 3 and 4 with resource: 'BANDWIDTH': size the stake with TotalNetLimit / TotalNetWeight and pass type: 0 to getcandelegatedmaxsize. To reclaim it, sum frozen_balance_for_bandwidth in step 6 and undelegate with resource: 'BANDWIDTH'.

5. Send a transfer without TRX (frontend)

Steps 1 to 4 run in a backend endpoint you define, here /api/sponsor. Call it with the user’s Dynamic JWT before each transfer. The backend activates the address once and delegates only when the user’s remaining Energy is too low:
sendGaslessTransfer calls requestSponsorship first, then sends the transfer with the embedded wallet’s TronWeb instance. TronWeb uses the active Dynamic network’s RPC, not your TRON_NODE, so both must be on the same network. This example sends USDT on mainnet:
feeLimit is in SUN. It caps the TRX the call can burn, and TRON also uses it to cap the Energy the call can use, including delegated Energy. Set it to at least the Energy the transfer needs times the network’s Energy fee (getEnergyFee from /wallet/getchainparameters), with headroom. The limit isn’t a charge: when delegated Energy covers the call, 100 TRX costs nothing. TronWeb rejects 0, and the network’s maximum is getMaxFeeLimit. If the delegated Energy runs out and the user holds no TRX, the transfer fails rather than burning TRX.
On Nile, test with a token whose sender pays the Energy, such as BTT (TNuoKL1ni8aoshfFL1ASca1Gou9RXwAzfn, 18 decimals). Nile’s USDT pays its senders’ Energy itself, so it succeeds even without delegation. A covered transfer’s receipt (/wallet/gettransactioninfobyid) has energy_usage and no energy_fee, net_fee, or origin_energy_usage.

6. Reclaim unused gas

When the user no longer needs coverage, undelegate the full amount lent to them, read from /wallet/getdelegatedresourcev2:
To recover the TRX, call /wallet/unfreezebalancev2 on undelegated stake, wait out Stake 2.0’s unstaking period (14 days on mainnet), then call /wallet/withdrawexpireunfreeze.

Next steps

Footnotes

  1. energy_usage_total of 64,285 and 130,285 in the five most recent mainnet USDT (TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t) transfers checked on October 28, 2026. TRON’s dynamic Energy pricing changes these values, so estimate your own. ↩
  2. The fee of 1,100,000 SUN on a Nile AccountCreateContract in October 2026 (28714d8d…): TRON’s 1 TRX account creation fee (getCreateNewAccountFeeInSystemContract) plus 0.1 TRX of Bandwidth. ↩
  3. Free Bandwidth of 600 a day on mainnet and Nile. The 345 is the net_usage of BTT transfers on Nile on October 29, 2026; the exact value depends on the transaction’s size. ↩
Last modified on October 30, 2026