地址与数据编码

说明 TRON API 中地址、交易数据和智能合约参数的编码方式,涵盖 Base58、Hex、visible 标志、ABI 编码,以及 raw_data 与 raw_data_hex 的区别。

接入 TRON API 时,最常见的编码问题集中在两处:

  • 地址格式:支持 Base58Check(以 T 开头)与 Hex(以 41 开头)两种相互等价的格式,并由 visible 参数标志控制切换。
  • 交易体格式:构建交易时,接口会同时返回结构化的 JSON 对象(raw_data)与其经过 Protobuf 序列化后的十六进制形式(raw_data_hex)。

任一环节编码错误,都可能导致签名校验失败或 CONTRACT_VALIDATE_ERROR。下面按地址、交易数据和合约参数三类场景说明。

📘

前置阅读


地址格式

TRON 地址在日常开发和链上数据交互中以两种可互换的格式存在:

格式示例长度典型适用场景
Base58CheckTPL66VK2gCXNCD7EJg9pgJRfqcRazjhUZY34 个字符,以 T 开头直接面向用户(如钱包 UI、区块浏览器、文档说明)
Hex41928c9af0651632157ef27a2cf17ca72c575a4d2142 个字符,在主网上以 41 开头底层协议与数据传输(如 Protobuf 结构、默认 API 请求)

这两种格式在底层均编码了相同的 21 字节地址:包括 1 字节的网络前缀(当前所有活跃的 TRON 网络——包括主网 Mainnet、Shasta 测试网和 Nile 测试网,其前缀均为 0x41),以及由公钥派生出的 20 字节哈希值。详细的派生算法请参阅账户管理。

📘

说明

在旧版文档或配置文件(如 framework/src/main/resources/config.conf)中可能出现的 0xa0 前缀是早期测试网遗留的配置选项(与当前的 Nile、Shasta 或本地私有网络配置无关)。目前所有活跃的 TRON 网络均不采用 0xa0 前缀。

visible 参数标志

大部分 TRON 节点 API 都支持在请求中传入 visible 字段。该参数用于控制请求和响应数据载荷(Payload)中的地址编码格式:

visible 值请求与响应中的地址格式
false(默认)十六进制 Hex 格式(以 41 开头)
trueBase58Check 格式(以 T 开头)

示例如下:

POST /wallet/createtransaction
Content-Type: application/json

{
  "visible": true,
  "owner_address": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
  "to_address": "TPL66VK2gCXNCD7EJg9pgJRfqcRazjhUZY",
  "amount": 1000000
}
⚠️

注意

请保持地址格式与 visible 标志一致。如果 visible 设为 false(或省略该字段)却传入 Base58 地址,节点会返回参数校验错误。反之,如果 visible 为 true 时传入 Hex 地址,同样会被拒绝。

某些 API 要求将 visible 作为 URL 查询字符串传入(例如 ?visible=true),而不是写入 JSON 请求体。调用前请确认对应接口的参数说明。

格式转换方法

主流 TRON SDK 都提供地址转换工具函数,通常不需要手动实现底层算法。

SDKBase58 转换为 HexHex 转换为 Base58
TronWeb (JavaScript)tronWeb.address.toHex(b58)tronWeb.address.fromHex(hex)
Trident (Java)Numeric.toHexString(Base58Check.base58ToBytes(b58))Base58Check.bytesToBase58(Numeric.hexStringToByteArray(hex))
gotron-sdk (Go)common.DecodeCheck(b58)common.EncodeCheck(hexBytes)

如果确实需要手动转换地址格式,算法步骤如下:

  • Hex 转换为 Base58Check:若地址缺少前缀,先在头部补齐 0x41 网络前缀字节。对得到的 21 字节地址执行两次连续的 SHA-256 计算。取第二次 SHA-256 计算结果的前 4 个字节作为校验和附加到该 21 字节地址的尾部。最后,对合并后的 25 字节数据进行 Base58 编码。
  • Base58Check 转换为 Hex:对 34 字符的 Base58Check 编码字符串进行 Base58 解码,得到 25 字节的数据。验证最后的 4 字节校验和是否正确。如果正确,剔除校验和,取前 21 字节即为带有网络前缀的 Hex 格式地址。
⚠️

重要

FullNode HTTP API 在 visible 为 false(或省略)时,通常使用带 41 网络前缀的 21 字节 Hex 地址。JSON-RPC 的 eth_* 方法则接受 20 字节的以太坊格式地址,并由节点在内部补上 41 前缀。请按具体接口的参数说明选择格式,不要在两类接口之间直接混用。


交易数据载荷:raw_data 与 raw_data_hex

调用构建交易的 API 时,返回的交易信息会同时包含两种数据载荷:

  • raw_data:一个结构化的 JSON 对象,包含了交易合同、TAPOS 引用区块数据、过期时间戳以及创建时间戳等明细。
  • raw_data_hex:raw_data 结构经 Protobuf 序列化并进行十六进制(Hex)编码后的原始数据字符串。

在密码学签名时,签名的计算对象是 SHA-256(raw_data_bytes)(其中 raw_data_bytes 代表 Protobuf 序列化后的字节数组)。在实际开发中,SDK 会自动解析 raw_data 或者直接从 raw_data_hex 获取原始字节完成签名。这两种路径在底层产生的签名哈希完全一致。

⚠️

注意

在构建交易与执行签名之间,不要修改 raw_data 中的字段值,也不要调整其中数组元素的顺序;这些变化会改变 Protobuf 序列化结果,进而改变 txID 并使原签名失效。单纯调整 JSON 对象的键顺序不会改变解析后的 Protobuf 消息,但自行重新序列化容易引入其他差异。将节点返回的 raw_data_hex 解码为字节后再计算签名哈希最为稳妥。


智能合约参数 ABI 编码

通过 /wallet/triggersmartcontract 或 /wallet/triggerconstantcontract 调用合约时,可以使用以下任一方式提供调用数据:

  • 提供 function_selector 和 parameter。function_selector 填写函数签名,例如 transfer(address,uint256);parameter 只包含函数参数的 ABI 编码。节点会计算函数选择器并将其添加到参数编码之前。
  • 提供 data。该字段包含完整的 ABI 调用数据,即 4 字节函数选择器与参数编码的拼接结果。使用此方式时,不再提供 function_selector 和 parameter。

TRON 采用与以太坊一致的 ABI 编码规范,但地址格式有一个关键差异。

编码规则

  • 函数选择器(Function Selector):取函数签名的 Keccak-256 哈希值的前 4 个字节。函数签名指“函数名(以逗号分隔的参数类型)”,不能包含空格。例如:transfer(address,uint256) 对应的函数选择器为 0xa9059cbb。原始 HTTP 请求中的 function_selector 字段填写函数签名,而不是这 4 个字节。
  • 参数编码:每个参数值必须对齐并填充到 32 字节(256 位):
    • uint256 等整数:转换为大端序的 32 字节整数。
    • address 账户地址:必须去掉 0x41 网络前缀,保留其余 20 字节,并在左侧填充零至 32 字节。
    • string、bytes 等动态类型:使用包含数据长度前缀的动态编码规则(参见 Solidity ABI 标准规范说明)。
⚠️

警告

在 ABI 编码中,必须移除地址头部的 0x41 前缀字节!
TRON 的虚拟机在处理合约中的地址参数时,使用的是 20 字节的原始地址(与以太坊格式对齐)并左侧补零填充至 32 字节——而非 21 字节的带有网络前缀的主网格式。传入带 0x41 的 21 字节地址(强行补零填充至 32 字节)是开发中最常见的合约调用报错原因。

示例:TRC-20 Token 转账

以在 TRC-20 Token 合约上调用 transfer(address,uint256),向地址 TXT7aiRQCLiMgsMZgFceLYbQu3MynssYUK 转账 100 个 Token 为例。假设该 Token 精度为 6,则 100 Token 对应 100,000,000 个最小单位,即 Hex 格式 0x5f5e100。

  1. 确定函数签名和选择器:函数签名为 transfer(address,uint256),对应的选择器为 a9059cbb。
  2. 转换并对齐接收地址:TXT7aiRQCLiMgsMZgFceLYbQu3MynssYUK 的 21 字节 Hex 地址为 41eba1c3a4a5e7a26acab1e10cd6e6c6e2db6ee9c2。剔除头部的 41 后为 eba1c3a4a5e7a26acab1e10cd6e6c6e2db6ee9c2。左侧补零对齐 32 字节后为 000000000000000000000000eba1c3a4a5e7a26acab1e10cd6e6c6e2db6ee9c2。
  3. 转换并对齐转账金额:100000000(即十进制 100,000,000)转换为十六进制为 5f5e100。右对齐、左侧补零至 32 字节后为 0000000000000000000000000000000000000000000000000000000005f5e100

直接调用原始 HTTP 接口时,可以选择以下任一请求形式。示例只展示与 ABI 编码相关的字段;实际请求还需提供 owner_address、contract_address 等接口要求的字段。

由节点计算并添加函数选择器:

{
  "function_selector": "transfer(address,uint256)",
  "parameter": "000000000000000000000000eba1c3a4a5e7a26acab1e10cd6e6c6e2db6ee9c20000000000000000000000000000000000000000000000000000000005f5e100"
}

或者自行拼接选择器与参数编码,并通过 data 提交完整调用数据:

{
  "data": "a9059cbb000000000000000000000000eba1c3a4a5e7a26acab1e10cd6e6c6e2db6ee9c20000000000000000000000000000000000000000000000000000000005f5e100"
}

使用第一种形式时,不要在 parameter 中重复加入 a9059cbb;节点会根据 function_selector 再添加一次选择器。

💡

提示

主流 SDK 的高级封装方法(如 TronWeb 中的 tronWeb.transactionBuilder.triggerSmartContract(...))通常会自动处理 ABI 编码。只有跳过 SDK、直接通过原始 HTTP 客户端调用节点接口,或目标合约没有公开 ABI 数据时,才需要手动执行上述编码步骤。


TRC-10 Token 的唯一性标识

与 TRC-20 Token 不同,TRC-10 资产是通过一个全局唯一的数字 Token ID 进行识别的,而不是通过智能合约地址。但在一些历史 API 接口的交易返回中,表示资产名称的 asset_name 字段的实际数据类型取决于交易打包入账时的区块高度:

打包区块范围asset_name 字段的实际内容示例数据
区块 5537806 及之后(当前绝大部分区块)全局唯一的数字 Token ID"1002000"
区块 5537806 之前(2019 年初及更早的历史区间)资产的文本名称字符串"USDJ"

这一变化源于早期协议演进:起初 TRON 要求每个 TRC-10 Token 注册名称保持唯一,因此可以直接将名称作为标识符。随着 14 号治理提议 激活,系统取消了命名唯一性限制,数字 ID 成为 TRC-10 资产的唯一标识符。

📘

说明

新建集成系统通常只需将 asset_name 视为数字形式的 Token ID,并过滤或拒绝区块 5537806 之前的历史转账。如果需要同步 TRON 全网完整历史数据(例如构建区块浏览器或历史索引服务),必须按区块高度做分支解析。


常见编码错误总结

  • 在构建与签名交易时,visible 标志配置不一致:构建交易的 API 响应结果与后续执行签名并广播的 Payload 中必须使用相同的 visible 状态。虽然各 SDK 会在内部自动调和,但如果直接使用原始 HTTP 接口,请确保该参数始终一致。
  • 在 ABI 编码的地址参数中遗留了 0x41 前缀:ABI 规范强制使用 20 字节的原始哈希。如果在 ABI 编码中包含了 0x41,将产生越界的 21 字节数据,导致智能合约无法正常解析并报错。
  • 在未启用 visible 参数时在 API 中传入 Base58 地址:默认情况下,节点会将输入视为十六进制。如果传入类似 T... 的地址,接口会因地址格式不符合预期而拒绝请求。此时需要显式设置 visible: true 或进行地址格式转换。
  • 将 raw_data_hex 按照字符串类型而非二进制字节数组进行签名哈希计算:交易签名是在 Protobuf 序列化后的原始二进制字节上执行的,而非对代表十六进制的 ASCII 字符串本身做哈希。使用封装好的 SDK 签名接口可完美规避此问题。
  • 签名前修改 raw_data 中的字段值或数组元素顺序:这会改变 Protobuf 序列化字节,导致 txID 变化并使原签名失效。JSON 对象的键顺序本身不影响解析后的 Protobuf 消息;为避免重新序列化引入差异,建议直接对 raw_data_hex 解码后的字节计算哈希。
  • 依赖 TRC-10 Token 的文本名称作为唯一身份主键:在 TRON 网络中,随着 14 号治理提议激活,多个 TRC-10 资产可以共享相同的名称。生产代码中必须仅使用数字形式的 ID(如 1002000)来关联与存储 TRC-10 资产信息。

相关资源