摘要:Aptos 把链上状态组织成挂在账户下的 Move 资源。本文用 Aptos 官方中文文档、Aptos Explorer 与全节点 REST API 实测,讲清 0x1::account::Account 资源、账户序列号 sequence_number 与交易 version 三者的区别,并用 ledger_version 回看一笔交易前后序列号 674→675,附无序交易 u64::MAX 的读法。

Aptos 和以太坊都是智能合约链,但「账户里存着什么」完全是两套思路:以太坊把数据放进每个合约自己的存储,Aptos 把链上状态组织成 Move 资源(resource),挂在账户或对象地址下面。和它最容易混的是 Sui:两条链都用 Move,但 Sui 以对象为中心,Aptos 以账户为中心。本文只讲三个字段怎么读、怎么在链上自己核对:0x1::account::Account 资源、账户 sequence_number(序列号)和交易的 version(账本版本号)。

本文用到的工具都是公开只读的:Aptos 官方中文文档、Aptos Explorer(explorer.aptoslabs.com)和官方全节点 REST API(fullnode.mainnet.aptoslabs.com/v1)。不用钱包、不签名、不碰私钥。

一、Aptos 账户里装的是「资源」,不是一个余额字段

先看 Aptos 文档 · Move 资源。官方的说法是:在 Aptos 上,链上状态组织为资源和模块,存放在账户(以及对象地址)下,「这不同于以太坊:后者每个合约拥有私有存储空间」。

Aptos Move 账户模型:资源、序列号与版本号在链上怎么核对-程序旅途
图1:aptos.dev/zh「Move 资源」:资源是带 key 能力的 struct,存于某个地址的全局存储;带 store 的实例只能放在资源内部;APT 余额在同质化资产迁移后放在主同质化存储中;界面为简体中文

几个要点,按文档原意整理:

  • 资源 = 带 key 能力的 struct,存在某个地址的全局存储里;带 store 的值只能嵌在资源内部,不能单独做顶层资源。
  • 资源用完整类型定位:地址::模块::结构体<类型参数>,例如 0x1::account::Account。0x1 是框架地址,account 是模块名。
  • 余额不一定在账户资源里:文档写明,同质化资产(Fungible Asset)迁移后,APT 余额放在「主同质化存储(primary fungible store)」中,新代币也应使用这套主存储。所以在浏览器「资源」页看不到余额字段很正常,要去「资产」页看。
  • 账户状态 = 代码(Move 模块)+ 数据(Move 资源),见 Aptos 文档 · 账户。每个账户由 32 字节地址标识,通常显示为 64 个十六进制字符。

二、在 Aptos Explorer 里看一个账户的资源

以一个真实的主网普通账户为例(地址 0xf5236913…9c37,下文的交易也是它发的)。打开 Explorer 账户页,切到「资源」标签:

Aptos Move 账户模型:资源、序列号与版本号在链上怎么核对-程序旅途
图2:explorer.aptoslabs.com 账户页「资源」标签:类型 0x1::account::Account,展开后是 authentication_key、guid_creation_num、key_rotation_events 等字段(往下翻是 sequence_number);界面为简体中文,2026-10-06 截图

这个 JSON 里值得认的字段:

字段 含义 怎么用
authentication_key 认证密钥;新建账户时它就等于地址 轮换密钥后会变,但地址永不改变(文档原话)
sequence_number 下一笔有序交易应携带的序列号 和以太坊 nonce 作用类似,见第三节
key_rotation_events 密钥轮换事件计数 counter 为 0 说明这个账户没换过密钥
guid_creation_num 该账户已创建的 GUID 计数 事件句柄等内部编号用,读数据时一般不用管

另外一点和以太坊不同:文档说明有了无状态账户(AIP-115)后,任何有效地址默认都视为账户,链上 Account 资源只在首次需要时才自动创建。所以你查一个新地址时,「资源」页可能是空的,这不代表地址无效。

三、序列号 sequence_number:有序交易的防重放计数器

文档「账户序列号」一节写得很直接:

Aptos Move 账户模型:资源、序列号与版本号在链上怎么核对-程序旅途
图3:aptos.dev/zh「账户 · 账户序列号」:每笔有序交易必须携带发送者的下一个序列号,内存池只转发能从当前序列号形成连续序列的交易;无序交易改用唯一 nonce,sequence_number 设为 u64::MAX、60 秒内过期;界面为简体中文
  1. 链上存的是「下一个」:账户资源里的 sequence_number 表示已在链上确认的有序交易数量,也就是下一笔交易要用的号。
  2. 必须连续:只有从当前序列号起能形成连续序列,内存池才会转发;执行会拒绝空缺。这与以太坊 nonce「有空号就卡住」的逻辑相同(以太坊这边见 以太坊 Nonce 是什么)。
  3. 多代理交易只加主发送者:多个签名账户参与时,次要签名者的序列号不变。
  4. 无序交易(AIP-123)不用它:改用唯一 replay_protection_nonce,sequence_number 填 u64::MAX(18446744073709551615),最长 60 秒过期,适合多台机器从同一账户并行提交。

四、交易页:version、区块与序列号是三个不同的数

打开这个账户发出的一笔交易 版本 7,500,957,288:

Aptos Move 账户模型:资源、序列号与版本号在链上怎么核对-程序旅途
图4:explorer.aptoslabs.com/txn/7500957288:版本 7,500,957,288、发送方、手续费支付方、函数 aptos_account::batch_transfer_fungible_assets;下方区块 1,093,889,402、序列号 674、过期时间戳、Gas 费用 0.000134 APT(134 个 Gas 单位)、Gas 单价 100 Octas;界面为简体中文,时间为 UTC

逐个对照:

版本(version)7,500,957,288
这是全链账本的交易序号。官方「交易和状态」写明:账本状态用一个与已执行交易数量对应的 u64 整数做版本控制。Aptos Explorer 的交易链接就是按 version 定位的,比哈希更短好记。
区块 1,093,889,402
一个区块里打包多笔交易,所以区块高度远小于 version。查「这笔交易在第几块」看它,查「在哪一刻的账本状态」看 version。
序列号 674
只属于发送方账户:这是该账户的第 675 笔有序交易(从 0 开始数)。它与全链 version 无关。
Gas 费用 = Gas 用量 × Gas 单价
截图里 134 个 Gas 单位 × 100 Octas = 13,400 Octas = 0.000134 APT(1 APT = 108 Octas)。这笔交易还有「手续费支付方」,说明是代付交易:Gas 由另一个账户出,但序列号仍记在发送方。具体数值只代表这一笔,Gas 单价随网络和提交者设置变化。

和 Sui 对比一下更好记:Sui 的版本号挂在每个对象上(见 Sui 对象模型:对象 ID、版本号与所有权);Aptos 的 version 是整条链的账本序号,账户再单独维护自己的 sequence_number。

五、用 REST API 自己核对:交易前后序列号 674 → 675

浏览器是别人做的界面,最硬的核对方式是直接问全节点。Aptos 文档写明可以用 GET /accounts/{address}/resource/{resource_type} 读资源;REST API 还支持带 ledger_version 参数读某个历史版本的状态。下面是本机真实执行的命令(只读,主网公开数据):

N=https://fullnode.mainnet.aptoslabs.com/v1
A=0xf523691394a86fd2ecc018c16a867daf0a9809530dbed4ae895c508e042b9c37

# 1) 节点当前账本版本、区块高度、最早可查版本
curl -s $N | jq '{chain_id,ledger_version,block_height,oldest_ledger_version}'

# 2) 读交易 7500957288 的关键字段
curl -s $N/transactions/by_version/7500957288 \
  | jq '{version,hash,sender,sequence_number,replay_protection_nonce,gas_used,gas_unit_price,success}'

# 3) 交易执行前(version-1)与执行后,账户资源里的序列号
curl -s "$N/accounts/$A/resource/0x1::account::Account?ledger_version=7500957287" | jq .data.sequence_number
curl -s "$N/accounts/$A/resource/0x1::account::Account?ledger_version=7500957288" | jq .data.sequence_number

# 4) 对照一笔无序交易
curl -s $N/transactions/by_version/7500957292 \
  | jq '{version,sender,sequence_number,replay_protection_nonce,expiration_timestamp_secs,timestamp}'
Aptos Move 账户模型:资源、序列号与版本号在链上怎么核对-程序旅途
图5:本机 bash 真实输出:交易 7500957288 的 sequence_number 为 674;同一账户在 version 7500957287 时资源里的序列号是 674、7500957288 时变为 675;无序交易 7500957292 的 sequence_number 为 u64::MAX 且带 replay_protection_nonce;2026-10-06 执行

从输出能读出四件事:

  1. 序列号是「下一个」:交易执行前账户里是 674,交易本身携带 674,执行后变成 675,和文档描述完全一致。
  2. version 能当时间轴用:带 ledger_version 就能回看任意一笔交易前后的资源状态,这是 Aptos 版本化数据库的直接用法。
  3. 历史有边界:节点返回了 oldest_ledger_version,比它更早的版本这个节点已经裁剪,查不到要换归档节点或索引服务。
  4. 无序交易长这样:sequence_number = 18446744073709551615,replay_protection_nonce 有值;过期时间与上链时间只差约 30 秒,在 60 秒上限之内。有序交易的 replay_protection_nonce 则是 null。

常见误读

  • 把 version 当成区块号:Explorer 链接里的数字是 version,不是区块高度,两者差好几倍。
  • 在「资源」页找余额:APT 和新代币走同质化资产主存储,余额看「资产」页或用余额接口,不在 0x1::account::Account 里。
  • 看到 sequence_number = 18446744073709551615 以为是 bug:那是无序交易的固定写法。
  • 查新地址资源为空就以为地址错:无状态账户在首次需要前不会创建 Account 资源;先核对地址是不是 64 位十六进制、是不是主网。
  • 拿以太坊思路找合约存储:Aptos 的数据按「谁的地址下有哪个类型的资源」来找,不是「某合约的 storage slot」。

想自己动手的话

只读查询不需要任何钱包。如果想实际收发 APT 或体验 Aptos 生态,可以用支持 Aptos 的自托管钱包,例如 欧易 Web3 钱包(创建时可填推荐码 CHENGXULVTU);手续费用 APT 支付,少量 APT 可以在 币安 或 欧易 买入后提到钱包,提币时网络选 Aptos。先在 Explorer 里把自己地址的「资源」「资产」两页对照一遍,再做第一笔转账。

相关阅读

说明:文中的版本号、区块高度、Gas 数值均来自 2026-10-06 的 Aptos Explorer 截图与全节点 API 实际返回,链上数据持续变化;机制描述以 aptos.dev 官方文档为准。本文只做技术科普,不构成任何投资建议。