diff --git a/mover/ctianming/co-learn-2411/images/note_share_6.png b/mover/ctianming/co-learn-2411/images/note_share_6.png new file mode 100644 index 000000000..ee52b8363 Binary files /dev/null and b/mover/ctianming/co-learn-2411/images/note_share_6.png differ diff --git a/mover/ctianming/co-learn-2411/images/readme.md b/mover/ctianming/co-learn-2411/images/readme.md index 4fc11a696..95853bd58 100644 --- a/mover/ctianming/co-learn-2411/images/readme.md +++ b/mover/ctianming/co-learn-2411/images/readme.md @@ -35,4 +35,7 @@ b站关注 第四篇笔记分享 ![alt text](image.png) -第五篇笔记分享 \ No newline at end of file +第五篇笔记分享 + +![alt text](image.png) +第六篇笔记分享 \ No newline at end of file diff --git a/mover/ctianming/co-learn-2411/project/move_game/sources/guess_game.move b/mover/ctianming/co-learn-2411/project/move_game/sources/guess_game.move index 6a3dd0de6..be78a377a 100644 --- a/mover/ctianming/co-learn-2411/project/move_game/sources/guess_game.move +++ b/mover/ctianming/co-learn-2411/project/move_game/sources/guess_game.move @@ -1,179 +1,200 @@ -module move_game::guess_game { - /// 所有,或者一无所有! - use sui::tx_context::{sender}; - use sui::event::emit; - use move_coin::faucet_coin::{Self, FAUCET_COIN}; - use move_nft::move_nft::{Self}; - use sui::coin::{Self,Coin,TreasuryCap}; - use sui::balance::{Self, Balance}; - use sui::address; - use sui::table::{Self, Table}; - use sui::bcs; +module move_game::guess_game; - const EInvalid:u64 = 0; - const EInputNotEnough:u64 = 1; - const ECurrencyNotEnough:u64 = 2; - const EHaveWon:u64 = 4; - const ENotUser:u64 = 5; +use move_coin::faucet_coin::{Self, FAUCET_COIN}; +use move_nft::move_nft; +use sui::address; +use sui::balance::{Self, Balance}; +use sui::bcs; +use sui::coin::{Self, Coin, TreasuryCap}; +use sui::event::emit; +use sui::table::{Self, Table}; +use sui::tx_context::sender; - /// 定义游戏 - public struct Game has key, store { - id: UID, - secret_number: Option, - attempts: u32, - over: bool, - winner: address, - cost_per_round: u64, - pool: u64, - game_pool: Balance, - users: Table, - } +const EInvalid: u64 = 0; +const EInputNotEnough: u64 = 1; +const ECurrencyNotEnough: u64 = 2; +const EHaveWon: u64 = 4; +const ENotUser: u64 = 5; - /// 定义游戏事件 - public enum GuessEvent has copy, drop, store { - TooSmall, - TooBig, - Correct, - } +/// 定义游戏 +public struct Game has key, store { + id: UID, + secret_number: Option, + attempts: u32, + over: bool, + winner: address, + cost_per_round: u64, + pool: u64, + game_pool: Balance, + users: Table, +} - public struct USER has store{ - balance: u64, - } +/// 定义游戏事件 +public enum GuessEvent has copy, drop, store { + TooSmall, + TooBig, + Correct, +} - public struct AdminCap has key{id:UID} +public struct USER has store { + balance: u64, +} - /// 初始化游戏 - fun init(ctx: &mut TxContext){ - let game = Game { - id: object::new(ctx), - secret_number: option::none(), - attempts: 0, - over: false, - winner: address::from_u256(0), - cost_per_round: 10, //define the cost per round is 10 faucet_coin - pool: 0, - game_pool: balance::zero(), - users: table::new(ctx), - }; - transfer::share_object(game); - transfer::transfer(AdminCap{id:object::new(ctx)},sender(ctx)); - } +public struct AdminCap has key { id: UID } - //get random number - // public entry fun get_random_number(_: &AdminCap, game: &mut Game, r: &Random, ctx: &mut TxContext) { - // let mut generator=random::new_generator(r,ctx); - // let random_value=random::generate_u8_in_range(&mut generator,1,101); - // game.secret_number = random_value as u32; - // } - public entry fun get_random_number(_: &AdminCap, game: &mut Game, magic_number: u64, ctx: &mut TxContext) { - let sender_address = tx_context::sender(ctx); - let timestamp = tx_context::epoch_timestamp_ms(ctx); - let address_bytes = address::to_bytes(sender_address); - let timestamp_bytes = bcs::to_bytes(×tamp); - let magic_number_bytes = bcs::to_bytes(&magic_number); - let mut hash_input = address_bytes; - vector::append(&mut hash_input, timestamp_bytes); - vector::append(&mut hash_input, magic_number_bytes); - let hash_output = sui::hash::blake2b256(&hash_input); - // 取哈希值的第一个字节并转换为随机数 [1, 100] - let random_byte = *vector::borrow(&hash_output, 0); // 解引用 &u8 - let random_value = (random_byte as u32) % 100 + 1; - game.secret_number = option::some(random_value); - } +/// 初始化游戏 +fun init(ctx: &mut TxContext) { + let game = Game { + id: object::new(ctx), + secret_number: option::none(), + attempts: 0, + over: false, + winner: address::from_u256(0), + cost_per_round: 10, //define the cost per round is 10 faucet_coin + pool: 0, + game_pool: balance::zero(), + users: table::new(ctx), + }; + transfer::share_object(game); + transfer::transfer(AdminCap { id: object::new(ctx) }, sender(ctx)); +} - //get faucet_coin - public entry fun get_faucet_coin( - treasury_cap:&mut TreasuryCap, - amount: u64, - ctx: &mut TxContext) { - faucet_coin::mint(treasury_cap,amount,tx_context::sender(ctx),ctx); - } +//get random number +// public entry fun get_random_number(_: &AdminCap, game: &mut Game, r: &Random, ctx: &mut TxContext) { +// let mut generator=random::new_generator(r,ctx); +// let random_value=random::generate_u8_in_range(&mut generator,1,101); +// game.secret_number = random_value as u32; +// } +public entry fun get_random_number( + _: &AdminCap, + game: &mut Game, + magic_number: u64, + ctx: &mut TxContext, +) { + let sender_address = tx_context::sender(ctx); + let timestamp = tx_context::epoch_timestamp_ms(ctx); + let address_bytes = address::to_bytes(sender_address); + let timestamp_bytes = bcs::to_bytes(×tamp); + let magic_number_bytes = bcs::to_bytes(&magic_number); + let mut hash_input = address_bytes; + vector::append(&mut hash_input, timestamp_bytes); + vector::append(&mut hash_input, magic_number_bytes); + let hash_output = sui::hash::blake2b256(&hash_input); + // 取哈希值的第一个字节并转换为随机数 [1, 100] + let random_byte = *vector::borrow(&hash_output, 0); // 解引用 &u8 + let random_value = (random_byte as u32) % 100 + 1; + game.secret_number = option::some(random_value); +} - //deposit Coin - public entry fun deposit(game: &mut Game, input: Coin, amount: u64, ctx: &mut TxContext) { - let caller = tx_context::sender(ctx); - // get the input value and assert - let input_value = coin::value(&input); - assert!(input_value >= amount, EInputNotEnough); - // transection the input value to Balance - let mut input_balance = coin::into_balance(input); - // if input valye much tran amount, change the excess - if (input_value > amount) { - balance::join( - &mut game.game_pool, - balance::split(&mut input_balance, amount), - ); - let change = coin::from_balance(input_balance, ctx); - transfer::public_transfer(change, caller); - } else { - balance::join(&mut game.game_pool, input_balance); - }; - // check if user in table `users` - if (!table::contains(&game.users, caller)) { - // insert new user, and set its balance - table::add(&mut game.users, caller, USER { balance: amount }); - } else { - // user is valid,update its balance - let user = table::borrow_mut(&mut game.users, caller); - user.balance = user.balance + amount; - } - } +//get faucet_coin +public entry fun get_faucet_coin( + treasury_cap: &mut TreasuryCap, + amount: u64, + ctx: &mut TxContext, +) { + faucet_coin::mint(treasury_cap, amount, tx_context::sender(ctx), ctx); +} - //withdraw Coin - public entry fun withdraw(_: &AdminCap, game: &mut Game, amount: u64, ctx: &mut TxContext) { - let output_balance = balance::split(&mut game.game_pool, amount); - let output = coin::from_balance(output_balance, ctx); - transfer::public_transfer(output, sender(ctx)); - game.pool = game.pool - amount; +//deposit Coin +public entry fun deposit( + game: &mut Game, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + // get the input value and assert + let input_value = coin::value(&input); + assert!(input_value >= amount, EInputNotEnough); + // transection the input value to Balance + let mut input_balance = coin::into_balance(input); + // if input valye much tran amount, change the excess + if (input_value > amount) { + balance::join( + &mut game.game_pool, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut game.game_pool, input_balance); + }; + // check if user in table `users` + if (!table::contains(&game.users, caller)) { + // insert new user, and set its balance + table::add(&mut game.users, caller, USER { balance: amount }); + } else { + // user is valid,update its balance + let user = table::borrow_mut(&mut game.users, caller); + user.balance = user.balance + amount; } +} - //restart - public entry fun restart(_: &AdminCap, game: &mut Game) { - game.secret_number = option::none(); - game.attempts = 0; - game.over = false; - game.winner = address::from_u256(0); - } +//withdraw Coin +public entry fun withdraw(_: &AdminCap, game: &mut Game, amount: u64, ctx: &mut TxContext) { + let output_balance = balance::split(&mut game.game_pool, amount); + let output = coin::from_balance(output_balance, ctx); + transfer::public_transfer(output, sender(ctx)); + game.pool = game.pool - amount; +} - /// user guess - public entry fun make_guess(game: &mut Game, coin: Coin, guess: u32, ctx: &mut TxContext) { - game.attempts = game.attempts + 1; - let caller = tx_context::sender(ctx); - // base check - assert!(game.secret_number != option::none(), EInvalid); - assert!(game.over != true, EHaveWon); - assert!(game.winner == address::from_u256(0), EHaveWon); - assert!(table::contains(&game.users, caller), ENotUser); - assert!(guess >= 1 && guess <= 100, EInvalid); - // check the balance - let user = table::borrow_mut(&mut game.users, caller); - assert!(user.balance >= game.cost_per_round, ECurrencyNotEnough); // currency not enough - user.balance = user.balance - game.cost_per_round; // deduct the cost - // input the balance into pool - let input_balance = coin::into_balance(coin); - balance::join(&mut game.game_pool, input_balance); - game.pool = game.pool + game.cost_per_round; - // compare guess with secret number - let secret_number = option::borrow(&game.secret_number); - if (guess < *secret_number) { +//restart +public entry fun restart(_: &AdminCap, game: &mut Game) { + game.secret_number = option::none(); + game.attempts = 0; + game.over = false; + game.winner = address::from_u256(0); +} + +/// user guess +public entry fun make_guess( + game: &mut Game, + coin: Coin, + guess: u32, + ctx: &mut TxContext, +) { + game.attempts = game.attempts + 1; + let caller = tx_context::sender(ctx); + // base check + assert!(game.secret_number != option::none(), EInvalid); + assert!(game.over != true, EHaveWon); + assert!(game.winner == address::from_u256(0), EHaveWon); + assert!(table::contains(&game.users, caller), ENotUser); + assert!(guess >= 1 && guess <= 100, EInvalid); + // check the balance + let user = table::borrow_mut(&mut game.users, caller); + assert!(user.balance >= game.cost_per_round, ECurrencyNotEnough); // currency not enough + user.balance = user.balance - game.cost_per_round; // deduct the cost + // input the balance into pool + let input_balance = coin::into_balance(coin); + balance::join(&mut game.game_pool, input_balance); + game.pool = game.pool + game.cost_per_round; + // compare guess with secret number + let secret_number = option::borrow(&game.secret_number); + if (guess < *secret_number) { emit(GuessEvent::TooSmall); // too small - } else if (guess > *secret_number) { + } else if (guess > *secret_number) { emit(GuessEvent::TooBig); // too big - } else { - // guess correct - emit(GuessEvent::Correct); - // game over - game.over = true; - game.winner = caller; - // get reward - // let reward_balance = balance::split(&mut game.game_pool, user.balance); - // let reward = coin::from_balance(reward_balance, ctx); - // NOTHING, OR EVERYTHING!!! - let reward_balance = balance::split(&mut game.game_pool, 0); - let reward = coin::from_balance(reward_balance, ctx); - transfer::public_transfer(reward, caller); - // give a NFT to winner - move_nft::mint(b"TruE", b"Bless Alysia! from ctianming≥v≤", b"https://avatars.githubusercontent.com/u/107739505?v=4", caller, ctx); - } + } else { + // guess correct + emit(GuessEvent::Correct); + // game over + game.over = true; + game.winner = caller; + // get reward + // let reward_balance = balance::split(&mut game.game_pool, user.balance); + // let reward = coin::from_balance(reward_balance, ctx); + // NOTHING, OR EVERYTHING!!! + let reward_balance = balance::split(&mut game.game_pool, 0); + let reward = coin::from_balance(reward_balance, ctx); + transfer::public_transfer(reward, caller); + // give a NFT to winner + move_nft::mint( + b"TruE", + b"Bless Alysia! from ctianming≥v≤", + b"https://avatars.githubusercontent.com/u/107739505?v=4", + caller, + ctx, + ); } } diff --git a/mover/ctianming/co-learn-2411/project/move_swap/Makefile b/mover/ctianming/co-learn-2411/project/move_swap/Makefile new file mode 100644 index 000000000..3632cc509 --- /dev/null +++ b/mover/ctianming/co-learn-2411/project/move_swap/Makefile @@ -0,0 +1,85 @@ +# Sui 项目 Makefile +# 项目路径设置 +MOVE_PATH := . +BUILD_PATH := $(MOVE_PATH)/build +SUI_CLI := sui + +# 默认网络为 devnet,可通过 `make NETWORK=` 来覆盖 +NETWORK := $(or $(NETWORK), mainnet) +GAS_BUDGET := $(or $(GAS_BUDGET), 2000000) + +# 默认目标 +all: build + +# 切换网络 +switch: + @echo "Switching to network: $(NETWORK)..." + @if [ -z "$(NETWORK)" ]; then \ + echo "Error: No network specified. Please set NETWORK."; \ + exit 1; \ + fi + $(SUI_CLI) client switch --env $(NETWORK) + @echo "Switched to network: $(NETWORK)" + export NETWORK=$(NETWORK) +# 编译 Move 代码 +build: + @echo "Building the Sui Move package..." + @if [ -n "$(NETWORK)" ] && [ "$(NETWORK)" != "devnet" ]; then \ + echo "Switching to network: $(NETWORK)"; \ + $(SUI_CLI) client switch --env $(NETWORK); \ + elif [ "$(NETWORK)" = "devnet" ]; then \ + echo "Using the current network $(NETWORK)"; \ + fi + $(SUI_CLI) move build --path $(MOVE_PATH) + +# 运行测试 +test: + @echo "Running tests on Sui Move package..." + @if [ -n "$(NETWORK)" ] && [ "$(NETWORK)" != "devnet" ]; then \ + echo "Switching to network: $(NETWORK)"; \ + $(SUI_CLI) client switch --env $(NETWORK); \ + elif [ "$(NETWORK)" = "devnet" ]; then \ + echo "Using the current network $(NETWORK)"; \ + fi + $(SUI_CLI) move test --path $(MOVE_PATH) + +# 发布合约到 Devnet +publish: + @echo "Publishing the package to $(SUI_NETWORK)..." + @if [ -n "$(NETWORK)" ] && [ "$(NETWORK)" != "devnet" ]; then \ + echo "Switching to network: $(NETWORK)"; \ + $(SUI_CLI) client switch --env $(NETWORK); \ + elif [ "$(NETWORK)" = "devnet" ]; then \ + echo "Using the current network $(NETWORK)"; \ + fi + $(SUI_CLI) client publish --gas-budget $(GAS_BUDGET) + +# 清理生成的文件 +clean: + @echo "Cleaning up the build directory..." + rm -rf $(BUILD_PATH) + +# 此命令仅适用于使用 brew 下载 sui 的用户 +update: + @echo "Updating brew and sui" + brew update + brew upgrade sui + +# 查看合约状态 +# 这里我只添加了三个命令,可以根据自己的需要增减 +status: + @echo "Checking the status of the Sui project..." + $(SUI_CLI) client active-env + $(SUI_CLI) client active-address + $(SUI_CLI) client balance + +# 帮助信息 +help: + @echo "使用 Makefile 中的以下命令进行常见操作:" + @echo " make build - 编译 Move 包" + @echo " make test - 测试 Move 包" + @echo " make publish - 发布合约到网络" + @echo " make clean - 清理生成的文件" + @echo " make status - 查看合约状态" + +.PHONY: all build test publish clean status help diff --git a/mover/ctianming/co-learn-2411/project/move_swap/Move.lock b/mover/ctianming/co-learn-2411/project/move_swap/Move.lock new file mode 100644 index 000000000..69b2f7eb5 --- /dev/null +++ b/mover/ctianming/co-learn-2411/project/move_swap/Move.lock @@ -0,0 +1,43 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 3 +manifest_digest = "BF44845A735B9C1D8C6CB3A3E586DAA70534751871EB3F8DB31B3B30D87046AF" +deps_digest = "3C4103934B1E040BB6B23F1D610B4EF9F2F1166A50A104EADCF77467C004C600" +dependencies = [ + { id = "Sui", name = "Sui" }, + { id = "move_coin", name = "move_coin" }, +] + +[[move.package]] +id = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/mainnet", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +id = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/mainnet", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, +] + +[[move.package]] +id = "move_coin" +source = { local = "/home/amyseer/sui/letsmove/mover/ctianming/co-learn-2411/project/move_coin" } + +dependencies = [ + { id = "Sui", name = "Sui" }, +] + +[move.toolchain-version] +compiler-version = "1.37.3" +edition = "2024.beta" +flavor = "sui" + +[env] + +[env.mainnet] +chain-id = "35834a8a" +original-published-id = "0x2ef63206c65f457cb14db4806f101958336ac4503675b04fd7120c7c33680ece" +latest-published-id = "0x2ef63206c65f457cb14db4806f101958336ac4503675b04fd7120c7c33680ece" +published-version = "1" diff --git a/mover/ctianming/co-learn-2411/project/move_swap/Move.toml b/mover/ctianming/co-learn-2411/project/move_swap/Move.toml new file mode 100644 index 000000000..12feb84ff --- /dev/null +++ b/mover/ctianming/co-learn-2411/project/move_swap/Move.toml @@ -0,0 +1,37 @@ +[package] +name = "move_swap" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +[dependencies] +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } +move_coin = { local = "/home/amyseer/sui/letsmove/mover/ctianming/co-learn-2411/project/move_coin/" } + +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + +[addresses] +move_swap = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. +# alice = "0xB0B" diff --git a/mover/ctianming/co-learn-2411/project/move_swap/sources/move_swap.move b/mover/ctianming/co-learn-2411/project/move_swap/sources/move_swap.move new file mode 100644 index 000000000..480092ba2 --- /dev/null +++ b/mover/ctianming/co-learn-2411/project/move_swap/sources/move_swap.move @@ -0,0 +1,180 @@ +module move_swap::move_swap; + +use move_coin::faucet_coin::FAUCET_COIN; +use move_coin::my_coin::MY_COIN; +use sui::balance::{Self, Balance}; +use sui::coin::{Self, Coin}; + +const EInputNotEnough: u64 = 0; +const EPoolNotEnough: u64 = 1; + +public struct AdminCap has key { id: UID } + +public struct Pool has key { + // 如果需要,可以加上两种代币分别的存量属性,这里先不添加 + id: UID, + faucet_coin: Balance, + //faucet_coin_balance: u64, + my_coin: Balance, + //faucet_coin_balance: u64, +} + +fun init(ctx: &mut TxContext) { + let pool = Pool { + id: object::new(ctx), + faucet_coin: balance::zero(), + my_coin: balance::zero(), + }; + transfer::share_object(pool); // pool需要公开,通过AdminCap给予自己额外的管理员权限 + transfer::transfer(AdminCap { id: object::new(ctx) }, tx_context::sender(ctx)); +} + +// 这里我踩了个坑,给pool铸造coin无法增加balance,必须使用balance_join +// 由于在单一函数中无法获取铸造的coin的对象(mint函数直接将coin转给recipient) +// 所以我放弃这个实现 +// public entry fun infuse( +// // 本函数用于给pool注入资金,注意调用这个函数的地址需要拥有MY_COIN的TreasuryCap +// // pool: &mut Pool, +// faucet_treasury_cap: &mut coin::TreasuryCap, +// my_treasury_cap: &mut coin::TreasuryCap, +// amount: u64, +// pool_address: address, +// ctx: &mut TxContext, +// ) { +// my_coin::mint(my_treasury_cap, amount, pool_address, ctx); +// faucet_coin::mint(faucet_treasury_cap, amount, pool_address, ctx); +// } +// 但是,我们可以通过deposit函数,将coin转入pool的balance中 +// 我们还可以指定一个amount,并资金是否足够进行检查,与我们在guess_game中的实现类似 +public entry fun deposit_my_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + if (input_value > amount) { + balance::join( + &mut pool.my_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.my_coin, input_balance); + }; +} + +public entry fun deposit__faucet_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + if (input_value > amount) { + balance::join( + &mut pool.faucet_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.faucet_coin, input_balance); + }; +} + +// 我们可以编写一个withdraw函数,并限制只有拥有管理员权限才能提取资金 +public entry fun withdraw_zzf222_coin( + _: &AdminCap, + pool: &mut Pool, + amount: u64, + ctx: &mut TxContext, +) { + let my_coin_balance = balance::split(&mut pool.my_coin, amount); + let my_coin = coin::from_balance(my_coin_balance, ctx); + transfer::public_transfer(my_coin, tx_context::sender(ctx)); +} + +public entry fun withdraw_zzf222_faucet_coin( + _: &AdminCap, + pool: &mut Pool, + amount: u64, + ctx: &mut TxContext, +) { + let faucet_coin_balance = balance::split(&mut pool.faucet_coin, amount); + let faucet_coin = coin::from_balance(faucet_coin_balance, ctx); + transfer::public_transfer(faucet_coin, tx_context::sender(ctx)); +} + +// 在swap函数中,我们可以沿用部分上一节guess_game的代码 +public entry fun swap_faucet_coin_to_my_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + // let faucet_amount = coin::value(& faucet_coin); + // let my_amount = faucet_amount * 1000 / 2000; + // balance::join(&mut pool.faucet_coin, coin::into_balance(faucet_coin)); // join 函数用于接收代币 + // let my_coin_balance = balance::split(&mut pool.my_coin, my_amount); // split函数用于将pool_balance减去对应于amount的数量 + // let my_coin = coin::from_balance(my_coin_balance, ctx); + // transfer::public_transfer(my_coin, tx_context::sender(ctx)); + + let caller = tx_context::sender(ctx); + // get the input value and assert + let input_value = coin::value(&input); + let output_value = amount * 1000 / 2000; // amount千万不要写成input_value! + assert!(input_value >= amount, EInputNotEnough); + // transection the input value to Balance + let mut input_balance = coin::into_balance(input); //Destruct a Coin wrapper and keep the balance. + assert!(balance::value(&pool.my_coin) >= output_value, EPoolNotEnough); + // if input value much than amount, change the excess + if (input_value > amount) { + balance::join( + // join 函数用于接收代币 + &mut pool.faucet_coin, + balance::split(&mut input_balance, amount), // split函数用于将pool_balance减去对应于amount的数量,并返回值为amount的Balance + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.faucet_coin, input_balance); + }; + let output_balance = balance::split(&mut pool.my_coin, output_value); + let output = coin::from_balance(output_balance, ctx); + transfer::public_transfer(output, caller); +} + +public entry fun swap_my_coin_to_faucet_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + let output_value = amount * 2000 / 1000; + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + assert!(balance::value(&pool.faucet_coin) >= output_value, EPoolNotEnough); + if (input_value > amount) { + balance::join( + &mut pool.my_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.my_coin, input_balance); + }; + let output_balance = balance::split(&mut pool.faucet_coin, output_value); + let output = coin::from_balance(output_balance, ctx); + transfer::public_transfer(output, caller); +} diff --git a/mover/ctianming/co-learn-2411/project/move_swap/tests/move_swap_tests.move b/mover/ctianming/co-learn-2411/project/move_swap/tests/move_swap_tests.move new file mode 100644 index 000000000..cfd3c3deb --- /dev/null +++ b/mover/ctianming/co-learn-2411/project/move_swap/tests/move_swap_tests.move @@ -0,0 +1,18 @@ +/* +#[test_only] +module move_swap::move_swap_tests; +// uncomment this line to import the module +// use move_swap::move_swap; + +const ENotImplemented: u64 = 0; + +#[test] +fun test_move_swap() { + // pass +} + +#[test, expected_failure(abort_code = ::move_swap::move_swap_tests::ENotImplemented)] +fun test_move_swap_fail() { + abort ENotImplemented +} +*/ diff --git a/mover/ctianming/co-learn-2411/readme.md b/mover/ctianming/co-learn-2411/readme.md index 7466691c3..163cb2e3c 100644 --- a/mover/ctianming/co-learn-2411/readme.md +++ b/mover/ctianming/co-learn-2411/readme.md @@ -28,7 +28,8 @@ - [x] 第二篇笔记:![第二篇笔记分享](./images/note_share_2.png) - [x] 第三篇笔记:![第三篇笔记分享](./images/note_share_3.png) - [x] 第四篇笔记: ![第四篇笔记分享](./images/note_share_4.png) -- [x] 第五篇笔记: ![第四篇笔记分享](./images/note_share_5.png) +- [x] 第五篇笔记: ![第五篇笔记分享](./images/note_share_5.png) +- [x] 第六篇笔记: ![第六篇笔记分享](./images/note_share_6.png) ## 对外输出学习笔记 @@ -37,6 +38,7 @@ - [x] 第三篇笔记【https://learnblockchain.cn/article/9912】 - [x] 第四篇笔记【https://learnblockchain.cn/article/9938】 - [x] 第五篇笔记【https://learnblockchain.cn/article/9969】 +- [x] 第五篇笔记【https://learnblockchain.cn/article/10021】 ## 在HOH社区公众号发布自己的技术文章 diff --git "a/mover/ctianming/notes/sui-move\345\237\272\347\241\200\357\274\210\345\205\255\357\274\211\357\274\232letsmove-task5.md" "b/mover/ctianming/notes/sui-move\345\237\272\347\241\200\357\274\210\345\205\255\357\274\211\357\274\232letsmove-task5.md" new file mode 100644 index 000000000..a71a573d7 --- /dev/null +++ "b/mover/ctianming/notes/sui-move\345\237\272\347\241\200\357\274\210\345\205\255\357\274\211\357\274\232letsmove-task5.md" @@ -0,0 +1,444 @@ +--- +title: sui-move基础(六):letsmove-task5 +date: 2024-11-24 17:27:00 +tags: +--- + +# sui-move基础(六):letsmove-task5 + +### 需求 + +- 完成 swap相关知识的学习 +- 完成第一个Swap合约的上链部署 +- swap 必须是 swap 自己发行的 task2 两个 Coin的互换,包名必须是自己的`github id` + +### 写在前面 + +本节的内容相对比较简单,基本上完成了前面一个任务就没有任何问题了(尤其是跟着看我的教程的朋友,我的上一个教程写的较难)。 + +所以,本节最重要的是,千万不要想复杂了。如果你发现一个比较简单的实现方法,但是不够优雅,没关系,写! + +毕竟,本task的要求只是:实现一个最简单的swap + +### 概述 + +实现一个最简单的swap,这确实很简单,我们只需要构筑一个池子(`Pool`),其中可以仅仅注入两种Coin(就是我们task2完成的那两种即可),然后编写两个接口,使得这两种Coin可以按照一定的比例兑换:存入x数量的CoinA,然后取走y数量的CoinB;反之亦然。 + +这有点类似原始的以物易物,只是我们以相对高科技的自动化手段实现:Move合约。 + +### 代码概览 + +***请一定注意看代码中的注释!!!*** + +```rust + +module move_swap::move_swap; + +use move_coin::faucet_coin::FAUCET_COIN; +use move_coin::my_coin::MY_COIN; +use sui::balance::{Self, Balance}; +use sui::coin::{Self, Coin}; + +const EInputNotEnough: u64 = 0; +const EPoolNotEnough: u64 = 1; + +public struct AdminCap has key { id: UID } + +public struct Pool has key { + // 如果需要,可以加上两种代币分别的存量属性,这里先不添加 + id: UID, + faucet_coin: Balance, + //faucet_coin_balance: u64, + my_coin: Balance, + //faucet_coin_balance: u64, +} + +fun init(ctx: &mut TxContext) { + let pool = Pool { + id: object::new(ctx), + faucet_coin: balance::zero(), + my_coin: balance::zero(), + }; + transfer::share_object(pool); // pool需要公开,通过AdminCap给予自己额外的管理员权限 + transfer::transfer(AdminCap { id: object::new(ctx) }, tx_context::sender(ctx)); +} + +// 这里我踩了个坑,给pool铸造coin无法增加balance,必须使用balance_join +// 由于在单一函数中无法获取铸造的coin的对象(mint函数直接将coin转给recipient) +// 所以我放弃这个实现 +// public entry fun infuse( +// // 本函数用于给pool注入资金,注意调用这个函数的地址需要拥有MY_COIN的TreasuryCap +// // pool: &mut Pool, +// faucet_treasury_cap: &mut coin::TreasuryCap, +// my_treasury_cap: &mut coin::TreasuryCap, +// amount: u64, +// pool_address: address, +// ctx: &mut TxContext, +// ) { +// my_coin::mint(my_treasury_cap, amount, pool_address, ctx); +// faucet_coin::mint(faucet_treasury_cap, amount, pool_address, ctx); +// } +// 但是,我们可以通过deposit函数,将coin转入pool的balance中 +// 我们还可以指定一个amount,并资金是否足够进行检查,与我们在guess_game中的实现类似 +public entry fun deposit_my_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + if (input_value > amount) { + balance::join( + &mut pool.my_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.my_coin, input_balance); + }; +} + +public entry fun deposit__faucet_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + if (input_value > amount) { + balance::join( + &mut pool.faucet_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.faucet_coin, input_balance); + }; +} + +// 我们可以编写一个withdraw函数,并限制只有拥有管理员权限才能提取资金 +public entry fun withdraw_zzf222_coin( + _: &AdminCap, + pool: &mut Pool, + amount: u64, + ctx: &mut TxContext, +) { + let my_coin_balance = balance::split(&mut pool.my_coin, amount); + let my_coin = coin::from_balance(my_coin_balance, ctx); + transfer::public_transfer(my_coin, tx_context::sender(ctx)); +} + +public entry fun withdraw_zzf222_faucet_coin( + _: &AdminCap, + pool: &mut Pool, + amount: u64, + ctx: &mut TxContext, +) { + let faucet_coin_balance = balance::split(&mut pool.faucet_coin, amount); + let faucet_coin = coin::from_balance(faucet_coin_balance, ctx); + transfer::public_transfer(faucet_coin, tx_context::sender(ctx)); +} + +// 在swap函数中,我们可以沿用部分上一节guess_game的代码 +public entry fun swap_faucet_coin_to_my_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + // let faucet_amount = coin::value(& faucet_coin); + // let my_amount = faucet_amount * 1000 / 2000; + // balance::join(&mut pool.faucet_coin, coin::into_balance(faucet_coin)); // join 函数用于接收代币 + // let my_coin_balance = balance::split(&mut pool.my_coin, my_amount); // split函数用于将pool_balance减去对应于amount的数量 + // let my_coin = coin::from_balance(my_coin_balance, ctx); + // transfer::public_transfer(my_coin, tx_context::sender(ctx)); + + let caller = tx_context::sender(ctx); + // get the input value and assert + let input_value = coin::value(&input); + let output_value = amount * 1000 / 2000; // amount千万不要写成input_value! + assert!(input_value >= amount, EInputNotEnough); + // transection the input value to Balance + let mut input_balance = coin::into_balance(input); //Destruct a Coin wrapper and keep the balance. + assert!(balance::value(&pool.my_coin) >= output_value, EPoolNotEnough); + // if input value much than amount, change the excess + if (input_value > amount) { + balance::join( + // join 函数用于接收代币 + &mut pool.faucet_coin, + balance::split(&mut input_balance, amount), // split函数用于将pool_balance减去对应于amount的数量,并返回值为amount的Balance + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.faucet_coin, input_balance); + }; + let output_balance = balance::split(&mut pool.my_coin, output_value); + let output = coin::from_balance(output_balance, ctx); + transfer::public_transfer(output, caller); +} + +public entry fun swap_my_coin_to_faucet_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + let output_value = amount * 2000 / 1000; + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + assert!(balance::value(&pool.faucet_coin) >= output_value, EPoolNotEnough); + if (input_value > amount) { + balance::join( + &mut pool.my_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.my_coin, input_balance); + }; + let output_balance = balance::split(&mut pool.faucet_coin, output_value); + let output = coin::from_balance(output_balance, ctx); + transfer::public_transfer(output, caller); +} + +``` + +我在注释中已经写明了绝大部分需要注意的内容,但我在这里仍然进行一下细节的讲解。 + +### 代码讲解 + +#### 资金池和管理员权限 + +```rust +public struct AdminCap has key { id: UID } + +public struct Pool has key { + // 如果需要,可以加上两种代币分别的存量属性,这里先不添加 + id: UID, + faucet_coin: Balance, + //faucet_coin_balance: u64, + my_coin: Balance, + //faucet_coin_balance: u64, +} +``` + +这是一个很简单的资金池,它只支持进行两种Coin之间的转换。另外,注意类型:`Balance`。 + +这个资金池必须自身拥有资金,才能进行swap,也就是说我们需要给它注入资金。 + +`AdminCap`的作用与前一节的作用相同,赋予管理员权限,用于构造一些只有拥有管理员权限才能调用的函数。 + +#### 初始化 + +```rust +fun init(ctx: &mut TxContext) { + let pool = Pool { + id: object::new(ctx), + faucet_coin: balance::zero(), + my_coin: balance::zero(), + }; + transfer::share_object(pool); // pool需要公开,通过AdminCap给予自己额外的管理员权限 + transfer::transfer(AdminCap { id: object::new(ctx) }, tx_context::sender(ctx)); +} +``` + +我们初始化一个Pool,将其公开,并将管理员权限赋予自己。 + +#### 踩坑记录 + +```rust +// 这里我踩了个坑,给pool铸造coin无法增加balance,必须使用balance_join +// 由于在单一函数中无法获取铸造的coin的对象(mint函数直接将coin转给recipient) +// 所以我放弃这个实现 +// public entry fun infuse( +// // 本函数用于给pool注入资金,注意调用这个函数的地址需要拥有MY_COIN的TreasuryCap +// // pool: &mut Pool, +// faucet_treasury_cap: &mut coin::TreasuryCap, +// my_treasury_cap: &mut coin::TreasuryCap, +// amount: u64, +// pool_address: address, +// ctx: &mut TxContext, +// ) { +// my_coin::mint(my_treasury_cap, amount, pool_address, ctx); +// faucet_coin::mint(faucet_treasury_cap, amount, pool_address, ctx); +// } +// 但是,我们可以通过deposit函数,将coin转入pool的balance中 +// 我们还可以指定一个amount,并资金是否足够进行检查,与我们在guess_game中的实现类似 +``` + +为什么我们需要注意类型:`Balance`,我就是因为没有注意所以踩了这个坑。 + +我的这个infuse实现仅仅将Coin直接铸造并转给了Pool而已,对它的两种Coin的Balance并没有影响,因为我们不曾调用balance包中的函数给其增加balance。 + +并且由于Coin是属于Pool的,我们无法直接操作它,除非额外在这个包中编写接口,但那太麻烦了。 + +#### 存取函数 + +既然无法直接注入资金,我们就编写deposit函数来进行存入,并编写有权限控制的withdraw函数。 + +```rust +public entry fun deposit_my_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + if (input_value > amount) { + balance::join( + &mut pool.my_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.my_coin, input_balance); + }; +} + +public entry fun deposit__faucet_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + if (input_value > amount) { + balance::join( + &mut pool.faucet_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.faucet_coin, input_balance); + }; +} + +// 我们可以编写一个withdraw函数,并限制只有拥有管理员权限才能提取资金 +public entry fun withdraw_zzf222_coin( + _: &AdminCap, + pool: &mut Pool, + amount: u64, + ctx: &mut TxContext, +) { + let my_coin_balance = balance::split(&mut pool.my_coin, amount); + let my_coin = coin::from_balance(my_coin_balance, ctx); + transfer::public_transfer(my_coin, tx_context::sender(ctx)); +} + +public entry fun withdraw_zzf222_faucet_coin( + _: &AdminCap, + pool: &mut Pool, + amount: u64, + ctx: &mut TxContext, +) { + let faucet_coin_balance = balance::split(&mut pool.faucet_coin, amount); + let faucet_coin = coin::from_balance(faucet_coin_balance, ctx); + transfer::public_transfer(faucet_coin, tx_context::sender(ctx)); +} +``` + +在deposit函数中,我们支持存入定量的资金,并可以使用上一节`game_guess`实现过的资金检查机制来进行检查。 + +#### swap实现 + +```rust +// 在swap函数中,我们可以沿用部分上一节guess_game的代码 +public entry fun swap_faucet_coin_to_my_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + // let faucet_amount = coin::value(& faucet_coin); + // let my_amount = faucet_amount * 1000 / 2000; + // balance::join(&mut pool.faucet_coin, coin::into_balance(faucet_coin)); // join 函数用于接收代币 + // let my_coin_balance = balance::split(&mut pool.my_coin, my_amount); // split函数用于将pool_balance减去对应于amount的数量 + // let my_coin = coin::from_balance(my_coin_balance, ctx); + // transfer::public_transfer(my_coin, tx_context::sender(ctx)); + + let caller = tx_context::sender(ctx); + // get the input value and assert + let input_value = coin::value(&input); + let output_value = amount * 1000 / 2000; // amount千万不要写成input_value! + assert!(input_value >= amount, EInputNotEnough); + // transection the input value to Balance + let mut input_balance = coin::into_balance(input); //Destruct a Coin wrapper and keep the balance. + assert!(balance::value(&pool.my_coin) >= output_value, EPoolNotEnough); + // if input value much than amount, change the excess + if (input_value > amount) { + balance::join( + // join 函数用于接收代币 + &mut pool.faucet_coin, + balance::split(&mut input_balance, amount), // split函数用于将pool_balance减去对应于amount的数量,并返回值为amount的Balance + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.faucet_coin, input_balance); + }; + let output_balance = balance::split(&mut pool.my_coin, output_value); + let output = coin::from_balance(output_balance, ctx); + transfer::public_transfer(output, caller); +} + +public entry fun swap_my_coin_to_faucet_coin( + pool: &mut Pool, + input: Coin, + amount: u64, + ctx: &mut TxContext, +) { + let caller = tx_context::sender(ctx); + let input_value = coin::value(&input); + let output_value = amount * 2000 / 1000; + assert!(input_value >= amount, EInputNotEnough); + let mut input_balance = coin::into_balance(input); + assert!(balance::value(&pool.faucet_coin) >= output_value, EPoolNotEnough); + if (input_value > amount) { + balance::join( + &mut pool.my_coin, + balance::split(&mut input_balance, amount), + ); + let change = coin::from_balance(input_balance, ctx); + transfer::public_transfer(change, caller); + } else { + balance::join(&mut pool.my_coin, input_balance); + }; + let output_balance = balance::split(&mut pool.faucet_coin, output_value); + let output = coin::from_balance(output_balance, ctx); + transfer::public_transfer(output, caller); +} +``` + +swap函数同样使用了之前我们实现过的资金检查机制。 + +我们设置MY_COIN和FAUCET_COIN的兑换比例为 `1 : 2` 。 + +注意我的注释`// amount千万不要写成input_value!`,阅读一下代码,我们将会发现,如果将amount写成input_value,output_value将是以input_value为乘数的积,这将导致检查不通过或者严重的资金损失。 + +上面的被注释掉的代码是没有检查的实现。 + +对于一些需要注意的函数,例如`balance::split`,我已在代码注释中进行了简单的讲解,请仔细阅读。 + +除此之外,其他你认为需要注意的或者感到好奇的函数,请通过`sui move new`新建一个move项目,将本代码复制进去。如果你正确安装了插件,你按住`Ctrl`并点击该函数将可以跳转到对应的源码。 + +阅读源码永远是理解库最好的办法!!! \ No newline at end of file diff --git a/mover/ctianming/readme.md b/mover/ctianming/readme.md index f4e8090ff..b71b00663 100644 --- a/mover/ctianming/readme.md +++ b/mover/ctianming/readme.md @@ -41,9 +41,9 @@ - [x] play game hash:4ZBZBzcqpFX99GM2zExSFXgxM84X4Gw8n8xTYSg8ytMx ## 05 Move Swap -- [] swap package id : -- [] call swap CoinA-> CoinB hash : -- [] call swap CoinB-> CoinA hash : +- [x] swap package id :0x2ef63206c65f457cb14db4806f101958336ac4503675b04fd7120c7c33680ece +- [x] call swap CoinA-> CoinB hash :EmpHg9SsXB2S1m27JLQu3nsn3e8DKS4Cio739K7YRYRc +- [x] call swap CoinB-> CoinA hash :GZKdW1LguJ3yJqjvcP4WHjc3p25iPmZsyBbm3QWXSx5Z ## 06 Dapp-kit SDK PTB - [] save hash :