/** * Source Code first verified at https://etherscan.io on Tuesday, April 16, 2019 (UTC) */ pragma solidity ^0.5.0; /** * (E)t)h)e)x) Jackpot Contract * This smart-contract is the part of Ethex Lottery fair game. * See latest version at https://github.com/ethex-bet/ethex-contracts * http://ethex.bet */ contract EthexJackpot { mapping(uint256 => address payable) public tickets; uint256 public numberEnd; uint256 public firstNumber; uint256 public dailyAmount; uint256 public weeklyAmount; uint256 public monthlyAmount; uint256 public seasonalAmount; bool public dailyProcessed; bool public weeklyProcessed; bool public monthlyProcessed; bool public seasonalProcessed; address payable private owner; address public lotoAddress; address payable public newVersionAddress; EthexJackpot previousContract; uint256 public dailyNumberStartPrev; uint256 public weeklyNumberStartPrev; uint256 public monthlyNumberStartPrev; uint256 public seasonalNumberStartPrev; uint256 public dailyStart; uint256 public weeklyStart; uint256 public monthlyStart; uint256 public seasonalStart; uint256 public dailyEnd; uint256 public weeklyEnd; uint256 public monthlyEnd; uint256 public seasonalEnd; uint256 public dailyNumberStart; uint256 public weeklyNumberStart; uint256 public monthlyNumberStart; uint256 public seasonalNumberStart; uint256 public dailyNumberEndPrev; uint256 public weeklyNumberEndPrev; uint256 public monthlyNumberEndPrev; uint256 public seasonalNumberEndPrev; event Jackpot ( uint256 number, uint256 count, uint256 amount, byte jackpotType ); event Ticket ( bytes16 indexed id, uint256 number ); event SuperPrize ( uint256 amount, address winner ); uint256 constant DAILY = 5000; uint256 constant WEEKLY = 35000; uint256 constant MONTHLY = 150000; uint256 constant SEASONAL = 450000; uint256 constant PRECISION = 1 ether; uint256 constant DAILY_PART = 84; uint256 constant WEEKLY_PART = 12; uint256 constant MONTHLY_PART = 3; constructor() public payable { owner = msg.sender; } function() external payable { } modifier onlyOwner { require(msg.sender == owner); _; } modifier onlyOwnerOrNewVersion { require(msg.sender == owner || msg.sender == newVersionAddress); _; } modifier onlyLoto { require(msg.sender == lotoAddress, "Loto only"); _; } function migrate() external onlyOwnerOrNewVersion { newVersionAddress.transfer(address(this).balance); } function registerTicket(bytes16 id, address payable gamer) external onlyLoto { uint256 number = numberEnd + 1; if (block.number >= dailyEnd) { setDaily(); dailyNumberStart = number; } else if (dailyNumberStart == dailyNumberStartPrev) dailyNumberStart = number; if (block.number >= weeklyEnd) { setWeekly(); weeklyNumberStart = number; } else if (weeklyNumberStart == weeklyNumberStartPrev) weeklyNumberStart = number; if (block.number >= monthlyEnd) { setMonthly(); monthlyNumberStart = number; } else if (monthlyNumberStart == monthlyNumberStartPrev) monthlyNumberStart = number; if (block.number >= seasonalEnd) { setSeasonal(); seasonalNumberStart = number; } else if (seasonalNumberStart == seasonalNumberStartPrev) seasonalNumberStart = number; numberEnd = number; tickets[number] = gamer; emit Ticket(id, number); } function setLoto(address loto) external onlyOwner { lotoAddress = loto; } function setNewVersion(address payable newVersion) external onlyOwner { newVersionAddress = newVersion; } function payIn() external payable { uint256 distributedAmount = dailyAmount + weeklyAmount + monthlyAmount + seasonalAmount; if (distributedAmount < address(this).balance) { uint256 amount = (address(this).balance - distributedAmount) / 4; dailyAmount += amount; weeklyAmount += amount; monthlyAmount += amount; seasonalAmount += amount; } } function settleJackpot() external { if (block.number >= dailyEnd) setDaily(); if (block.number >= weeklyEnd) setWeekly(); if (block.number >= monthlyEnd) setMonthly(); if (block.number >= seasonalEnd) setSeasonal(); if (block.number == dailyStart || (dailyStart < block.number - 256)) return; uint48 modulo = uint48(bytes6(blockhash(dailyStart) << 29)); uint256 dailyPayAmount; uint256 weeklyPayAmount; uint256 monthlyPayAmount; uint256 seasonalPayAmount; uint256 dailyWin; uint256 weeklyWin; uint256 monthlyWin; uint256 seasonalWin; if (dailyProcessed == false) { dailyPayAmount = dailyAmount * PRECISION / DAILY_PART / PRECISION; dailyAmount -= dailyPayAmount; dailyProcessed = true; dailyWin = getNumber(dailyNumberStartPrev, dailyNumberEndPrev, modulo); emit Jackpot(dailyWin, dailyNumberEndPrev - dailyNumberStartPrev + 1, dailyPayAmount, 0x01); } if (weeklyProcessed == false) { weeklyPayAmount = weeklyAmount * PRECISION / WEEKLY_PART / PRECISION; weeklyAmount -= weeklyPayAmount; weeklyProcessed = true; weeklyWin = getNumber(weeklyNumberStartPrev, weeklyNumberEndPrev, modulo); emit Jackpot(weeklyWin, weeklyNumberEndPrev - weeklyNumberStartPrev + 1, weeklyPayAmount, 0x02); } if (monthlyProcessed == false) { monthlyPayAmount = monthlyAmount * PRECISION / MONTHLY_PART / PRECISION; monthlyAmount -= monthlyPayAmount; monthlyProcessed = true; monthlyWin = getNumber(monthlyNumberStartPrev, monthlyNumberEndPrev, modulo); emit Jackpot(monthlyWin, monthlyNumberEndPrev - monthlyNumberStartPrev + 1, monthlyPayAmount, 0x04); } if (seasonalProcessed == false) { seasonalPayAmount = seasonalAmount; seasonalAmount -= seasonalPayAmount; seasonalProcessed = true; seasonalWin = getNumber(seasonalNumberStartPrev, seasonalNumberEndPrev, modulo); emit Jackpot(seasonalWin, seasonalNumberEndPrev - seasonalNumberStartPrev + 1, seasonalPayAmount, 0x08); } if (dailyPayAmount > 0) getAddress(dailyWin).transfer(dailyPayAmount); if (weeklyPayAmount > 0) getAddress(weeklyWin).transfer(weeklyPayAmount); if (monthlyPayAmount > 0) getAddress(monthlyWin).transfer(monthlyPayAmount); if (seasonalPayAmount > 0) getAddress(seasonalWin).transfer(seasonalPayAmount); } function paySuperPrize(address payable winner) external onlyLoto { uint256 superPrizeAmount = dailyAmount + weeklyAmount + monthlyAmount + seasonalAmount; emit SuperPrize(superPrizeAmount, winner); winner.transfer(superPrizeAmount); } function loadTickets(address payable[] calldata addresses, uint256[] calldata numbers) external { for (uint i = 0; i < addresses.length; i++) tickets[numbers[i]] = addresses[i]; } function setOldVersion(address payable oldAddress) external onlyOwner { previousContract = EthexJackpot(oldAddress); dailyStart = previousContract.dailyStart(); dailyEnd = previousContract.dailyEnd(); dailyProcessed = previousContract.dailyProcessed(); weeklyStart = previousContract.weeklyStart(); weeklyEnd = previousContract.weeklyEnd(); weeklyProcessed = previousContract.weeklyProcessed(); monthlyStart = previousContract.monthlyStart(); monthlyEnd = previousContract.monthlyEnd(); monthlyProcessed = previousContract.monthlyProcessed(); seasonalStart = previousContract.seasonalStart(); seasonalEnd = previousContract.seasonalEnd(); seasonalProcessed = previousContract.seasonalProcessed(); dailyNumberStartPrev = previousContract.dailyNumberStartPrev(); weeklyNumberStartPrev = previousContract.weeklyNumberStartPrev(); monthlyNumberStartPrev = previousContract.monthlyNumberStartPrev(); seasonalNumberStartPrev = previousContract.seasonalNumberStartPrev(); dailyNumberStart = previousContract.dailyNumberStart(); weeklyNumberStart = previousContract.weeklyNumberStart(); monthlyNumberStart = previousContract.monthlyNumberStart(); seasonalNumberStart = previousContract.seasonalNumberStart(); dailyNumberEndPrev = previousContract.dailyNumberEndPrev(); weeklyNumberEndPrev = previousContract.weeklyNumberEndPrev(); monthlyNumberEndPrev = previousContract.monthlyNumberEndPrev(); seasonalNumberEndPrev = previousContract.seasonalNumberEndPrev(); numberEnd = previousContract.numberEnd(); dailyAmount = previousContract.dailyAmount(); weeklyAmount = previousContract.weeklyAmount(); monthlyAmount = previousContract.monthlyAmount(); seasonalAmount = previousContract.seasonalAmount(); firstNumber = numberEnd; previousContract.migrate(); } function getAddress(uint256 number) public returns (address payable) { if (number <= firstNumber) return previousContract.getAddress(number); return tickets[number]; } function setDaily() private { dailyProcessed = dailyNumberEndPrev == numberEnd; dailyStart = dailyEnd; dailyEnd = dailyStart + DAILY; dailyNumberStartPrev = dailyNumberStart; dailyNumberEndPrev = numberEnd; } function setWeekly() private { weeklyProcessed = weeklyNumberEndPrev == numberEnd; weeklyStart = weeklyEnd; weeklyEnd = weeklyStart + WEEKLY; weeklyNumberStartPrev = weeklyNumberStart; weeklyNumberEndPrev = numberEnd; } function setMonthly() private { monthlyProcessed = monthlyNumberEndPrev == numberEnd; monthlyStart = monthlyEnd; monthlyEnd = monthlyStart + MONTHLY; monthlyNumberStartPrev = monthlyNumberStart; monthlyNumberEndPrev = numberEnd; } function setSeasonal() private { seasonalProcessed = seasonalNumberEndPrev == numberEnd; seasonalStart = seasonalEnd; seasonalEnd = seasonalStart + SEASONAL; seasonalNumberStartPrev = seasonalNumberStart; seasonalNumberEndPrev = numberEnd; } function getNumber(uint256 startNumber, uint256 endNumber, uint48 modulo) pure private returns (uint256) { return startNumber + modulo % (endNumber - startNumber + 1); } }