狗哥区块链与AI精品内容集@NonceGeek

2025年03月28日更新 75 人订阅
原价: ¥ 20 限时优惠
专栏简介 「造」最关键的是什么?| Hackathon 漫游指南(贰) Why Hackathon?| Hackathon 漫游指南(壹) 设计「众人维护」的 BuidlerBoard | BeWater.xyz Movement 白皮书中文版 Rebuidl RSS 生产因素与反生产因素 | 独立黑客创业手册(陆) 组织 | 独立黑客创业手册(伍) 生产 | 独立黑客创业手册(肆) 销售 | 独立黑客创业手册(叄) 最优先的能力 | 独立黑客创业手册(贰) 为什么从独立黑客开始 | 独立黑客创业手册(壹) Aptos Token Object V2 | Move dApp 极速入门(贰拾肆) 可編程交易塊 | Move dApp 極速入門(貳拾叁) Aptos 密鑰輪換 | Move dApp 極速入門(貳拾貳) Aptos 对象模型 | Move dApp 极速入门(贰拾壹) Aptos Moveflow SDK使用指南 | Move dApp 极速入门(贰拾) Sui 上简单 Swap 的实现 | Move dApp 极速入门(拾玖) 用 Elixir 交互 Aptos | Move dApp 极速入门(拾捌) Sui 链上数据查询 | Move dApp 极速入门(拾柒) SUI 合约测试攻略 | Move dApp 极速入门(拾陆) Sui 数据类型详解 | Move dApp 极速入门(拾伍) Airdropper Contract in Aptos | Move dApp 极速入门(拾肆) Sandwich合约源码解析 | Move dApp 极速入门(拾叁) Sui 极速上手 | Move dApp 极速入门(拾贰) scaffold-aptos 脚手架 | Move dApp 极速入门(拾壹) 对 DID Document 的思考 | Move dApp 极速入门(九) DID中地址聚合器的实现 | Move dApp 极速入门(八) 值的存取应用3.0 | Web3.0 dApp 开发(五) 合约数据类型综述 | Move dApp 极速入门(四) 操作资源 | Move dApp 极速入门(三) 第一个 Move dApp | Move dApp 极速入门(二) Hello Move | Move dApp极速入门(一) Staker | Web3.0 dApp 开发(九) Token 自动售卖机 | Web3.0 dApp 开发(七) SVG NFT 全面实践 | Web3.0 dApp 开发(六) 值的存取应用2.0 | Web3.0 dApp 开发(四) 值的存取应用1.0 | Web3.0 dApp开发(三) Scaffold-eth 快速上手 | Web3.0 dApp 开发(二) eth.build 快速上手 | Web3.0 dApp 开发(一) 1 小时理解比特币系统 【NonceGeek Workshop 0x01总结】基于链上数据生成游戏地图 Remix 完全本地化部署 NFT:实体与虚拟载体的主与辅 | 狗哥的元宇宙思辨(一) Web3Camp 内容大全@NonceGeek 用 Python 创建一条 Pow 区块链(上) 区块链与共识机制演变史 基于 Etherscan 实现 Blockchain Syncer 【论文分享】去中心化社会:寻找 Web3 的灵魂(上) 【论文分享】去中心化社会:寻找 Web3 的灵魂(下) Ted Yin | 2021 年的区块链基础设施将是什么? 0. 公链、联盟链与分布式未来(全文) 基于 Infura 与 Web3py 部署调用 Hello 合约全过程 | 以太坊开发极速入门 太上中的基因设计与Binary | 函数式与区块链(一) 理解以太坊合约数据读取过程 | 函数式与区块链(二) Hello, Ink! | 用 Rust 写智能合约(一) Mapping 数据结构 | 用 Rust 写智能合约(二) 用 Rust 程序和 Webase 交互 | Rust 学习笔记(四) 用 Sqlite 存储 WeId | Rust 学习笔记(五) 链上注册WeId与错误处理 | Rust 学习笔记(六) WeId 链上创建与本地存储的完整闭环 | Rust 学习笔记(七) 以太坊上的核心开发者 Austin | 以太坊上的最佳开发实践 1. FISCO BCOS 开发环境节点搭建全攻略 伪代码简述 ECDSA 签名过程 | 联盟链开发 WeIdentity 源码分析 | 狗哥解码 WeIdentity 源码分析 | 狗哥解码 FISCO BCOS 介绍 | 联盟链开发 给Remix升个级 | 联盟链开发 2. 控制台的安装与使用 3. 【实验】补全一个区块链应用 4. 控制台的Web化 5. Web化控制台2.0:打造团队共用区块链学习平台 6. 使用脚手架快速搭建 Java DApp 【视频+文字】分布式思维 Rebuidl RSS (EN)

Sandwich合约源码解析 | Move dApp 极速入门(拾叁)

  • 李大狗
  • 发布于 2023-01-03 13:13
  • 阅读 3731

Make a delicious sandwich on Sui!

Sandwich 是 Sui 官方 Examples 里的案例之一:

https://github.com/MystenLabs/sui/tree/main/sui_programmability/examples

https://github.com/NonceGeek/Web3-dApp-Camp/tree/main/move-dapp/sui/sandwich

Sandwich 是一个很好的「MVP」案例,帮助我们理解 Sui 合约的基本结构。

0x01 合约结构

我们可以抽象地把合约分为三个部分:

// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/// Example of objects that can be combined to create
/// new objects
module basics::sandwich {
    // packages: 包

  // structs: 结构体

  // consts: 常量

  // functions: 函数
}

0x02 Sandwich 的结构体

包含HamBreadSandwichGroceryGroceryOwnerCapability 这几个组成部分:

struct Ham has key {
    id: UID
}

struct Bread has key {
    id: UID
}

struct Sandwich has key {
    id: UID,
}

// This Capability allows the owner to withdraw profits
struct GroceryOwnerCapability has key {
    id: UID
}

// Grocery is created on module init
struct Grocery has key {
    id: UID,
    profits: Balance<SUI>
}

关于 Sui 中的基础类型,可见:

https://docs.sui.io/build/programming-with-objects/ch1-object-basics

https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/sources/object.move

2.1 UID

https://github.com/MystenLabs/sui/blob/aa5fe5bf68b20cc2def0392cbab71f8bcdad0060/crates/sui-framework/sources/object.move#L35

  • 定义存储中对象 ID 的全局唯一 ID。 对任何具备 Key 能力的 Struct 也即 Object 而言,必须将"id: UID"作为其第一个字段。
  • 不会有两个 UID 类型的值相等,也即它们全局唯一。换句话说,对于任意两个值 id1: UIDid2: UIDid1 != id2
  • 这是一个 privileged 类型,只能从 TxContext 派生。
  • UID 没有 drop 能力,所以删除 UID 需要调用 delete

0x03 函数

3.1 初始化函数

在模块初始化的时候会调取的函数,在本例中,init 函数会创建一个grocery

/// On module init, create a grocery
fun init(ctx: &mut TxContext) {
    transfer::share_object(Grocery {
        id: object::new(ctx),
        profits: balance::zero<SUI>()
    });

    transfer::transfer(GroceryOwnerCapability {
        id: object::new(ctx)
    }, tx_context::sender(ctx));
}

3.1.1 transfer::share_object 函数

https://docs.sui.io/devnet/build/move/sui-move-library#shared

要使 obj共享,可以调用:

transfer::share_object(obj);

在这个调用之后,obj保持可变,但被所有人共享,即任何人都可以发送交易来改变这个对象。 但是,这样的对象不能作为字段传输或嵌入到另一个obj中。 有关详细信息,请参阅共享对象文档:

https://docs.sui.io/devnet/build/move/sui-move-library#:~:text=details%2C%20see%20the-,shared%20objects,-documentation.

3.1.2 transfer::transfer 函数

https://docs.sui.io/devnet/build/move/sui-move-library#owned-by-an-address

Transfer 模块提供了操作对象所有权所需的 API。

最常见的情况是将obj传输到地址。 例如,当一个新obj被创建时,它通常被转移到一个地址,这样该地址就拥有该obj。 要将对象 obj传输到地址的方法如下:

use sui::transfer;

transfer::transfer(obj, recipient);

此调用将完全消耗该obj,使其在当前交易中不再可访问。 一旦一个地址拥有一个obj,对于这个obj的任何未来使用(读取或写入),交易的签名者必须是该obj的所有者。

3.1.3 object::new 函数

https://docs.sui.io/build/programming-with-objects/ch1-object-basics#create-sui-object

既然我们已经学会了如何定义一个 Sui 对象类型,那么我们如何创建/实例化一个 Sui 对象呢? 为了根据其类型创建一个新的 Sui 对象,我们必须为每个字段分配一个初始值,包括 id。 为 Sui 对象创建新 UID 的唯一方法是调用 object::new。 新函数将当前交易内容作为生成唯一 ID 的参数。 交易内容是 &mut TxContext 类型,应该从入口函数(可以直接从事务中调用的函数)向下传递。以ColorObject 为例定义构造函数:

// object creates an alias to the object module, which allows us call
// functions in the module, such as the `new` function, without fully
// qualifying, e.g. `sui::object::new`.
use sui::object;
// tx_context::TxContext creates an alias to the the TxContext struct in tx_context module.
use sui::tx_context::TxContext;

fun new(red: u8, green: u8, blue: u8, ctx: &mut TxContext): ColorObject {
    ColorObject {
        id: object::new(ctx),
        red,
        green,
        blue,
    }
}

3.1.4 init 函数做了什么?

回顾init函数,我们就可以给其加上注释了:

/// On module init, create a grocery
fun init(ctx: &mut TxContext) {
        /// 将 Grocery 对象设置为共享状态
    transfer::share_object(Grocery {
        id: object::new(ctx),
        profits: balance::zero<SUI>()
    });
        /// transfer 一个 GroceryOwnerCapability 对象给交易发送人
    transfer::transfer(GroceryOwnerCapability {
        id: object::new(ctx)
    }, tx_context::sender(ctx));
}

3.2 交易函数

和 aptos 一样,public entry fun的函数即是可以外部访问的。

/// Exchange `c` for some ham
public entry fun buy_ham(
    grocery: &mut Grocery,
    c: Coin<SUI>,
    ctx: &mut TxContext
) {
    let b = coin::into_balance(c);
    assert!(balance::value(&b) >= HAM_PRICE, EInsufficientFunds);
    balance::join(&mut grocery.profits, b);
    transfer::transfer(Ham { id: object::new(ctx) }, tx_context::sender(ctx))
}

/// Exchange `c` for some bread
public entry fun buy_bread(
    grocery: &mut Grocery,
    c: Coin<SUI>,
    ctx: &mut TxContext
) {
    let b = coin::into_balance(c);
    assert!(balance::value(&b) >= BREAD_PRICE, EInsufficientFunds);
    balance::join(&mut grocery.profits, b);
    transfer::transfer(Bread { id: object::new(ctx) }, tx_context::sender(ctx))
}

3.2.1 coin::into_balance函数

https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/sources/coin.move#L122

销毁一枚 Coin 的 wrapper (unwrapper) 并返回其余额。

3.2.2 balance::join 函数

https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/sources/balance.move#L71

将两个余额合并到一起。

3.2.3 buy_ham 函数做了什么?

加上注释:

/// Exchange `c` for some ham
public entry fun buy_ham(
    grocery: &mut Grocery,
    c: Coin<SUI>,
    ctx: &mut TxContext
) {
    let b = coin::into_balance(c); /// 销毁 Coin 并获得其余额
    assert!(balance::value(&b) >= HAM_PRICE, EInsufficientFunds); // 判断是否支付了正确的金额
    balance::join(&mut grocery.profits, b); // 合并现有余额和 b 
    transfer::transfer(Ham { id: object::new(ctx) }, tx_context::sender(ctx)) // 转让 Ham
}

3.3 组合函数

销毁 Ham 和 Bread 两个obj,然后转让一个 Sandwich obj 给交易发送人。

/// Combine the `ham` and `bread` into a delicious sandwich
public entry fun make_sandwich(
    ham: Ham, bread: Bread, ctx: &mut TxContext
) {
    let Ham { id: ham_id } = ham;
    let Bread { id: bread_id } = bread;
    object::delete(ham_id);
    object::delete(bread_id);
    transfer::transfer(Sandwich { id: object::new(ctx) }, tx_context::sender(ctx))
}

3.4 Profits 相关函数

关于 Funcs 类型详细的描述可见:

https://docs.sui.io/build/move#move-functions

https://mp.weixin.qq.com/s/OXLyiUKzpFzAzc-PVxLvTA

/// See the profits of a grocery
public fun profits(grocery: &Grocery): u64 {
    balance::value(&grocery.profits)
}

/// Owner of the grocery can collect profits by passing his capability
public entry fun collect_profits(_cap: &GroceryOwnerCapability, grocery: &mut Grocery, ctx: &mut TxContext) {
    let amount = balance::value(&grocery.profits);

    assert!(amount > 0, ENoProfits);

    // Take a transferable `Coin` from a `Balance`
    let coin = coin::take(&mut grocery.profits, amount, ctx);

    transfer::transfer(coin, tx_context::sender(ctx));
}

3.4.1 coin::take 函数

https://github.com/MystenLabs/sui/blob/b8ace6ff5e3045f6e3fdd9a7ff076dfd2c236a61/crates/sui-framework/sources/coin.move#L131

Balance 中取出价值 valueCoin,如果 value > balance.value 则中止。

0x4 Sandwich 合约部署与调用实践

CLI 的安装以及基础指令参考:

https://mp.weixin.qq.com/s/jrz3p9x495HpAvQEYRNiZw

4.1 部署

$ sui move build
$ sui client publish ./  --gas 0x82db13db77f034873cf3f1f2e43fc1237e08664e --gas-budget 30000 --verify-dependencies

image-20221231230711910

4.1.1 在浏览器中查看部署信息

https://explorer.sui.io/transaction/2U84nEgz9QM94zH5mZ79Az1EVypF2WyzdWsCM14qAJZH

image-20230101131351144

image-20230101131423901

💡 Tips:sui client 地址相关用例。

$ sui client new-address secp256k1 | ed25519 # 新建地址
$ sui client addresses # 查看所有地址
$ sui client switch --address [addr] # 切换地址

4.2 调用

💡 Tips:sui client 转账相关用例。

$ sui client gas
$ sui client transfer-sui --to 0x181bd292dbe70628479b85e873460caa3e180fe2 --sui-coin-object-id 0x82db13db77f034873cf3f1f2e43fc1237e08664e --gas-budget 30000

4.2.1 查看所有的 gas

$ sui client gas

image-20230101123029884

4.2.2 构建调用方法

$ sui client call --function buy_bread --module sandwich --package 0x08204ed92afcfdf9d0f6727a2c7d40db93a059d8 --args 0xca3f4ad2b0dea3264f38878db34cecb75a6336e0 0x520e1cecf280effc4f2129d5109f11be0c000d35 --gas [one_gas_object] --gas-budget 30000

对应函数源码:

public entry fun buy_bread(
    grocery: &mut Grocery,
    c: Coin<SUI>,
    ctx: &mut TxContext
) {
    ……
}

Grocery 是合约的obj,因此我们填入 4.1.1 中查看到的 obj_id 到参数1,再填入一个 4.2.1 中所查看的 gas obj 到参数 2。

image-20230101132714362

我们可以在浏览器中查看到调用记录:

https://explorer.sui.io/transaction/5sK6AecDrwhjCpYGw7gEmkYMMihq5QCMH9SXXkjRtpmZ

image-20230101132959512

点击 Created,我们可以看到成功创建了一片bread

image-20230101133044781

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

0 条评论

请先 登录 后评论