Sui.

帖子

分享您的知识。

article banner.
harry phan.
Apr 24, 2025
文章

灵魂绑定逻辑和退货收据模式

#灵魂绑定逻辑和退货收据模式

现在是使用父子机制的特殊设计模式:灵魂绑定物体. 受灵魂束缚的物品旨在与特定所有者绑定,不能永久转让. 但是,你可能想临时在单笔交易中授予某人访问它的权限——例如,对它执行一些操作——但要确保在该交易结束之前它返回给你. 这是通过我们所谓的ReturnReceipt 模式来实现的,这是 “传球” 模式的一种变体.

###Sui 中的传球模式

“hot potato” 图案以儿童游戏的名字命名——如果你收到传球游戏 🌡️🥔,你就无法抓住它;你必须快速传递它. 用 Move 术语来说,传球通常是指在同一个事务中必须消耗或传递的对象或资源,否则交易将失败. 这通常是通过赋予物体不掉落能力(因此你不能忽略它或丢弃它)和设计逻辑,使摆脱它的唯一方法是执行所需的操作(例如退还贷款)来实现的.

一个常见的用例是闪贷:你在交易中借入一些硬币(获得贷款对象,再加上 “债务” 代币). 如果你在交易结束时没有还款,你就无法完成交易,因为你仍然持有你无权放弃的债务代币,这实际上是迫使你退还贷款或中止贷款.

###灵魂绑定对象示例

假设我们有一个 SoulBound 对象,我们一直希望保留其原始所有者的地址. 我们可以强制规定,如果有人 “借用” 它(作为某个父对象下的子对象),他们必须在同一个交易中将其返回. 怎么样?使用退货收据.

以下是受 Sui 的灵魂束缚对象文档示例启发的简化版本

module demo::soul_bound {
    use sui::transfer::{Self, Receiving};
    use sui::object::UID;
    use sui::transfer;

    const EWrongObject: u64 = 0;

    /// A soul-bound object that cannot be permanently transferred.
    /// It has only `key` ability (no `store`), to tighten transfer/receive rules [oai_citation_attribution:40‡docs.sui.io](https://docs.sui.io/concepts/transfers/transfer-to-object#:~:text=%2F%2F%2F%20This%20object%20has%20,id%3A%20UID%2C).
    public struct SoulBound has key {
        id: UID,
        data: u64
    }

    /// A receipt that proves a SoulBound object must be returned.
    /// No abilities: cannot be copied, dropped, or stored (implicitly).
    public struct ReturnReceipt {
        /// The object ID of the soul-bound object that must be returned.
        object_id: UID,
        /// The address (or object ID) it must be returned to (the original owner).
        return_to: address
    }

    /// Allows the owner of `parent` to retrieve their SoulBound child.
    /// Returns the SoulBound object *and* a ReturnReceipt that compels return.
    public fun take_soul_bound(parent: &mut UID, sb_ticket: Receiving<SoulBound>): (SoulBound, ReturnReceipt) {
        let sb = transfer::receive(parent, sb_ticket);  // receive SoulBound (only this module can do it, since SoulBound has no store) [oai_citation_attribution:41‡docs.sui.io](https://docs.sui.io/concepts/transfers/transfer-to-object#:~:text=%2F%2F%2F%20,to_address%28%29%2C%20object_id%3A%20object%3A%3Aid%28%26soul_bound%29%2C) 
        let receipt = ReturnReceipt {
            object_id: sb.id,
            return_to: parent.to_address()
        };
        (sb, receipt)
    }

    /// Return a SoulBound object using the ReturnReceipt.
    /// This must be called in the same transaction after take_soul_bound.
    public fun return_soul_bound(sb: SoulBound, receipt: ReturnReceipt) {
        // Verify the receipt matches this SoulBound object
        assert!(sb.id == receipt.object_id, EWrongObject);
        // Send the SoulBound object back to the original owner address
        transfer::transfer(sb, receipt.return_to);
        // (ReturnReceipt is consumed/destroyed here as function param)
    }
}

在这个设计中:

-SoulBound 是一个仅限密钥的对象. 通过不向其提供存储,我们防止在其上使用 public_transfer 或 public_receive,这意味着传输或接收它的唯一方法是通过其定义模块的函数(确保使用我们的自定义逻辑). -take_soul_bound(类似于文档中的 “get_object”)是所有者为了从某个父对象手中夺走灵魂绑定对象而调用的函数. 它调用 transfer:: receive 来获取 SoulBound 对象. 由于SoulBound没有存储,因此只能在其模块中进行此调用(这很好). 然后,它创建一个 ReturnReceipt 结构,其中包含灵魂绑定对象的 ID 和要返回的地址(父地址). 我们同时退还物品和收据. -ReturnReceipt 没有掉落、存储或复制能力(我们没有申报任何能力,因此它被视为不能丢弃或复制的资源). 这意味着交易必须对其进行一些处理;否则,它将是剩余的资源,虚拟机不会让交易成功结束. 本质上,ReturnReceipt 是我们的传球代币 🔥🥔. -与退货相关的唯一有效方法是在同一笔交易中调用 return_soul_bound(或类似的指定函数). 在 return_soul_bound 中,我们验证 SoulBound 对象的 ID 是否与收据的记录相符(以防止有人试图用错误的收据退回不同的物品). 然后我们调用 transfer:: transfer(sb,receipt.return_to),它将 SoulBound 对象发送回收据中的地址(原始所有者). 这有效地将受灵魂束缚的物体恢复到其应有的位置. ReturnReceipt 作为参数使用(因此销毁). -如果用户尝试在不调用 return_soul_bound 的情况下完成交易,他们仍将持有退货收据(来自 take_soul_bound 输出). 由于 ReturnReceipt 没有丢弃,因此 Move 虚拟机将拒绝完成交易(在 Move 中,你不能简单地丢弃资源;它必须在某个地方使用或存储). 该交易将中止或被视为无效,这意味着整个操作将回退. **结果:**你不能随便带着灵魂束缚的物体逃跑;如果你不归还它,tx 就会失败,它会留在家长手中.

从你调用 take_soul_bound 的那一刻到你在交易中调用 return_soul_bound 的那一刻,你确实有 SoulBound 对象值可用——大概是为了对其执行一些允许的操作(可能读取它,或者根据需要使用它). 但是,由于收据强制执行,您必须在交易结束之前将其退回.

这种模式通常被描述为 “灵魂绑定 = 无法离开其所有者”,但更准确地说,它可以在单个事务中作为子对象离开,并保证它会回来. 这就像借阅图书馆的书籍一样,你必须在图书馆当天关闭之前归还 😅.

为什么不根本不转移呢?在某些情况下,临时将对象转移到另一个对象(或上下文)很有用. 一个例子是共享对象上下文:在某些操作过程中,SoulBound 物品可能保存在共享对象下,需要由原始所有者取出. 另一个是允许受控合成——你可能想让某个模块对你的对象进行操作,方法是将其交给该模块的对象,然后将其取回.

通过将 SoulBound 设为仅限密钥,我们确保在没有模块参与的情况下,任何外部 public_receive 都无法提取它. 通过使用收据,我们强制遵守退货政策. Sui 示例代码中的注释甚至指出,ReturnReceipt 可以防止交换——如果在一次交易中取出了两个受灵魂束缚的物品,则每张收据都带有特定的对象 ID,因此你不能将它们混在一起并返回错误的物品来作弊.

**概括一下这个想法:**只要你想强制退还物品,就可以使用 ReturnReceipt 模式. 闪电贷款使用类似的概念(贷款代币和必须偿还的债务代币). 每当你有一个不变的 “对象 X 必须在 tx 结束时回到地址 Y” 时,你就可以创建收据资源来保存它.

###快速回顾:

-灵魂绑定对象:仅限密钥,仅通过其模块的逻辑检索. -returnReceipt:与对象一起返回的虚拟资源,以确保其被返回. 它是非删除的,因此用户必须调用返回函数,否则将失败. -Hot potato:如果您持有 ReturnReceipt,则最好在 “音乐停止”(交易结束)之前对其进行处理(归还对象). -如果未返回对象,则交易将无法成功执行——您的更改会回滚,从而有效地执行灵魂绑定规则.

至此,我们已经介绍了 Sui Move 中亲子关系的主要概念!

##5. 项目结构和测试策略(GitHub 就绪)

为了巩固这些概念,你可能需要设置一个 Sui Move 项目并试一试这些例子. 以下是建议的项目布局,其中包括上述示例的模块. 您可以编译和运行单元测试,也可以使用 Sui CLI 与它们进行交互.

parent_child_demo/
├── Move.toml
├── sources/
│   ├── toy_box.move        (Parent & child in same module example)
│   ├── parcel.move         (Child module for Parcel)
│   ├── warehouse.move      (Parent module for Warehouse, uses public_receive)
│   └── soul_bound.move     (SoulBound and ReturnReceipt module)
└── tests/
    └── parent_child_test.move   (Integration tests for the modules)

**move.toml:**请务必包含 Sui 框架和软件包的所有地址. 例如:

[package]
name = "parent_child_demo"
version = "0.0.1"
dependencies = [ 
    { name = "Sui", git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework", rev = "devnet" }
]

[addresses]
demo = "0x0"

*(如果 Sui 要求,请使用正确的地址而不是 0x0,或者在发布时分配一个命名地址. ) *

在源代码目录中:

-toy_box.move可能包含第 2 部分中的 example:: toy_box 模块,其中包含玩具和盒子的定义以及 take_toy 函数. -parcel.movewarehouse.move将包含第 3 部分中的模块. -soul_bound.move将包含第 4 部分中的模块.

每个模块都应根据需要使用 sui::... 进行传输、对象、tx_context 等,如示例所示. 然后,tests/parent_child_test.move 可以导入这些模块并模拟场景:

module demo::parent_child_test {
    use 0xYourAddr::toy_box;
    use 0xYourAddr::warehouse;
    use 0xYourAddr::parcel;
    use 0xYourAddr::soul_bound;
    use sui::tx_context::TxContext;

    #[test]
    fun test_toy_withdraw() {
        let ctx = TxContext::new(); // pseudo, context is usually provided
        let my_box = toy_box::Box { id: object::new(&mut ctx) };
        let my_toy = toy_box::Toy { id: object::new(&mut ctx), name: b"Ball".to_vec() };
        // Transfer toy into the box
        transfer::transfer(my_toy, my_box.id); 
        // Now simulate receiving it back
        let ticket = transfer::Receiving<toy_box::Toy>{ /*...*/ }; // In real test, use transfer::make_receiver or a transaction call
        let returned_toy = toy_box::take_toy(&mut my_box, ticket);
        assert!(returned_toy.name == b"Ball".to_vec());
    }

    // Additional tests for warehouse/parcel and soul_bound can be written similarly.
}

以上是概念性示例. 实际上,Sui 的测试上下文可能会有所不同——在传输到对象(如果在模块外)时,你可能需要在测试中使用 transfer:: public_transfer,并使用来自交易上下文的接收. Sui 提供了一个 transfer:: make_receiver(仅限测试的函数),它可以在给定 ID 和版本的情况下构造接收,这在单元测试中可用于模拟子对象输入.

**运行测试:**您可以运行 sui move test,它将执行 # [test] 函数. 或者将软件包部署到本地 Sui 网络,并通过 CLI 或 SDK 调用入口函数来观察行为:

-创建包裹,创建仓库,通过 CLI 调用 transfer:: public_transfer 将包裹放入仓库,然后致电撤回包裹. -创建一个 SoulBound 对象(也许通过让一个条目变得有趣来铸造一个对象),将其转移到某个父对象(或共享对象),然后在单个交易中调用 take_soul_bound 并省略 return_soul_bound 以查看交易失败(预期),而不是包括返回调用以查看成功.

该项目的每个部分都涉及一个主题:

-toy_box.move:基本的亲子和接收器. -parcel.move & warehouse.move:跨模块子模块和 public_receive. -soul_bound.move:使用 ReturnReceipt 获得灵魂束缚.

通过尝试这些,你将加深对Sui对象模型的理解. 该代码的结构具有教育意义,在没有安全审查的情况下不应按原样用于生产,但它提供了一个坚实的起点.


**结论:**Sui Move 的父子对象功能非常强大. 它允许您通过细粒度的访问控制在链上创建复杂的数据结构(如库存、钱包、馆藏). transfer:: receive/public_receive 和 Move 的类型系统的组合确保只有经过授权的代码才能检索子对象,而像 ReturnReceipt 这样的模式可以强制执行临时所有权规则(灵魂绑定、闪贷等). 我们添加了一些有趣的类比和表情符号,但归根结底,这些都是建筑商的强大工具. 现在开始建造一些嵌套物体魔法!🚀🔥

当一个对象被转移到另一个对象而不是账户地址时,它们会形成父子关系. 你可以这样想:

-父项= 容器对象 -孩子= 里面的东西

Move 不关心你是要转移到账户还是对象 ID. 它只是 移动.

public struct Parent has key {
    id: UID,
    name: String,
}

public struct Child has key {
    id: UID,
    description: String,
}

##创建父对象和子对象

父级必须是可变的并且必须存在. 你不能把东西塞进一个不可变的盒子里!

  • Sui
  • Architecture
4
分享
评论
.

Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.

610帖子1335答案
Sui.X.Peera.

赚取你的 1000 Sui 份额

获取声誉积分,并因帮助 Sui 社区成长而获得奖励。

奖励活动七月