跳到主要内容

调用合约

调用合约时,最常用的功能是 sui client, 可以先简单浏览源代码。这一小节会过一遍常用的功能。

我们先把上一节课的示例代码发布到测试网上,为了方便学习,这里也直接发布了一个版本,方便大家调用。testnet package ID 是 0xf88ed6fdffa373a09a1a54fbad1ac4730219142f7fa798bdcf632d5f159e4a18.

签名并执行交易 signAndExecuteTransaction

先跳转到example_project文件夹,然后执行execute.ts程序。

bun run execute.ts

可以看到执行结果返回一串哈希码,把该哈希码复制到区块链浏览器可以看到执行记录。

在这个最简单的execute.ts示例程序中。

const client = new SuiClient({
url: getFullnodeUrl("testnet"),
});

是在程序内初始化一个Sui客户端,并且指定网络是在testnet.

const tx = new Transaction();
tx.moveCall({
package: PACKAGE,
module: "profile_clock",
function: "mint",
arguments: [
tx.pure(bcs.string().serialize("Example").toBytes()),
],
});

初始化一个交易,并且声明交易内容,是调用PACKAGEprofile_clock模块的mint函数。 对比原始合约代码,输入参数有两个 handle: String, ctx: &mut TxContext, 其中ctx可以免输入。 String变量的输入需要用bcs编码方式编码,记住常见的输入格式即可。

const result = await client.signAndExecuteTransaction({ signer: keypair, transaction: tx });
console.log(result);

这一步骤就是关键,会使用私钥签署交易并执行。最后打印输出执行结果。

查询地址持有的Object getOwnedObjects

在调用合约click函数时,需要输入用户持有的Profile Object 的地址和时间Clock作为参数。

getOwnedObjects可以查询某个地址持有的所有Object.

const object_list = await client.getOwnedObjects({owner: address});

查询输入的参数应该如何填写,可以查看源代码。

这里只需要查找目前地址下的Profile Object 的信息,可以额外输入筛选信息。并且返回所有检索到符合条件的第一个Object的ID, 保存为profile_id.

const struct_type = "0xf88ed6fdffa373a09a1a54fbad1ac4730219142f7fa798bdcf632d5f159e4a18::profile_clock::Profile";
const object_list = await client.getOwnedObjects({ owner: address, filter: { StructType: struct_type } });
const profile_id = object_list.data[0].data!.objectId;

这里的StructType是由package_id::module::struct格式组成的字符串。

有了这些信息之后,就可以调用click函数。 在click函数中,需要输入的第二个参数是Clock, 是系统定义的ID为0x6的共享Object。

const PACKAGE: string = '0xf88ed6fdffa373a09a1a54fbad1ac4730219142f7fa798bdcf632d5f159e4a18';
const tx = new Transaction();
tx.moveCall({
package: PACKAGE,
module: "profile_clock",
function: "click",
arguments: [
tx.object(profile_id),
tx.object("0x6"),
],
});

可编程交易块 PTB(Programmable Transaction Blocks)

Sui 的一个很棒的特性是,可以使用可编程交易块把很多不同的交易请求组合起来,甚至不同package的不同函数。这让构建应用变得更加灵活自由。

ptb示例代码中,就在可编程交易块里组装了三个交易。分别是创建ProfileObject, 使用该ProfileObject 执行click函数一次,然后销毁该ProfileObject.

const tx = new Transaction();
const [profile] = tx.moveCall({
package: PACKAGE,
module: "profile_clock",
function: "new",
arguments: [
tx.pure(bcs.string().serialize("Example").toBytes()),
],
});
tx.moveCall({
package: PACKAGE,
module: "profile_clock",
function: "click",
arguments: [
profile,
tx.object("0x6"),
],
});
tx.moveCall({
package: PACKAGE,
module: "profile_clock",
function: "burn",
arguments: [
profile,
],
});

模拟交易 dryRunTransactionBlock

在使用钱包签署交易之前,可以预览到交易的执行结果。这是用到了 Sui 的模拟交易功能。

dryrun示例程序示范了如何使用模拟交易功能。

tx.setSender(address);
const dataSentToFullnode = await tx.build({ client: client });
const result = await client.dryRunTransactionBlock({
transactionBlock: dataSentToFullnode,
});

这段模拟交易的执行会报错。因为这段代码中执行的 points 函数,需要输入&Profile, 但这个ProfileObject的所有权并不在当前地址。

查看函数 devInspectTransactionBlock

devInspect函数同样可以查看函数的执行效果,有点像EVMview函数,而且不需要在意Object所有权的问题。

devinspect示例程序示范了如何使用devInspectTransactionBlock在不需要消耗gas的情况下,执行智能合约里写好的读函数。

const res = await client.devInspectTransactionBlock({
sender: normalizeSuiAddress(address),
transactionBlock: tx,
});
console.log(res?.results?.[0]?.returnValues?.[0]?.[0]);

查询Object信息 getObject

devInspect的使用需要提前在合约里写好读函数,如果没有,其实也可以直接使用getObject在链上查询Object的数据。

getobject示例程序中给出了直接根据Object id查询属性的方法。

作业

重新发布合约,然后使用本小节提及的client函数写调用函数,读取数据的TypeScript脚本。