Sui Move 实现一个简单的无需管理的自动开奖系统 —— Self-service lottery

一:功能简介

  • 任何人都可以发布彩票,自定义售价、奖金、终止日期等基础信息。
  • 任何人都可以购买彩票并获得对应的购买凭证。
  • 当彩票售罄或者超过售卖日期时,任何人都可以调用开奖函数,获奖信息将公示。
  • 获奖者可凭票根兑奖,奖金将全额打入获奖者账户,与此同时,此彩票的收入将一分为二, 1 % \text 1\% 1% 将打入该彩票系统发布者账户,剩下的将打入该彩票发布者账户。
  • 若中奖者长期不兑奖,提供回收奖金功能,奖金将打回该彩票发布者账户,而彩票收入依旧按照上述比例分配。

二:结构设计

2.1 彩票系统

维护发布的所有彩票信息,以及系统发布者的收入。

public struct LotterySystem has key {
    id: UID,
    lotteries: Table<ID, Lottery>,
    ls_income: Balance<SUI>,
}

2.2 彩票

维护彩票信息,包括剩余可售数量、终止日期、单价、总奖金数等。

public struct Lottery has store {
    lottery_name: String,
    price: u64,
    total_amount: u64,
    remaining_amount: u64,
    end_epoch: u64,
    bonus: Balance<SUI>,
    announcement: bool,
    winner_code: vector<u8>,
    income: Balance<SUI>,
    creater: address,
}

2.3 购买凭证

存储有所购买的彩票的唯一编码,以及对应的兑奖码。

public struct LotteryStub has key {
    id: UID,
    lottery_id: ID,
    code: vector<u8>,
}

2.4 一次性见证与事件

为了让系统管理员提现更安全,使用 O n e \mathit {One} One- T i m e \mathit {Time} Time- W i t n e s s \mathit {Witness} Witness 来生成唯一的Publisher并转移给他/她。

public struct SELF_SERVICE_LOTTERY has drop {}

为了让系统运行过程中的一些流程更加公开透明,在关键节点将触发事件信息进行公示。

public struct NewLotteryEvent has copy, drop {
    lottery_id: ID,
    lottery_name: String,
    price: u64,
    total_amount: u64,
    end_epoch: u64,
    bonus_value: u64,
}

public struct WinEvent has copy, drop {
    lottery_name: String,
    winner_code: vector<u8>,
}

public struct EndEvent has copy, drop {
    hint: String,
}

三:功能实现

3.1 初始化

生成唯一的Publisher,同时构造并共享LotterySystem

fun init(otw: SELF_SERVICE_LOTTERY, ctx: &mut TxContext) {
    // generate Publisher and transfer it
    package::claim_and_keep(otw, ctx);
    // generate LotterySystem and share it
    transfer::share_object(LotterySystem {
        id: object::new(ctx),
        lotteries: table::new<ID, Lottery>(ctx),
        ls_income: balance::zero(),
    });
}

3.2 系统构建者提现

Publisher只用作身份确认,所以用_来表示函数中并不会实际使用。
当且仅当收入不为零时,将所有收入转入个人账户。

entry fun withdraw(_: &Publisher, lottery_system: &mut LotterySystem, ctx: &mut TxContext) {
    // check coin value
    let amount = lottery_system.ls_income.value();
    assert!(amount > 0, ENotIncome);
    // withdraw income
    transfer::public_transfer(coin::from_balance(lottery_system.ls_income.split(amount), ctx), ctx.sender());
}

3.3 创建彩票

根据是否自定义可售期限,提供了两个方法:create_lotterycreate_lottery_with_epoch,它们都将接受彩票的基本信息来生成一套可供售卖的彩票,唯一的区别是可售期限是否为默认。一旦创建成功,将触发事件信息公示这一款全新的彩票的基础信息。

entry fun create_lottery(lottery_system: &mut LotterySystem, lottery_name: String, price: u64, total_amount: u64, bonus: Coin<SUI>, ctx: &mut TxContext) {
    // default continuous 15 epoch
    create_lottery_with_epoch(lottery_system, lottery_name, price, total_amount, 15, bonus, ctx);
}

entry fun create_lottery_with_epoch(lottery_system: &mut LotterySystem, lottery_name: String, price: u64, total_amount: u64, continuous_epoch: u64, bonus: Coin<SUI>, ctx: &mut TxContext) {
    // check bonus value
    assert!(bonus.value() > 0, ENotBonus);
    // generate Lottery ID
    let lottery_id = object::id_from_address(ctx.fresh_object_address());
    // generate Lottery
    let lottery = Lottery {
        lottery_name,
        price,
        total_amount,
        remaining_amount: total_amount,
        end_epoch: ctx.epoch() + continuous_epoch,
        bonus: bonus.into_balance(),
        announcement: false,
        winner_code: vector<u8>[],
        income: balance::zero(),
        creater: ctx.sender(),
    };
    // emit event
    event::emit(NewLotteryEvent {
        lottery_id,
        lottery_name,
        price,
        total_amount,
        end_epoch: ctx.epoch() + continuous_epoch,
        bonus_value: lottery.bonus.value(),
    });
    // store it
    lottery_system.lotteries.add(lottery_id, lottery);
}

3.4 赎回奖金

当且仅当该套彩票一张未售,或者开奖后超过了兑奖期限时,可以通过此方法赎回奖金。

entry fun fedeem_bonus(lottery_system: &mut LotterySystem, lottery_id: ID, ctx: &mut TxContext) {
    // check lottery_id
    assert!(lottery_system.lotteries.contains(lottery_id), ENotCorrectLotteryID);
    // get Lottery
    let mut lottery = lottery_system.lotteries.remove(lottery_id);
    // check sales status and epoch
    assert!(lottery.total_amount == lottery.remaining_amount || ctx.epoch() > lottery.end_epoch + 15, ENotFedeem);
    calculate_fee(lottery_system, &mut lottery.income);
    destroy_lottery(lottery, ctx);
}

fun calculate_fee(lottery_system: &mut LotterySystem, income: &mut Balance<SUI>) {
    // calc fee amount(1%)
    let amount = income.value() / 100;
    if (amount > 0) {
        lottery_system.ls_income.join(income.split(amount));
    };
}

fun destroy_lottery(lottery: Lottery, ctx: &mut TxContext) {
    let Lottery {
        lottery_name: _,
        price: _,
        total_amount: _,
        remaining_amount: _,
        end_epoch: _,
        bonus,
        announcement: _,
        mut winner_code,
        mut income,
        creater,
    } = lottery;

    // destroy winner_code
    while (winner_code.length() > 0) {
        winner_code.pop_back();
    };
    winner_code.destroy_empty();

    // destroy bonus
    if (bonus.value() > 0) {
        income.join(bonus);
    } else {
        bonus.destroy_zero();
    };

    // transfer income
    transfer::public_transfer(coin::from_balance(income, ctx), creater);
}

其中,calculate_fee的功能是计算将提供给系统管理者多少分成,destroy_lottery是摧毁一套彩票,将里面所包含的 S U I \mathit {SUI} SUI 转入个人账户。
在转账过程中,bonusincome不可能都为零,而且同时支持赎回奖金以及中奖者兑奖后的彩票收入提现。

3.5 开奖公告

当彩票售罄时将自动触发开奖,如果超过了购买日期,所有人也可以手动触发。
随机过程使用了hmac_sha3_256来生成一系列数字,并对这一系列数字进行运算得到第几个购买者为中奖者,再用同样的方法生成该顾客手上的兑奖码进行公示。

entry fun announcement(lottery_system: &mut LotterySystem, lottery_id: ID, ctx: &mut TxContext) {
    // check lottery_id
    assert!(lottery_system.lotteries.contains(lottery_id), ENotCorrectLotteryID);
    // get Lotter
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值