Sui Move One Time Witness, Publisher, Object Display

本文通过Sui区块链技术,探讨了一次性见证(OneTimeWitness)、发布者(Publisher)和对象显示(ObjectDisplay)在创建国王、勇士和骑士角色中的应用,以及如何确保独特性和权限控制。通过实例展示了如何使用这些概念进行能力值提升和对象操作,以形成一个易于理解的学习示例。
摘要由CSDN通过智能技术生成

一:概要

国王将从勇士当中挑选有能力的晋升为骑士,整个过程可以分成如下几点:

  • 国王有且只有一个,在神的指导下可以提升自身能力值。
  • 只要有能力,勇士可以有无数个,在自身努力之下可以提升能力值。
  • 晋升为骑士的条件是勇士的能力不低于国王的能力值,每一个骑士将公开自身信息但他/她的能力值也就此止步,无法再提升。

二:分析

思考如何通过一次性见证 ( O n e   T i m e   W i t n e s s ) \mathit {(One\ Time\ Witness)} (One Time Witness),发布者 ( P u b l i s h e r ) \mathit {(Publisher)} (Publisher)以及对象显示 ( O b j e c t   D i s p l a y ) \mathit {(Object\ Display)} (Object Display)来实现?

注意: 本篇内容仅针对初学者,目的是将这三者尽可能串联起来,形成一个不那么枯燥又容易理解的例子,所以设计过程及最终呈现可能存在优化空间。

2.1 一次性见证

一次性见证 ( O n e   T i m e   W i t n e s s ,   O T W ) \mathit {(One\ Time\ Witness,\ OTW)} (One Time Witness, OTW)是一种特殊类型的实例,该类型的定义需要具备如下条件:

  • 以模块的名字命名,下划线 _ \mathit {\_} _ 保留,但所有字母大写。
  • 只拥有 d r o p \mathit {drop} drop​ 能力修饰符。

O T W \mathit {OTW} OTW 只在模块初始化器中创建,并保证是唯一的,可以用types::is_one_time_witness(&witness)来判断传入的 w i t n e s s \mathit {witness} witness 是不是 O T W \mathit {OTW} OTW

借助 O T W \mathit {OTW} OTW,对创建国王的函数进行限制,以此来保证其唯一性。

2.2 发布者

发布者 ( P u b l i s h e r ) \mathit {(Publisher)} (Publisher)对象用于代表发布者的权限,它本身并不代表任何特定的用例,主要通过package::from_module<T>package::from_package<T>来检查传入的类型为 T \mathit T T (泛型或指定一个类型)的参数与 p u b l i s h e r \mathit {publisher} publisher 是否处在同一个模块或包中。

为了保证模块当中发布者的唯一性,需要用到上面提到的 O T W \mathit {OTW} OTW,通过package::claim_and_keep(otw, ctx)创建一个 p u b l i s h e r \mathit {publisher} publisher 并将其所有权转移给发布者;如果不着急转移所有权,可以通过let publisher = package::claim(otw, ctx)来获得 p u b l i s h e r \mathit {publisher} publisher,接下去可以借助这个对象,来做一些其它的事情(比如定义对象显示等),但是在最后,不要忘记将它的所有权手动通过 t r a n s f e r \mathit {transfer} transfer 移交给发布者。

借助发布者的权限,比肩神明,唯有此方能指导国王能力提升

2.3 对象显示

拥有 P u b l i s h e r \mathit {Publisher} Publisher 对象的构建者可以通过sui::display模块来自定义对象的显示属性,以供生态系统在链下处理数据,所有属性都可以通过{property}语法访问同时作为字符串插入其中,例如:

{
    "name": "{name}",
    "link": "https://sui-heroes.io/hero/{id}",
    "img_url": "ipfs://{img_url}",
    "description": "A true Hero of the Sui ecosystem!",
    "project_url": "https://sui-heroes.io",
    "creator": "Unknown Sui Fan"
}

MyObjectDisplay<MyObject>匹配时,可以通过web进行查看自定义的属性显示,具体规范请点击 s u i _ g e t O b j e c t \mathit {sui\_getObject} sui_getObject,不要忘记将其中的 s h o w D i s p l a y \mathit {showDisplay} showDisplay 填为 t r u e \mathit {true} true

三:代码实现

国王:

  • 创建 k i n g \mathit {king} king 的时候需要传入一个 w i t n e s s \mathit {witness} witness,以此来进行限制。
  • 提升能力值需要 p u b l i s h e r \mathit {publisher} publisher,且其要与 K i n g \mathit {King} King 结构定义所处同一个包内。
module king_knight::king {
    use sui::object::{Self, UID};
    use sui::tx_context::{Self, TxContext};
    use sui::transfer;
    use sui::types;
    use sui::package::{Self, Publisher};

    const ENOTWITNESS: u64 = 0;
    const ENOTPACKAGE: u64 = 1;

    struct King has key {
        id: UID,
        ability: u64,
    }

    public fun create_king<T: drop>(witness: T, ctx: &mut TxContext) {
        assert!(types::is_one_time_witness(&witness), ENOTWITNESS);
        transfer::transfer(King {
            id: object::new(ctx),
            ability: 66,
        }, tx_context::sender(ctx));
    }

    entry fun rise(publisher: &Publisher, king: &mut King) {
        assert!(package::from_package<King>(publisher), ENOTPACKAGE);
        king.ability = king.ability + 1;
    }

    public fun get_ability(king: &King): u64 {
        king.ability
    }
}

勇士:

  • 创建函数不加限制,因为所有人都可以拥有属于自己的勇士。
  • 晋升后勇士对象将不复存在,没有为它赋予 d r o p \mathit {drop} drop 能力,所以需要手动解构。
    这里稍微有点特殊,晋升为骑士后的属性是一致的,所以需要将 n a m e ,   a b i l i t y \mathit {name},\ \mathit {ability} name, ability 用类似于 p y t h o n \mathit {python} python 当中的元组的形式作为返回值。
module king_knight::warrior {
    use sui::object::{Self, UID};
    use sui::tx_context::{Self, TxContext};
    use sui::transfer;
    use std::string::String;

    struct Warrior has key {
        id: UID,
        name: String,
        ability: u64,
    }

    entry fun create_warrior(name: String, ability: u64, ctx: &mut TxContext) {
        transfer::transfer(Warrior {
            id: object::new(ctx),
            name,
            ability,
        }, tx_context::sender(ctx));
    }

    entry fun rise(warrior: &mut Warrior) {
        warrior.ability = warrior.ability + 1;
    }

    public fun get_ability(warrior: &Warrior): u64 {
        warrior.ability
    }

    public fun destroy(warrior: Warrior): (String, u64) {
        let Warrior{id, name, ability} = warrior;
        object::delete(id);
        (name, ability)
    }
}

骑士:

  • i n i t \mathit {init} init 当中用 o t w \mathit {otw} otw 生成 p u b l i s h e r \mathit {publisher} publisher,再借助其创建 d i s p l a y \mathit {display} display,其中的 k e y s \mathit {keys} keys v a l u e s \mathit {values} values 想要更改的话可以用 a d d _ m u l t i p l e ,   e d i t ,   r e m o v e \mathit {add\_multiple},\ \mathit {edit},\ \mathit {remove} add_multiple, edit, removesui::display当中的函数进行操作,具体请点击这里,当然,最后别忘记调用 u p d a t e _ v e r s i o n \mathit {update\_version} update_version 来触发事件,使网络中各个完整节点监听此事件并获取该类型的新显示模板。
  • 晋升为骑士后无法再对其进行更改,所以使用 f r e e z e _ o b j e c t \mathit {freeze\_object} freeze_object 将其所有权变更为不可变共享。
module king_knight::knight {
    use sui::object::{Self, UID};
    use sui::tx_context::{Self, TxContext};
    use sui::transfer;
    use std::string::{Self, String};
    use sui::package;
    use sui::display;

    struct KNIGHT has drop {}

    struct Knight has key {
        id: UID,
        name: String,
        ability: u64,
    }

    fun init(otw: KNIGHT, ctx: &mut TxContext) {
        let keys = vector[
            string::utf8(b"name is"),
            string::utf8(b"ability is"),
        ];

        let values = vector[
            string::utf8(b"{name}"),
            string::utf8(b"{ability}"),
        ];

        let publisher = package::claim(otw, ctx);

        let display = display::new_with_fields<Knight>(&publisher, keys, values, ctx);
        display::update_version(&mut display);

        transfer::public_transfer(publisher, tx_context::sender(ctx));
        transfer::public_transfer(display, tx_context::sender(ctx));
    }

    public fun create_knight(name: String, ability: u64, ctx: &mut TxContext) {
        let knight = Knight {
            id: object::new(ctx),
            name,
            ability,
        };
        transfer::freeze_object(knight);
    }
}

交互:

  • 通过 o t w \mathit {otw} otw 创建另一个模块当中的国王。
  • 晋升时判断能力值,如果满足条件,则将勇士解构后得到的信息用于创建骑士。
module king_knight::interact {
    use sui::tx_context::TxContext;

    use king_knight::warrior::{Self, Warrior};
    use king_knight::king::{Self, King};
    use king_knight::knight::create_knight;

    const ENOTENOUGHABILITY: u64 = 0;

    struct INTERACT has drop {}

    fun init(otw: INTERACT, ctx: &mut TxContext) {
        king::create_king(otw, ctx);
    }

    entry fun rise(warrior: Warrior, king: &King, ctx: &mut TxContext) {
        assert!(warrior::get_ability(&warrior) >= king::get_ability(king), ENOTENOUGHABILITY);
        let (name, ability) = warrior::destroy(warrior);
        create_knight(name, ability, ctx);
    }
}

四:发布并调用

sui client publish --gas-budget 100000000

根据发布成功的信息, e x p o r t \mathit {export} export 几个值,方便后续调用。

export PACKAGE_ID=0x4f81d54db52fee57cae7f6d22e0746f729c59b3ad4a8f513857cbd57417d18e8
export PUBLISHER=0x44c23e62e75a1c765dc10e48212cf5f35d658d84813e1caf19feefd619437c62
export KING=0xaf25300b110c4a41e52dbdfa998a36de88b3c2cdf25757814ac2e7aa5571d3c3

其中,可以通过sui client object $KING来查看国王的能力值是否为默认设定的 66 \text {66} 66

我们用sui client call --package $PACKAGE_ID --module warrior --function create_warrior --args Nigdle 65 --gas-budget 100000000来创建一个 w a r r i o r \mathit {warrior} warrior,姓名为 N i g d l e \mathit {Nigdle} Nigdle,初始能力值设定为 65 \text {65} 65
别忘记把它的对象地址也 e x p o r t \mathit {export} export 一下export WARRIOR=0xb6c63d92e7042cc4ede8201ac515ce54aac62808b1e5a237830f093d8386481c
此时如果尝试晋升为骑士,会得到如下报错:

Error executing transaction: Failure {
    error: "MoveAbort(MoveLocation { module: ModuleId { address: 4f81d54db52fee57cae7f6d22e0746f729c59b3ad4a8f513857cbd57417d18e8, name: Identifier(\"interact\") }, function: 1, instruction: 10, function_name: Some(\"rise\") }, 0) in command 0",
}

这是因为能力值不足,通过sui client call --package $PACKAGE_ID --module warrior --function rise --args $WARRIOR --gas-budget 100000000提升至 66 \text {66} 66,至于是否提升成功,可以通过sui client object $WARRIOR来查看。

这个时候再通过sui client call --package $PACKAGE_ID --module interact --function rise --args $WARRIOR $KING --gas-budget 100000000尝试晋升为骑士,就成功了。

原来的 w a r r i o r \mathit {warrior} warrior 已经不复存在,sui client object $WARRIOR也将报错说这个对象已经被删除。
复制下来新创建的骑士的对象地址,用类似的命令去查看里面存储的内容,发现它的名字是 N i g d l e \mathit {Nigdle} Nigdle,能力值是 66 \text {66} 66。这是一个共享的不可变对象,谁都可见,同时谁都无法进行更改。
如果你有条件,可以通过 s u i _ g e t O b j e c t \mathit {sui\_getObject} sui_getObject 进行实验并查看 K n i g h t \mathit {Knight} Knight w e b \mathit {web} web 上的显示是否如同预期的那样。

接下去,国王在神明 ( p u b l i s h e r ) \mathit (publisher) (publisher)授意下提升自身能力:
sui client call --package $PACKAGE_ID --module king --function rise --args $PUBLISHER $KING --gas-budget 100000000
这个时候,如果创建一个能力值为 66 \text {66} 66 w a r r i o r \mathit {warrior} warrior 就无法再晋升,而那一位名为 N i g d l e \mathit {Nigdle} Nigdle 的骑士由于占尽先机,就与其形成了鲜明的对比,但 N i g d l e \mathit {Nigdle} Nigdle 的上限也到此为止了。

注意: 本篇内容仅针对初学者,目的是将这三者尽可能串联起来,形成一个不那么枯燥又容易理解的例子,所以设计过程及最终呈现可能存在优化空间。

五:加入组织,共同进步!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值