TRC-20 approve 与其他 TRON 合约调用的能量
能量是 TRON 对运行合约代码收的费,而 USDT 转账只是众多合约调用中的一种。approve 同样要花能量,之后划转被授权资金的 transferFrom 也一样。2026 年 10 月 28 日,我们在 USDT 合约上对两者各做了模拟运行:授权额度为零时,一次 approve 用了 99,764 单位能量,授权额度已经设过时用了 33,764;一次 transferFrom 在无限授权额度下用了 65,123,在有限授权额度下用了 89,325。对其他合约的调用各有各的数字,而租来的能量为其中任何一种付费,方式和为转账付费一样。
一笔代理会抬高它所落在的账户的能量上限,而这个账户签出的每一次合约调用,都先从这个上限里花,然后才烧 TRX(TRON 能量是什么)。代理里没有任何东西指明它是为哪个合约或哪个函数准备的。转账之外的调用不同在于花多少:这取决于它调用的合约和函数,以及它在那里碰到的状态;在某些合约上,还取决于其中有多少由签名方来付。
USDT 的几种调用在模拟运行里花了多少
模拟运行就是一次常量调用:节点对着自己那份链的副本执行这次调用,报告它用了多少能量,然后把结果丢掉。不签名,不广播,也不花钱。我们的调用是 2026 年 10 月 28 日 08:29 到 08:34 UTC 之间通过一个公共全节点做的。所有者是真实账户,是在各自刚给某个被授权方授权之后,从最新区块里挑出来的,划扣则是从这些被授权方发起的模拟运行。从零开始的 approve,授权给的是我们为 9 月测量生成的一个地址,没有任何所有者授权过它。每个授权额度和余额,我们都在调用前一刻读过。下面每个数字,都来自一次本来会成功的调用。转账和两次划扣都向同一个地址挪动了一个单位,即百万分之一个 USDT,这个地址持有 USDT。
| USDT 调用,2026 年 10 月 28 日 | 使用的能量 | 其中,报告为 energy_penalty 的部分 | 按每单位 100 sun 烧掉 |
|---|---|---|---|
transfer 转给持有 USDT 的地址 | 64,285 | 49,635 | 6.4285 TRX |
approve,授权额度为零 | 99,764 | 77,076 | 9.9764 TRX |
approve,授权额度已设置 | 33,764 | 26,076 | 3.3764 TRX |
被授权方发起的 transferFrom,无限授权额度 | 65,123 | 50,281 | 6.5123 TRX |
另一个被授权方发起的 transferFrom,有限授权额度 | 89,325 | 68,976 | 8.9325 TRX |
两种 approve 来自同一个所有者,新授权额度的数额也相同。一个被授权方还没有授权额度,另一个已有授权额度,前者那次调用多花了 66,000 单位。这与转给没有 USDT 的地址和转给持有者的差距相同,我们 9 月量到的是 130,285 和 64,285(两个转账数字)。两对里,较便宜的那次调用都是覆写一个本来就在那里的数,较贵的那次是写在一个零上。
除非合约的部署者同意承担其中一部分(JustLend DAO 的就是这样,见下文),否则谁签名一次调用,谁就付它的能量。在客户给商户的合约授权、商户之后划扣资金的流程里,每一次 approve 由客户付费,而发起划扣的调用由签名者付费:既包括表里的 transferFrom,也包括划扣在商户合约内部运行时它自己的代码。我们能做模拟运行的两次划扣,授权额度不同。在无限授权额度下,被授权方的 transferFrom 比把同样金额转给同一地址的普通转账多 838 单位,而在有限授权额度下,又在此基础上多花了 24,202。每种只有一对样本,而且这些对子的所有者和被授权方也各不相同。
每个数字里有略多于四分之三,落在节点称为 energy_penalty 的字段里,而且五种调用的这个比例都一样,approve 和 transfer 一模一样。
一个借贷合约:JustLend DAO 的两个调用
从 JustLend DAO 租用能量,本身就是一对合约调用,一个开仓位,一个归还它(那次租用花了多少)。我们从合约的公开记录里,把截至 2026 年 9 月 2 日傍晚的 24 小时里的 30 次租用与它们的归还配了对,并从各自的回执里读出每次调用合计用了多少。
| JustLend DAO 调用,2026 年 9 月 2 日 | 最少 | 中位数 | 最多 |
|---|---|---|---|
租用,rentResource | 52,672 | 69,306 | 84,290 |
归还,returnResource | 67,195 | 74,135 | 74,135 |
表里给的是合计,因为签名者并不总是付全部。这个合约设定向调用方收取能量的十分之一,其余由部署者自己的能量来补,每次调用最多 90,000 单位,只要那些能量还在。那天傍晚我们自己的那次租用用了 84,290 单位,签名者付了其中的 8,429。比这早不到三个小时,一个第三方的租用全部 52,672 都是自己付的,因为部署者的能量已经用光,它十二秒后的归还,在 74,135 里付了 62,200。模拟运行报告的是一次调用用了多少,而签名者要付的,从合约向调用方收取的那十分之一到全部都有可能,取决于那一秒部署者的能量,所以要按全部来为这次调用定量。
为没人替您量过的调用定量
没有哪张表能覆盖每个合约,而且上面的数字会随调用碰到的状态而变。该信的数字,是对那个确切调用做的模拟运行,从将要签名的账户发起,在发送之前不久做。任何全节点都能回答:
POST /wallet/triggerconstantcontract
{"owner_address": "T…(将要签名的账户)",
"contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"function_selector": "approve(address,uint256)",
"parameter": "…(参数,ABI 编码)",
"visible": true}
拿这个数之前,先检查答复,因为一次会回滚的调用也会报告能量。我们最初的几次 transferFrom 尝试里,有两次对着已经付不出的对子:一份授权额度在我们询问前几秒刚被划扣掉,一个所有者根本没有 USDT。它们回来的是 5,947 和 4,845 单位,对应的是以失败告终的工作。它们的 result.result 读出来依然是 true。把它们区分出来的,是 FAILED 这个值,它出现在 transaction.ret 里;成功的调用在那个位置留下的是一个空条目。
在这个数之上留出余地,因为从模拟运行到打进区块之间,授权额度可能被花掉,收款方也可能被清空。我们自己这边也留了余地:我们标准的转账数字 65,000 和 131,000,比两种转账实测的数字高出 715 单位;而在 mode B 里交到我们手上的已签名 USDT 转账,如果模拟运行的结果高于转给空地址的实测值,我们就按实测值加一个余量去买,并按实测值本身来判断是否交付到位。
把交易的 fee_limit 设成能按网络每单位 100 sun 的价格覆盖整个数字,即使能量是租来的。节点把一次调用封顶在它的 fee_limit 除以能量价格,并把代理来的能量也算在这个封顶之内,所以限额低了,会拦住一次账户上已有全部所需单位的调用。带宽是另一只表:每次调用也要为自己的字节付费,而能量不覆盖字节(能量与带宽)。
通过交易所合约的兑换,是同样的算术,只是多了一个合约:先是让交易所能挪动代币的一次 approve,再是对交易所的调用。我们没有对此做过模拟运行。
按单位为一次调用租能量
我们的 mode A 和 mode B 是围绕一笔 USDT 转账搭起来的,mode B 只接受已签名的 USDT transfer,别的一概不收。在 mode A 里,当转账的收款方原来是个合约时,我们替您做那次模拟运行:条目以 kind: custom 返回,它的 energy_units 就是实测值,并带一条 contract_recipient 警告说明原因(custom 条目,在任何东西被签名之前)。
其他每一种调用,都有 mode C。您写明钱包和数量,我们把能量代理给那个钱包,不涉及任何收款方(API 演练里的 mode C)。数量可以用 energy_units,每个钱包 65,000 到 50,000,000 之间的任何整数,条目以 kind: bulk 返回。mode C 不收 custom,因为没有收款方可让我们做模拟运行,所以实测要由您来做。钱包必须在链上已激活,为一个未激活的钱包下的订单会被拒绝,返回 inactive_wallet。
五分钟租期接受最多 131,000 的数量。更大的必须走一小时租期,在较短的租期上我们会拒绝,返回 bulk_needs_longer_lease:大数量是由几个来源拼起来的,分几块、相隔几秒落地。一次从零开始的 approve 单独放在这条线以下,还有余量的空间。在它后面再接一笔转给持有者的转账,这一对就越过了这条线,所以要走一小时。我们卖的最小数量,覆盖一次已有授权额度上的 approve,还剩下几乎同样多的一份。
POST /v1/estimate 带上同样的 energy_units,会回答这个数量在此刻要收多少费,以及在 coverage 里,市场现在有没有这么多。一旦交付,条目会停在 ready,一直到 send_before,之后读作 expired,这里的意思是租期用完了:这是正常的结局。收费是为交付收的,钱包没花掉的能量随代理一起回去。
我们没有量过的:一次兑换,对 USDT 和 JustLend DAO 之外任何合约的调用,一次转给没有 USDT 地址的 transferFrom,以及这些数字能不能在数周之内保持不变。这里每个 USDT 数字都是一个节点回答的一次常量调用;我们没有发送任何交易,所以没有一个数字对照过回执。
先模拟运行这次调用,再把这个数量放到将要签名的钱包上:POST /v1/orders 上的 mode C 和 energy_units →