sui-move学习笔记
参考资料:
相关书籍:
变量
变量这一章比较简单, 轻松入门sui: https://reference.sui-book.com/packages.html 中写得也非常详细!
let _a = 10u32;//下划线开头,表示后续没有使用
包-模块-方法
包:包是同一个合约地址包含的全部代码的集合,由很多模块组成,也就是sui move new <name>
生成的文件夹
a_move_package
├── Move.toml (必需)
├── Move.lock (生成的)
├── sources (必需)
├── doc_templates (可选)
├── examples (可选,测试和开发模式)
└── tests (可选,测试模式)
有关包下具体的配置信息,可以参见
模块-module:代码模块是代码划分访问权限和代码的组织方式
创建格式:
|
方法访问权限控制
我把这里的方法理解为其他语言中的函数
方法签名 | 调用范围 | 返回值 |
---|---|---|
fun call() | 只能模块内调用 | 可以有 |
public fun call() | 全部合约能调用 | 可以有 |
public entry fun call() | 全部合约和Dapp(RPC)能调用 | 无 |
entry fun call() | 只能Dapp(RPC)调用 | 无 |
public(package) fun call() | 只能当前的包能调用 | 可以有 |
|
init 方法
只能是私有的
会在发布合约的是时候自动调用一次
只有两种形式
fun init (ctx: &mut TxContext){}
fun init (witness: Struct, ctx: &mut TxContext) {}
引用-注释
mut: 可变引用
&:不可变引用
|
控制流
这里比较简单,与其他语言差不多,结合代码学习很快就能上手
if-条件语句
|
while-循环语句
|
break-跳出循环
|
continue-跳过当前条件的循环,直接进入下一个应该进入循环
的值
|
loop-只有遇到break时才跳出循环
相当于 c语言中的while(1),这里等价于while(true)
|
结构体-所有权-对象
struct-结构体
结构体是自定义类型,由字段组成,可以简单地理解成”key-value”存储,其中 key 是字段的名称,而 value 是存储的内容,使用关键字 struct 定义,可以为空。
注:结构体只能在模块内部定义,并且以关键字 public struct 开头,结构体名称首字母需要大写
|
*UTXO
每个交易产生一个或多个UTXO,每一个UTXO都对应者一个ID,代表未花费的金额,可以简单理解为你的零钱。
sui基于UTXO模型
object-对象
对象在Sui上存储,维护了一个全局的Map数据结构 Map<ID,object>, id 是唯一的,通过这个唯一的id 查找到object。
sui上的资产都是对象,对象可以相互嵌套,所有的对象都是全局存储。
对象的定义
必须有 key 能力
必须第一个字段 是id,而且类型为sui::object::UID
|
所有权
定义资产
资产也就是个人拥有所有权的物品合集
常见资产:银行余额,支付宝微信余额,房产等。
资产所有权
资产所有权可以分为:独有资产和共有资产,拥有所有权,则可以改变、删除、增加资产内容。
在Object中可以用关键字来标记所有权的类型,也就是能力,具体在下一章
分为:
- key
- copy
- drop
- store
所有权在函数之间的三种传递方式
|
所有权的方法
方法 | 生成的方法 | 属性 |
---|---|---|
transfer | 独享对象 | key |
public_transfer | 独享对象 | key + store |
freeze_object | 共享对象 - 常量 | key |
public_freeze_object | 共享对象 - 常量 | key + store |
share_object | 共享对象 | key |
public_share_object | 共享对象 | key + store |
能力-Event-常量错误处理
能力
四种能力可以相互组合
- key - 被值修饰的键可以对全局进行访问。
- copy - 被修改的值可以被复制。
- drop - 被成员函数在作用域结束时被丢弃。
- store被修改的建议 存储在紧急情况下
没有任何能力:只能存活在同一个交易中
只有key:对象,自定义转移规则,对象有全局ID,可以被全局存储和查找(实现灵魂绑定)
只有drop:被修饰的值在离开作用域的时候会被自动解构 (删除),基本数据类型默认实现了drop
只有store:没法使用所有权,可以通过放在其他结构体中来实现所有权的使用,实现结构体的嵌套
key + store:对象,可以被任意转移,不被转移规则限定,对象有全局ID,可以被全局存储和查找
注 : 打印日志:copy+drop 不会更改的量即为常量,创建常量 abort 配合if语句来终止程序 assert!(num>10,ErrMustGet10) 断言,不满足条件时报错 debug 调试代码 泛型是具体类型或其他属性的抽象替代品,使得在编写 Move 代码时提供更强的灵活性,并避免逻辑重复。 我理解的泛型,就是在起初定义结构体/方法时不定义其类型,在后续使用时再定义类型。这样一个语句就能被多次使用,从而避免了重复定义类似的结构体。 结构体泛型示例: 方法泛型示例: 通过store、drop、key、copy,对泛型进行约束(区别一下泛型的约束和能力的约束) 申明一个类型参数但并不使用它,用于区分或者约束 (这个我还不是很清晰,后续会补充一下) 使用场景: 泛型未被使用 容器能力规则不满足 这一节主要结合代码学习:https://github.com/404ll/letsmove/tree/main/tutorial/bootcamp/08_design_pattern 我没有将过多的代码放上来,建议自己手搓学,多写注释。 当你需要对结构体进行一些操作时,必须由传入这个结构体的实例来验证你是不是有这个权限,这个权限一般来说是一个有key能力的object,同时可以适当加上store能力,可以多次使用。 实际上就是实施权限控制,有权限的人才可以调用该操作 具体示例: 简单理解为 这个结构体(资源)创建出来的实例是为了 注:此结构体没有字段,只有drop(销毁)能力,实例只能使用一次 示例代码: 该结构体是一个特殊的见证者:同一个包下面的同一个结构体,只能创建出来一个实例来做 ‘见证’ ,同一个结构体只能用一次,不然会报错。例如创建一个 注:名称必须与包的名字完全相同,并且全部大写;没有字段,只有drop(销毁)能力;通过 这见证者结构体可以创建后放在一个容器里面,随着容器转移所有权,需要用到的时候在取出来做见证 注: 结构体没有字段,只有drop(销毁)和store(实现存储)和能力,需要一个object的容器来包装,存储在链上。 用这个结构体 简单理解就是烫手的山芋,你拿到手里肯定处理不了,所以你只能还回去 注:结构体没有任何能力,提供对外方法来创建结构体和销毁结构体。 闪电贷的特点 在一个交易里面必须完成借和还 无需抵押 不还款一定会报错 主要用于套利和加杠杆 借款人通过智能合约请求贷款,在同一交易中利用这笔贷款进行各种操作,如投资、交易或其他金融活动,同时借款人必须在同一交易中将贷款金额与利息(gas fee)还清。如果借款人未能按时还款或不按协议还款,整个交易将被取消,贷款金额将被返还给贷款提供方,这时借款人仍然需要支付 闪电贷的具体研究可以看看这篇文章: [闪电贷详解]: https://academy.binance.com/zh/articles/what-are-flash-loans-in-defi#how-does-a-flash-loan-work “什么是defi中的闪电贷?” 建议去官方的库中查看学习 Sui_framework,是Sui-move编程功能的合集,有很多相关的库 Token的产生是由Coin抽象出来,限制 name(名称) - 对象的名称。用户查看对象时显示此名称。 description(描述) - 对象的描述。用户查看对象时显示此描述。 link(链接) - 用于应用程序中的对象链接。 image_url(图片链接) - 对象的图像链接,可以是URL或者图像的二进制数据。 thumbnail_url(缩略图链接) - 用作钱包、浏览器和其他产品中的预览的小图像的URL。 project_url(项目链接) - 与对象或创建者相关联的网站链接。 creator(创建者) - 表示对象创建者的字符串信息。 NFT = Object + Display Sui Object Display 是一种模板引擎,可以实现对类型的链上管理与链下表示(显示)的模板化。通过它,你可以将对象的数据替换为模板字符串。该标准不限制你可以设置的字段。你可以使用{property}语法访问所有对象属性,然后将它们作为模板字符串的一部分插入其中。 使用前文的Print()来调试 Coin有两种所有权: ps: 代码来自官方标准库 生成一个Coin 接下来我们一步一步的认识这个函数 使用 同时 还有很多其他功能:这里就不一一列举了,建议用文档学习
Event
常量
const Name : Type = Value
错误处理
module std::debug {
//打印数值
native public fun print<T>(x: &T);
//打印当前堆栈
native public fun print_stack_trace();
}泛型
module generics::obj_generics {
public struct Box<T> {
value: T
}
//定义多个泛型
public struct Box<T,Y,X> {
value1: T,
value2: Y,
}
}
//使用泛型
fun init(ctx:&mut TxContext){
let box = Box<u8,u16>{
value1:23,
value2:45,
};
}module generics::generics {
public struct Box<T> {
value: T
}
public fun create_box<T>(value: T): Box<T> {
Box<T> { value }
}
//伪代码
let box:Box<u32> = create_box<u32>{value:1u32}
let box:Box<u32> = create_box<_>{value:1u32}
}泛型的能力限制
public struct Box3<T: store + drop,Y:store> has key, store {
id:UID,
value: T,
value2:X,
}泛型如何传参
sui client call --package $PACKAGE --module $MODULE --function "create_box" --args $OBJECT_ID --type-args "0x2::coin::Coin<0x2::sui::SUI>" --gas-budget 100000000
phanton 泛型
public struct Box <phanton T> has store{
value: u64
}设计模式
Capability 权限设计模式
public struct AdminCap has key { id: UID }
module design_pattern::capability {
use std::string::{Self,String};
use sui::transfer
use sui::object::{Self, UID};
use sui::tx_context::{Self,TxContext};
//生成管理员权限结构体
public struct AdminCap has key { id: UID }
//类似于NFT的类型
public struct Item has key, store { id: UID, name: String }
//创建一个管理员权限,并传递给发行者
fun init(ctx: &mut TxContext) {
let my_address = ctx.sender();
let addmin_cap = AdminCap {
id: object::new(ctx)
};
//将权限转移transfer::transfer(addmin_cap, my_address);
let addmin_cap2 = AdminCap {
id: object::new(ctx)
}; transfer::transfer(addmin_cap2, @0x1111);
}
//运行示例
public fun create_and_send(
//检验
_: &AdminCap, name: vector<u8>, to: address, ctx: &mut TxContext
) {
transfer::transfer(Item {
id: object::new(ctx),
name: name.to_string()
}, to)
}
}witness 见证者设计模式
public struct Name has drop {}
见证
另一个资源的创建,类似于做标记。
module design::guardian {
public struct Guardian<phantom T: drop> has key, store {
id: UID
}
public fun create_guardian<T: drop>(
//做标记,见证Guardian资源的创建
_witness: T, ctx: &mut TxContext
): Guardian<T> {
Guardian { id: object::new(ctx) }
}
//结束后删除
}
module design::peace_guardian {
use design::guardian;
public struct PEACE has drop {}
fun init(ctx: &mut TxContext) {
//生成对应的示例
let peace = PEACE{};
transfer::public_transfer(
guardian::create_guardian(peace, ctx),
ctx.sender()
)
}
}one-time-witness 见证者模式
public struct OTW has drop {}
types::is_one_time_witness(&witness)Coin
,一条链对应一个Coin
fun init (witness: Struct, ctx: &mut TxContext) {}
传入Transferable Witness 可以转移见证者模式
public struct WITNESS has store, drop {}
public struct WitnessBox has key { id: UID, witness: WITNESS }hot-potato 设计模式
public struct Receipt { price: u64 }
public fun create(xx:XX,...):(Receipt{},Coin<x>)
public fun burn(rece:Receipt,...)public struct Receipt { price: u64 }
来检测是否符合交易的条件,防止别人盗走资源。hot-potato具体例子-闪电贷
gas fee
Sui_framework
Balance/Coin/Token-定义及特点
Coin的自由转发
Module
Main type
Capability
Abilities
sui::balance
Balance
Supply
store
sui::coin
Coin
TreasuryCapT>
key + store
sui:: token
Token
TreasuryCap
key
display standard -NFT
Kiosk
Unit Test(单元测试)
Coin协议
public_transfer(treasury_cap,sender(ctx))
public_share_object(treasury_cap)
module examples::my_coin {
use sui::coin::{Self, TreasuryCap};
public struct MY_COIN has drop {}
//采用一次见证
fun init(witness: MY_COIN, ctx: &mut TxContext) {
let (treasury, metadata) = coin::create_currency(witness, 6, b"MY_COIN", b"", b"", option::none(), ctx);
//所有权共享 不可变共享
transfer::public_freeze_object(metadata);
//向合约发布者共享所有权
transfer::public_transfer(treasury, ctx.sender())
}coin::create_currency
时,创建硬币的智能合约的发布者会收到一个TreasuryCap
对象和Coin元数据
。该TreasuryCap
对象是铸造新硬币或销毁现有硬币所必需的。TreasuryCap
对象是可转让的,因此如果您转让该对象,第三方可以接管您创建的代币的管理TreasuryCap
。但是,在转让该功能后,您将无法再自行铸造和销毁代币。