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]); } }