Skip to content

Fee Model & Delegation

TRON replaces traditional gas with a dual-resource model: Energy for smart contract execution and Bandwidth for transaction data. This guide explains how to acquire these resources through staking, the impact of the Dynamic Energy Model (DEM) on popular contracts, and how to implement fee delegation for a gasless user experience.


Energy is consumed by smart contract execution. Every TVM opcode costs Energy, analogous to EVM gas.

MethodHow it works
Stake TRXFreeze TRX for Energy. Your share: (your staked TRX ÷ total network staked TRX) × 180,000,000,000 units/day
Burn TRX on demand100 sun per Energy unit (0.0001 TRX). Used automatically when your staked Energy is exhausted.

The network allocates 180 billion Energy per day across all stakers. Staked Energy recovers gradually over 24 hours.

stake_energy.js
// Task: Freeze native TRX to acquire Energy resources for smart contract calls.
// Stake 1,000 TRX for Energy (amount in sun: 1 TRX = 1,000,000 sun)
await tronWeb.trx.freezeBalanceV2(
1_000_000_000, // sun
1, // resource type: 0 = Bandwidth, 1 = Energy
);
// Check resulting Energy balance
const resources = await tronWeb.trx.getAccountResources('TYourAddress...');
console.log('Energy available:', resources.EnergyLimit - resources.EnergyUsed);
  1. Staked Energy (free, recovers over 24 hours)
  2. TRX burn at 100 sun per unit

Bandwidth measures transaction size in bytes. Every transaction consumes Bandwidth proportional to its byte length in the network database.

MethodHow it works
Free allocation600 Bandwidth per account per day, automatically — sufficient for ~2–3 simple TRX transfers
Stake TRXAdditional Bandwidth from staking. Network pool: 43,200,000,000 units/day
Burn TRX on demand1,000 sun per Bandwidth unit when free and staked allocation is exhausted

TRX transfers and TRC-10 token transfers cost only Bandwidth (no Energy). Smart contract calls cost both: Bandwidth for the transaction bytes, Energy for execution.


The Dynamic Energy Model (DEM) applies a penalty multiplier to contracts that consume more than their proportional share of the network’s daily Energy pool. It prevents a single contract from monopolising network resources.

The model introduces a variable called energy_factor for every contract.

  • Normal State: energy_factor = 0. You pay the baseline energy cost.
  • Congested State: If a contract’s energy consumption exceeds a threshold (currently 5,000,000,000 energy per 6-hour Epoch), the energy_factor increases for the next available maintenance period.

The factor doesn’t jump instantly. It scales exponentially during each maintenance period (the 6-second event at the end of each 6-hour Epoch) that the contract stays above the threshold:

  • Period 1: energy_factor = 0.2 (Total cost: 1.2x baseline)
  • Period 2: energy_factor = (1 + 0.2) * (1 + 0.2) - 1 = 0.44 (Total cost: 1.44x baseline)
  • Continuance: This continues until it hits the 3.4 cap.
ParameterValue
Daily threshold5,000,000,000 Energy
Increase factor0.2 — penalty increases 20% per evaluation period above threshold
Maximum factor3.4 — penalty can reach up to 340% of baseline
Decrease factor0.25 — penalty decreases when contract falls below threshold
Final Energy = Baseline Energy × (1 + energy_factor)

Developers and users are often confused by whether the maximum fee is 3.4x or 4.4x. Here is the distinction:

  • The 3.4x (energy_factor): This is the “penalty” value itself. When a contract is heavily used, the energy_factor climbs until it hits the cap of 3.4.
  • The 4.4x (Total Multiplier): Because the total energy formula is Baseline Energy × (1 + energy_factor), a max factor of 3.4 results in a final cost of 4.4x the base energy (1 (Base) + 3.4 (Penalty) = 4.4).

When you see 3.4x, it refers to the added penalty. When you see 4.4x (or 440%), it refers to the final total cost you are being charged.

The base Energy for a USDT transfer is ~14,650 (existing USDT holder) or ~29,650 (recipient with zero balance). At energy_factor = 0 (no congestion): 14,650 Energy. At maximum factor (340% penalty, energy_factor = 3.4): 14,650 × 4.4 = ~64,460 Energy (existing holder) or 29,650 × 4.4 = ~130,460 Energy (new account).

Terminal
# Task: Query the current dynamic energy factor for a specific contract.
curl -X POST https://api.trongrid.io/wallet/getcontractinfo \
-d '{ "value": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "visible": true }'
# Response includes: "energy_factor" field

Every state-changing transaction requires a feeLimit — the maximum TRX the caller is willing to burn if Energy runs out. (Think of this like setting a strict timeout or spending cap on a complex database query to ensure a bug doesn’t drain your account).

PropertyValue
UnitSun (1 TRX = 1,000,000 sun)
Maximum allowed15,000 TRX (15,000,000,000 sun)
Default if omittedImplementation-defined, often too low
If exceededTransaction reverts; a portion of fee limit is consumed
fee_limit.js
// Task: Define the maximum TRX to be burned for Energy before a transaction reverts.
await contract.someMethod(arg1, arg2).send({
feeLimit: 150_000_000, // 150 TRX max
shouldPollResponse: true, // wait for confirmation
});

Estimating Energy Before Setting Fee Limits

Section titled “Estimating Energy Before Setting Fee Limits”

Method 1 — triggerconstantcontract (simulate without broadcasting)

Terminal
# Task: Simulate a contract execution without broadcasting to estimate Energy.
curl -X POST https://api.trongrid.io/wallet/triggerconstantcontract \
-d '{
"owner_address": "TCallerAddress...",
"contract_address": "TContractAddress...",
"function_selector": "transfer(address,uint256)",
"parameter": "000000000000000000000000<recipient_hex_without_41>...",
"visible": true
}'

Returns energy_used. Multiply by 1.5–2.0× as a margin when setting feeLimit (the simulation runs at current factor; mainnet factor can increase before your transaction confirms).

Method 2 — estimateenergy (java-tron 4.7.0.1+, more accurate)

Terminal
# Task: Use the estimateenergy API for accurate, real-time consumption data.
curl -X POST https://api.trongrid.io/wallet/estimateenergy \
-d '{
"owner_address": "TCallerAddress...",
"contract_address": "TContractAddress...",
"function_selector": "transfer(address,uint256)",
"parameter": "...",
"visible": true
}'
Fee limit (TRX) ≈ (estimated Energy × 2) ÷ 10,000

For a USDT transfer to an existing holder at max DEM (14,650 base × 4.4 = ~64,460 Energy) with 1.5× margin:
96,690 ÷ 10,000 ≈ 10 TRX → set feeLimit = 10_000_000 (or 20 TRX to also cover new-account transfers).


Fee delegation lets a contract deployer subsidise some or all of the Energy cost for users calling their contract. Users pay less or nothing; the deployer’s staked Energy absorbs the cost. This is the foundation of gasless UX in TRON DApps.

ParameterTypeDefaultDescription
consume_user_resource_percent0–100100Percentage of Energy borne by the caller. Set to 0 for deployer-pays-all.
origin_energy_limituintvariesMaximum Energy the deployer will provide per single call. Acts as a per-call cap on deployer subsidies.
deploy_delegated.js
// Task: Deploy a contract where the deployer subsidizes 100% of user Energy fees.
const deployed = await tronWeb.contract().new({
abi: compiledAbi,
bytecode: compiledBytecode,
feeLimit: 1_000_000_000,
userFeePercentage: 0, // user pays 0% of Energy
originEnergyLimit: 10_000_000, // deployer covers up to 10M Energy per call
});

With userFeePercentage: 0, callers need no staked Energy and can set feeLimit: 0. Their transactions succeed as long as the deployer’s Energy pool is sufficient and the call stays within originEnergyLimit.

deploy_split.js
// Task: Deploy a contract with shared Energy costs (30% user, 70% deployer).
const deployed = await tronWeb.contract().new({
abi: compiledAbi,
bytecode: compiledBytecode,
feeLimit: 1_000_000_000,
userFeePercentage: 30, // user pays 30% of Energy
originEnergyLimit: 5_000_000, // deployer covers up to 5M Energy per call
});
Total EnergyLimit available for a call =
Caller's bearable Energy + Deployer's bearable Energy
Caller's bearable = min(
feeLimit ÷ 100 sun, ← converted to Energy units
caller's staked Energy + TRX balance ÷ 100 sun
)
Deployer's bearable = min(
origin_energy_limit, ← per-call cap
deployer's current staked Energy
)

If the call requires more Energy than this total, it reverts. The deployer’s originEnergyLimit prevents a single malicious or expensive call from draining the deployer’s entire staked pool.

Set originEnergyLimit to cover the maximum Energy your most expensive contract function can consume, including the Dynamic Energy Model multiplier:

originEnergyLimit ≥ baseline_energy_cost × max_energy_factor
Example for a complex DeFi function (100,000 baseline × 3.4 max factor):
originEnergyLimit = 340,000

Set it generously during development and tighten after you have real call data from TRONSCAN transaction history.


Resource Delegation (Stake and Delegate to Another Address)

Section titled “Resource Delegation (Stake and Delegate to Another Address)”

Separate from contract-level fee delegation, you can delegate your staked resources to another address. Useful for:

  • Covering the Energy costs of a hot wallet without giving it access to your TRX
  • Providing Energy to a contract account for activation of new user addresses
  • Gas sponsorship where a separate treasury account covers DApp operation costs
Terminal
# Task: Manually delegate staked Energy to another address.
curl -X POST https://api.trongrid.io/wallet/delegateresource \
-d '{
"owner_address": "TStakerAddress...",
"receiver_address": "TOperatorAddress...",
"balance": 1000000000,
"resource": "ENERGY",
"lock": false,
"visible": true
}'
delegate_sdk.js
// Task: Manage resource delegation programmatically via the SDK.
// Delegate
await tronWeb.trx.delegateResource(
1_000_000_000, // sun
'TOperatorAddress...',
'ENERGY',
'TStakerAddress...',
false,
);
// Reclaim
await tronWeb.trx.undelegateResource(
1_000_000_000,
'TOperatorAddress...',
'ENERGY',
'TStakerAddress...',
);

Setting lock: true with a lock_period (in blocks, approximately 3 seconds each) prevents the staker from reclaiming resources for the specified duration — useful for service-level agreements where the receiver needs guaranteed resource availability:

Terminal window
{
"lock": true,
"lock_period": 86400 // ~3 days (86,400 blocks × 3s 259,200 seconds)
}

Contracts can stake, delegate, and vote directly from Solidity using TVM precompile extensions. Useful for contracts that manage user funds and need to optimise resource costs programmatically.

staking_api.sol
// Task: Execute staking and delegation directly from contract logic using precompiles.
// Stake TRX for Energy from within a contract
address(0x100000b).delegatecall(
abi.encode(uint(amount), uint(1)) // freezeBalanceV2
);
// Or use high-level syntax (tvm-solc 0.8.18+)
address payable receiver = payable(receiverAddress);
receiver.delegateResource(amount, 1); // 1 = Energy
receiver.unDelegateResource(amount, 1);
// Vote for Super Representatives
address[] memory srList = new address[](1);
uint[] memory tpList = new uint[](1);
srList[0] = srAddress;
tpList[0] = votingPower;
address(srList[0]).vote(srList, tpList);

CheckNotes
Run triggerconstantcontract on mainnet forkGet baseline Energy, not testnet estimate
Multiply by 1.5–2.0× for fee limitMargin for DEM variance
Check energy_factor for target contractUSDT and other popular contracts often have factor above 0
Set originEnergyLimit if using fee delegationMust cover worst-case call including dynamic penalty
Budget for first-user activation (25,000 Energy)Required when contract transfers to unactivated addresses
Ensure deployer wallet has sufficient staked EnergyUnder-staked deployer = users pay fees unexpectedly