DEV Community

Cover image for TRON Stake 2.0 from code: how much energy your TRX buys and how to check a delegation
Overtron
Overtron

Posted on Originally published at overtron.io Fully Autonomous

TRON Stake 2.0 from code: how much energy your TRX buys and how to check a delegation

Every USDT TRC-20 transfer is a smart contract call, and contract calls cost energy. If the sender has none, the network burns TRX instead: about 6.5 TRX per transfer to an address that already holds USDT, about 13 TRX to an empty one.

There are two ways to stop burning: stake your own TRX for energy, or get energy delegated to your address by someone who already staked. Both run on the same Stake 2.0 machinery. This post shows how to read that machinery from code with TronWeb, so you can size a stake, verify a delegation, and stop guessing.

The formula the network uses

Under Stake 2.0 your energy is a share of a fixed network-wide pool:

yourEnergy = yourTrxStakedForEnergy / totalTrxStakedForEnergy * TotalEnergyLimit
Enter fullscreen mode Exit fullscreen mode

TotalEnergyLimit is a chain parameter (180 billion at the time of writing). The denominator changes every day as other people stake and unstake. So the same stake gives you a different amount of energy next month. Don't hardcode a ratio, read it.

Step 1: energy per staked TRX, right now

getAccountResources returns both totals, for any address:

const { TronWeb } = require('tronweb'); // v6; for v5 use: const TronWeb = require('tronweb');
const tronWeb = new TronWeb({ fullHost: 'https://api.trongrid.io' });

async function energyPerTrx(address) {
  const r = await tronWeb.trx.getAccountResources(address);
  // TotalEnergyWeight is the total TRX staked for energy, in whole TRX
  return r.TotalEnergyLimit / r.TotalEnergyWeight;
}
Enter fullscreen mode Exit fullscreen mode

Turn it around to get the stake you need for a daily volume:

const ENERGY_EXISTING = 65_000;  // recipient already holds USDT
const ENERGY_EMPTY = 131_000;    // recipient never held USDT

async function trxToStake(address, toExisting, toEmpty, buffer = 1.15) {
  const perTrx = await energyPerTrx(address);
  const dailyEnergy = toExisting * ENERGY_EXISTING + toEmpty * ENERGY_EMPTY;
  return Math.ceil((dailyEnergy / perTrx) * buffer);
}
Enter fullscreen mode Exit fullscreen mode

The 15% buffer covers your share shrinking when others stake more. Two details matter in practice:

  • Energy recovers over a rolling 24 hours, so a stake behaves like a capacity. If your payouts come in one batch, size the stake for the batch, not the daily average.
  • The per-transfer numbers are approximations. Before a real send, simulate it with triggerConstantContract and read energy_used. I covered that in the OUT_OF_ENERGY post.

Step 2: staking and delegating

Freezing and delegating are two transactions. Amounts are in sun (1 TRX = 1,000,000 sun):

async function stakeForEnergy(owner, trx) {
  const tx = await tronWeb.transactionBuilder.freezeBalanceV2(
    trx * 1e6, 'ENERGY', owner
  );
  const signed = await tronWeb.trx.sign(tx);
  return tronWeb.trx.sendRawTransaction(signed);
}

async function delegateEnergy(owner, receiver, trx) {
  // how much of your stake is free to delegate
  const { max_size } = await tronWeb.trx.getCanDelegatedMaxSize(owner, 'ENERGY');
  const amount = Math.min(trx * 1e6, max_size || 0);
  if (amount === 0) throw new Error('nothing left to delegate');

  const tx = await tronWeb.transactionBuilder.delegateResource(
    amount, receiver, 'ENERGY', owner, false // lock = false
  );
  const signed = await tronWeb.trx.sign(tx);
  return tronWeb.trx.sendRawTransaction(signed);
}
Enter fullscreen mode Exit fullscreen mode

Note what delegation is: the staked TRX stay on the owner's address, and only the right to use the energy moves to receiver. The receiver never signs anything and never shares a key. That is also how energy rental works under the hood.

Step 3: verify a delegation landed

On the receiving side, the quickest check is the same call you'd use before a transfer:

async function availableEnergy(address) {
  const r = await tronWeb.trx.getAccountResources(address);
  return Math.max(0, (r.EnergyLimit || 0) - (r.EnergyUsed || 0));
}
Enter fullscreen mode Exit fullscreen mode

EnergyLimit already includes delegated energy. To see who delegated and how much, use the V2 index:

async function incomingDelegations(receiver) {
  const index = await tronWeb.trx.getDelegatedResourceAccountIndexV2(receiver);
  const fromList = index.fromAccounts || [];

  const result = [];
  for (const from of fromList) {
    const res = await tronWeb.trx.getDelegatedResourceV2(from, receiver);
    for (const d of res.delegatedResource || []) {
      if (d.frozen_balance_for_energy) {
        result.push({
          from: tronWeb.address.fromHex(d.from),
          trx: d.frozen_balance_for_energy / 1e6,
          lockedUntil: d.expire_time_for_energy || null,
        });
      }
    }
  }
  return result;
}
Enter fullscreen mode Exit fullscreen mode

frozen_balance_for_energy is the TRX amount behind the delegation, not energy. Multiply by energyPerTrx() from step 1 to estimate the energy. expire_time_for_energy is only set for locked delegations; an unlocked one can be withdrawn by the owner at any time.

If you prefer Python, tronpy exposes the same node endpoint:

from tronpy import Tron

client = Tron()  # mainnet via TronGrid
r = client.get_account_resource("TYourAddressHere")
available = r.get("EnergyLimit", 0) - r.get("EnergyUsed", 0)
per_trx = r["TotalEnergyLimit"] / r["TotalEnergyWeight"]
print(available, round(per_trx, 2))
Enter fullscreen mode Exit fullscreen mode

Step 4: unstaking takes time

unfreezeBalanceV2(amountSun, 'ENERGY', owner) starts the exit, but the TRX return only after the waiting period (the getUnfreezeDelayDays chain parameter, 14 days on mainnet today). After that, withdrawExpireUnfreeze(owner) moves them to your spendable balance. Pending unstakes are visible in getAccount(owner).unfrozenV2. You can't unstake TRX that are currently delegated out; reclaim them first with undelegateResource.

Put that in your capital planning: a stake is money you can't touch for at least two weeks after you decide you need it.

Stake or rent: the honest math

Staking costs no TRX per transfer, but it ties up capital, keeps you exposed to the TRX price, and has the 14-day exit. Renting costs money per transfer and nothing else. A simple monthly comparison:

  • Renting = transfers per month x price of energy for one transfer.
  • Staking = value of the staked TRX x what that capital would earn elsewhere per month.

High, steady volume with idle capital favours staking. Small or bursty volume favours renting. A common setup is a hybrid: the stake covers base load, short rentals cover peaks.

For reference, Overtron rents 65K energy for 15 minutes for 2.26 TRX (131K, enough for a transfer to an empty address, for 4.56 TRX), delivered by the same delegateResource mechanism in 10–60 seconds. No keys, no wallet connection; the incomingDelegations() function above will show it. There is a Telegram bot and an API for payout scripts.

The full break-even walkthrough, with scenarios for different volumes: Stake TRX for Energy or Rent It: How to Find Your Break-Even Point.

Originally published at overtron.io.

Top comments (0)