基于以太坊的福利彩票solidity智能合约设计

基于以太坊的福利彩票solidity智能合约设计

    • 1.投注规则设计
    • 2.合约实现
      • 2.1 创建合约结构
      • 2.2 投注函数
      • 2.3 开奖函数
      • 2.4 辅助函数
      • 2.5 退款函数
    • 3.尾语

1.投注规则设计

  1. 合约中包含一个管理员来负责开奖和退奖。
  2. 每次购买彩票的金额固定。(例子设为1 ether)
  3. 每人可以多次投注。投注次数越多,获奖概率越大。
  4. 所有人在规定时间内可以投注。过了投注时间后30分钟内,管理员可以开奖。
  5. 如果管理员只能在过了投注时间后的30分钟内开奖,并且如果在该时间段内未开奖,就无法开奖。
  6. 如果管理员未在规定时间内开奖,那么可以后面给每个用户退款。金额为每个用户投注的金额。
  7. 开奖或者退款后,彩票期数自动加一,下次投注时间和结束投注时间各加一天。
  8. 开奖时,合约会根据当前区块的挖矿难度、时间、已投注人数等信息进行哈希运算,并对已投注人数取模。所得结果为本期中奖人的索引值。
  9. 中奖人将按比例获取奖池内的以太坊,管理员会按比例抽水用于平台的维护。(我暂时设定为中奖人拿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神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
后现代泼痞浪漫主义奠基人
公众号名称:后现代泼痞浪漫主义奠基人

你可能感兴趣的:(以太坊智能合约)