参考资料:
相关书籍:
变量
变量这一章比较简单, 轻松入门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:代码模块是代码划分访问权限和代码的组织方式
创建格式:
1 | module <address>::<name>{ |
方法访问权限控制
我把这里的方法理解为其他语言中的函数
方法签名 | 调用范围 | 返回值 |
---|---|---|
fun call() | 只能模块内调用 | 可以有 |
public fun call() | 全部合约能调用 | 可以有 |
public entry fun call() | 全部合约和Dapp(RPC)能调用 | 无 |
entry fun call() | 只能Dapp(RPC)调用 | 无 |
public(package) fun call() | 只能当前的包能调用 | 可以有 |
1 | fun a(){ |
init 方法
只能是私有的
会在发布合约的是时候自动调用一次
只有两种形式
fun init (ctx: &mut TxContext){}
fun init (witness: Struct, ctx: &mut TxContext) {}
引用-注释
mut: 可变引用
&:不可变引用
1 | let mut a: u32 = 32;//mut表示 后续会改变它 |
控制流
这里比较简单,与其他语言差不多,结合代码学习很快就能上手
if-条件语句
1 | if (condition) true_branch // implied default: else () |
while-循环语句
1 | fun sum(n: u64): u64 { |
break-跳出循环
1 | fun min_factor(n: u64): u64 { |
continue-跳过当前条件的循环,直接进入下一个应该进入循环
的值
1 | fun sum(n: u64): u64 { |
loop-只有遇到break时才跳出循环
相当于 c语言中的while(1),这里等价于while(true)
1 | fun sum(n: u64): u64 { |
结构体-所有权-对象
struct-结构体
结构体是自定义类型,由字段组成,可以简单地理解成”key-value”存储,其中 key 是字段的名称,而 value 是存储的内容,使用关键字 struct 定义,可以为空。
注:结构体只能在模块内部定义,并且以关键字 public struct 开头,结构体名称首字母需要大写
1 | public struct NAME { |
*UTXO
每个交易产生一个或多个UTXO,每一个UTXO都对应者一个ID,代表未花费的金额,可以简单理解为你的零钱。
sui基于UTXO模型
object-对象
对象在Sui上存储,维护了一个全局的Map数据结构 Map<ID,object>, id 是唯一的,通过这个唯一的id 查找到object。
sui上的资产都是对象,对象可以相互嵌套,所有的对象都是全局存储。
对象的定义
必须有 key 能力
必须第一个字段 是id,而且类型为sui::object::UID
1 | module book::obj { |
所有权
定义资产
资产也就是个人拥有所有权的物品合集
常见资产:银行余额,支付宝微信余额,房产等。
资产所有权
资产所有权可以分为:独有资产和共有资产,拥有所有权,则可以改变、删除、增加资产内容。
在Object中可以用关键字来标记所有权的类型,也就是能力,具体在下一章
分为:
- key
- copy
- drop
- store
所有权在函数之间的三种传递方式
1 | fun f(consume: T, write: &mut T, read: &T) |
所有权的方法
方法 | 生成的方法 | 属性 |
---|---|---|
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,可以被全局存储和查找
注
:
- key 和 dorp 不能同时存在,也就是对资产进行操作后,不会销毁资产
- key 和 copy不能同时存在,也就是资产不可复制
Event
打印日志:copy+drop
常量
不会更改的量即为常量,创建常量const Name : Type = Value
错误处理
abort 配合if语句来终止程序
assert!(num>10,ErrMustGet10) 断言,不满足条件时报错
debug 调试代码
1
2
3
4
5
6
7module std::debug {
//打印数值
native public fun print<T>(x: &T);
//打印当前堆栈
native public fun print_stack_trace();
}
泛型
泛型是具体类型或其他属性的抽象替代品,使得在编写 Move 代码时提供更强的灵活性,并避免逻辑重复。
我理解的泛型,就是在起初定义结构体/方法时不定义其类型,在后续使用时再定义类型。这样一个语句就能被多次使用,从而避免了重复定义类似的结构体。
结构体泛型示例:
1 | module generics::obj_generics { |
方法泛型示例:
1 | module generics::generics { |
泛型的能力限制
通过store、drop、key、copy,对泛型进行约束(区别一下泛型的约束和能力的约束)
1 | public struct Box3<T: store + drop,Y:store> has key, store { |
泛型如何传参
1 | 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 泛型
申明一个类型参数但并不使用它,用于区分或者约束
(这个我还不是很清晰,后续会补充一下)
使用场景:
泛型未被使用
容器能力规则不满足
1 | public struct Box <phanton T> has store{ |
设计模式
这一节主要结合代码学习:https://github.com/404ll/letsmove/tree/main/tutorial/bootcamp/08_design_pattern
我没有将过多的代码放上来,建议自己手搓学,多写注释。
Capability 权限设计模式
public struct AdminCap has key { id: UID }
当你需要对结构体进行一些操作时,必须由传入这个结构体的实例来验证你是不是有这个权限,这个权限一般来说是一个有key能力的object,同时可以适当加上store能力,可以多次使用。
实际上就是实施权限控制,有权限的人才可以调用该操作
具体示例:
1 | module design_pattern::capability { |
witness 见证者设计模式
public struct Name has drop {}
简单理解为 这个结构体(资源)创建出来的实例是为了见证
另一个资源的创建,类似于做标记。
注:此结构体没有字段,只有drop(销毁)能力,实例只能使用一次
示例代码:
1 |
|
one-time-witness 见证者模式
1 | public struct OTW has drop {} |
该结构体是一个特殊的见证者:同一个包下面的同一个结构体,只能创建出来一个实例来做 ‘见证’ ,同一个结构体只能用一次,不然会报错。例如创建一个Coin
,一条链对应一个Coin
注:名称必须与包的名字完全相同,并且全部大写;没有字段,只有drop(销毁)能力;通过fun init (witness: Struct, ctx: &mut TxContext) {}
传入
Transferable Witness 可以转移见证者模式
1 | public struct WITNESS has store, drop {} |
这见证者结构体可以创建后放在一个容器里面,随着容器转移所有权,需要用到的时候在取出来做见证
注: 结构体没有字段,只有drop(销毁)和store(实现存储)和能力,需要一个object的容器来包装,存储在链上。
hot-potato 设计模式
1 | public struct Receipt { price: u64 } |
用这个结构体public struct Receipt { price: u64 }
来检测是否符合交易的条件,防止别人盗走资源。
简单理解就是烫手的山芋,你拿到手里肯定处理不了,所以你只能还回去
注:结构体没有任何能力,提供对外方法来创建结构体和销毁结构体。
hot-potato具体例子-闪电贷
闪电贷的特点
在一个交易里面必须完成借和还
无需抵押
不还款一定会报错
主要用于套利和加杠杆
借款人通过智能合约请求贷款,在同一交易中利用这笔贷款进行各种操作,如投资、交易或其他金融活动,同时借款人必须在同一交易中将贷款金额与利息(gas fee)还清。如果借款人未能按时还款或不按协议还款,整个交易将被取消,贷款金额将被返还给贷款提供方,这时借款人仍然需要支付gas fee
闪电贷的具体研究可以看看这篇文章:
[闪电贷详解]: https://academy.binance.com/zh/articles/what-are-flash-loans-in-defi#how-does-a-flash-loan-work “什么是defi中的闪电贷?”
Sui_framework
建议去官方的库中查看学习
Sui_framework,是Sui-move编程功能的合集,有很多相关的库
- deepbook
- move-stdlib 标准库
- sui-framework
- sui-system
Balance/Coin/Token-定义及特点
- Balance:一个通用的余额可存储处理程序。在Coin 模块中用于允许余额操作,并可用于实现具有Supply 和 Balance 的自定义货币。
- Coin:定义了 Coin类型-表示可互换的令牌和货币的平台范围内的表示。Coin 可以被描述为围绕Balance 类型的安全包装器。
- Token: Token 模块实现了一个可配置的闭环令牌系统。该策略由一组规则定义,必须满足这些规则才能对令牌执行操作。
Token的产生是由Coin抽象出来,限制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
name(名称) - 对象的名称。用户查看对象时显示此名称。
description(描述) - 对象的描述。用户查看对象时显示此描述。
link(链接) - 用于应用程序中的对象链接。
image_url(图片链接) - 对象的图像链接,可以是URL或者图像的二进制数据。
thumbnail_url(缩略图链接) - 用作钱包、浏览器和其他产品中的预览的小图像的URL。
project_url(项目链接) - 与对象或创建者相关联的网站链接。
creator(创建者) - 表示对象创建者的字符串信息。
NFT = Object + Display
Sui Object Display 是一种模板引擎,可以实现对类型的链上管理与链下表示(显示)的模板化。通过它,你可以将对象的数据替换为模板字符串。该标准不限制你可以设置的字段。你可以使用{property}语法访问所有对象属性,然后将它们作为模板字符串的一部分插入其中。
Kiosk
Unit Test(单元测试)
- #[test]-只跑一遍某个函数
- #[test_only]-工具函数,只在测试的时候才被编译
- #[expect_failure(abort_code = test_sui_hello ::my_coin::ENotlmplemented)]-测试指定函数,报出指定的错误,可以理解为用错误条件来检验程序是否正确
使用前文的Print()来调试
Coin协议
Coin有两种所有权:
- 独有所有权:
public_transfer(treasury_cap,sender(ctx))
- 共享所有权:
public_share_object(treasury_cap)
ps: 代码来自官方标准库
生成一个Coin
1 | module examples::my_coin { |
接下来我们一步一步的认识这个函数
使用coin::create_currency
时,创建硬币的智能合约的发布者会收到一个TreasuryCap
对象和Coin元数据
。该TreasuryCap
对象是铸造新硬币或销毁现有硬币所必需的。
同时TreasuryCap
对象是可转让的,因此如果您转让该对象,第三方可以接管您创建的代币的管理TreasuryCap
。但是,在转让该功能后,您将无法再自行铸造和销毁代币。
还有很多其他功能:这里就不一一列举了,建议用文档学习