Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
网络与协议指南阅读约 12 分钟

通过 RPC 读取 Sui 对象:getObject、动态字段与分页

了解如何通过 JSON-RPC 读取 Sui 对象:getObject、multiGetObjects、动态字段、拥有的对象以及游标分页。

TL;DR

通过 JSON-RPC 读取 Sui 对象需要理解对象模型:每个对象都有 ID、版本和摘要,所有权可以是地址、对象、共享或不可变。使用 sui_getObject 进行单次读取,使用 suix_multiGetObjects 进行批量读取,使用 suix_getDynamicFields 分页浏览动态字段,使用 suix_getOwnedObjects 列出地址的主要对象。本指南解释了这些机制,提供了可运行的 curl 示例,并包含常见问题(如对象缺失和分页循环)的故障排查清单。

直接回答:如何正确读取 Sui 对象

要通过 JSON-RPC 读取 Sui 对象,首先必须理解 Sui 对象并非简单的键值对。每个对象由一个 32 字节的对象 ID 标识,具有一个每次写入时递增的版本整数,以及一个随每次写入而变化的摘要(哈希)。所有权可以是地址拥有、对象拥有、共享或不可变。要获取对象的当前状态,请使用对象 ID 调用 suix_getObject(或旧版 sui_getObject)。要一次请求获取多个对象,请使用 suix_multiGetObjects。要列出地址拥有的对象,请使用 suix_getOwnedObjects 并附带可选过滤器。动态字段(允许对象嵌套任意数据)通过 suix_getDynamicFields(用于分页浏览)和 suix_getDynamicFieldObject(用于获取单个字段)单独读取。分页基于游标,必须正确处理 nextCursorhasNextPage 字段以避免循环。本指南通过可运行的示例和故障排查清单逐步介绍每种方法。

Sui 对象模型:ID、版本、摘要和所有权

在 Sui 中,一切都是对象。基于 Move 的模型将对象存储在以 32 字节 ObjectID 为键的全局映射中。每个对象都有一个 version(单调递增的整数)和一个 digest(对象内容和版本的哈希)。当对象被修改时,其版本递增,摘要也会改变。这个三元组——ID、版本、摘要——是所有读取的基础。所有权可以是四种类型之一:地址拥有(由单个地址控制)、对象拥有(由另一个对象拥有,支持层级结构)、共享(任何人都可访问,常用于共享状态)和不可变(无法修改,例如已发布的包)。

官方 Sui 文档 解释说,对象可以包装在其他对象内部,这会影响它们的可见性。例如,包装后的对象不再能通过其 ID 直接访问;它“隐藏”在父对象内部。当 getObject 调用返回未找到,即使对象在链上存在,这也是常见的困惑来源。

读取对象时,你会收到其当前状态,包括 data 布局(例如 moveObjectpackage)、所有者以及引用(ID、版本、摘要)。如果只请求引用(通过将 showContent 设置为 false),你会得到一个轻量级响应,这对于跟踪更改而不下载完整内容非常有用。

读取对象:sui_getObject 和 suix_multiGetObjects

读取单个对象的主要方法是 suix_getObject(旧版 sui_getObject 已弃用,但在许多节点上仍然有效)。它接受一个对象 ID 和一组显示选项(例如 showContentshowOwnershowType)。响应包括对象的 objectIdversiondigestowner 以及包含 Move 类型和字段的 data 字段。

对于批量读取,使用 suix_multiGetObjects 并传入 ID 数组和相同的显示选项。当需要一次往返获取多个对象时,这非常高效,可减少延迟和速率限制使用。请注意,响应顺序与请求顺序一致,如果对象不存在,对应的条目将为 null

以下是一个使用公共 Sui RPC 端点的可运行 curl 示例(如有需要,请将 URL 替换为您自己的提供商)。该示例获取一个已知对象(一个 Sui 币)并打印响应:

curl -X POST https://fullnode.mainnet.sui.io:443 \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "suix_getObject",
    "params": [
      "0x2::sui::SUI",
      {
        "showContent": true,
        "showOwner": true,
        "showType": true
      }
    ]
  }'

列出拥有的对象:suix_getOwnedObjects 和过滤器

要枚举地址拥有的对象,请使用 suix_getOwnedObjects。此方法返回地址的“主要”拥有的对象——即由地址直接拥有且未包装在其他对象内部的对象。默认情况下,它返回动态字段或币余额。要按类型或包过滤,请使用带有 StructTypePackage 过滤器的 filter 参数。

例如,要列出地址拥有的所有 SUI 币,您可以按 0x2::coin::Coin<0x2::sui::SUI> 过滤。响应包含用于分页的游标。以下是一个 curl 示例:

curl -X POST https://fullnode.mainnet.sui.io:443 \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "suix_getOwnedObjects",
    "params": [
      "0xYOUR_ADDRESS",
      {
        "filter": {
          "StructType": "0x2::coin::Coin<0x2::sui::SUI>"
        },
        "options": {
          "showContent": true,
          "showOwner": true
        },
        "limit": 10
      }
    ]
  }'

动态字段:读取嵌套数据

Sui 对象可以包含动态字段,这些字段是存储在对象本身上的键值对。这些字段允许灵活的数据结构,可以随时间添加或删除。动态字段不属于对象的固定模式;它们单独存储,必须使用专用方法读取。

要分页浏览父对象的所有动态字段,请使用 suix_getDynamicFields,并传入父对象 ID、游标和限制。响应包含动态字段名称列表(base58 编码)及其类型,以及用于分页的 nextCursor。要获取特定动态字段的值,请使用 suix_getDynamicFieldObject,并传入父 ID 和字段名称(作为包含 typevalueDynamicFieldName 对象)。

以下是一个分页浏览动态字段的示例:

curl -X POST https://fullnode.mainnet.sui.io:443 \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "suix_getDynamicFields",
    "params": [
      "0xPARENT_OBJECT_ID",
      null,  // cursor
      10     // limit
    ]
  }'

使用游标分页:避免循环和数据缺失

Sui RPC 中的所有列表方法(例如 suix_getOwnedObjectssuix_getDynamicFields)都使用基于游标的分页。响应包含 nextCursor 字段和 hasNextPage 布尔值。要获取下一页,请在下一个请求中将 nextCursor 作为游标参数传递。如果 hasNextPage 为 false,则表示已到达末尾。

一个常见错误是重复使用相同的游标,导致无限循环。始终从响应中更新游标。另外,请注意 limit 参数受节点提供商的限制;默认值通常为 50,但可能有所不同。如果您设置的限制高于上限,节点可能会返回错误或静默截断。请查阅提供商的文档以了解确切限制。

以下是一个可填写的结果表格,用于记录您自己的分页结果:

  • | 页数 | 使用的游标 | 返回的对象数 | nextCursor | hasNextPage |
  • |------|-------------|------------------|------------|-------------|
  • | 1 | null | [填写] | [填写] | [填写] |
  • | 2 | [填写] | [填写] | [填写] | [填写] |
  • | ... | ... | ... | ... | ... |

读取历史版本和归档节点

要读取对象的特定历史版本,您必须显式请求该版本。suix_getObject 方法接受一个可选的 version 参数(作为 SuiObjectDataOptions 的一部分?实际上,标准方法不支持版本;您需要使用 suix_tryGetPastObject 或类似方法)。在旧版 API 中,sui_getObject 不支持版本;您必须使用 sui_tryGetPastObject(现在是 suix_tryGetPastObject)来获取过去的版本。此方法需要对象 ID 和所需的版本号。

但是,过去的版本仅在存储完整历史的归档节点上可用。非归档节点的全节点将只拥有最新状态和有限的检查点历史。如果您请求的版本不可用,节点将返回错误。对于需要完整对象历史的应用,您必须连接到归档 RPC 提供商。OnFinality 提供 Sui 归档节点和历史数据,保留完整历史。

或者,您可以通过事件或使用 showPreviousTransactionsuix_getObject 来跟踪对象更改,以查看最后修改该对象的交易,但这不会提供完整历史。

故障排查清单:常见失败和修复

读取 Sui 对象时,您可能会遇到几个常见问题。使用此清单来诊断和修复它们:

  • 对象未找到:如果 suix_getObject 返回 null 或错误,则对象可能已被包装、删除或从未存在。检查对象 ID 是否有拼写错误。如果对象已被包装,则无法直接访问;您必须通过其父对象的动态字段读取它。
  • 不可变对象语义:不可变对象(如包)永远不会改变。它们的版本始终为 1,摘要是恒定的。如果您期望版本递增,则可能正在查看错误的对象。
  • 字段不存在:使用 suix_getDynamicFieldObject 时,确保字段名称和类型与存储的键完全匹配。动态字段名称是 base58 编码且类型敏感的。不匹配将返回错误。
  • 版本不可用:如果您在非归档节点上请求历史版本,则会收到错误。请使用归档节点或将查询调整为最新版本。
  • 游标误用:始终使用上一个响应中的 nextCursor。如果您传递相同的游标,可能会无限循环。此外,确保正确处理 hasNextPage
  • 分页限制上限limit 参数受提供商限制。如果超过限制,节点可能会返回错误或截断结果。请查阅提供商的文档以了解最大限制。在 OnFinality,限制已记录在案,并可能因计划而异;请参阅 RPC 定价

对象读取的限制和权衡

通过 RPC 读取 Sui 对象存在固有的限制。首先,suix_getOwnedObjects 仅返回主要拥有的对象;它不包括嵌套在动态字段中的对象或非直接拥有的币。要获得完整的清单,您必须递归遍历动态字段,这可能代价高昂。

其次,对象读取是时间点读取。要跟踪随时间的变化,您必须轮询对象的版本和摘要,或订阅事件。轮询可能对速率限制敏感;考虑使用 WebSocket 订阅进行实时更新。OnFinality 的 Sui WebSocket 事件订阅 指南说明了如何设置。

第三,JSON-RPC API 正在向类型化 SDK 迁移。在 Sui 2.0 中,许多原始方法正在被抽象 RPC 层的 SDK 方法所取代。此迁移目前不会破坏现有功能,但您应该计划更新代码。官方 Sui JSON-RPC 迁移指南 提供了详细信息。

最后,读取许多对象的成本可能会累积。使用 suix_multiGetObjects 进行批量读取比单独调用更高效。对于大规模数据提取,请考虑使用专门的数据服务或索引。

后续步骤和进一步阅读

既然您了解了如何读取 Sui 对象,就可以应用这些知识来构建资产跟踪器、库存系统或任何需要查询链上状态的应用。为了加深理解,请探索以下资源:

  • API 服务 – OnFinality 的托管 RPC 端点。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始