抽奖合约功能说明

1. 转盘模型抽奖合约功能说明

合约项目地址 github

1.1 功能

  • 合约owner 新增、删除管理员
  • 管理员角色 新建抽奖,设置抽奖活动规则
  • 管理员角色 新增奖品,设置中奖概率
  • 管理员角色 开启、关闭抽奖
  • 用户抽奖
  • 根据抽奖id查询抽奖信息
  • 根据抽奖id和奖品id查询奖品及中奖信息

1.2 合约结构

  • LotteryData合约:抽奖数据合约,记录新建的抽奖、奖品、概率、中奖信息。所有数据的修改只能通过外部逻辑合约执行。
  • LotteryCore合约:可操作抽奖数据合约。可新增admin角色,只有admin角色可执行逻辑合约的抽奖方法。owner角色管理admin角色。
  • LotteryCoreWithRule:带有各种规则限制的抽奖逻辑合约,继承自LotteryCore,在此基础上新增了各种规则限制,开发者可以自定义修改。具体规则可以参考合约里的数据结构注释。

数据逻辑结构分离,基本数据结构固定,外层逻辑更新不影响数据合约。

1.3 合约部署流程

  1. 使用 owner 地址作初始化参数,部署 LotteryData 合约
  2. 使用 owner 地址和 LotteryData 地址作参数,部署 LotteryCoreWithRule 合约(构造函数将设置msg.sender为admin)
  3. 调用 LotteryData.setLotteryCore 方法,参数为 LotteryCoreWithRule 地址,设置可执行数据合约的逻辑合约地址

1.4 LotteryCoreWithRule 逻辑合约功能操作步骤

  1. 部署 LotteryCoreWithRule 合约时,已设置 owner 同时为 admin 角色
  2. 使用 admin 角色的账户调用 LotteryCoreWithRule.createLottery 方法新建抽奖,参数为 _lotteryName及其他规则参数,返回 lotteryId
  3. 使用 admin 角色的账户调用 LotteryCoreWithRule.addLotteryPrize 向 lotteryId 的抽奖新增抽奖奖品,参数为 奖品名称,奖品数量,中奖概率倒数
  4. 重复步骤3,直到奖品设置完毕
  5. 使用 admin 角色的账户调用 LotteryCoreWithRule.startLottery 开启 id 为 lotteryId 的抽奖活动
  6. 用户参与抽奖调用 LotteryCoreWithRule.userDraw ,直到抽奖活动出发结束条件(奖品发完 或 admin 角色账户关闭抽奖活动)
  7. 查询 LotteryData.getLotteryPrizeInfo 奖品及用户中奖信息

1.5 LotteryData 数据合约功能操作步骤

  1. 根据抽奖id查询抽奖活动信息 LotteryData.getLotteryInfo(_lotteryId)
  2. 根据抽奖id查询奖品和中奖用户信息 LotteryData.getLotteryPrizeInfo(_lotteryId, prizeIndex)
  3. 根据抽奖id查询抽奖活动状态 LotteryData.getLotteryStatus(_lotteryId)
  4. 获取lotteries数组长度 LotteryData.getLotteriesLength()
  5. 获取抽奖的奖品数组长度 LotteryData.getLotteryPrizesLength()

1.6 LotteryCoreWithRule 合约注意事项

  • 新建合约时的输入参数为 owner地址、lotteryData地址
  • 新建抽奖活动时,活动的起始时间和结束时间都是时间戳,参数每天抽奖起始时间每天抽奖结束时间的输入都是整点数的整数值(如8:00则输入8)
  • 每天抽奖起始和结束时间的限制是按照UTC(+8)时区来限制的,其他时区请自行修改参数

1.7 LotteryCoreWithRule 自定义规则

struct LotteryRule {     
    uint startTime;         // 抽奖活动起始时间戳
    uint endTime;           // 抽奖活动结束时间戳
    uint daysStartTime;     // 每天抽奖起始时间,0 为不限制
    uint daysEndTime;       // 每天抽奖结束时间,0 为不限制
    uint participateCnt;    // 抽奖活动总次数限制, 0 为不限制
    uint perAddressPartCnt; // 每个地址能参与的抽奖次数,0为不限制
}

新建抽奖活动时的参数根据需求自定义抽奖规则

1.8 关键实现

  1. 计算中奖数组

在管理员启动抽奖时,合约方法计算此次抽奖的所有奖品的中奖概率倒数的最小公倍数,然后使用最小公倍数除以每个奖品的中奖概率,得出每个奖品的中奖数组。
例:
抽奖 TestLottery 的奖品信息及中奖数组

奖品名称 中奖概率 概率倒数 中奖数组
奖品A 1% 100 [0]
奖品B 2% 50 [1, 2]
奖品C 5% 20 [3, 4, 5, 6, 7]

如上:三种奖品的中奖概率倒数的最小公倍数为100,以100除以每个奖品的概率倒数,得到的结果做中奖数组的长度,所有结果的值依次递增。

  1. 用户抽奖随机逻辑

用户抽奖时,以抽奖时的uint(keccak256(拼接字符串 (上一块的blockhash + msg.sender + 全局自增index)))的uint值做随机数,对上面计算的最小公倍数取模,得到随机值。如果随机值结果在以上奖品的中奖数组里,即表示抽得对应的奖品。

1.9 注意事项

hash的产生依赖的是产生随机数的交易时的上一块的hash,迅雷链对外的交易确认是秒级确认,但并不会立即同步对外的交易记录,以此保证上一块交易hash不会被用来作恶。

2. 抽奖合约奖池模型

合约项目地址 github

该合约实现了抽奖活动奖池模型主要功能,共包含两个合约,LotteryControl控制合约与LotteryData数据合约,控制合约提供开发者访问合约的入口,数据合约处理、保存相关数据。

2.1 合约功能

  • 添加、移除抽奖合约管理员账户地址
  • 新建抽奖
  • 设置抽奖合约参数配置
  • 查看抽奖合约参数配置
  • 开启抽奖
  • 参与抽奖
  • 查询抽奖信息
  • 查询抽奖结果

2.2 合约部署

  1. 部署LotteryData合约

  2. 部署LotteryControl合约

  3. 调用LotteryData合约 setLotteryControl方法,设置LotteryControl合约地址

2.3 使用方法

开发者或用户通过调用LotteryControl合约方法进行合约交互

2.3.1 创建抽奖

开发者调用newLottery方法创建抽奖活动,该方法返回活动ID,开发者需保存活动ID,后续调用合约方法要求传入该活动ID;

创建后再调用setLotteryConfig进行活动规则设置,开发者可根据需求传入参数进行规则配置,配置参数说明如下。

  // 设置抽奖配置
  function setLotteryConfig (
    uint lotteryID,            //抽奖活动ID
    uint startTime,            //活动开始时间, Unix时间戳
    uint endTime,              //活动结束时间, Unix时间戳
    bool dayLimit,             //是否限制每天抽奖时间
    uint dayStartTime,         //每天抽奖开始时间
    uint dayEndTime,           //每天抽奖结束时间
    int timesPerItem,          //参与者抽奖次数限制
    uint amountPerAction,      //每次抽奖的token金额 
    uint openCondition,        //开奖条件,  抽奖模式为 0:奖池模式,奖池金额达到指定数量开奖  1:开奖时间模式,达到指定时间开奖(Unix时间戳)  2:地址模式,参与用户账户地址达到指定数量开奖
    uint copies                //奖品平分成几份发放
    )

调用setLotteryConfig后,抽奖活动自动生效。

2.3.2 参与抽奖

用户调用joinLottery方法参与抽奖,若满足抽奖规则,则成功参与,用户可在抽奖开启后查看抽奖结果;

2.3.3 开启抽奖

奖池金额、参与用户数量固定模式

抽奖活动满足开奖条件时,合约内部会触发OpenLottery事件,开发者可通过监听该事件判断可以已开启抽奖,然后调用调用openLottery开启抽奖。若抽奖活动结束,仍未监听事件发生,开发者直接调用openLottery开启抽奖。

开奖时间固定模式

开发者在开奖时间到达后调用openLottery开启抽奖。

2.3.4 查询抽奖

开启抽奖后抽奖活动最终有以下两种状态,开发者或用户可通过调用queryLotteryResult方法查看结果

  • 满足开奖条件,合约内部自动执行抽奖,给中奖用户发送奖品,活动状态为已开奖
  • 不满足开奖条件,合约内部自动退还参与抽奖用户相应的token,活动状态为已退款

2.4 抽奖机制

奖池内全部token平分成N份(开发者可修改合约参数配置), 依次将每份奖励随机分配给抽奖参与用户,一个用户可能获得多份奖励。