抢红包合约
1.实现功能
编写一个抢红包的智能合约,该合约应该包含以下功能:
- 发红包人可以设置红包的口令,类型,最多抢红包人数和红包有效时间。
- 可以查看某个人的某个红包当前剩余的钱。
- 可以查看当前所有红包剩余的钱。
- 可以查看当前红包中还有钱的对应的所有发钱包人的地址。
- 可以查看某个红包的抢红包结果。
- 可以查看某个人(地址)在某个发红包人发的某个红包中抢到了多少钱。
- 一个人可以发送多个红包,每个人对同一个红包只能够抢一次。
- 好友功能,只有红包拥有者的好友能够抢红包。
- 红包需要设置有效期,有效期内好友能够抢红包,有效期过后红包的拥有者能够撤回红包(仅仅在红包的超过有效期后,红包拥有者能够撤回红包)。
- 红包合约同时支持存在多个红包,并且任意的人能够调用红包合约不断新添加红包,每个红包对应自己的口令,抢红包的人需要输入该口令才能抢红包。
- 支持平分和随机分红包。
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个人无法抢红包了。
- 查看该红包的抢红包结果,分别是运气王的编号,地址和抢红包的结果
- 抢平均红包,设置红包大小为1000000wei,口令为1234,最多可以有3个人抢红包,并且存活时间为300秒。
- 此下三个人抢红包
- 查看红包发送者的地址和红包余额
- 第一个人抢红包
- 检查抢完后红包余额
- 抢完后查看抢红包结果
- 红包抢完后自动删除发红包人的地址,此下是其他人的地址
- 抢平均红包,设置红包大小为3000000wei,口令为123,最多可以有3个人抢红包,并且存活时间为1秒。
-
创建好红包一秒后就已经无法抢红包了。
-
如下账户添加好友后选择取钱
-
发生过期异常
- 但该红包中还是有余额的
- 发红包人可以通过withdraw将红包中的钱拿回。
- 钱已经收回了,但是运行撤回操作需要花费一定量的gas。
- 一个人可以发多个红包
- 上述用户发送了如下两个红包
- 可以看到上述发红包人地址是第四个,因此其地址编号为3,并且存在其他发红包人的地址
- 查找编号为3的人发送的所有红包的余额如下