基于以太坊的福利**solidity智能合约设计
1.**规则设计
- 合约中包含一个管理员来负责开奖和退奖。
- 每次购买**的金额固定。(例子设为1 ether)
- 每人可以多次**。**次数越多,获奖概率越大。
- 所有人在规定时间内可以**。过了**时间后30分钟内,管理员可以开奖。
- 如果管理员只能在过了**时间后的30分钟内开奖,并且如果在该时间段内未开奖,就无法开奖。
- 如果管理员未在规定时间内开奖,那么可以后面给每个用户退款。金额为每个用户**的金额。
- 开奖或者退款后,**期数自动加一,下次**时间和结束**时间各加一天。
- 开奖时,合约会根据当前区块的挖矿难度、时间、已**人数等信息进行哈希运算,并对已**人数取模。所得结果为本期中奖人的索引值。
- 中奖人将按比例获取奖池内的以太坊,管理员会按比例抽水用于平台的维护。(我暂时设定为中奖人拿80%,管理员抽水20%,这个比例可以在合约中修改)
注:我使用的solidity编译器版本为0.5.0
2.合约实现
2.1 创建合约结构
pragma solidity^0.5.0;
contract Lottery {
//管理员地址
address payable manager;
//中奖人地址
address payable winner;
//**人地址集合
address payable [] lotteryPlayers;
//中奖人的编号
uint public winningNum;
//**期数
uint public roundNum;
//中奖人获奖比例(例:当获奖金比例为80%时,该值为80.)
uint public rewardRate;
//中奖人获得奖金的具体金额
uint public winningReward;
//每次**的金额
uint public lotteryBet;
//每次管理员可以开奖的起始时间点
uint public drawStartTime;
//每次管理员可以开奖的结束时间点
uint public drawEndTime;
constructor() public {
//创建合约者自动为管理员
manager = msg.sender;
//奖池内以太坊的80%给中奖者
rewardRate = 80;
//每次限投 1 ether
lotteryBet = 1 ether;
drawStartTime = now;
//允许开奖时间为 30 minutes
drawEndTime = now +30 minutes;
}
根据自己的需求,可在构造函数中修改对应值。
2.2 **函数
function throwIn() public payable{
//保证彩民**的金额为额定金额
require(msg.value == lotteryBet);
//只有在开奖时间之前才可以**
require(now < drawStartTime);
//将该彩民的地址添加进数组
lotteryPlayers.push(msg.sender);
}
任何人都可以**,并且可以多次**。概率上讲,你**次数越多,中奖的概率越大。
2.3 开奖函数
//修饰器,确保只有管理员有权限来操作
modifier managerLimit {
require(msg.sender == manager);
_;
}
//事务,用于测试
//event test(uint,uint);
function draw() public managerLimit {
//确保当前盘内有人**
require(lotteryPlayers.length != 0);
//确保在允许的开奖时间段内
require(now >= drawStartTime && now < drawEndTime);
//利用当前区块的时间戳、挖矿难度和盘内**彩民数来取随机值
bytes memory randomInfo = abi.encodePacked(now,block.difficulty,lotteryPlayers.length);
bytes32 randomHash =keccak256(randomInfo);
//利用随机值来取获奖人在数组中的索引
winningNum = uint(randomHash)%lotteryPlayers.length;
//确定中奖人地址
winner=lotteryPlayers[winningNum];
//根据当前盘内以太坊总额来确定本次中奖人可得到的奖金
winningReward = address(this).balance*rewardRate/100;
//转账给中奖人
winner.transfer(winningReward);
//用于测试
//emit test(reward,address(this).balance);
//给管理员抽水
manager.transfer(address(this).balance);
//**期数+1
roundNum++;
//下次开奖开始时间和结束时间增加1天
drawStartTime+=1 days;
drawEndTime+=1 days;
//清空本次**者数组
delete lotteryPlayers;
}
2.4 辅助函数
设置一些辅助函数用于测试和被前端web3调用
//返回当前奖池中以太坊的总额
function getBalance()public view returns(uint){
return address(this).balance;
}
//返回已结束的一期**的中奖地址
function getWinner()public view returns(address){
return winner;
}
//返回管理员的地址
function getManager()public view returns(address){
return manager;
}
//返回当前参与到**的彩民地址集合
function getLotteryPlayers() public view returns(address payable [] memory){
return lotteryPlayers;
}
//返回当前参与到**的彩民人数
function getPlayersNum() public view returns(uint){
return lotteryPlayers.length;
}
2.5 退款函数
//只有管理员才能发出退款操作
function refund()public managerLimit{
//确保此时盘内有人参与**
require(lotteryPlayers.length != 0);
//只有在开奖结束时间之后才可以进行退款操作
require(now>=drawEndTime);
uint lenLotteryPlayers = lotteryPlayers.length;
//给本轮所有参与的**人退款,款额为额定款额
for(uint i = 0; i<lenLotteryPlayers;i++){
lotteryPlayers[i].transfer(lotteryBet);
}
//因为流局,期数+1
roundNum++;
//因为流局,下一次开奖开始时间和开奖结束时间增加一天
drawStartTime+=1 days;
drawEndTime+=1 days;
//清空参与的**人集合
delete lotteryPlayers;
}
如果管理员在开奖时间内没有如期开奖,那么只能通过给所有**人发起退款。产生流局,并进入下一期。
3.尾语
由于管理员没有及时开奖,而导致的退款,所有手续费都由管理员承担。
如果管理员不愿意承担退款费用,那么**将永远无法进入下一期,即管理员也无法从中获取抽水。所以说,这个**设计也是一种基于卖家和买家双方获益博弈的一种双赢设计。
只有大家都遵守游戏规则,才能获得最大利益。
ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
公众号名称:后现代泼痞浪漫主义奠基人