Home指南API 参考手册
指南API 参考手册社区Discord博客FAQ漏洞赏金公告中心English(英文版)Log In
指南

FeeLimit参数设置

FeeLimit 是智能合约交易中的一个重要参数,用于设定调用者可接受的能量(Energy)消耗上限。该参数以 sun 为单位(1 TRX = 1,000,000 sun),默认值为 0,目前允许的最大上限为 15,000 TRX。

在执行智能合约过程中,Energy 的消耗与扣费是逐条指令进行的。一旦实际消耗超出设定的 FeeLimit,合约交易将执行失败,且已消耗的 Energy 不予退还。因此,建议用户在部署或调用合约前,为交易设置一个合理的 FeeLimit 值,以确保合约能够顺利执行。

接下来的内容将围绕以下三方面,详细说明如何评估合约交易的能量消耗,并正确设置 FeeLimit 参数:

  1. 如何预估智能合约交易的能量消耗?
  2. 如何根据预估值确定合理的 FeeLimit 参数?
  3. 如何为交易设置 FeeLimit?

如何预估能量消耗?

在发起合约交易前,用户可以通过调用wallet/triggerconstantcontract 接口来预估合约调用所需的能量消耗。

下面通过示例说明如何预估合约调用或部署交易的能量消耗:

如何预估合约调用交易的能量消耗

$ curl -X POST  https://nile.trongrid.io/wallet/triggerconstantcontract -d '{
    "owner_address": "TTGhREx2pDSxFX555NWz1YwGpiBVPvQA7e",
    "contract_address": "TVSvjZdyDSNocHm7dP3jvCmMNsCnMTPa5W",
    "function_selector": "transfer(address,uint256)",
    "parameter": "0000000000000000000000002ce5de57373427f799cc0a3dd03b841322514a8c00000000000000000000000000000000000000000000000000038d7ea4c68000",
    "visible": true
}'

执行结果为:

{
   ……
   "result": {
       "result": true
   },
   "energy_used": 46236,
   "energy_penalty": 32983,
   ……


}

在接口返回值中:

  • result.result字段为true表示本次预估执行成功;若为 false,则表示执行失败,请检查请求参数是否设置正确,以确保合约调用的有效性。
  • energy_used 表示本次交易预估的总能量消耗,其中
    • 基础能量消耗量 =(energy_used - energy_penalty)
    • 额外消耗量 = energy_penalty

注意:

triggerconstantcontract 接口可用于预估链上绝大多数智能合约调用交易的能量消耗,例如USDD、USDT、USDC、TUSD等主流合约。自Java-tron 4.7.0.1起,新增了一个 estimateEnergy 接口, 相较于 triggerconstantcontract, 该接口在预估极少数特殊合约的能量消耗时更为准确。需要注意的是, estimateEnergy 属于可选接口,部分 FullNode 节点可能未开启此功能。若在调用过程中遇到错误提示 this node does not support estimate energy,建议开发者退而使用 triggerconstantcontract 接口进行能量预估。

如何预估合约部署交易的能量消耗

对于构造函数无参数的合约,在预估其部署所需的能量时,只需将合约的字节码填入 wallet/triggerconstantcontract 接口的 data 字段中即可。而对于构造函数带有参数的合约,在部署时还需要传入相应的参数。这些参数同样通过 data 字段传递:参数需进行 ABI 编码,并附加在合约字节码之后。请参考以下示例。

  • 预估部署构造函数无参数的合约所需的能量
    合约示例:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

contract SimpleContract {
    uint storedData;
    
    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

合约编译后,得到如下字节码:

608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f80fd5b5061015b806100355f395ff3fe608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f80fd5b506004361061004c575f3560e01c806360fe47b1146100505780636d4ce63c1461006c575b5f80fd5b61006a600480360381019061006591906100d2565b61008a565b005b610074610093565b604051610081919061010c565b60405180910390f35b805f8190555050565b5f8054905090565b5f80fd5b5f819050919050565b6100b18161009f565b81146100bb575f80fd5b50565b5f813590506100cc816100a8565b92915050565b5f602082840312156100e7576100e661009b565b5b5f6100f4848285016100be565b91505092915050565b6101068161009f565b82525050565b5f60208201905061011f5f8301846100fd565b9291505056fea26474726f6e58221220ca11b5749b47f126a08ed4dd6de453cf3e3e1d68c1105af0acdd8a38c18b37ac64736f6c63430008140033

将合约字节码填入 wallet/triggerconstantcontract 接口的 data 字段中。预估能量消耗的命令如下:

curl --request POST \
 --url https://api.shasta.trongrid.io/wallet/triggerconstantcontract \
 --header 'accept: application/json' \
 --header 'content-type: application/json' \
 --data '
{
  "owner_address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g",
  "data":"608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f80fd5b5061015b806100355f395ff3fe608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f80fd5b506004361061004c575f3560e01c806360fe47b1146100505780636d4ce63c1461006c575b5f80fd5b61006a600480360381019061006591906100d2565b61008a565b005b610074610093565b604051610081919061010c565b60405180910390f35b805f8190555050565b5f8054905090565b5f80fd5b5f819050919050565b6100b18161009f565b81146100bb575f80fd5b50565b5f813590506100cc816100a8565b92915050565b5f602082840312156100e7576100e661009b565b5b5f6100f4848285016100be565b91505092915050565b6101068161009f565b82525050565b5f60208201905061011f5f8301846100fd565b9291505056fea26474726f6e58221220ca11b5749b47f126a08ed4dd6de453cf3e3e1d68c1105af0acdd8a38c18b37ac64736f6c63430008140033",
  "visible": true
}'

返回:

{
	"result": {
		"result": true
	},
	"energy_used": 69558,
	......
}
  • 预估部署构造函数带参数的合约所需的能量

合约示例:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

contract SimpleContract {
    uint storedData;
    uint public num;

    constructor(uint _num) {
        num = _num;
    }
    
    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

合约编译后,得到合约的字节码:

608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f80fd5b5060405161024f38038061024f8339818101604052810190610049919061008d565b80600181905550506100b8565b5f80fd5b5f819050919050565b61006c8161005a565b8114610076575f80fd5b50565b5f8151905061008781610063565b92915050565b5f602082840312156100a2576100a1610056565b5b5f6100af84828501610079565b91505092915050565b61018a806100c55f395ff3fe608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f80fd5b5060043610610057575f3560e01c80634e70b1dc1461005b57806360fe47b1146100795780636d4ce63c14610095575b5f80fd5b6100636100b3565b60405161007091906100e2565b60405180910390f35b610093600480360381019061008e9190610129565b6100b9565b005b61009d6100c2565b6040516100aa91906100e2565b60405180910390f35b60015481565b805f8190555050565b5f8054905090565b5f819050919050565b6100dc816100ca565b82525050565b5f6020820190506100f55f8301846100d3565b92915050565b5f80fd5b610108816100ca565b8114610112575f80fd5b50565b5f81359050610123816100ff565b92915050565b5f6020828403121561013e5761013d6100fb565b5b5f61014b84828501610115565b9150509291505056fea26474726f6e582212205d0adb1a1985b9d6f432a9defc416a17dfe1c931bfb8554978c0f37cfe4cc99f64736f6c63430008140033

该合约在部署时需要传入一个 uint 类型的参数。假设部署时传入的值为 2,则其 ABI 编码结果为
0000000000000000000000000000000000000000000000000000000000000002, 该编码结果应附加在合约字节码之后,组成完整的 data 字段值。预估能量消耗的命令如下:

curl --request POST \
 --url https://api.shasta.trongrid.io/wallet/triggerconstantcontract \
 --header 'accept: application/json' \
 --header 'content-type: application/json' \
 --data '
{
  "owner_address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g",
  "data":"608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f80fd5b5060405161024f38038061024f8339818101604052810190610049919061008d565b80600181905550506100b8565b5f80fd5b5f819050919050565b61006c8161005a565b8114610076575f80fd5b50565b5f8151905061008781610063565b92915050565b5f602082840312156100a2576100a1610056565b5b5f6100af84828501610079565b91505092915050565b61018a806100c55f395ff3fe608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f80fd5b5060043610610057575f3560e01c80634e70b1dc1461005b57806360fe47b1146100795780636d4ce63c14610095575b5f80fd5b6100636100b3565b60405161007091906100e2565b60405180910390f35b610093600480360381019061008e9190610129565b6100b9565b005b61009d6100c2565b6040516100aa91906100e2565b60405180910390f35b60015481565b805f8190555050565b5f8054905090565b5f819050919050565b6100dc816100ca565b82525050565b5f6020820190506100f55f8301846100d3565b92915050565b5f80fd5b610108816100ca565b8114610112575f80fd5b50565b5f81359050610123816100ff565b92915050565b5f6020828403121561013e5761013d6100fb565b5b5f61014b84828501610115565b9150509291505056fea26474726f6e582212205d0adb1a1985b9d6f432a9defc416a17dfe1c931bfb8554978c0f37cfe4cc99f64736f6c634300081400330000000000000000000000000000000000000000000000000000000000000002",
  "visible": true
}'

返回:

{
	"result": {
		"result": true
	},
	"energy_used": 99278,
	......
}

在上述返回值中:

  • 如果 result.result 字段为 true,表示预估操作执行成功,energy_used 字段即为该交易预估的能量消耗值。如果 result.result 字段为 false,则表示预估失败,请检查请求参数是否设置正确。

例如:

$ curl -X POST  https://nile.trongrid.io/wallet/estimateenergy -d '{
    "owner_address": "TTGhREx2pDSxFX555NWz1YwGpiBVPvQA7e",
    "contract_address": "TVSvjZdyDSNocHm7dP3jvCmMNsCnMTPa5W",
    "function_selector": "transfer(address,uint256)",
    "parameter": "0000000000000000000000002ce5de57373427f799cc0a3dd03b841322514a8c00000000000000000000000000000000000000000000000000038d7ea4c68000",
    "visible": true
}'

执行结果:

{
   "result": {
      "result": true
   },
   "energy_required": 34830
}

在接口返回值中:

  • result.result 字段为 true 表示本次预估执行成功;
  • energy_required 字段表示该交易的能量消耗量的预估值,其中包含了执行该合约交易的能量基础消耗量和额外消耗量。

如何确定FeeLimit参数值?

由于动态能量模型机制,热门合约的能量消耗是动态变化的。因此,在不同时间段调用同一个合约,其能量消耗可能会有所不同。为确保交易能够顺利执行,调用热门合约时应根据实际情况设置合理的 feelimit 参数。

下面介绍三种常见的 feelimit 设置方法:

  • 每次合约调用前预估能量消耗
    在每次发送交易前,调用 triggerconstantcontract 接口预估该合约调用的总能量消耗量,并据此动态设置 FeeLimit 参数:

    合约交易的FeeLimit = 预估的总能量消耗量  * EnergyPrice

    该方法的优点是 FeeLimit 设置最为精确,可根据实时能量消耗进行动态调整。但缺点是操作繁琐,每次发送交易前都需要额外调用接口进行预估。

  • 每个维护周期均获取一次合约energy_factor
    先通过triggerconstantcontract API预估或者结合历史数据,来确定合约函数的基础能量消耗量。然后,在每一个维护周期中,获取一次该合约当前的energy_factor参数:

    合约交易的FeeLimit = 基础能量消耗 * (1 + energy_factor)* EnergyPrice

    该方法在保持较高精度的同时,减少了调用频率,只需每个维护周期(约6小时)获取一次合约的 energy_factor 参数;但仍然存在一定的操作成本。

  • 使用链上全局的max_factor设置feelimit
    预估基础能量消耗后,结合链上的max_factor参数来计算 Feelimit。max_factor是能量惩罚系数的最大值,无论合约多热门,其能量消耗涨幅都不会超过该上限:

    合约交易的FeeLimit = 基础能量消耗 *(1 + max_factor)* EnergyPrice

    该方式最大的优点是实现简单,无需频繁获取链上参数或进行动态计算,适用于对操作便利性要求较高,或需要确保交易 100% 成功的场景。然而,由于 max_factor 表示能量惩罚系数的最大值,通常高于实际的 energy_factor,因此基于此计算出的 FeeLimit 往往偏高。


如何为交易设置 FeeLimit?

在交易结构体中,fee_limit 字段位于 raw_data 字段内部,结构示例如下所示:

{
  "raw_data": {
    ...
    "fee_limit": 10000000000
  },
  "signature": [
    "47b1f77b3e30cfbbfa41d795dd34475865240617dd1c5a7bad526f5fd89e52cd057c80b665cc2431efab53520e2b1b92a0425033baee915df858ca1c588b0a1800"
  ]
}

在预估并确定了交易的 FeeLimit 值后,可以通过多种方式为交易设置 FeeLimit 。根据创建交易的方式不同,设置 FeeLimit 的方法也有所区别:

通过节点 HTTP API 创建交易

如果使用 /wallet/triggersmartcontract/wallet/deploycontract 接口创建交易,fee_limit 是接口的必填参数之一,只需将其设置为合适的数值即可(单位为 sun)。

使用 TronWeb 创建交易

使用 send() 方法

在调用 send() 方法时,可通过传入包含 feeLimit 的可选项参数来设置交易的能量费用上限。例如,将 feeLimit 设置为 150 TRX(即 150e6 sun):

let abi = [...];
let contract = await tronWeb.contract(abi, 'contractAddress');
let result = await contract.function_name(param1, param2, ...).send({
  feeLimit: 150e6,  // 设置为 150 TRX
});

使用 triggerSmartContract() 方法

在调用 triggerSmartContract() 方法时,第三个参数为可选项配置,可以在其中设置 feeLimit。例如,如果希望将 feeLimit 设置为 150 TRX(即 150e6 sun),则可按如下方式设置:

const functionSelector = 'transfer(address,uint256)';
const parameter = [
  { type: 'address', value: 'ACCOUNT_ADDRESS' },
  { type: 'uint256', value: 100 }
];
const tx = await tronWeb.transactionBuilder.triggerSmartContract(
  'USDT_ADDRESS',
  functionSelector,
  { feeLimit: 150e6 },
  parameter
);

使用 Trident 创建交易

通过 triggerContract 或 deployContract 构建交易

在 Trident 中使用 triggerContract 或 deployContract 构建智能合约交易时,由于接口本身包含 feeLimit 参数,因此只需在调用时直接传入即可。

// Transfer tokens
Function trc20Transfer = new Function(
    "transfer",
    Arrays.asList(
        new Address(toAddress),
        new Uint256(BigInteger.valueOf(10).multiply(BigInteger.valueOf(10).pow(6))) // decimals
    ),
    Collections.singletonList(new TypeReference<Bool>() {})
);
String encodedHex = FunctionEncoder.encode(trc20Transfer);

TransactionExtention transactionExtention = client.triggerContract(
    fromAddr,        // 发起地址
    contractAddress, // 合约地址
    encodedHex,      // 编码后的函数调用数据
    0,               // callValue
    0,               // tokenValue
    null,            // tokenId
    150_000_000L     // feeLimit,单位为sun
);

// 签名并广播
Transaction signedTxn = client.signTransaction(transactionExtention);
String txid = client.broadcastTransaction(signedTxn);

使用 TransactionBuilder 为交易设置 feeLimit

如果交易已通过 Trident 构建完成,也可以使用 TransactionBuilder 单独设置 feeLimit 或添加其他可选参数,如备注等。

TransactionBuilder builder = new TransactionBuilder(transaction); // transaction 为 transactionExtention.getTransaction()
builder.setFeeLimit(100_000_000L);  // 设置 feeLimit,仅智能合约交易需要
Transaction transaction = builder.build();

使用 TronBox 创建交易

在使用 TronBox 部署或调用合约时,feeLimit 一般统一配置在 TronBox 的配置文件中,所有合约部署和调用交易将共用该 feeLimit 设置。但是也可以在交易中单独设置,具体配置方式请参考 TronBox 配置文档

使用 TronIDE 创建交易

在 TronIDE 中部署或调用智能合约时,可通过界面中的 FEE LIMIT 输入框设置交易的 feeLimit 值。只需填写一个合适的数值,即可应用于本次交易。