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 sun1 sun(最小单位)
1 trx1,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 是独立于 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)

在合约中,只需通过一次简单的 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)

合约通过该函数查询指定地址的特定 TRC-10 Token 余额:

function getTokenBalance(address account, trcToken id) public view returns (uint256) {
    return account.tokenBalance(id);
}

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 / address payable 的内置辅助方法形式调用。关于这些内置函数与辅助方法的完整定义与用法,请参阅 Stake 2.0 Solidity SDK 参考。

下面列出最常用的几个:

// 质押 TRX 以获取带宽(resourceType = 0)或能量(resourceType = 1)
freezebalancev2(amount, resourceType);

// 发起解质押——等待期由链参数 getUnfreezeDelayDays 决定
unfreezebalancev2(amount, resourceType);

// 提取已过等待期的 TRX
withdrawexpireunfreeze();

// 为超级代表投票
vote(srs, voteCounts);

// 领取累积的投票奖励
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 对照指南。


相关资源