solidity抢红包合约的实现

抢红包合约

1.实现功能
编写一个抢红包的智能合约,该合约应该包含以下功能:

  1. 发红包人可以设置红包的口令,类型,最多抢红包人数和红包有效时间。
  2. 可以查看某个人的某个红包当前剩余的钱。
  3. 可以查看当前所有红包剩余的钱。
  4. 可以查看当前红包中还有钱的对应的所有发钱包人的地址。
  5. 可以查看某个红包的抢红包结果。
  6. 可以查看某个人(地址)在某个发红包人发的某个红包中抢到了多少钱。
  7. 一个人可以发送多个红包,每个人对同一个红包只能够抢一次。
  8. 好友功能,只有红包拥有者的好友能够抢红包。
  9. 红包需要设置有效期,有效期内好友能够抢红包,有效期过后红包的拥有者能够撤回红包(仅仅在红包的超过有效期后,红包拥有者能够撤回红包)。
  10. 红包合约同时支持存在多个红包,并且任意的人能够调用红包合约不断新添加红包,每个红包对应自己的口令,抢红包的人需要输入该口令才能抢红包。
  11. 支持平分和随机分红包。

2.代码及注释

  • 本合约的运行版本及语言

在这里插入图片描述

  • 代码
pragma solidity ^0.4.0;

contract red_envelope{
    
    
    address private owner;		//保存合约主人的地址
    
    uint uid = 0;		//记录当前发红包人的编号
    address[] user_ad;	//记录当前发红包人的地址

    mapping(address => mapping(address => uint)) Friends;	//检查某个人是否是发红包人的好友的映射
    mapping(uint => address) I2U;		//发红包人编号到地址的映射
    mapping(address => Red_List) U2RE;	//发红包人地址到发送的红包的映射
    
    //红包的结构体
    struct Red_Envelope{
        mapping(address => uint) rewards;       //rewards用于保存每个人(地址)领取了多少红包
        uint left_money;		//left_money用于保存红包现在还剩下多少钱
        uint passwd;	//红包口令
        uint _type;		//红包类型
        uint max_receiver;		//红包最多可以有多少人收取
        uint average_money;		//若使用平均抢钱,红包平均钱的多少
        uint num;		// num用于保存当前已经有多少人领过红包了
        uint[] result;	//抢红包人的抢红包结果
        address[] ad_;  //抢到红包人的地址	
        uint end_time;	//红包的持续时间
    }
    
    //红包列表的结构体(一个人可以发送多个红包)
    struct Red_List{
        Red_Envelope[] red;		//用于存储多个红包
        uint[] lm;		//用于存储每个红包当前还剩下多少钱
        uint reid;		//用于存储红包的id(个数-1)
    }

    
    constructor() public payable {
        owner = msg.sender;		//设置第一个调用合约的人是合约的拥有者
    }
        
    //创建红包
    function create_RedEnvelope(uint password, uint __type, uint max_re, uint time_length) public payable {
        
        I2U[uid] = msg.sender;
        user_ad.push(msg.sender);
        uid++;
        address(this).send(msg.value);		//将钱发送到该合约中
        
        require(	//抢红包人数不能为0
            max_re != 0,
            "You can't set the receiver number as 0"
        );
        
        //对红包进行初始化并放入红包列表中
        U2RE[msg.sender].red.push(Red_Envelope({
            left_money: msg.value,
            passwd: password,
            _type: __type,
            max_receiver: max_re,
            average_money: msg.value / max_re,
            num: 0,
            result: new uint[](0),
            ad_: new address[](0),
            end_time: now + time_length
        }));
        //
        U2RE[msg.sender].reid++;
        U2RE[msg.sender].lm.push(msg.value);
    }
 
 
 	//抢随机红包
    function get_random_money(uint uid, uint reid, uint password) public payable returns(uint){
    	//根据发红包人id uid和红包id reid找到想要抢的红包
        address user = I2U[uid]; 		
        Red_Envelope storage re = U2RE[user].red[reid];
        
        require(		//检查口令
            re.passwd == password,
            "Wrong Password!"    
        );
        require(		//检查红包类型
            re._type == 0,
            "This red envelope is randomly distributed!" 
        );
        require(		//检查当前抢红包的人是否以及抢了红包了
            re.rewards[msg.sender] == 0,
            "You have received your red envelope!"
        );
        require(		//检查红包是否在有效期
            now < re.end_time,
            "This red envelope is expired!"
        );
        require(		//检查抢红包的人是否是发红包人的好友
            Friends[user][msg.sender] == 1,
            "You haven't added the sender's friend!"
        );
        
        require(		//检查红包是否还有钱
        
            re.left_money > 0,
            "The red envelope is empty!"
            );	
            
        if (re.num == re.max_receiver - 1){		//对随机抢红包的最后一个人特殊处理:将剩余所有钱都给最后一个人
        	re.rewards[msg.sender] = re.left_money;		//将最后一个人和抢到的红包存储起来
        	re.result.push(re.left_money);		//将剩下的钱保存到抢钱记录数组中
        	re.ad_.push(msg.sender);			//将最后一个人的地址保存到抢钱地址数组中
            msg.sender.send(re.left_money);		//发送钱给最后一个人
            
            U2RE[I2U[uid]].lm[reid] -= re.left_money;		//将当前红包的剩余钱清0
            
            re.left_money = 0;
            
            if (re.left_money == 0){	//在红包被抢完后,将发送人的地址从保存发送红包人地址数组中删除
                uint index;
                for (uint i = 0; i < user_ad.length; i++){
                    if (I2U[uid] == user_ad[i]){
                        index = i;
                        break;
                    }
                }
                for (uint j = index; j < user_ad.length - 1; j++){
                    user_ad[j] = user_ad[j+1];
                }
                delete user_ad[user_ad.length - 1];
                user_ad.length -= 1;
            }
            
            return re.left_money;
        }


        //根据当前块的时间戳,区块的难度,以及是第几个抢的红包生成一个0~99随机数
        uint random = uint8(uint256(keccak256(block.timestamp, block.difficulty, re.num))%100);
        uint temp = random * re.left_money / 100; 		//生成随机比例的红包
        
        re.left_money -= temp;		//将红包发给抢红包的人
        msg.sender.send(temp);
        
        U2RE[I2U[uid]].lm[reid] -= temp;		//跟新将当前红包的剩余钱
        
        re.rewards[msg.sender] = temp;		//保存某个人和这个人抢的红包的钱
        re.result.push(temp);
        re.ad_.push(msg.sender);
        
        re.num++;
        return re.left_money;

    }
    
	//抢平分红包
    function get_average_money(uint uid, uint reid, uint password) public payable {
        address user = I2U[uid]; 
        Red_Envelope storage re = U2RE[user].red[reid];
        
        require(
            re.passwd == password,
            "Wrong Password!"    
        );
        require(
            re._type == 1,
            "This red envelope is evenly distributed!" 
        );
        require(
            re.rewards[msg.sender] == 0,
            "You have received your red envelope!"
        );
        require(
            now < re.end_time,
            "This red envelope is expired!"
        );
        require(
            Friends[user][msg.sender] == 1,
            "You haven't added the sender's friend!"
        );
        
        require(		//若当前抢红包的人数还没有到限制的人数
            re.num < re.max_receiver,
            "This red envelope reached its limit on the maximum number of receivers"
        );
        require(
            re.left_money > 0,
            "The red envelope is empty!"
        );		//若红包还有钱

        

        re.left_money -= re.average_money;
        msg.sender.send(re.average_money);
        
        U2RE[I2U[uid]].lm[reid] -= re.average_money;
        
        re.rewards[msg.sender] = re.average_money;		//保存某个人和这个人抢的红包的钱
        re.result.push(re.average_money);
        re.ad_.push(msg.sender);
        
        re.num++;
        //若平分红包的时候遇到了无法平分的情况,将剩余钱都给最后一个人
        if (re.num == re.max_receiver && re.left_money != 0){
            re.rewards[msg.sender] += re.left_money;
            re.result[re.num-1] += re.left_money;
            msg.sender.send(re.left_money);
            
            U2RE[I2U[uid]].lm[reid] -= re.average_money;
            
            re.left_money = 0;
        }
        
        if (re.left_money == 0){
            uint index;
            for (uint i = 0; i < user_ad.length; i++){
                if (I2U[uid] == user_ad[i]){
                    index = i;
                    break;
                }
            }
            for (uint j = index; j < user_ad.length - 1; j++){
                user_ad[j] = user_ad[j+1];
            }
            delete user_ad[user_ad.length - 1];
            user_ad.length -= 1;
        }
    }
    
    
    //获取当前红包内还有钱的所有发红包人地址
    function get_sender() public view returns(address[]) {
        return user_ad;
    }
    
    //获取某个发红包人所有红包的余额
    function get_re(uint uid) public view returns(uint[]) {
        return U2RE[I2U[uid]].lm;
    }
    
    
    //获得某个人抢红包抢了多少钱
    function get_rewards(uint uid, uint reid, address _ad) public view returns(uint) {
        address user = I2U[uid]; 
        Red_Envelope storage re = U2RE[user].red[reid];
        
        return re.rewards[_ad];
    }
    
    //获得某个发红包人的某个红包的余额
    function get_leftmoney(uint uid, uint reid) public view returns(uint) {
        address user = I2U[uid]; 
        Red_Envelope storage re = U2RE[user].red[reid];
        
        return re.left_money;
    }
    
    //获得某个发红包人的某个红包的抢红包情况
    function get_result(uint uid, uint reid) public view returns(uint, address, uint[]) {
        address user = I2U[uid]; 
        Red_Envelope storage re = U2RE[user].red[reid];
        
        uint max = 0;
        if(re.num == re.max_receiver){
            re.num -= 1;
        }
        for(uint i = 1; i <= re.num; i++){
            if (re.result[max] < re.result[i])
                max = i;
        }
        return (max, re.ad_[max], re.result);		//返回运气王的编号,运气王的地址以及所有抢红包的情况
    }
    
    //添加给定发红包人编号的好友
    function add_friend(uint id) public {
        address user = I2U[id];        
        Friends[user][msg.sender] = 1;
    }
    
    //若时间超过且红包还有钱,发红包人可以将钱取回
    function withdraw(uint uid, uint reid) public payable{
        address user = I2U[uid];
        Red_Envelope storage re = U2RE[user].red[reid];
        
        require(
            now > re.end_time,
            "You can't withdraw your money before the red envelope is due!"
        );
        require(
            re.left_money > 0,
            "This red envelope is empty!"
        );
        require(
            msg.sender == user,
            "You are not the owner of the red envelope!"
        );
        msg.sender.send(re.left_money);
        re.left_money = 0;
    }
}

代码优化版本:

pragma solidity ^0.4.0;

contract red_envelope{
    
    address private owner;		//保存红包发起方的地址
    
    uint uid = 0;
    address[] user_ad;

    mapping(address => mapping(address => uint)) Friends;
    mapping(uint => address) I2U;
    mapping(address => Red_List) U2RE;
    
    struct Red_Envelope{
        mapping(address => uint) rewards;       //rewards用于保存每个人(地址)领取了多少红包
        uint left_money;		//left_money用于保存红包现在还剩下多少钱
        uint passwd;
        uint _type;
        uint max_receiver;
        uint average_money;
        uint num;		// num用于保存当前已经有多少人领过红包了
        uint[] result;
        address[] ad_;  
        uint end_time;      
    }

    
    struct Red_List{
        Red_Envelope[] red;
        uint[] lm;
        uint reid;
    }

    
    constructor() public payable {
        
        owner = msg.sender;		//设置
    }
    
    
    function create_RedEnvelope(uint password, uint __type, uint max_re, uint time_length) public payable {
        
        bool flag = true;
        
        for (uint i = 0; i < user_ad.length; i++){
            if (user_ad[i] == msg.sender){
                flag = false;
                break;
            }
        }
        
        if (flag){
            user_ad.push(msg.sender);
            I2U[uid] = msg.sender;
            uid++;    
        }
        
        address(this).send(msg.value);
        
        require(
            msg.value > 0,
            "The red envelope's money should lager than 0!"
        );
        
        require(
            max_re != 0,
            "You can't set the receiver number as 0"
        );
        require(
            __type == 0 || __type == 1,
            "Please enter 0 or 1 to select the red envelope type, 0 for randomly, 1 for evenly!"
        );
        
        U2RE[msg.sender].red.push(Red_Envelope({
            left_money: msg.value,
            passwd: password,
            _type: __type,
            max_receiver: max_re,
            average_money: msg.value / max_re,
            num: 0,
            result: new uint[](0),
            ad_: new address[](0),
            end_time: now + time_length
        }));
        U2RE[msg.sender].reid++;
        U2RE[msg.sender].lm.push(msg.value);
    }
 
    function get(uint uid, uint reid, uint password) public payable {
        address user = I2U[uid]; 
        Red_Envelope storage re = U2RE[user].red[reid];
        
        require(
            re.passwd == password,
            "Wrong Password!"    
        );
        
        // require(
        //     re._type == 0,
        //     "This red envelope is randomly distributed!" 
        // );
        
        require(
            re.rewards[msg.sender] == 0,
            "You have received your red envelope!"
        );
        require(
            now < re.end_time,
            "This red envelope is expired!"
        );
        require(
            Friends[user][msg.sender] == 1,
            "You haven't added the sender's friend!"
        );
        
        require(
            re.left_money > 0,
            "The red envelope is empty!"
            );		//若红包还有钱
            
        if (re._type == 0){
            get_random_money(uid, reid, password);
        }
        
        else if (re._type == 1){
            get_average_money(uid, reid, password);
        }
            
    }
 
 
    function get_random_money(uint uid, uint reid, uint password) public payable returns(uint){
        address user = I2U[uid]; 
        Red_List storage rel = U2RE[user];
        Red_Envelope storage re = rel.red[reid];
        
        if (re.num == re.max_receiver - 1){		//最多5个人,若已经是第5个人了,将红包剩余的钱都给第5个人
        	re.rewards[msg.sender] = re.left_money;
        	re.result.push(re.left_money);
        	re.ad_.push(msg.sender);
            msg.sender.send(re.left_money);
            
            rel.lm[reid] = 0;
            
            re.left_money = 0;
            isDelete(user, rel, re);
            
            return re.left_money;
        }

        //根据当前块的时间戳,区块的难度,以及是第几个抢的红包生成一个0~99随机数
        uint random = uint8(uint256(keccak256(block.timestamp, block.difficulty, re.num))%100);
        uint temp = random * re.left_money / 100; 		//生成随机比例的红包
        
        re.left_money -= temp;		//将红包发给抢红包的人
        msg.sender.send(temp);
        
        rel.lm[reid] -= temp;
        
        re.rewards[msg.sender] = temp;		//保存某个人和这个人抢的红包的钱
        re.result.push(temp);
        re.ad_.push(msg.sender);
        
        re.num++;
        return re.left_money;

    }
    

    function get_average_money(uint uid, uint reid, uint password) payable {
        address user = I2U[uid]; 
        Red_List storage rel = U2RE[user];
        Red_Envelope storage re = rel.red[reid];
        
        require(
            re.num < re.max_receiver,
            "This red envelope reached its limit on the maximum number of receivers"
        );

        re.left_money -= re.average_money;
        msg.sender.send(re.average_money);
        
        U2RE[I2U[uid]].lm[reid] -= re.average_money;
        
        re.rewards[msg.sender] = re.average_money;		//保存某个人和这个人抢的红包的钱
        re.result.push(re.average_money);
        re.ad_.push(msg.sender);
        
        re.num++;
        if (re.num == re.max_receiver && re.left_money != 0){
            re.rewards[msg.sender] += re.left_money;
            re.result[re.num-1] += re.left_money;
            msg.sender.send(re.left_money);
            
            U2RE[I2U[uid]].lm[reid] = 0;
            
            re.left_money = 0;
        }
        
        isDelete(user, rel, re);
    }
    
    
    function isDelete(address user, Red_List rel, Red_Envelope re) internal {
        
            bool flag = true;
            if (re.left_money == 0){
                uint index;
                
                for (uint i = 0; i < user_ad.length; i++){
                    if (user == user_ad[i]){
                        index = i;
                        break;
                    }
                }
                
                for (uint j = 0; j < rel.lm.length; j++){
                    if (rel.lm[j] != 0){
                        flag = false;
                        break;
                    }
                }
                
                if (flag){
                    for (uint k = index; k < user_ad.length - 1; k++){
                        user_ad[k] = user_ad[k+1];
                    }
                    delete user_ad[user_ad.length - 1];
                    user_ad.length -= 1;
                }
            }
    }
   
    
    
    function get_sender() public view returns(address[]) {
        return user_ad;
    }
    
    
    function get_re(uint uid) public view returns(uint[]) {
        return U2RE[I2U[uid]].lm;
    }
    
    
    //获得某个人抢红包抢了多少钱
    function get_rewards(uint uid, uint reid, address _ad) public view returns(uint) {
        address user = I2U[uid]; 
        Red_Envelope storage re = U2RE[user].red[reid];
        
        return re.rewards[_ad];
    }
    
    
    function get_leftmoney(uint uid, uint reid) public view returns(uint) {
        address user = I2U[uid]; 
        Red_Envelope storage re = U2RE[user].red[reid];
        
        return re.left_money;
    }
    
    
    function get_result(uint uid, uint reid) public view returns(uint, address, uint[]) {
        address user = I2U[uid]; 
        Red_Envelope storage re = U2RE[user].red[reid];
        
        uint max = 0;
        if(re.num == re.max_receiver){
            re.num -= 1;
        }
        for(uint i = 1; i <= re.num; i++){
            if (re.result[max] < re.result[i])
                max = i;
        }
        return (max, re.ad_[max], re.result);
    }
    
    function add_friend(uint id) public {
        address user = I2U[id]; 
        
        Friends[user][msg.sender] = 1;
    }
    
    
    function withdraw(uint uid, uint reid) public payable{
        address user = I2U[uid];
        Red_Envelope storage re = U2RE[user].red[reid];
        
        require(
            now > re.end_time,
            "You can't withdraw your money before the red envelope is due!"
        );
        require(
            re.left_money > 0,
            "This red envelope is empty!"
        );
        require(
            msg.sender == user,
            "You are not the owner of the red envelope!"
        );
        msg.sender.send(re.left_money);
        re.left_money = 0;
        U2RE[user].lm[reid] = 0;
        isDelete(user, U2RE[user], re)
    }
}

3.使用合约

  • 抢随机红包,设置红包大小为3000000wei,口令为123,最多可以有4个人抢红包,并且存活时间为300秒。
    创建红包。
    在这里插入图片描述
    在这里插入图片描述
  • 可以查询到该红包余额已经有3000000wei了

在这里插入图片描述

  • 若不是好友无法抢红包。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 以下用户添加发红包人的好友。

    在这里插入图片描述
    在这里插入图片描述

  • 开始抢随机红包。

在这里插入图片描述

  • 可以查看当前所有红包内还有余额的发送红包人地址。

在这里插入图片描述
在这里插入图片描述

  • 第5个人无法抢红包了。

在这里插入图片描述

  • 查看该红包的抢红包结果,分别是运气王的编号,地址和抢红包的结果

在这里插入图片描述

  1. 抢平均红包,设置红包大小为1000000wei,口令为1234,最多可以有3个人抢红包,并且存活时间为300秒。

在这里插入图片描述

在这里插入图片描述

  • 此下三个人抢红包

在这里插入图片描述

  • 查看红包发送者的地址和红包余额

在这里插入图片描述
在这里插入图片描述

  • 第一个人抢红包

在这里插入图片描述

  • 检查抢完后红包余额

在这里插入图片描述

  • 抢完后查看抢红包结果

在这里插入图片描述

  • 红包抢完后自动删除发红包人的地址,此下是其他人的地址

在这里插入图片描述

  1. 抢平均红包,设置红包大小为3000000wei,口令为123,最多可以有3个人抢红包,并且存活时间为1秒。

在这里插入图片描述
在这里插入图片描述

  • 创建好红包一秒后就已经无法抢红包了。

  • 如下账户添加好友后选择取钱

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 发生过期异常

在这里插入图片描述

  • 但该红包中还是有余额的

在这里插入图片描述

  • 发红包人可以通过withdraw将红包中的钱拿回。

在这里插入图片描述

在这里插入图片描述

  • 钱已经收回了,但是运行撤回操作需要花费一定量的gas。

在这里插入图片描述

  1. 一个人可以发多个红包

在这里插入图片描述

  • 上述用户发送了如下两个红包

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 可以看到上述发红包人地址是第四个,因此其地址编号为3,并且存在其他发红包人的地址

在这里插入图片描述

  • 查找编号为3的人发送的所有红包的余额如下

在这里插入图片描述

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值