文件 1 的 3:EthexHouse.sol
pragma solidity ^0.5.0;
contract EthexHouse {
address payable private owner;
constructor() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function payIn() external payable {
}
function withdraw() external onlyOwner {
owner.transfer(address(this).balance);
}
}
文件 2 的 3:EthexJackpot.sol
pragma solidity ^0.5.0;
contract EthexJackpot {
mapping(uint256 => address payable) tickets;
uint256 public numberEnd;
uint256 public dailyAmount;
uint256 public weeklyAmount;
uint256 public monthlyAmount;
uint256 public seasonalAmount;
bool private dailyProcessed;
bool private weeklyProcessed;
bool private monthlyProcessed;
bool private seasonalProcessed;
uint256 private dailyNumberStartPrev;
uint256 private weeklyNumberStartPrev;
uint256 private monthlyNumberStartPrev;
uint256 private seasonalNumberStartPrev;
uint256 private dailyStart;
uint256 private weeklyStart;
uint256 private monthlyStart;
uint256 private seasonalStart;
uint256 private dailyEnd;
uint256 private weeklyEnd;
uint256 private monthlyEnd;
uint256 private seasonalEnd;
uint256 private dailyNumberStart;
uint256 private weeklyNumberStart;
uint256 private monthlyNumberStart;
uint256 private seasonalNumberStart;
uint256 private dailyNumberEndPrev;
uint256 private weeklyNumberEndPrev;
uint256 private monthlyNumberEndPrev;
uint256 private seasonalNumberEndPrev;
address public lotoAddress;
address payable private owner;
event Jackpot (
uint256 number,
uint256 count,
uint256 amount,
byte jackpotType
);
event Ticket (
bytes16 indexed id,
uint256 number
);
uint256 constant DAILY = 5000;
uint256 constant WEEKLY = 35000;
uint256 constant MONTHLY = 140000;
uint256 constant SEASONAL = 420000;
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;
dailyStart = block.number / DAILY * DAILY;
dailyEnd = dailyStart + DAILY;
dailyProcessed = true;
weeklyStart = block.number / WEEKLY * WEEKLY;
weeklyEnd = weeklyStart + WEEKLY;
weeklyProcessed = true;
monthlyStart = block.number / MONTHLY * MONTHLY;
monthlyEnd = monthlyStart + MONTHLY;
monthlyProcessed = true;
seasonalStart = block.number / SEASONAL * SEASONAL;
seasonalEnd = seasonalStart + SEASONAL;
seasonalProcessed = true;
}
function() external payable { }
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier onlyLoto {
require(msg.sender == lotoAddress, "Loto only");
_;
}
function migrate(address payable newContract) external onlyOwner {
newContract.transfer(address(this).balance);
}
function registerTicket(bytes16 id, address payable gamer) external onlyLoto {
uint256 number = numberEnd + 1;
if (block.number >= dailyEnd) {
setDaily();
dailyNumberStart = number;
}
if (block.number >= weeklyEnd) {
setWeekly();
weeklyNumberStart = number;
}
if (block.number >= monthlyEnd) {
setMonthly();
monthlyNumberStart = number;
}
if (block.number >= seasonalEnd) {
setSeasonal();
seasonalNumberStart = number;
}
numberEnd = number;
tickets[number] = gamer;
emit Ticket(id, number);
}
function setLoto(address loto) external onlyOwner {
lotoAddress = loto;
}
function payIn() external payable {
uint256 distributedAmount = dailyAmount + weeklyAmount + monthlyAmount + seasonalAmount;
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)
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)
tickets[dailyWin].transfer(dailyPayAmount);
if (weeklyPayAmount > 0)
tickets[weeklyWin].transfer(weeklyPayAmount);
if (monthlyPayAmount > 0)
tickets[monthlyWin].transfer(monthlyPayAmount);
if (seasonalPayAmount > 0)
tickets[seasonalWin].transfer(seasonalPayAmount);
}
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);
}
}
文件 3 的 3:EthexLoto.sol
pragma solidity ^0.5.0;
import "./EthexJackpot.sol";
import "./EthexHouse.sol";
contract EthexLoto {
struct Bet {
uint256 blockNumber;
uint256 amount;
bytes16 id;
bytes6 bet;
address payable gamer;
}
struct Payout {
uint256 amount;
bytes32 blockHash;
bytes16 id;
address payable gamer;
}
Bet[] betArray;
address payable public jackpotAddress;
address payable public houseAddress;
address payable private owner;
event Result (
uint256 amount,
bytes32 blockHash,
bytes16 indexed id,
address indexed gamer
);
uint8 constant N = 16;
uint256 constant MIN_BET = 0.01 ether;
uint256 constant MAX_BET = 100 ether;
uint256 constant PRECISION = 1 ether;
uint256 constant JACKPOT_PERCENT = 10;
uint256 constant HOUSE_EDGE = 10;
constructor(address payable jackpot, address payable house) public payable {
owner = msg.sender;
jackpotAddress = jackpot;
houseAddress = house;
}
function() external payable { }
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function placeBet(bytes22 params) external payable {
require(msg.value >= MIN_BET, "Bet amount should be greater or equal than minimal amount");
require(msg.value <= MAX_BET, "Bet amount should be lesser or equal than maximal amount");
require(bytes16(params) != 0, "Id should not be 0");
bytes16 id = bytes16(params);
bytes6 bet = bytes6(params << 128);
uint256 jackpotFee = msg.value * JACKPOT_PERCENT * PRECISION / 100 / PRECISION;
uint256 houseEdgeFee = msg.value * HOUSE_EDGE * PRECISION / 100 / PRECISION;
betArray.push(Bet(block.number, msg.value - jackpotFee - houseEdgeFee, id, bet, msg.sender));
uint8 markedCount;
for (uint i = 0; i < bet.length; i++) {
if (bet[i] > 0x13)
continue;
markedCount++;
}
if (markedCount > 1)
EthexJackpot(jackpotAddress).registerTicket(id, msg.sender);
EthexJackpot(jackpotAddress).payIn.value(jackpotFee)();
EthexHouse(houseAddress).payIn.value(houseEdgeFee)();
}
function settleBets() external {
if (betArray.length == 0)
return;
Payout[] memory payouts = new Payout[](betArray.length);
Bet[] memory missedBets = new Bet[](betArray.length);
uint256 totalPayout;
uint i = betArray.length;
do {
i--;
if(betArray[i].blockNumber >= block.number || betArray[i].blockNumber < block.number - 256)
missedBets[i] = betArray[i];
else {
bytes32 blockHash = blockhash(betArray[i].blockNumber);
uint256 coefficient = PRECISION;
uint8 markedCount;
uint8 matchesCount;
uint256 divider = 1;
for (uint8 j = 0; j < betArray[i].bet.length; j++) {
if (betArray[i].bet[j] > 0x13)
continue;
markedCount++;
byte field;
if (j % 2 == 0)
field = blockHash[29 + j / 2] >> 4;
else
field = blockHash[29 + j / 2] & 0x0F;
if (betArray[i].bet[j] < 0x10) {
if (field == betArray[i].bet[j])
matchesCount++;
else
divider *= 15 + N;
continue;
}
if (betArray[i].bet[j] == 0x10) {
if (field > 0x09 && field < 0x10) {
matchesCount++;
divider *= 6;
} else
divider *= 10 + N;
continue;
}
if (betArray[i].bet[j] == 0x11) {
if (field < 0x0A) {
matchesCount++;
divider *= 10;
} else
divider *= 6 + N;
continue;
}
if (betArray[i].bet[j] == 0x12) {
if (field < 0x0A && field & 0x01 == 0x01) {
matchesCount++;
divider *= 5;
} else
divider *= 11 + N;
continue;
}
if (betArray[i].bet[j] == 0x13) {
if (field < 0x0A && field & 0x01 == 0x0) {
matchesCount++;
divider *= 5;
} else
divider *= 11 + N;
continue;
}
}
if (matchesCount == 0)
coefficient = 0;
else {
uint256 missedCount = markedCount - matchesCount;
divider *= missedCount ** missedCount;
coefficient = coefficient * 16**uint256(markedCount) / divider;
}
uint payoutAmount = betArray[i].amount * coefficient / PRECISION;
if (payoutAmount == 0 && matchesCount > 0)
payoutAmount = matchesCount;
payouts[i] = Payout(payoutAmount, blockHash, betArray[i].id, betArray[i].gamer);
totalPayout += payoutAmount;
}
betArray.pop();
} while (i > 0);
i = missedBets.length;
do {
i--;
if (missedBets[i].id != 0)
betArray.push(missedBets[i]);
} while (i > 0);
uint balance = address(this).balance;
for (i = 0; i < payouts.length; i++) {
if (payouts[i].id > 0) {
if (totalPayout > balance)
emit Result(balance * payouts[i].amount * PRECISION / totalPayout / PRECISION, payouts[i].blockHash, payouts[i].id, payouts[i].gamer);
else
emit Result(payouts[i].amount, payouts[i].blockHash, payouts[i].id, payouts[i].gamer);
}
}
for (i = 0; i < payouts.length; i++) {
if (payouts[i].amount > 0) {
if (totalPayout > balance)
payouts[i].gamer.transfer(balance * payouts[i].amount * PRECISION / totalPayout / PRECISION);
else
payouts[i].gamer.transfer(payouts[i].amount);
}
}
}
function migrate(address payable newContract) external onlyOwner {
newContract.transfer(address(this).balance);
}
}
{
"compilationTarget": {
"EthexLoto.sol": "EthexLoto"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"jackpotAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"settleBets","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newContract","type":"address"}],"name":"migrate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"houseAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"params","type":"bytes22"}],"name":"placeBet","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[{"name":"jackpot","type":"address"},{"name":"house","type":"address"}],"payable":true,"stateMutability":"payable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"blockHash","type":"bytes32"},{"indexed":true,"name":"id","type":"bytes16"},{"indexed":true,"name":"gamer","type":"address"}],"name":"Result","type":"event"}]