TRON 上的 Solidity
TRON 特有的 Solidity 语言特性:trx 单位关键字、用于 TRC-10 的 trcToken 类型、 msg.tokenid 和 msg.tokenvalue,以及在合约内调用质押和投票的内置函数。
TRON 提供的 Solidity 编译器是基于上游 Solidity 编译器的分支版本,并在其之上增加了少量 TRON 专属的语法扩展。该编译器支持绝大多数以太坊 Solidity 特性,以太坊合约通常无需修改即可编译。差异集中在四类 TRON 特有扩展:trx 货币单位关键字、专门标识 TRC-10 Token 的 trcToken 类型、消息调用扩展属性(msg.tokenid 与 msg.tokenvalue),以及用于在合约内发起质押与投票的内置函数指针。
本页是 TRON Solidity 语言参考。需要完整的 Stake 2.0 封装 SDK,见 Stake 2.0 Solidity SDK 参考。
TRX 作为单位关键字
在上游 Solidity 中,以太坊使用 wei、gwei 和 ether 作为面值单位后缀。与之对应, TRON Solidity 编译器引入了 trx 作为原生 Token 的等价单位,并使用 sun 作为最小 Token 单位:
| 单位字面量 | 值 |
|---|---|
1 sun | 1 sun(最小单位) |
1 trx | 1,000,000 sun |
在 Solidity 支持数字字面量的任何位置都可以使用这两个单位:
require(msg.value >= 1 trx, "Pay at least 1 TRX");
uint256 fee = 100 sun;编译器仍支持上游的 wei、gwei、ether,但编写 TRON 合约时建议避免:改用 trx 和 sun,Token 单位含义更清晰。
TRC-10 Token 内置函数
TRC-10 是 TRON 协议层直接支持的 Token 类型,不依赖 TRON 虚拟机(TVM);TVM 提供了一组内置函数,使智能合约能够与 TRC-10 Token 交互。关于其完整标准,请参阅 TRC-10 Token 说明。
trcToken 类型
trcToken 类型trcToken 是独立于 uint256 的专属类型,专门用来标识特定的 TRC-10 Token ID。编写合约时,可以将 trcToken 与 uint256 相互强制转换:
trcToken id = 1000001;
uint256 idAsUint = uint256(id);
trcToken back = trcToken(idAsUint);
ID 有效范围按照 TRON 协议规定,合法的 TRC-10
tokenId必须大于 1,000,000。但由于编译器在编译期并不强制约束这一规则,因此当合约函数接收外部传入的tokenId时,建议加require(id > 1000000)断言校验。
address.transferToken(value, tokenId)
address.transferToken(value, tokenId)在合约中,只需通过一次简单的 Solidity 调用,即可将指定数量的 TRC-10 Token 发送到目标地址:
function transferTokenTest(address payable toAddress, uint256 tokenValue, trcToken id) public {
require(id > 1000000);
toAddress.transferToken(tokenValue, id);
}该函数的调用行为与发送 TRX 的 address.transfer(uint256) 完全一致:接收方地址类型必须被声明为 payable,并且在发起调用时,只会向接收方账户的 fallback 函数转发 2,300 能量。
address.tokenBalance(tokenId)
address.tokenBalance(tokenId)合约通过该函数查询指定地址的特定 TRC-10 Token 余额:
function getTokenBalance(address account, trcToken id) public view returns (uint256) {
return account.tokenBalance(id);
}msg.tokenid 和 msg.tokenvalue
msg.tokenid 和 msg.tokenvalue当外部账户通过附带 TRC-10 Token 的方式调用合约函数时(底层通过 CALLTOKEN 指令执行,或在调用 triggerSmartContract 时传入了 callTokenValue 与 tokenId 参数),TVM 会在内置的 msg 上下文对象中自动填充以下两个额外字段:
function receiveTRC10() public payable returns (trcToken, uint256) {
trcToken id = msg.tokenid;
uint256 amount = msg.tokenvalue;
return (id, amount);
}如果调用时未携带任何 TRC-10 Token,这两个字段的数值默认均为 0。请注意,这些属性字段为 TRON 网络所独有,在以太坊网络中并不存在。
质押、投票与资源代理
TRON 把 Stake 2.0 质押、资源分配、投票和领取收益等系统级功能,以内置函数形式扩展到 Solidity 的 address 类型上。关于所有 26 个内置系统接口的完整定义与用法,请参阅 Stake 2.0 Solidity SDK 参考。
下面列出最常用的几个:
// 质押 TRX 以获取带宽(resourceType = 0)或能量(resourceType = 1)
address.freezeBalanceV2(amount, resourceType);
// 开始解除质押——启动 14 天等待期
address.unfreezeBalanceV2(amount, resourceType);
// 提取已过 14 天等待期的 TRX
address.withdrawExpireUnfreeze();
// 为超级代表投票
address.voteWitness(srs, voteCounts);
// 领取累积的投票奖励
address.withdrawReward();编译器版本支持
TRON 专属的 Solidity 编译器开源维护于 github.com/tronprotocol/solidity。目前 TRON 虚拟机 (TVM) 支持以下几个主要编译器版本分支:
0.4.x— TVM 支持的最早编译器版本分支,适用于兼容旧版历史合约(例如0.4.24和0.4.25)。0.5.x— 历史上使用最广泛的稳定版分支,全面支持自0.5.0至0.5.16之间的各个小版本。0.6.x与0.7.x— TVM 亦对其提供了完整支持。0.8.x— 当前推荐用于新合约的版本分支。使用时用具体补丁号显式锁定版本,例如pragma solidity 0.8.20;(不含^;具体推荐补丁版本见 TronBox 版本发布说明)。
把以太坊 Solidity 合约移植到 TRON 时,选用对应的 TRON 编译器版本,代码通常能直接通过编译。若在编译过程中遇到任何版本兼容性问题,请参阅从以太坊迁移。
与上游 Solidity 的差异
TRON Solidity 与以太坊标准 Solidity 编译器存在以下几处细节差异:
gas关键字 —— 出于向后兼容的考虑,合约代码中仍支持编写此关键字,但 TVM 虚拟机在底层运行时会将其转换为能量进行折算。调用gasleft()会返回当前交易剩余的能量额度。tx.gasprice—— 调用此属性将返回当前网络的energyPrice,而非传统以太坊网络中的 Gas 价格。block.difficulty—— 属性返回值始终为0。这是因为 TRON 不采用以太坊早期的工作量证明(PoW)共识机制,而是基于委托权益证明(DPoS)机制。block.basefee—— 属性返回值将直接映射为energyPrice。 TRON 协议中并未实现以太坊的 EIP-1559 提案。
关于虚拟机指令集(Opcode)层面的详细技术差异,请参阅 TVM vs EVM 对照指南。
相关资源
- Stake 2.0 Solidity SDK 参考 —— 在合约代码中直接实现质押、资源代理和投票等功能的内置辅助函数参考手册
- TVM vs EVM —— 详细对比两条链的 Opcode 指令集和预编译智能合约的实现差异
- Opcodes —— 完整的 TRON 虚拟机(TVM)指令集开发字典
- 从以太坊迁移至 TRON —— 提供以太坊智能合约移植的实操指南
- 在智能合约中转移 TRC-10 —— 演示如何在合约代码中接收和发送 TRC-10 Token
- TIP-43 技术标准 —— 智能合约支持 TRC-10 Token 交易的技术规范文档
Updated 26 days ago