pragma solidity ^0.4.24; contract NintyNineBillion { /** * VARIABLES */ address public admin; bool public jackpotAwarded; uint256 public totalVolume; uint256 public payIndex; uint256 public increase = 1001; //100.1% uint256 public minBuy = 10000000000; //10000 trx uint256 public goal = 99000000000000000; //approximate trx supply mapping (address => uint256) public totalBought; mapping (address => uint256) public totalPaid; bool public lottoAwarded; uint256 public highestNumber; address public currentWinner; mapping (address => bool) hasPlayed; mapping (address => uint256) lottoNumber; Order[] public orders; struct Order { uint256 amount; address owner; } /** * CONSTRUCTOR */ constructor() public { admin = msg.sender; } /** * EVENTS */ event newOrder( address indexed buyer, uint256 amount, uint256 totalVol ); event jackpotWon( address indexed winner, uint256 jackpotAmount ); event lottoPlayed( address indexed player, uint256 number ); event lottoWon( address indexed winner, uint256 prizeAmount ); event queueMoved( address indexed payee, uint256 amount, uint256 index ); /** * USER FUNCTIONS */ function() public payable { buy(address(0)); } function buy(address _ref) public payable { //min buy to prevent dust clogging up the queue require (msg.value >= minBuy); address referrer = _checkRef(_ref); uint256 valueMultiplied = (msg.value * increase) / 1000; //1.001x uint256 prizeAmount = msg.value / 1000; //0.1% uint256 refAmount = msg.value / 2000; //0.05% //force send to prevent malicious contracts blocking buys if (!referrer.send(refAmount)) {} admin.transfer(refAmount); totalVolume += msg.value; totalBought[msg.sender] += msg.value; orders.push( Order({ amount: valueMultiplied, owner: msg.sender }) ); if (totalVolume >= goal && !jackpotAwarded) { uint256 prize = getJackpotPrize(); jackpotAwarded = true; if (!msg.sender.send(prize)) {} emit jackpotWon(msg.sender, prize); } _processQueue(msg.value - (prizeAmount + (refAmount * 2))); emit newOrder(msg.sender, msg.value, totalVolume); } function forceQueue() public { //under normal circumstances there should be no extra value in the contract //this provides a way to process any funds accidential stuck in the contract require (availableBalance() > 0 && payIndex < orders.length); _processQueue(availableBalance()); } function playLotto() external returns(uint256) { require (!hasPlayed[msg.sender]); uint256 num = _pseudoRNG(); hasPlayed[msg.sender] = true; lottoNumber[msg.sender] = num; if (num > highestNumber) { highestNumber = num; currentWinner = msg.sender; } emit lottoPlayed(msg.sender, num); return num; } function claimLottoPrize() external { require (!lottoAwarded); require (jackpotAwarded = true); require (totalVolume > goal); require (msg.sender == currentWinner); lottoAwarded = true; uint256 prize = getLottoPrize(); if (!msg.sender.send(prize)) {} emit lottoWon(msg.sender, prize); } /** * ADMIN FUNCTIONS */ function closeContract() public { require (msg.sender == admin); require (now > 1561075200); //june 21, 2019 forceQueue(); //send anything available to the queue admin.transfer(address(this).balance); } /** * VIEW FUNCTIONS */ function getQueueLength() external view returns(uint256) { return orders.length; } function getAmountToGoal() external view returns(uint256) { return totalVolume < goal ? goal - totalVolume : 0; } function contractBalance() external view returns(uint256) { return address(this).balance; } function availableBalance() public view returns(uint256) { return address(this).balance > (totalVolume / 1000) ? address(this).balance - (totalVolume / 1000) : 0; } function getLottoPrize() public view returns(uint256) { return !jackpotAwarded ? ((totalVolume / 1000) * 20) / 100 : totalVolume / 1000; } function getJackpotPrize() public view returns(uint256) { return !jackpotAwarded ? ((totalVolume / 1000) * 80) / 100 : 0; } function totalOwed(address _user) public view returns(uint256) { uint256 owed = totalBought[_user] * increase; if (totalPaid[_user] < owed) { return owed - totalPaid[_user]; } else { return 0; } } /** * INTERNAL FUNCTIONS */ function _processQueue(uint256 _value) internal { uint256 value = _value; while (payIndex < orders.length && value > 0) { Order storage order = orders[payIndex]; if (value < order.amount) { totalPaid[order.owner] += value; order.amount -= value; uint256 tempValue = value; value = 0; //force send to prevent malicious contracts holding up queue if (!order.owner.send(tempValue)) {} } else { totalPaid[order.owner] += order.amount; value -= order.amount; tempValue = order.amount; order.amount = 0; if (!order.owner.send(tempValue)) {} emit queueMoved(order.owner, order.amount, payIndex); payIndex++; } } } function _checkRef(address _ref) internal view returns(address) { address referrer = _ref; if (_ref == msg.sender || _ref == address(0)) { referrer = admin; } return referrer; } function _pseudoRNG() public view returns(uint256) { return uint256( keccak256( abi.encodePacked( blockhash(block.number - 1), block.coinbase, msg.sender ) ) ) % 99000000000; } // function testWithdraw() external { // require (msg.sender == admin); // admin.transfer(address(this).balance); // } }
0.4.24