一、游戏思路1、初始化一个游戏池,合约发布者拥有向游戏池存钱和从游戏池取钱的权限2、玩家玩游戏,押注一定额度代币,玩家猜硬币正反面和合约随机生成的boolean值比较,若一致则玩家赢,从游戏池拿出等额代币给玩家,若不一致则玩家输,玩家押注的代币存入游戏池二、代码设置错误码//错误码
1、初始化一个游戏池,合约发布者拥有向游戏池存钱和从游戏池取钱的权限
2、玩家玩游戏,押注一定额度代币,玩家猜硬币正反面和合约随机生成的boolean值比较,若一致则玩家赢,从游戏池拿出等额代币给玩家,若不一致则玩家输,玩家押注的代币存入游戏池
// 错误码
const EPLAYERNOTENOUGH:u64 = 101; // 玩家额度不够
const EGAMEPOOLNOTENOUGH:u64 = 102; // 游戏池额度不够
const EINPUTNOTRIGHT: u64 = 103; // 玩家输入信息不正确
const EDEPOSITNOTENOUGH:u64 = 104; // 存款额度不够
游戏池结构体是玩家共享的,都可以参与游戏
游戏池中显示的是代币balance,要注意和coin的区别
// 游戏池结构体
public struct GamePool has key {
id: UID, // 资金池id
name: String, // 资金池名称
balance: Balance<PUBLICCOIN> // 资金池余额,存放的是PUBLICCOIN,所以显示的是PUBLICCOIN的余额
}
// 游戏池控制权结构体,拥有这个控制权的地址才能从游戏池取钱
public struct AdminCap has key {
id: UID
}
// 初始化init
fun init (ctx: &mut TxContext) {
let game_pool = GamePool {
id: object::new(ctx),
name: utf8(b"资金池"),
balance: balance::zero()
};
// 共享资金池,所有玩家都可以看到
transfer::share_object(game_pool);
let admin_cap = AdminCap{ id: object::new(ctx) };
// 转移资金池的控制权限给合约发布者,只有合约发布者才能取钱
// 此处transfer和public_transfer有区别,注意结构体ability
transfer::transfer(admin_cap, ctx.sender());
}
理论上应该只有合约发布者可以向游戏池存钱,此处没有设置权限,玩家也可向游戏池存钱.
这里存钱,不一定是将账户所有代币都存入资金池,所以这里需要coin和balance的转换
// 游戏池存钱
public entry fun deposit(game_pool: &mut GamePool, input_coin: Coin<PUBLICCOIN>, coin_amount: u64, ctx: &mut TxContext) {
// 判断存钱账户额度是否足够
let coin_value = input_coin.value();
assert!(coin_value >= coin_amount, EDEPOSITNOTENOUGH);
// 拆出要存储的代币额度,储存到游戏池
let mut input_balance = coin::into_balance(input_coin);
let split_balance = input_balance.split(coin_amount);
game_pool.balance.join(split_balance);
// 将剩余代币返还给存钱账户
let refund_coin = coin::from_balance(input_balance, ctx);
transfer::public_transfer(refund_coin, ctx.sender());
}
只有合约发布者可以取钱,限制权限
将balance转成coin,然后转移到用户地址
// 合约发布者(有AdminCap权限)取钱
public entry fun withdrow(_:&AdminCap, game_pool: &mut GamePool, coin_amount: u64, ctx: &mut TxContext) {
// 判断游戏池里的balance是否足够
let game_pool_balance = game_pool.balance.value();
assert!(game_pool_balance >= coin_amount, EGAMEPOOLNOTENOUGH);
// 取钱:把balance转换成coin,然后把coin转移给合约发布者
let withdrow_coin = coin::take(&mut game_pool.balance, coin_amount, ctx);
transfer::public_transfer(withdrow_coin, ctx.sender());
}
// 玩游戏,参数:游戏池、玩家猜字、质押代币、质押代币额度、随机数、TxContent
entry fun play (game_pool: &mut GamePool, input_guess: u64, input_coin: &mut Coin<PUBLICCOIN>, coin_amount: u64, r: &Random, ctx: &mut TxContext) {
// 获取传入代币的额度
let coin_value = input_coin.value();
// 判断玩家有没有足够的代币额度
assert!(coin_value >= coin_amount, EPLAYERNOTENOUGH);
// 获取游戏池中代币额度
let game_pool_value:u64 = game_pool.balance.value();
// 判断游戏池有没有足够的代币额度
assert!(game_pool_value >= coin_amount, EGAMEPOOLNOTENOUGH);
// 判断输入参数是否正确
assert!(input_guess == 1 || input_guess == 2, EINPUTNOTRIGHT);
// 开始玩游戏
// 创建一个随机数生成器
let mut random_generator = random::new_generator(r, ctx);
let random_bool = random::generate_bool(&mut random_generator);
if((input_guess == 1 && random_bool) || (input_guess == 2 && !random_bool)) { // 猜中
// 先从游戏池拆出blance,再转换成coin,再转移给玩家
let reward_blance = balance::split(&mut game_pool.balance, coin_amount);
let reward_coin = coin::from_balance(reward_blance, ctx);
transfer::public_transfer(reward_coin, ctx.sender());
}else { // 猜错
// 玩家拆出coin,转换成balance,再转移到游戏池
let player_coin = input_coin.split(coin_amount, ctx);
let player_balance = coin::into_balance(player_coin);
game_pool.balance.join(player_balance);
}
}
/*
猜硬币正反面
质押一定额度代币
输入 1-true代表正面 2-false代表反面
系统随机数生成boolean
比较:两者一致从游戏池拿走同样额度的代币;不一致质押代币放入游戏池
*/
module mygame::mygame;
use std::string::{String, utf8};
use publiccoin::publiccoin::PUBLICCOIN; // 引用的另一个合约
use sui::balance;
use sui::balance::Balance;
use sui::coin;
use sui::coin::Coin;
use sui::object;
use sui::random;
use sui::random::Random;
use sui::transfer;
// 错误码
const EPLAYERNOTENOUGH:u64 = 101; // 玩家额度不够
const EGAMEPOOLNOTENOUGH:u64 = 102; // 游戏池额度不够
const EINPUTNOTRIGHT: u64 = 103; // 玩家输入信息不正确
const EDEPOSITNOTENOUGH:u64 = 104; // 存款额度不够
// 游戏池结构体
public struct GamePool has key {
id: UID, // 资金池id
name: String, // 资金池名称
balance: Balance<PUBLICCOIN> // 资金池余额,存放的是PUBLICCOIN,所以显示的是PUBLICCOIN的余额
}
// 游戏池控制权结构体,拥有这个控制权的地址才能从游戏池取钱
public struct AdminCap has key {
id: UID
}
// 初始化init
fun init (ctx: &mut TxContext) {
let game_pool = GamePool {
id: object::new(ctx),
name: utf8(b"资金池"),
balance: balance::zero()
};
// 共享资金池,所有玩家都可以看到
transfer::share_object(game_pool);
let admin_cap = AdminCap{ id: object::new(ctx) };
// 转移资金池的控制权限给合约发布者,只有合约发布者才能取钱
// 此处transfer和public_transfer有区别,注意结构体ability
transfer::transfer(admin_cap, ctx.sender());
}
// 游戏池存钱
public entry fun deposit(game_pool: &mut GamePool, input_coin: Coin<PUBLICCOIN>, coin_amount: u64, ctx: &mut TxContext) {
// 判断存钱账户额度是否足够
let coin_value = input_coin.value();
assert!(coin_value >= coin_amount, EDEPOSITNOTENOUGH);
// 拆出要存储的代币额度,储存到游戏池
let mut input_balance = coin::into_balance(input_coin);
let split_balance = input_balance.split(coin_amount);
game_pool.balance.join(split_balance);
// 将剩余代币返还给存钱账户
let refund_coin = coin::from_balance(input_balance, ctx);
transfer::public_transfer(refund_coin, ctx.sender());
}
// 玩游戏,参数:游戏池、玩家猜字、质押代币、质押代币额度、随机数、TxContent
entry fun play (game_pool: &mut GamePool, input_guess: u64, input_coin: &mut Coin<PUBLICCOIN>, coin_amount: u64, r: &Random, ctx: &mut TxContext) {
// 获取传入代币的额度
let coin_value = input_coin.value();
// 判断玩家有没有足够的代币额度
assert!(coin_value >= coin_amount, EPLAYERNOTENOUGH);
// 获取游戏池中代币额度
let game_pool_value:u64 = game_pool.balance.value();
// 判断游戏池有没有足够的代币额度
assert!(game_pool_value >= coin_amount, EGAMEPOOLNOTENOUGH);
// 判断输入参数是否正确
assert!(input_guess == 1 || input_guess == 2, EINPUTNOTRIGHT);
// 开始玩游戏
// 创建一个随机数生成器
let mut random_generator = random::new_generator(r, ctx);
let random_bool = random::generate_bool(&mut random_generator);
if((input_guess == 1 && random_bool) || (input_guess == 2 && !random_bool)) { // 猜中
// 先从游戏池拆出blance,再转换成coin,再转移给玩家
let reward_blance = balance::split(&mut game_pool.balance, coin_amount);
let reward_coin = coin::from_balance(reward_blance, ctx);
transfer::public_transfer(reward_coin, ctx.sender());
}else { // 猜错
// 玩家拆出coin,转换成balance,再转移到游戏池
let player_coin = input_coin.split(coin_amount, ctx);
let player_balance = coin::into_balance(player_coin);
game_pool.balance.join(player_balance);
}
}
// 合约发布者(有AdminCap权限)取钱
public entry fun withdrow(_:&AdminCap, game_pool: &mut GamePool, coin_amount: u64, ctx: &mut TxContext) {
// 判断游戏池里的balance是否足够
let game_pool_balance = game_pool.balance.value();
assert!(game_pool_balance >= coin_amount, EGAMEPOOLNOTENOUGH);
// 取钱:把balance转换成coin,然后把coin转移给合约发布者
let withdrow_coin = coin::take(&mut game_pool.balance, coin_amount, ctx);
transfer::public_transfer(withdrow_coin, ctx.sender());
}
该合约依赖了publiccion合约,需要将其作为依赖引入
1、在Move.toml文件中的dependencies引入
[dependencies]
Sui = { git = "https://gitee.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
publiccoin = { local = "../../task2/publiccoin" }
# 第一种引入方式:远程引入
# 将引入的合约放到git仓库,git 为https地址,subdir 为引入合约Move.toml文件所在的文件夹,rev为分支、标签或者提交哈希。
# 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" }
2、在合约中将需要用到的依赖结构体、方法等引入
use publiccoin::publiccoin::PUBLICCOIN; // 引用的另一个合约
coin和balance都是sui标准库提供的功能,可以相互转换。结合以下合约代码,学习理解。
1、coin转换为balance
// 游戏池存钱----方法一
public entry fun deposit(game_pool: &mut GamePool, input_coin: Coin<PUBLICCOIN>, coin_amount: u64, ctx: &mut TxContext) {
// 判断存钱账户额度是否足够
let coin_value = input_coin.value();
assert!(coin_value >= coin_amount, EDEPOSITNOTENOUGH);
// 拆出要存储的代币额度,储存到游戏池
let mut input_balance = coin::into_balance(input_coin);
let split_balance = input_balance.split(coin_amount);
game_pool.balance.join(split_balance);
// 将剩余代币返还给存钱账户
let refund_coin = coin::from_balance(input_balance, ctx);
transfer::public_transfer(refund_coin, ctx.sender());
}
// 游戏池存钱----方法二
public entry fun deposit_V2(game_pool: &mut GamePool, input_coin: &mut Coin<PUBLICCOIN>, coin_amount: u64, ctx: &mut TxContext) {
// 判断存钱账户额度是否足够
let coin_value = input_coin.value();
assert!(coin_value >= coin_amount, EDEPOSITNOTENOUGH);
// 先拆出要存储的coin,然后转换成balance,存入游戏池
let split_coin = input_coin.split(coin_amount,ctx);
// 转换成balance
let split_balance = coin::into_balance(split_coin);
// 存入游戏池
game_pool.balance.join(split_balance);
}
从存钱方法一中可以看到,合约先将传入的coin转换为balance,然后再将balance拆分,之后再将剩余balance转换成coin,再将coin转移给存钱用户。其中参数input_coin为不可变变量。
从存钱方法二中可以看到,合约先将传入的coin拆分出一个要存入额度的coin,然后再将这个coin转换成balance,再将balance存入游戏池。其中参数input_coin为可变变量,并且不需要将剩余coin再进行转移。
2、balance转换为coin
// 合约发布者(有AdminCap权限)取钱----方法一
public entry fun withdrow(_:&AdminCap, game_pool: &mut GamePool, coin_amount: u64, ctx: &mut TxContext) {
// 判断游戏池里的balance是否足够
let game_pool_balance = game_pool.balance.value();
assert!(game_pool_balance >= coin_amount, EGAMEPOOLNOTENOUGH);
// 取钱:把balance转换成coin,然后把coin转移给合约发布者,这里拆分和转换同时进行
let withdrow_coin = coin::take(&mut game_pool.balance, coin_amount, ctx);
transfer::public_transfer(withdrow_coin, ctx.sender());
}
// 合约发布者(有AdminCap权限)取钱----方法二
public entry fun withdrow_V2(_:&AdminCap, game_pool: &mut GamePool, coin_amount: u64, ctx: &mut TxContext) {
// 判断游戏池里的balance是否足够
let game_pool_balance = game_pool.balance.value();
assert!(game_pool_balance >= coin_amount, EGAMEPOOLNOTENOUGH);
// 先拆分出balance
let split_balance = balance::split(&mut game_pool.balance, coin_amount);
// 再将拆分出的balance转为coin
let split_coin = coin::from_balance(split_balance, ctx);
// 转移
transfer::public_transfer(split_coin, ctx.sender());
}
从取钱方法一中可以看到,coin::take()方法将拆分balance和balance转化为coin同时进行了,直接得到了需要转移的coin。
从取钱方法二中可以看到,拆分balance和将balance转为coin是分开进行的。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!