Sui 链上数据查询 | Move dApp 极速入门(拾柒)

  • 李大狗
  • 更新于 2023-08-20 12:21
  • 阅读 1447

如何查询 Sui 的链上数据?

示例代码见: https://github.com/NonceGeek/Web3-dApp-Camp/tree/main/move-dapp/sui/sui-data-fetch

0x01 通过ID / address直接查询

Related Link:

https\://docs.sui.io/sui-jsonrpc#sui_getObject

在sui中查询数据最直接的方式就是通过Object Id获取object信息。

import { JsonPrcProvider } from '@mysten/sui.js'
const provider = new JsonRpcProvider();
// 查询单个对象数据
const objectId = '0xd7d27a9a68c11b9035be08428feceea9bf374510';
provider.getObject(objectId);

// 查询多个对象数据
const objectIds = [
  '0x9177ffd1ab19b7dfe6323f2674fb58c29c75bf7a',
  '0xa46d8caa8ac4d39791a0542ae99de1f8ffb9a18d'
];
provider.getObjectBatch(objectIds);

// 通过对象ID查询其包含的其他对象
provider.getObjectsOwnedByObject(objectId)

// 查询某个地址下拥有的对象
const address = '0x81b9154bf2135207168ee73ac47a45af20af6431';
provider.getObjectsOwnedByAddress(address)

0x02 利用 devInspectTransaction 查询数据

Related Links:

https\://docs.sui.io/sui-jsonrpc#sui_devInspectTransaction

https\://github.com/MystenLabs/sui/pull/6538

devInspectTransaction 原理是:利用实时的链上数据模拟方法调用,解析调用结果来获取需要的数据。

它的优势在于:

  • 模拟调用,无需gas
  • 可以直接调用非 entry的 public方法,并获取返回结果
  • 自定义查询逻辑,支持各类复杂的查询需求
module test_query::test_query {
  
  use std::vector;
  use std::type_name::{TypeName, get};
  use sui::object::{Self, UID};
  use sui::table::{Self, Table};
  use sui::tx_context::TxContext;
  use sui::transfer;
  
  struct A has drop {}
  struct B has drop {}
  
  struct BalanceSheet has copy, store, drop {
    cash: u64,
    debt: u64
  }
  
  struct BalanceSheets has key {
    id: UID,
    table: Table<TypeName, BalanceSheet>
  }
  
  struct QueryResult has copy, drop {
    typeName: TypeName,
    balanceSheet: BalanceSheet,
  }
  
  fun init(ctx: &mut TxContext) {
    let balanceSheets = BalanceSheets {
      id: object::new(ctx),
      table: table::new(ctx)
    };
    table::add(&mut balanceSheets.table, get<A>(), BalanceSheet { cash: 100, debt: 20 });
    table::add(&mut balanceSheets.table, get<B>(), BalanceSheet { cash: 80, debt: 10 });
    transfer::share_object(balanceSheets)
  }
  
  public fun query(balanceSheets: &BalanceSheets): vector<QueryResult> {
    let res = vector::empty<QueryResult>();
    let typeA = get<A>();
    let balanceSheetA = table::borrow(&balanceSheets.table, typeA);
    let queryResultA = QueryResult { typeName: typeA, balanceSheet: *balanceSheetA };
    
    let typeB = get<B>();
    let balanceSheetB = table::borrow(&balanceSheets.table, typeB);
    let queryResultB = QueryResult { typeName: typeB, balanceSheet: *balanceSheetB };
    
    vector::push_back(&mut res, queryResultA);
    vector::push_back(&mut res, queryResultB);
    res
  }
}

上面这个合约中query就是返回对BalanceSheets下table数据的一个查询结果。

query方法自定了一套查询逻辑,并将结果包装成QueryResult列表返回出来。下面看看如何调用这个方法获取查询数据。

import { JsonPrcProvider, bcs } from '@mysten/sui.js'
const provider = new JsonRpcProvider();

/******* 第一步: 需要构造解析返回结果的bcs解析器 *******/

// 解析类型根据 合约中定义的struct对于填写,例如下面的BalanceSheet
/****
  struct BalanceSheet has copy, store, drop {
    cash: u64,
    debt: u64
  }
*****/
bcs.registerStructType('BalanceSheet', {
  cash: 'u64',
  debt: 'u64',
})

bcs.registerStructType('TypeName', {
  name: 'string'
})

bcs.registerStructType('QueryResult', {
  typeName: 'TypeName',
  balanceSheet: 'BalanceSheet',
})

function des(data: Uint8Array) {
  return bcs.de('vector<QueryResult>', data)
}

/******* 第二步: 通过devInspectMoveCall模拟调用query方法 *******/

(async () => {
  const balanceSheetsId = '0x92a403ee9467f3753a28a8725a053eda6f64cca4';
  const testPkgId = '0xd7d27a9a68c11b9035be08428feceea9bf374510';
  const sender = '7738ccc64bd64bb7b3524296db285042f7876281'; // 地址可以任意使用
  const res = await provider.devInspectTransaction(sender, {
    kind: 'moveCall',
    data: {
      packageObjectId: testPkgId,
      module: 'test_query',
      function: 'query',
      typeArguments: [],
      arguments: [balanceSheetsId] 
    }
  })
  // 下面这段解析逻辑是精华,返回的数据结构很复杂,并且需要bcs反序列化
  if ('Ok' in res.results) {
    const returnValues = res.results.Ok[0][1].returnValues;
    if (returnValues) {
      const returnData = returnValues[0][0];
      const d = Uint8Array.from(returnData);
      let decoded = des(d) // 在这里用到了第一步的bcs解析器
      console.log(decoded)
    }
  }
})()

成功的话会打印出结果:

[
  {
    typeName: { name: 'd7d27a9a68c11b9035be08428feceea9bf374510::test_dev::A' },
    balanceSheet: { cash: 100n, debt: 20n }
  },
  {
    typeName: { name: 'd7d27a9a68c11b9035be08428feceea9bf374510::test_dev::B' },
    balanceSheet: { cash: 80n, debt: 10n }
  }
]

0x03 总结

  • 对于大多数简单查询,直接通过address,Id进行对象数据查询即可。

  • devInSpectMoveCall 可以做到查询任意数据,实现复杂的组合查询。代价就是需要自己写链上查询函数,以及链下的 bcs 解析逻辑。


前文链接:

SUI 合约测试攻略 | Move dApp 极速入门(拾陆)\

Sui 数据类型详解 | Move dApp 极速入门(十五)\

Airdropper Contract in Aptos | Move dApp 极速入门(拾肆)\

Sandwich合约案例实践 | Move dApp 极速入门(拾叁)\

Sui 极速上手 | Move dApp 极速入门(拾贰)

Move 高阶语法 | 共学课优秀笔记\

Move 基础语法 | 共学课优秀学习笔记

scaffold-aptos 脚手架 | Move dApp 极速入门(拾壹)

Aptos NFT 发行指南 | Move dApp 极速入门(十)

对 DID Document 的思考 | Move dApp 极速入门(九)\

DID中地址聚合器的实现 | Move dApp 极速入门(八)

Aptos 中的智能合约形式化验证 | Move dApp 极速入门(七)

Aptos CLI使用指南与REPL设计建议 | Move dApp 极速入门(六)\

实现一套 DID 之总体设计 | Move dApp 极速入门(五)\

合约数据类型综述 | Move dApp 极速入门(四)\

操作资源 | Move dApp极速入门(三)\

第一个 Move dApp | Move dApp极速入门(二)\

Hello Move | Move dApp极速入门(一)

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
李大狗
李大狗
0x73c7...6A1d
面向炫酷编程