Object Wrapping 例子

我们来对成绩记录单的例子实现 object wrapping, 让 WrappableTranscriptFolder object 封装起来,使得 Folder object 可以被解包,因而内部的成绩记录单只能被特定的地址/观察者获取。

修改 WrappableTranscriptFolder

首先,需要对我们上一节中定制的类型 WrappableTranscriptFolder 做一些调整。

  1. WrappableTranscript 类型定义加上 key 的能力,可以成为资产和被转移。

回想一下,在 Sui Move 中具备了 keystore 能力的定制类型可以认为是资产。

#![allow(unused)]
fn main() {
public struct WrappableTranscript has key, store {
        id: UID,
        history: u8,
        math: u8,
        literature: u8,
}
}
  1. 我们需要为 Folder struct 增加一个额外的属性 intended_address 用来声明内部被封装起来的成绩记录单的目标观察者的地址。
#![allow(unused)]
fn main() {
public struct Folder has key {
    id: UID,
    transcript: WrappableTranscript,
    intended_address: address
}
}

请求 Transcript 方法

#![allow(unused)]
fn main() {
public entry fun request_transcript(transcript: WrappableTranscript, intended_address: address, ctx: &mut TxContext){
    let folderObject = Folder {
        id: object::new(ctx),
        transcript,
        intended_address
    };
    //We transfer the wrapped transcript object directly to the intended address
    transfer::transfer(folderObject, intended_address)
}
}

这个方法简易地把一个 WrappableTranscript object 封装到一个 Folder object 里头,然后将被封装的成绩记录单转移到目标地址。

解封装 Transcript 方法

#![allow(unused)]
fn main() {
public entry fun unpack_wrapped_transcript(folder: Folder, ctx: &mut TxContext){
    // Check that the person unpacking the transcript is the intended viewer
    assert!(folder.intended_address == tx_context::sender(ctx), 0);
    let Folder {
        id,
        transcript,
        intended_address:_,
    } = folder;
    transfer::transfer(transcript, tx_context::sender(ctx));
    // Deletes the wrapper Folder object
    object::delete(id)
    }
}

如果这个方法的调用者就是成绩记录单的目标观察者,就会从 Folder wrapper object 中解封装出 WrappableTranscript object, 然后将其发送给方法的调用者。

问题: 我们为什么需要在这里手动删除 wrapper object? 如果我们不删除会怎样?

Assert

我们使用 assert! 语法来判定在交易中解包出成绩记录单的地址与 Folder wrapper object 中的 intended_address 属性是一致的。

这个 assert! 宏会以下面的格式输入两个参数:

#![allow(unused)]
fn main() {
assert!(<bool expression>, <code>)
}

这里的布尔表达式必须判断为真,否则就会弹出错误码 <code> 并中止。

定义错误码

上面是用了默认的0来作为错误码,但其实我们也可以用下面的方式来定义其他错误码:

#![allow(unused)]
fn main() {
    const ENotIntendedAddress: u64 = 1;
}

这个错误码会在应用层上被消耗掉并进行合适的处理。

这里能找到对应这里的第二版处于开发进展中版本的代码: WIP transcript.move