本文转自本人微信公众号的文章,所以带有wx水印,希望大家能理解一下。后续新文会自己留存不带水印。
1.1 move Generics
创建合约项目
sui move new generics && cd generics
新建generics.move
module generics::generics {
use sui::transfer;
use sui::object::{Self, UID};
use sui::tx_context::{Self, TxContext};
struct Box<T: store> has key, store {
id: UID,
value: T
}
struct SimpleBox has key, store {
id: UID,
value: u8
}
struct PhantomBox<phantom T: drop> has key {
id: UID,
}
public fun create_box<T: store>(value: T, ctx: &mut TxContext){
transfer::transfer(Box<T> {id: object::new(ctx), value }, tx_context::sender(ctx))
}
public fun create_simple_box(value: u8, ctx: &mut TxContext){
transfer::transfer(SimpleBox {id: object::new(ctx), value }, tx_context::sender(ctx))
}
public fun create_phantom_box<T: drop >(_value: T, ctx: &mut TxContext){
transfer::transfer(PhantomBox<T> {id: object::new(ctx)}, tx_context::sender(ctx))
}
}
部署合约
sui client publish --gas-budget 100000000 --skip-dependency-verification
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0xab619c63c6ad6d3e5cc94875913f85b6957bc77d8706b54a95f4f6fd5d72bd13 │
│ │ Version: 1 │
│ │ Digest: ASCqHLXCydiDt6Rr6siviwwG27shmWsVJKb34BjXG7ts │
│ │ Modules: generics
调用create_box创建box
将之前创建的helloworld存储到box
sui client call --package 0xab619c63c6ad6d3e5cc94875913f85b6957bc77d8706b54a95f4f6fd5d72bd13 --module generics --function create_box --args 0x0496c2f2fccf89ebab659324f4735297a06d0c45a54348babf1443ced89ea796 --type-args "0x2ddc2bd1445a8046487b645cb4626764ea812f62bdaef8253182719a96c81426::hello_world::HelloWorldObject" --gas-budget 1000000
将sui代币存储到box
sui client call --package 0xab619c63c6ad6d3e5cc94875913f85b6957bc77d8706b54a95f4f6fd5d72bd13 --module generics --function create_box --args 0xb4fbb54b7ee3d76b6d04787f48b44113efc4228e7803aa3b0b8bea410b9c43b3 --type-args "0x2::coin::Coin<0x2::sui::SUI>" --gas-budget 10000000
但是如果放入的类型本身不拥有 store 能力,则无法被存储
sui client call --package 0xab619c63c6ad6d3e5cc94875913f85b6957bc77d8706b54a95f4f6fd5d72bd13 --module generics --function create_box --args 0x6b2a315363bb27da5628a2271b161c78eeb364826f68c0b84cc34e750cbcb0e5 --type-args "0x86cac9f3c094c7e0bfed3aae880a4400130626d4aacc2ada137554b6b7ae50c3::transcript::Folder" --gas-budget 10000000
查看之前的代码,Folder只拥有key的能力
struct Folder has key {
id: UID,
transcript: WrappableTranscript,
intended_address: address
}
1.2 Generics解封装
解封装拿出sui,新增transfer_value函数
public fun transfer_value<T: key + store>(box: Box<T>, ctx: &mut TxContext) {
let Box{
id,
value
}= box;
transfer::public_transfer<T>(value, tx_context::sender(ctx));
object::delete(id)
}
然后需要进行合约upgrade,需要先获取第一次发布合约时获取的UpgradeCap
0xa4238a21ba7c9832d4e3b3b4a5a814fbb234a60309b5cc2259b501a81f6235a4
然后修改move.toml文件,新增publish-at参数填入已发布合约的packageid
[package]
name = "generics"
version = "0.0.1"
published-at = "0xab619c63c6ad6d3e5cc94875913f85b6957bc77d8706b54a95f4f6fd5d72bd13"
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" }
[addresses]
generics = "0x0"
然后执行upgrade命令,这里面填入的就是UpgradeCap objectid
sui client upgrade --upgrade-capability 0x560bf0c92d904b64dc6988c6267b7c869b31e411684e8657ae065b3707d643ad --gas-budget 100000000 --skip-dependency-verification
然后更新合约之后执行解封装
sui client call --package 0x3a00c46eb80db77c807af34b37e8a10c8df5285d6d66b8741a755c73886c566e --module generics --function transfer_value --args 0xfe8f1d8d3ef7b2da414084a331c8f50ab138cd2483dcd2f82500cb1c382a0b39 --type-args "0x2::coin::Coin<0x2::sui::SUI>" --gas-budget 10000000
然后查看钱包地址,sui已经转移回来了
1.3 Witness设计模式
witness合约创建
sui move new witness && cd witnes
新建witness.move
module witness::peace {
use sui::object::{Self, UID};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
/// Phantom parameter T can only be initialized in the `create_guardian`
/// function. But the types passed here must have `drop`.
struct Guardian<phantom T: drop> has key, store {
id: UID
}
/// This type is the witness resource and is intended to be used only once.
struct PEACE has drop {}
/// The first argument of this function is an actual instance of the
/// type T with `drop` ability. It is dropped as soon as received.
public fun create_guardian<T: drop>(
_witness: T, ctx: &mut TxContext
): Guardian<T> {
Guardian { id: object::new(ctx) }
}
/// Module initializer is the best way to ensure that the
/// code is called only once. With `Witness` pattern it is
/// often the best practice.
fun init(witness: PEACE, ctx: &mut TxContext) {
transfer::transfer(
create_guardian(witness, ctx),
tx_context::sender(ctx)
)
}
}
合约部署后init调用create_guardian创建一个Guardian<T>资源实例,即Guardian<PEACE>,_witness 参数:这是一个类型为 T 的参数,传入函数时立即被放弃,这符合传入类型必须具备的 drop 能力。
│ ┌── │
│ │ ObjectID: 0x7197997621b1f8eb4982de7ef39ef5cf9eaa5cdcaea602d5ae57fbd9632c9f03 │
│ │ Sender: 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 │
│ │ Owner: Account Address ( 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 ) │
│ │ ObjectType: 0x6021c10099a3922849af2a46b8aad983b9e7f66344fa6e5de4d8a59b2b3a43b8::peace::Guardian<0x6021c10099a3922849af2a46b8aad983b9e7f66344fa6e5de4d8a59b2b3a43b8::peace::PEACE> │
│ │ Version: 1125852 │
│ │ Digest: 43p1huAqh9gWMNARdpoaA9ThzAPXj2P2h67cSDZBSa5Y │
│ └── │
然后查询也可以看到根据代码存储了新建的objectid
struct Guardian<phantom T: drop> has key, store {
id: UID
}
在该模式中,可以确保某些行为(比如合约的初始化过程)只被执行一次。
1.4 发布STOMCOIN
新建stomcoin项目,然后新建stomcoin.move
module stomcoin::stomcoin{
use std::option;
use sui::coin::{Self, Coin, TreasuryCap};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
//coin's name
struct STOMCOIN has drop {}
//init
fun init(witness: STOMCOIN,ctx: &mut TxContext) {
let (treasury_cap,metadata) = coin::create_currency<STOMCOIN>(witness, 2, b"STOMCOIN", b"SC", b"", option::none(), ctx);
transfer::public_freeze_object(metadata);
transfer::public_transfer(treasury_cap, tx_context::sender(ctx))
}
//mint new coins
public fun mint(
treasury_cap: &mut TreasuryCap<STOMCOIN>, amount: u64, recipient: address, ctx: &mut TxContext
) {
coin::mint_and_transfer(treasury_cap, amount, recipient, ctx)
}
//burn coins
public fun burn(treasury_cap: &mut TreasuryCap<STOMCOIN>, coin: Coin<STOMCOIN>) {
coin::burn(treasury_cap, coin);
}
#[test_only]
// Wrapper of module initializer for testing
public fun test_init(ctx: &mut TxContext) {
init(STOMCOIN {}, ctx)
}
}
部署合约后获取TreacuryCap和packageid
│ ┌── │
│ │ ObjectID: 0x10c003a1dbd584a81a3929db3b530e86404c7853b60385227e78bfd61ee49040 │
│ │ Sender: 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 │
│ │ Owner: Account Address ( 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 ) │
│ │ ObjectType: 0x2::coin::TreasuryCap<0x33b47e6048ab066bc17bc444b5593e93e423e685bc17a6576f49aa98948d176f::stomcoin::STOMCOIN> │
│ │ Version: 1125857 │
│ │ Digest: 9mb2NRB4eyeNEJQPK4WYsDevUnohztkQ5KSU6q4Lf2qr │
│ └──
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0x33b47e6048ab066bc17bc444b5593e93e423e685bc17a6576f49aa98948d176f │
│ │ Version: 1 │
│ │ Digest: E8tcFZCBCAjgRGDtdcjNfsKJgBdNbzEHjuVQrMCweEMq │
│ │ Modules: stomcoin │
│ └──
然后通过mint铸造代币
sui client call --package 0x33b47e6048ab066bc17bc444b5593e93e423e685bc17a6576f49aa98948d176f --module stomcoin --function mint --args 0x10c003a1dbd584a81a3929db3b530e86404c7853b60385227e78bfd61ee49040 100000000 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 --gas-budget 10000000
Transaction Digest: EXMiYy4E4W7sgEes79vbo2HjfpicTsqnzFs9U9W22CYK
然后账户地址下就有了自己发布的代币
1.5 发布STOMLOCKCOIN
module stomlockcoin::stomlockcoin{
use std::option;
use sui::transfer;
use sui::object::{Self, UID};
use sui::tx_context::{sender, TxContext};
use sui::coin::{Self, TreasuryCap};
use sui::balance::{Self, Balance};
use sui::clock::{Self, Clock};
// Transferrable object for storing the vesting coins
struct Locker has key, store {
id: UID,
start_date: u64,
final_date: u64,
original_balance: u64,
current_balance: Balance<STOMLOCKCOIN>
}
// Witness
struct STOMLOCKCOIN has drop {}
// Withdraw the available vested amount assuming linear vesting
#[lint_allow(self_transfer)]
public fun withdraw_vested(locker: &mut Locker, clock: &Clock, ctx: &mut TxContext){
let total_duration = locker.final_date - locker.start_date;
let elapsed_duration = clock::timestamp_ms(clock) - locker.start_date;
let total_vested_amount = if (elapsed_duration > total_duration) {
locker.original_balance
} else {
locker.original_balance * elapsed_duration / total_duration
};
let available_vested_amount = total_vested_amount - (locker.original_balance-balance::value(&locker.current_balance));
transfer::public_transfer(coin::take(&mut locker.current_balance, available_vested_amount, ctx), sender(ctx))
}
fun init(otw: STOMLOCKCOIN, ctx: &mut TxContext){
let (treasury_cap, metadata) = coin::create_currency<STOMLOCKCOIN>(otw, 8, b"STOMLOCKCOIN", b"SLC", b"", option::none(), ctx);
transfer::public_freeze_object(metadata);
transfer::public_transfer(treasury_cap, sender(ctx))
}
// Mints and transfers a locker object with the input amount of coins and specified vesting schedule
public fun locked_mint(treasury_cap: &mut TreasuryCap<STOMLOCKCOIN>, recipient: address, amount: u64, lock_up_duration: u64, clock: &Clock, ctx: &mut TxContext){
let coin = coin::mint(treasury_cap, amount, ctx);
let start_date = clock::timestamp_ms(clock);
let final_date = start_date + lock_up_duration;
transfer::public_transfer(Locker {
id: object::new(ctx),
start_date: start_date,
final_date: final_date,
original_balance: amount,
current_balance: coin::into_balance(coin)
}, recipient);
}
#[test_only]
public fun test_init(ctx: &mut TxContext) {
init(STOMLOCKCOIN {}, ctx)
}
}
部署合约
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0x61bb5e5be66748013c6b65f1096329cbcaa29c537cbe12b1fa28d2dcb9e2c3c2 │
│ │ Version: 1 │
│ │ Digest: 2JdGqWfBaGDJL2F7VuP7h5wMaRozhEzjMHLi9ZVGjsHY │
│ │ Modules: stomlockcoin │
│ └── │
然后铸造代币
│ ┌── │
│ │ ObjectID: 0x34fcaad32fb7672767d571109803f143392c62aeaee9c99dd084e36174f67015 │
│ │ Sender: 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 │
│ │ Owner: Account Address ( 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 ) │
│ │ ObjectType: 0x2::coin::TreasuryCap<0x61bb5e5be66748013c6b65f1096329cbcaa29c537cbe12b1fa28d2dcb9e2c3c2::stomlockcoin::STOMLOCKCOIN> │
│ │ Version: 1125859 │
│ │ Digest: 4hQKwVjK4Wq1ouTwwrgY1MtLB53w3Eq8qAwGddqDQsa8 │
│ └── │
调用后会创建一个Locker,这里我设置的是10ms的锁定时间,锁定了数量balance:100000000
然后时间到了之后提取锁定的STOMLOCKCOIN
sui client call --package 0x61bb5e5be66748013c6b65f1096329cbcaa29c537cbe12b1fa28d2dcb9e2c3c2 --module stomlockcoin --function locked_mint --args 0x34fcaad32fb7672767d571109803f143392c62aeaee9c99dd084e36174f67015 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 100000000 10 0x6 --gas-budget 10000000
│ │
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0xbdfe2a3e1d1e1a4ec1ff4bb1635ed93966c6e2d2e0894cdbc18ebbf92691bbd3 │
│ │ Sender: 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 │
│ │ Owner: Account Address ( 0x21f62d821142d1a72d2b78ce0e3fee2ae01f38293e583c46e9a4be3f91544767 ) │
│ │ ObjectType: 0x61bb5e5be66748013c6b65f1096329cbcaa29c537cbe12b1fa28d2dcb9e2c3c2::stomlockcoin::Locker │
│ │ Version: 22846350 │
│ │ Digest: 9zBL51hBKP4ZnDwhdH6ndsmL9u3a1WRtT4iMbBsu3Fnm
查询账户下已经有了刚才被锁定的代币
Sui move_cn社交账号
telegram: https://t.me/move_cn
X(twitter): https://twitter.com/move_cn
QQ群: 79489587
微信公众号:Move中文
Sui中文开发群: https://t.me/sui_dev_cn
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!