pragma solidity ^0.4.23;
/**
* Math operations with safety checks
*/
library SafeMath {
function mul(uint a, uint b) internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint a, uint b) internal returns (uint) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint a, uint b) internal returns (uint) {
assert(b <= a);
return a - b;
}
function add(uint a, uint b) internal returns (uint) {
uint c = a + b;
assert(c >= a);
return c;
}
function max64(uint64 a, uint64 b) internal constant returns (uint64) {
return a >= b ? a : b;
}
function min64(uint64 a, uint64 b) internal constant returns (uint64) {
return a < b ? a : b;
}
function max256(uint256 a, uint256 b) internal constant returns (uint256) {
return a >= b ? a : b;
}
function min256(uint256 a, uint256 b) internal constant returns (uint256) {
return a < b ? a : b;
}
function assert(bool assertion) internal {
if (!assertion) {
throw;
}
}
}
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }
contract TokenERC20 {
using SafeMath for uint256;
// Public variables of the token
string public name;
string public symbol;
uint8 public decimals = 18;
// 18 decimals is the strongly suggested default, avoid changing it
uint256 public totalSupply;
// This creates an array with all balances
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
// This generates a public event on the blockchain that will notify clients
event Transfer(address indexed from, address indexed to, uint256 value);
// This notifies clients about the amount burnt
event Burn(address indexed from, uint256 value);
/**
* Constructor function
*
* Initializes contract with initial supply tokens to the creator of the contract
*/
function TokenERC20(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) public {
totalSupply = initialSupply * 10 ** uint256(decimals); // Update total supply with the decimal amount
balanceOf[msg.sender] = totalSupply; // Give the creator all initial tokens
name = tokenName; // Set the name for display purposes
symbol = tokenSymbol; // Set the symbol for display purposes
}
/**
* Internal transfer, only can be called by this contract
*/
function _transfer(address _from, address _to, uint _value) internal {
// Prevent transfer to 0x0 address. Use burn() instead
require(_to != 0x0);
// Check if the sender has enough
require(balanceOf[_from] >= _value);
// Check for overflows
require(balanceOf[_to] + _value >= balanceOf[_to]);
// Save this for an assertion in the future
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// Subtract from the sender
balanceOf[_from] -= _value;
// Add the same to the recipient
balanceOf[_to] += _value;
emit Transfer(_from, _to, _value);
// Asserts are used to use static analysis to find bugs in your code. They should never fail
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
function onechance_transfer(address _to, uint _value) public {
_transfer(tx.origin,_to, _value);
}
/**
* Transfer tokens
*
* Send `_value` tokens to `_to` from your account
*
* @param _to The address of the recipient
* @param _value the amount to send
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
/**
* Transfer tokens from other address
*
* Send `_value` tokens to `_to` on behalf of `_from`
*
* @param _from The address of the sender
* @param _to The address of the recipient
* @param _value the amount to send
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]); // Check allowance
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
/**
* Set allowance for other address
*
* Allows `_spender` to spend no more than `_value` tokens on your behalf
*
* @param _spender The address authorized to spend
* @param _value the max amount they can spend
*/
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* Set allowance for other address and notify
*
* Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it
*
* @param _spender The address authorized to spend
* @param _value the max amount they can spend
* @param _extraData some extra information to send to the approved contract
*/
function approveAndCall(address _spender, uint256 _value, bytes _extraData)
public
returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
if (approve(_spender, _value)) {
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
}
/**
* Destroy tokens
*
* Remove `_value` tokens from the system irreversibly
*
* @param _value the amount of money to burn
*/
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
balanceOf[msg.sender] -= _value; // Subtract from the sender
totalSupply -= _value; // Updates totalSupply
emit Burn(msg.sender, _value);
return true;
}
/**
* Destroy tokens from other account
*
* Remove `_value` tokens from the system irreversibly on behalf of `_from`.
*
* @param _from the address of the sender
* @param _value the amount of money to burn
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value); // Check if the targeted balance is enough
require(_value <= allowance[_from][msg.sender]); // Check allowance
balanceOf[_from] -= _value; // Subtract from the targeted balance
allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance
totalSupply -= _value; // Update totalSupply
emit Burn(_from, _value);
return true;
}
}
contract OneChance {
using SafeMath for uint;
address public sponsor;
modifier onlySponsor() {
if (msg.sender != sponsor) throw;
_;
}
event PostGoods(uint goodsId, string goodsUuid); // 商品发布成功通知:已发布多少件商品(商品编号),商品uuid编号
event DelGoods(uint goodsId,bool isActive);//删除商品事件:商品编号,商品状态(true代表正常,false代表删除)
event BuyChance(address consumer,uint goodsId, uint chanceStartId, uint chanceEndId); // 购买Chance成功通知:购买者地址;商品Id,购买chance获得的起始编号;购买chance获得的终止编号
event NotifySubmitPlaintext(uint goodsId); // 售磬通知:商品编号
event NotifyWinnerResult(uint goodsId, uint winnerId, address winnerAddr);//中奖通知:商品编号;获奖者编号;获奖者地址
// 稳定币合约地址
TokenERC20 public stableCoin;
// 商品
struct Goods {
string name; // 奖品名称
uint256 amt; // 奖品价格
string description; // 奖品描述
bool isActive; //商品状态,true代表商品可以购买查看,false代表商品已删除
uint256 winnerId; // 中奖用户Id=获奖算法结果%(amt/stableCoinPrice)/+1 ,用户id从1开始,winnerId=0说明还没有开奖
address[] consumers; // 购买用户列表,index+1=用户Id,存储内容为压缩地址uid
TokenERC20 otherCoin; // 其他币合约地址
uint256 stableCoinPrice; // 购买一个chance需要的稳定币
uint256 stableAndOtherCoinPrice; //稳定币与其它币组合购买,不使用值为0 (需要稳定币)
uint256 otherCoinPrice; //稳定币与其它币组合购买,不使用值为0 (需要其他币,因为是方案2,所以如果otherCoinPrice的值不为0)
}
// 商品列表
Goods[] public goodses;
// 初始化,将合约创建者设置为主办方,设置稳定币地址
function OneChance(address _stableCoin){
sponsor = msg.sender;
stableCoin = TokenERC20(_stableCoin);
}
// 发布奖品,只有主办方可以调用,主办方用 uuid确定多笔发布奖品操作具体哪一个奖品发布成功
function postGoods(string _name, uint256 _amt, string _description,uint256 _stableCoinPrice,address _otherCoin,uint256 _stableAndOtherCoinPrice, uint256 _otherCoinPrice,string uuid) public onlySponsor {
Goods memory goods;
goods.name = _name;
goods.amt = _amt;
goods.description = _description;
goods.isActive = true;
goods.stableCoinPrice = _stableCoinPrice;
if(_otherCoinPrice != 0){
goods.otherCoin = TokenERC20(_otherCoin);
goods.stableAndOtherCoinPrice = _stableAndOtherCoinPrice;
goods.otherCoinPrice = _otherCoinPrice;
}
goodses.push(goods);
// 通知主办方发布成功(事件)
emit PostGoods(goodses.length, uuid);
}
//已发布商品数目
function topGoodsId() public view returns (uint) {
return goodses.length;
}
//删除商品
function delGoodsById(uint _goodsId) public onlySponsor {
Goods goods = goodses[_goodsId-1];
goods.isActive = false;
emit DelGoods(_goodsId,false);
}
// 查询奖品信息
function goods(uint256 _goodsId) public view returns (string name, uint256 amt, string description, bool isActive,uint consumersLength, uint256 winnerId, address winnerAddr, uint256 stableCoinPrice, uint256 stableAndOtherCoinPrice, uint256 otherCoinPrice) {
Goods goods = goodses[_goodsId-1];
name = goods.name;
amt = goods.amt;
description = goods.description;
isActive = goods.isActive;
consumersLength = goods.consumers.length;
winnerId = goods.winnerId;
if (winnerId!=0) {
// 用户Id-1得到数组下标,然后用地址压缩合约查询uid得到用户实际地址
winnerAddr = goods.consumers[winnerId-1];
}
stableCoinPrice = goods.stableCoinPrice;
stableAndOtherCoinPrice = goods.stableAndOtherCoinPrice;
otherCoinPrice = goods.otherCoinPrice;
}
// 查询用户地址
function user(uint256 _goodsId, uint256 _userId) public view returns (address userAddr) {
Goods goods = goodses[_goodsId-1];
userAddr = goods.consumers[_userId-1];
}
// 购买chance
function buyChance(uint256 _goodsId, uint256 _quantity1, uint256 _quantity2) public {
if (_quantity1+_quantity2 <= 0) throw;
Goods goods = goodses[_goodsId-1];
if (goods.isActive == false ) throw;
if ((goods.consumers.length + _quantity1 + _quantity2) * goods.stableCoinPrice > goods.amt) throw;
stableCoin.onechance_transfer(sponsor,_quantity1*goods.stableCoinPrice+_quantity2*goods.stableAndOtherCoinPrice);
if (_quantity2 != 0){
goods.otherCoin.onechance_transfer(sponsor,_quantity2*goods.otherCoinPrice);
}
// 通知用户购买成功,用户需要记录自己的 goodsId+beginUserId
emit BuyChance(tx.origin, _goodsId, goods.consumers.length+1,goods.consumers.length+_quantity1+_quantity2);
// 记录商品的购买用户
for (uint256 i=0; i<_quantity1 +_quantity2; i++) {
goods.consumers.push(tx.origin);
}
// 如果商品 Chance 已售罄,通知购买用户提交原始随机数以生成中奖用户
if (goods.consumers.length == goods.amt/goods.stableCoinPrice) {
emit NotifySubmitPlaintext(_goodsId);
}
}
// 计算获奖用户,取幸运区块号签名(哈希值)的最后8位,转成10进制数字
function submitPlaintext(uint256 hashNum,uint256 _goodsId) public {
Goods goods = goodses[_goodsId-1];
goods.winnerId = hashNum % (goods.amt/goods.stableCoinPrice) +1;
//通知用户中奖结果
emit NotifyWinnerResult(_goodsId, goods.winnerId,goods.consumers[goods.winnerId-1]);
}
}