本片笔记继续学习Move 的相关模式
在 Move 中,entry
修饰符用于标记那些可以用于链下调用的函数,这些函数被称为入口函数。例如:
entry fun new() {
// ...
}
直接调用:带有 entry
修饰符的函数可以在链下(通过前端代码或 sui cli
)直接调用,从而触发特定的操作,不能在模块内调用
参数要求:入口函数的参数必须是交易块的输入,不能是该块中前面交易的结果或修改过的值。这样设计的目的是确保每个入口函数调用都是独立的,避免复杂的依赖关系和潜在的安全风险。假设在一个交易块中有两个交易:交易 A,它调用一个普通函数,计算某个数值。交易 B,它调用入口函数。在这种情况下,交易 B 的入口函数参数不能是交易 A 的结果。也就是说,如果交易 A 修改了某个数值,交易 B 的入口函数不能依赖这个修改后的数值作为其参数。
返回值:入口函数通常不返回值,但它们可以有返回值,这些返回值必须具有 drop 能力,表示返回的值可以被安全地丢弃,不会在系统中存储或保留。这个限制确保了入口函数在修改状态后不会产生未预期的副作用。
可编程交易模块 (Programmable Transaction Block, PTB) 是 Sui 区块链的一项核心技术,旨在提高交易的灵活性和可编程性。PTB 允许用户在一个交易中组合多个操作,使得复杂的交易逻辑可以通过一次提交来实现,类似于批量提交交易。这不仅简化了操作流程,还提高了区块链系统的效率和可扩展性。
PTB 主要解决了区块链交易中的以下几个问题:
我们可以使用 sui client ptb
命令来构建、预览和执行这些交易块:
sui client ptb [OPTIONS]
OPTIONS
参数:
--assign <NAME> <VALUE>
:为变量名称赋值,可以在后续的 PTB 中使用。
--assign A 100
--assign B '[100, 5000]'
--merge-coins <INTO_COIN> <[COIN OBJECTS]>
:将多个 coin
合并到指定的 coin
中,这里对象的 id
需要以 @ 符号为前缀。如果是merge gas
,则需要预留一个gas对象用来支付
--merge-coins @into_coin_obj '[@coin_obj_1, @coin_obj_2]'
--move-call <PACKAGE::MODULE::FUNCTION> <TYPE_ARGS> <FUNCTION_ARGS>
:调用指定的 Move 函数。
--move-call std::option::is_none <u64> none
--assign a none
--move-call std::option::is_none <u64> a
--split-coins <COIN> <[AMOUNT]>
:将 coin 拆分为多个指定金额的 coin。
--split-coins gas '[1000, 5000, 75000]'
--transfer-objects <[OBJECTS]> <TO_ADDRESS>
:将对象转移到指定地址。
--split-coins gas [1000, 5000, 75000]
--assign new_coins
--transfer-objects [new_coins.0, new_coins.1, new_coins.2] @to_address
在 Sui Move 中模块初始化器是一个特殊的函数:init 函数,它在模块首次发布到区块链上时自动执行,用于初始化模块的状态和配置,例如创建初始对象、配置全局参数等。并且这个init函数只会在发布模块时执行一次。
虽然init函数可以用来一次性创建重要的对象,但我们也需要注意,模块中的其他函数可能仍然可以创建相同的对象。因此,init函数是设置模块初始状态的好地方,但不能单独作为安全措施。
One Time Witness (OTW) 是 Sui Move 中的一个重要机制,旨在确保某些操作只能执行一次。它类似于其他编程语言中的单例模式,保证只能创建一个 OTW 类型的实例,并且使用完就销毁,不能再次创建。它的设计初衷是防止重复操作,保证系统的安全性和稳定性。
OTW 解决了这样一个问题:在某些场景下,我们希望某个操作只能执行一次。例如,代币的创建必须是一次性的,否则可能会造成系统中的代币重发,产生非预期行为。通过使用 OTW,我们可以强制这种唯一性约束。
单次见证是一个特殊的数据类型,它有如下特点:
1.命名规则:其结构体名称通常是模块名称的大写,确保与其他类型名称的区分。
2.能力限制:OTW 类型仅具有 drop 能力,这意味着它不能被复制,只能被消耗。
3.字段限制:OTW 通常不包含任何字段,或者仅包含一个布尔字段。
4.唯一性:OTW 的实例在模块的生命周期中只会被创建一次,并且通常在模块的初始化函数中作为参数传递。
在 move
模块中定义一个与模块同名且全大写的结构体 MOVE
作为一次性见证的结构体类型,并赋予其 drop
能力。
module hello::move {
use sui::types;
const ENotOneTimeWitness: u64 = 0;
// One Time Witness Struct
public struct MOVE has drop {}
fun init(otw: MOVE, ctx: &mut TxContext) {
assert!(types::is_one_time_witness(&otw), ENotOneTimeWitness);
}
}
init
函数的第一个参数为 MOVE
类型,该函数在合约发布时由虚拟机调用和实例化一个 MOVE
对象,并不需要我们手动传入,并且由虚拟机保证只有一个 MOVE
对象。如果再次初始化 MOVE
对象,则会提示:Invalid one-time witness construction. One-time witness types cannot be created manually, but are passed as an argument 'init’
利用一次性见证者的特点创建代币
module hello::hiro {
use sui::coin;
public struct HIRO has drop {}
fun init(otw: HIRO, ctx: &mut TxContext) {
let (treasuryCap, metaData) = coin::create_currency(
otw,
9u8,
b"HIRO",
b"HIRO Token",
b"A test coin",
option::none(),
ctx
);
transfer::public_freeze_object(metaData);
transfer::public_transfer(treasuryCap, ctx.sender());
}
}
============================================================================================================
module sui::coin {
public fun create_currency<T: drop>(
witness: T,
decimals: u8,
symbol: vector<u8>,
name: vector<u8>,
description: vector<u8>,
icon_url: Option<Url>,
ctx: &mut TxContext
): (TreasuryCap<T>, CoinMetadata<T>) {
// Make sure there's only one instance of the type T
assert!(sui::types::is_one_time_witness(&witness), EBadWitness);
// ...
}
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!