pragma solidity 0.4.20;
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a / b;
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function Ownable() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract Claimable is Ownable {
address public pendingOwner;
modifier onlyPendingOwner() {
require(msg.sender == pendingOwner);
_;
}
function transferOwnership(address newOwner) onlyOwner public {
pendingOwner = newOwner;
}
function claimOwnership() onlyPendingOwner public {
OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
}
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
modifier whenNotPaused() {
require(!paused);
_;
}
modifier whenPaused() {
require(paused);
_;
}
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
library SafeERC20 {
function safeTransfer(ERC20Basic token, address to, uint256 value) internal {
assert(token.transfer(to, value));
}
function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal {
assert(token.transferFrom(from, to, value));
}
function safeApprove(ERC20 token, address spender, uint256 value) internal {
assert(token.approve(spender, value));
}
}
contract CanReclaimToken is Ownable {
using SafeERC20 for ERC20Basic;
function reclaimToken(ERC20Basic token) external onlyOwner {
uint256 balance = token.balanceOf(this);
token.safeTransfer(owner, balance);
}
}
contract JackpotAccessControl is Claimable, Pausable, CanReclaimToken {
address public cfoAddress;
function JackpotAccessControl() public {
cfoAddress = msg.sender;
}
modifier onlyCFO() {
require(msg.sender == cfoAddress);
_;
}
function setCFO(address _newCFO) external onlyOwner {
require(_newCFO != address(0));
cfoAddress = _newCFO;
}
}
contract JackpotBase is JackpotAccessControl {
using SafeMath for uint256;
bool public gameStarted;
address public gameStarter;
address public lastPlayer;
address public player2;
address public player3;
address public player4;
address public player5;
uint256 public lastWagerTimeoutTimestamp;
uint256 public player2Timestamp;
uint256 public player3Timestamp;
uint256 public player4Timestamp;
uint256 public player5Timestamp;
uint256 public timeout;
uint256 public nextTimeout;
uint256 public minimumTimeout;
uint256 public nextMinimumTimeout;
uint256 public numberOfWagersToMinimumTimeout;
uint256 public nextNumberOfWagersToMinimumTimeout;
uint256 currentTimeout;
uint256 public wagerIndex = 0;
uint256 public currentBalance;
function calculateTimeout() public view returns(uint256) {
if (wagerIndex >= numberOfWagersToMinimumTimeout || numberOfWagersToMinimumTimeout == 0) {
return minimumTimeout;
} else {
uint256 difference = timeout - minimumTimeout;
uint256 decrease = difference.mul(wagerIndex).div(numberOfWagersToMinimumTimeout);
return (timeout - decrease);
}
}
}
contract PullPayment {
using SafeMath for uint256;
mapping(address => uint256) public payments;
uint256 public totalPayments;
function withdrawPayments() public {
address payee = msg.sender;
uint256 payment = payments[payee];
require(payment != 0);
require(this.balance >= payment);
totalPayments = totalPayments.sub(payment);
payments[payee] = 0;
assert(payee.send(payment));
}
function asyncSend(address dest, uint256 amount) internal {
payments[dest] = payments[dest].add(amount);
totalPayments = totalPayments.add(amount);
}
}
contract JackpotFinance is JackpotBase, PullPayment {
uint256 public feePercentage = 2500;
uint256 public gameStarterDividendPercentage = 2500;
uint256 public price;
uint256 public nextPrice;
uint256 public prizePool;
// The current 5th wager pool (in wei).
uint256 public wagerPool5;
// The current 13th wager pool (in wei).
uint256 public wagerPool13;
function setGameStarterDividendPercentage(uint256 _gameStarterDividendPercentage) external onlyCFO {
require(_gameStarterDividendPercentage <= 4000);
gameStarterDividendPercentage = _gameStarterDividendPercentage;
}
function _sendFunds(address beneficiary, uint256 amount) internal {
if (!beneficiary.send(amount)) {
asyncSend(beneficiary, amount);
}
}
function withdrawFreeBalance() external onlyCFO {
uint256 freeBalance = this.balance.sub(totalPayments).sub(prizePool).sub(wagerPool5).sub(wagerPool13);
cfoAddress.transfer(freeBalance);
currentBalance = this.balance;
}
}
contract JackpotCore is JackpotFinance {
function JackpotCore(uint256 _price, uint256 _timeout, uint256 _minimumTimeout, uint256 _numberOfWagersToMinimumTimeout) public {
require(_timeout >= _minimumTimeout);
nextPrice = _price;
nextTimeout = _timeout;
nextMinimumTimeout = _minimumTimeout;
nextNumberOfWagersToMinimumTimeout = _numberOfWagersToMinimumTimeout;
//NextGame(nextPrice, nextTimeout, nextMinimumTimeout, nextNumberOfWagersToMinimumTimeout);
}
//event NextGame(uint256 price, uint256 timeout, uint256 minimumTimeout, uint256 numberOfWagersToMinimumTimeout);
event Start(address indexed starter, uint256 timestamp, uint256 price, uint256 timeout, uint256 minimumTimeout, uint256 numberOfWagersToMinimumTimeout);
event End(address indexed winner, uint256 timestamp, uint256 prize);
event Bet(address player, uint256 timestamp, uint256 timeoutTimestamp, uint256 wagerIndex, uint256 newPrizePool);
event TopUpPrizePool(address indexed donater, uint256 ethAdded, string message, uint256 newPrizePool);
function bet(bool startNewGameIfIdle) external payable {
require(msg.value >= price);
_processGameEnd();
if (!gameStarted) {
require(!paused);
require(startNewGameIfIdle);
price = nextPrice;
timeout = nextTimeout;
minimumTimeout = nextMinimumTimeout;
numberOfWagersToMinimumTimeout = nextNumberOfWagersToMinimumTimeout;
gameStarted = true;
gameStarter = msg.sender;
Start(msg.sender, now, price, timeout, minimumTimeout, numberOfWagersToMinimumTimeout);
}
// Calculate the fees and dividends.
uint256 fee = price.mul(feePercentage).div(100000);
uint256 dividend = price.mul(gameStarterDividendPercentage).div(100000);
uint256 wagerPool5Part;
uint256 wagerPool13Part;
// Calculate the wager pool part.
wagerPool5Part = price.mul(2).div(10);
wagerPool13Part = price.mul(3).div(26);
// Add funds to the wager pool.
wagerPool5 = wagerPool5.add(wagerPool5Part);
wagerPool13 = wagerPool13.add(wagerPool13Part);
prizePool = prizePool.add(price);
prizePool = prizePool.sub(fee);
prizePool = prizePool.sub(dividend);
prizePool = prizePool.sub(wagerPool5Part);
prizePool = prizePool.sub(wagerPool13Part);
if (wagerIndex % 5 == 4) {
// On every 5th wager, give 2x back
uint256 wagerPrize5 = price.mul(2);
// Calculate the missing wager pool part, remove earlier added wagerPool5Part
uint256 difference5 = wagerPrize5.sub(wagerPool5);
prizePool = prizePool.sub(difference5);
msg.sender.transfer(wagerPrize5);
wagerPool5 = 0;
}
if (wagerIndex % 13 == 12) {
// On every 13th wager, give 3x back
uint256 wagerPrize13 = price.mul(3);
uint256 difference13 = wagerPrize13.sub(wagerPool13);
prizePool = prizePool.sub(difference13);
msg.sender.transfer(wagerPrize13);
wagerPool13 = 0;
}
player5 = player4;
player4 = player3;
player3 = player2;
player2 = lastPlayer;
player5Timestamp = player4Timestamp;
player4Timestamp = player3Timestamp;
player3Timestamp = player2Timestamp;
if (lastWagerTimeoutTimestamp > currentTimeout) {
player2Timestamp = lastWagerTimeoutTimestamp.sub(currentTimeout);
}
currentTimeout = calculateTimeout();
lastPlayer = msg.sender;
lastWagerTimeoutTimestamp = now + currentTimeout;
wagerIndex = wagerIndex.add(1);
Bet(msg.sender, now, lastWagerTimeoutTimestamp, wagerIndex, prizePool);
_sendFunds(gameStarter, dividend);
//_sendFunds(cfoAddress, fee);
uint256 excess = msg.value - price;
if (excess > 0) {
msg.sender.transfer(excess);
}
currentBalance = this.balance;
}
function topUp(string message) external payable {
require(gameStarted || !paused);
require(msg.value > 0);
prizePool = prizePool.add(msg.value);
TopUpPrizePool(msg.sender, msg.value, message, prizePool);
}
function setNextGame(uint256 _price, uint256 _timeout, uint256 _minimumTimeout, uint256 _numberOfWagersToMinimumTimeout) external onlyCFO {
require(_timeout >= _minimumTimeout);
nextPrice = _price;
nextTimeout = _timeout;
nextMinimumTimeout = _minimumTimeout;
nextNumberOfWagersToMinimumTimeout = _numberOfWagersToMinimumTimeout;
//NextGame(nextPrice, nextTimeout, nextMinimumTimeout, nextNumberOfWagersToMinimumTimeout);
}
function endGame() external {
require(_processGameEnd());
}
function _processGameEnd() internal returns(bool) {
if (!gameStarted) {
return false;
}
if (now <= lastWagerTimeoutTimestamp) {
return false;
}
// gameStarted AND past the time limit
uint256 excessPool = wagerPool5.add(wagerPool13);
_sendFunds(lastPlayer, prizePool);
_sendFunds(cfoAddress, excessPool);
End(lastPlayer, lastWagerTimeoutTimestamp, prizePool);
gameStarted = false;
gameStarter = 0x0;
lastPlayer = 0x0;
player2 = 0x0;
player3 = 0x0;
player4 = 0x0;
player5 = 0x0;
lastWagerTimeoutTimestamp = 0;
player2Timestamp = 0;
player3Timestamp = 0;
player4Timestamp = 0;
player5Timestamp = 0;
wagerIndex = 0;
prizePool = 0;
wagerPool5 = 0;
wagerPool13 = 0;
currentBalance = this.balance;
return true;
}
}
{
"compilationTarget": {
"JackpotCore.sol": "JackpotCore"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"totalPayments","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cfoAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"player5Timestamp","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"message","type":"string"}],"name":"topUp","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"player4","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_gameStarterDividendPercentage","type":"uint256"}],"name":"setGameStarterDividendPercentage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"reclaimToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"wagerIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"player2Timestamp","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"player3","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newCFO","type":"address"}],"name":"setCFO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"startNewGameIfIdle","type":"bool"}],"name":"bet","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"wagerPool5","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"player4Timestamp","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gameStarter","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"player2","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextNumberOfWagersToMinimumTimeout","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gameStarted","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawPayments","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"endGame","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"timeout","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"prizePool","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"player5","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastWagerTimeoutTimestamp","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextTimeout","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feePercentage","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"price","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextMinimumTimeout","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wagerPool13","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"numberOfWagersToMinimumTimeout","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_price","type":"uint256"},{"name":"_timeout","type":"uint256"},{"name":"_minimumTimeout","type":"uint256"},{"name":"_numberOfWagersToMinimumTimeout","type":"uint256"}],"name":"setNextGame","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"calculateTimeout","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minimumTimeout","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"payments","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastPlayer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"player3Timestamp","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawFreeBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nextPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"gameStarterDividendPercentage","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_price","type":"uint256"},{"name":"_timeout","type":"uint256"},{"name":"_minimumTimeout","type":"uint256"},{"name":"_numberOfWagersToMinimumTimeout","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"starter","type":"address"},{"indexed":false,"name":"timestamp","type":"uint256"},{"indexed":false,"name":"price","type":"uint256"},{"indexed":false,"name":"timeout","type":"uint256"},{"indexed":false,"name":"minimumTimeout","type":"uint256"},{"indexed":false,"name":"numberOfWagersToMinimumTimeout","type":"uint256"}],"name":"Start","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"winner","type":"address"},{"indexed":false,"name":"timestamp","type":"uint256"},{"indexed":false,"name":"prize","type":"uint256"}],"name":"End","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"player","type":"address"},{"indexed":false,"name":"timestamp","type":"uint256"},{"indexed":false,"name":"timeoutTimestamp","type":"uint256"},{"indexed":false,"name":"wagerIndex","type":"uint256"},{"indexed":false,"name":"newPrizePool","type":"uint256"}],"name":"Bet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"donater","type":"address"},{"indexed":false,"name":"ethAdded","type":"uint256"},{"indexed":false,"name":"message","type":"string"},{"indexed":false,"name":"newPrizePool","type":"uint256"}],"name":"TopUpPrizePool","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]