游戏内代币实战
本节将所学的代币知识应用于实际游戏场景,涵盖忠诚积分、游戏货币和代币兑换等常见模式。我们将设计一个完整的游戏经济系统,展示如何在 Move 中实现各种代币用例。
忠诚积分系统
忠诚积分是闭环代币的经典应用——积分只能通过特定操作获取,只能在指定商店消费:
module game::loyalty_points;
use std::string;
use sui::coin;
use sui::coin_registry;
use sui::token::{Self, Token, ActionRequest, TokenPolicy, TokenPolicyCap};
public struct LOYALTY_POINTS() has drop;
public struct ShopOwnerRule() has drop;
public struct ShopConfig has store {
shop_owner: address,
}
fun init(otw: LOYALTY_POINTS, ctx: &mut TxContext) {
let (initializer, treasury_cap) = coin_registry::new_currency_with_otw<LOYALTY_POINTS>(
otw, 0,
string::utf8(b"LP"),
string::utf8(b"Loyalty Points"),
string::utf8(b"Earn points, redeem rewards"),
string::utf8(b""),
ctx,
);
let metadata_cap = coin_registry::finalize(initializer, ctx);
let (mut policy, policy_cap) = token::new_policy(&treasury_cap, ctx);
token::add_rule_for_action<LOYALTY_POINTS, ShopOwnerRule>(
&mut policy, &policy_cap, token::spend_action(), ctx,
);
token::add_rule_config(
ShopOwnerRule(), &mut policy, &policy_cap,
ShopConfig { shop_owner: ctx.sender() }, ctx,
);
token::share_policy(policy);
transfer::public_transfer(policy_cap, ctx.sender());
transfer::public_transfer(treasury_cap, ctx.sender());
transfer::public_transfer(metadata_cap, ctx.sender());
}
/// 玩家完成任务后获得积分
public fun reward_player(
treasury_cap: &mut coin::TreasuryCap<LOYALTY_POINTS>,
amount: u64,
player: address,
ctx: &mut TxContext,
) {
let token = token::mint(treasury_cap, amount, ctx);
let request = token::transfer(token, player, ctx);
token::confirm_with_treasury_cap(treasury_cap, request, ctx);
}
/// 消费积分兑换奖励
public fun spend_points(
token: Token<LOYALTY_POINTS>,
policy: &TokenPolicy<LOYALTY_POINTS>,
ctx: &mut TxContext,
) {
let mut request = token::spend(token, ctx);
// 验证消费规则
let config: &ShopConfig = token::rule_config(ShopOwnerRule(), policy);
assert!(ctx.sender() == config.shop_owner || true); // 示例:任何人都可消费
token::add_approval(ShopOwnerRule(), &mut request, ctx);
token::confirm_request(policy, request, ctx);
}
游戏货币(双币系统)
许多游戏采用双币系统——一种免费获取的软币和一种需要购买的硬币:
module game::currencies;
use std::string;
use sui::coin::{Self, Coin, TreasuryCap};
use sui::coin_registry;
/// 软币:通过游戏获取,可自由转移
public struct GOLD() has drop;
/// 硬币:通过充值获取(固定供应或可控铸造)
public struct GEM() has drop;
/// 游戏商店
public struct GameShop has key {
id: UID,
gold_treasury: TreasuryCap<GOLD>,
gold_per_quest: u64,
gem_to_gold_rate: u64,
}
/// 初始化游戏货币
public fun init_gold(otw: GOLD, ctx: &mut TxContext): TreasuryCap<GOLD> {
let (initializer, treasury_cap) = coin_registry::new_currency_with_otw<GOLD>(
otw, 0,
string::utf8(b"GOLD"),
string::utf8(b"Gold"),
string::utf8(b"In-game currency earned by playing"),
string::utf8(b""),
ctx,
);
let metadata_cap = coin_registry::finalize(initializer, ctx);
transfer::public_transfer(metadata_cap, ctx.sender());
treasury_cap
}
/// 完成任务获得金币
public fun complete_quest(
shop: &mut GameShop,
player: address,
ctx: &mut TxContext,
) {
let reward = coin::mint(
&mut shop.gold_treasury, shop.gold_per_quest, ctx,
);
transfer::public_transfer(reward, player);
}
/// 用宝石兑换金币
public fun exchange_gem_for_gold(
shop: &mut GameShop,
gem: Coin<GEM>,
ctx: &mut TxContext,
): Coin<GOLD> {
let gem_amount = gem.value();
let gold_amount = gem_amount * shop.gem_to_gold_rate;
// 销毁宝石(需要 GEM 的 TreasuryCap)
// 铸造对应的金币
let gold = coin::mint(
&mut shop.gold_treasury, gold_amount, ctx,
);
transfer::public_transfer(gem, @0x0); // 简化处理
gold
}
代币兑换市场
实现一个简单的代币兑换合约:
module game::exchange;
use sui::coin::{Self, Coin};
use sui::balance::{Self, Balance};
public struct Exchange<phantom CoinA, phantom CoinB> has key {
id: UID,
reserve_a: Balance<CoinA>,
reserve_b: Balance<CoinB>,
rate_a_to_b: u64, // 1 A = rate 个 B(以最小单位计)
rate_b_to_a: u64,
}
const EInsufficientReserve: u64 = 1;
/// 创建兑换池
public fun create_exchange<CoinA, CoinB>(
initial_a: Coin<CoinA>,
initial_b: Coin<CoinB>,
rate_a_to_b: u64,
rate_b_to_a: u64,
ctx: &mut TxContext,
) {
let exchange = Exchange<CoinA, CoinB> {
id: object::new(ctx),
reserve_a: initial_a.into_balance(),
reserve_b: initial_b.into_balance(),
rate_a_to_b,
rate_b_to_a,
};
transfer::share_object(exchange);
}
/// 用 A 换 B
public fun swap_a_for_b<CoinA, CoinB>(
exchange: &mut Exchange<CoinA, CoinB>,
coin_a: Coin<CoinA>,
ctx: &mut TxContext,
): Coin<CoinB> {
let amount_a = coin_a.value();
let amount_b = amount_a * exchange.rate_a_to_b;
assert!(exchange.reserve_b.value() >= amount_b, EInsufficientReserve);
// 存入 A
exchange.reserve_a.join(coin_a.into_balance());
// 取出 B
let balance_b = exchange.reserve_b.split(amount_b);
balance_b.into_coin(ctx)
}
/// 用 B 换 A
public fun swap_b_for_a<CoinA, CoinB>(
exchange: &mut Exchange<CoinA, CoinB>,
coin_b: Coin<CoinB>,
ctx: &mut TxContext,
): Coin<CoinA> {
let amount_b = coin_b.value();
let amount_a = amount_b * exchange.rate_b_to_a;
assert!(exchange.reserve_a.value() >= amount_a, EInsufficientReserve);
exchange.reserve_b.join(coin_b.into_balance());
let balance_a = exchange.reserve_a.split(amount_a);
balance_a.into_coin(ctx)
}
奖励分发模式
按比例分发代币奖励的常见模式:
module game::rewards;
use sui::coin::{Self, Coin, TreasuryCap};
public struct RewardPool<phantom T> has key {
id: UID,
treasury: TreasuryCap<T>,
reward_per_action: u64,
total_distributed: u64,
max_distribution: u64,
}
const EPoolExhausted: u64 = 1;
public fun claim_reward<T>(
pool: &mut RewardPool<T>,
player: address,
ctx: &mut TxContext,
) {
assert!(
pool.total_distributed + pool.reward_per_action <= pool.max_distribution,
EPoolExhausted,
);
let reward = coin::mint(
&mut pool.treasury, pool.reward_per_action, ctx,
);
pool.total_distributed = pool.total_distributed + pool.reward_per_action;
transfer::public_transfer(reward, player);
}
public fun remaining_rewards<T>(pool: &RewardPool<T>): u64 {
pool.max_distribution - pool.total_distributed
}
测试游戏代币
#[test]
fun loyalty_reward_and_spend() {
use sui::test_scenario;
use sui::token::{Token, TokenPolicy};
let shop_owner = @0xSHOP;
let player = @0xPLAYER;
let mut scenario = test_scenario::begin(shop_owner);
// 初始化忠诚积分
init(LOYALTY_POINTS(), scenario.ctx());
// 奖励玩家
scenario.next_tx(shop_owner);
{
let mut tcap = scenario.take_from_sender<coin::TreasuryCap<LOYALTY_POINTS>>();
reward_player(&mut tcap, 100, player, scenario.ctx());
scenario.return_to_sender(tcap);
};
// 玩家消费积分
scenario.next_tx(player);
{
let token = scenario.take_from_sender<Token<LOYALTY_POINTS>>();
let policy = scenario.take_shared<TokenPolicy<LOYALTY_POINTS>>();
spend_points(token, &policy, scenario.ctx());
test_scenario::return_shared(policy);
};
scenario.end();
}
小结
- 忠诚积分适合使用闭环代币(Token),限制获取和消费渠道
- 双币系统(软币 + 硬币)是游戏经济的常见模式,软币用 Coin 实现,硬币可用固定供应
- 代币兑换可通过 Balance 管理储备池实现简单的定价机制
- 奖励分发模式需要控制总分发量,防止通胀
- 根据场景选择 Coin(自由转移)或 Token(受限操作),或两者结合