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
Section titled “Energy”Energy is consumed by smart contract execution. Every TVM opcode costs Energy, analogous to EVM gas.
Acquiring Energy
Section titled “Acquiring Energy”| Method | How it works |
|---|---|
| Stake TRX | Freeze TRX for Energy. Your share: (your staked TRX ÷ total network staked TRX) × 180,000,000,000 units/day |
| Burn TRX on demand | 100 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.
Staking for Energy (tronweb)
Section titled “Staking for Energy (tronweb)”// 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 balanceconst resources = await tronWeb.trx.getAccountResources('TYourAddress...');console.log('Energy available:', resources.EnergyLimit - resources.EnergyUsed);Consumption Priority
Section titled “Consumption Priority”- Staked Energy (free, recovers over 24 hours)
- TRX burn at 100 sun per unit
Bandwidth
Section titled “Bandwidth”Bandwidth measures transaction size in bytes. Every transaction consumes Bandwidth proportional to its byte length in the network database.
Acquiring Bandwidth
Section titled “Acquiring Bandwidth”| Method | How it works |
|---|---|
| Free allocation | 600 Bandwidth per account per day, automatically — sufficient for ~2–3 simple TRX transfers |
| Stake TRX | Additional Bandwidth from staking. Network pool: 43,200,000,000 units/day |
| Burn TRX on demand | 1,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.
Dynamic Energy Model (DEM)
Section titled “Dynamic Energy Model (DEM)”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.
How Punitive Scaling Works
Section titled “How Punitive Scaling Works”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_factorincreases 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.4cap.
Mainnet Parameters
Section titled “Mainnet Parameters”| Parameter | Value |
|---|---|
| Daily threshold | 5,000,000,000 Energy |
| Increase factor | 0.2 — penalty increases 20% per evaluation period above threshold |
| Maximum factor | 3.4 — penalty can reach up to 340% of baseline |
| Decrease factor | 0.25 — penalty decreases when contract falls below threshold |
Effective Cost Formula
Section titled “Effective Cost Formula”Final Energy = Baseline Energy × (1 + energy_factor)The Math: 3.4x Penalty vs 4.4x Total Cost
Section titled “The Math: 3.4x Penalty vs 4.4x Total Cost”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, theenergy_factorclimbs 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).
Checking a Contract’s Current Factor
Section titled “Checking a Contract’s Current Factor”# 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" fieldSetting Fee Limits
Section titled “Setting Fee Limits”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).
| Property | Value |
|---|---|
| Unit | Sun (1 TRX = 1,000,000 sun) |
| Maximum allowed | 15,000 TRX (15,000,000,000 sun) |
| Default if omitted | Implementation-defined, often too low |
| If exceeded | Transaction reverts; a portion of fee limit is consumed |
// 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)
# 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)
# 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 }'Rule of Thumb
Section titled “Rule of Thumb”Fee limit (TRX) ≈ (estimated Energy × 2) ÷ 10,000For 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
Section titled “Fee Delegation”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.
Two Deployment Parameters
Section titled “Two Deployment Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
consume_user_resource_percent | 0–100 | 100 | Percentage of Energy borne by the caller. Set to 0 for deployer-pays-all. |
origin_energy_limit | uint | varies | Maximum Energy the deployer will provide per single call. Acts as a per-call cap on deployer subsidies. |
Deployer Pays All Fees
Section titled “Deployer Pays All Fees”// 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.
Split Fees (Deployer 70%, User 30%)
Section titled “Split Fees (Deployer 70%, User 30%)”// 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});Energy Limit Calculation
Section titled “Energy Limit Calculation”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.
Sizing originEnergyLimit
Section titled “Sizing originEnergyLimit”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,000Set 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
# 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 }'// Task: Manage resource delegation programmatically via the SDK.// Delegateawait tronWeb.trx.delegateResource( 1_000_000_000, // sun 'TOperatorAddress...', 'ENERGY', 'TStakerAddress...', false,);
// Reclaimawait tronWeb.trx.undelegateResource( 1_000_000_000, 'TOperatorAddress...', 'ENERGY', 'TStakerAddress...',);Locked Delegation
Section titled “Locked Delegation”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:
{ "lock": true, "lock_period": 86400 // ~3 days (86,400 blocks × 3s ≈ 259,200 seconds)}Staking Solidity API
Section titled “Staking Solidity API”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.
// Task: Execute staking and delegation directly from contract logic using precompiles.// Stake TRX for Energy from within a contractaddress(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 = Energyreceiver.unDelegateResource(amount, 1);
// Vote for Super Representativesaddress[] memory srList = new address[](1);uint[] memory tpList = new uint[](1);srList[0] = srAddress;tpList[0] = votingPower;address(srList[0]).vote(srList, tpList);Pre-Deployment Checklist
Section titled “Pre-Deployment Checklist”| Check | Notes |
|---|---|
Run triggerconstantcontract on mainnet fork | Get baseline Energy, not testnet estimate |
| Multiply by 1.5–2.0× for fee limit | Margin for DEM variance |
Check energy_factor for target contract | USDT and other popular contracts often have factor above 0 |
Set originEnergyLimit if using fee delegation | Must 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 Energy | Under-staked deployer = users pay fees unexpectedly |