Transaction Hash:
Block:
12882789 at Jul-23-2021 01:03:20 PM +UTC
Transaction Fee:
0.000683748 ETH
$1.47
Gas Used:
37,986 Gas / 18 Gwei
Emitted Events:
| 8 |
EthexLoto.RefundBet( amount=48000000000000000, id=27377, gamer=[Sender] 0xee4e761cb2e031d332f1e6027ed7fe9c0bbfab20 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x1A48bE4c...c7735d3B1 | 26.096826666666667535 Eth | 26.048826666666667535 Eth | 0.048 | ||
|
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 2,545.673285898418621203 Eth | 2,545.673969646418621203 Eth | 0.000683748 | |
| 0xEE4E761c...c0bBfAB20 |
0.726842525709284506 Eth
Nonce: 81
|
0.774158777709284506 Eth
Nonce: 82
| 0.047316252 |
Execution Trace
EthexLoto.CALL( )
- ETH 0.048
0xee4e761cb2e031d332f1e6027ed7fe9c0bbfab20.CALL( )
// File: contracts/DeliverFunds.sol
pragma solidity 0.5.17;
contract DeliverFunds {
constructor(address payable target) public payable {
selfdestruct(target);
}
}
// File: contracts/Ownable.sol
pragma solidity 0.5.17;
contract Ownable {
address payable public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
owner = msg.sender;
emit OwnershipTransferred(address(0), owner);
}
modifier onlyOwner() {
require(msg.sender == owner, "Ownable: caller is not the owner");
_;
}
function transferOwnership(address payable newOwner) public onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
// File: contracts/EthexJackpot.sol
pragma solidity 0.5.17;
/**
* (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 is Ownable {
mapping(uint256 => address payable) public tickets;
mapping(uint256 => Segment[4]) public prevJackpots;
uint256[4] public amounts;
uint256[4] public starts;
uint256[4] public ends;
uint256[4] public numberStarts;
uint256 public numberEnd;
uint256 public firstNumber;
address public lotoAddress;
address payable public newVersionAddress;
EthexJackpot public previousContract;
struct Segment {
uint256 start;
uint256 end;
bool processed;
}
event Jackpot (
uint256 number,
uint256 count,
uint256 amount,
byte jackpotType
);
event Ticket (
uint256 number
);
event Superprize (
uint256 amount,
address winner
);
uint256 internal constant PRECISION = 1 ether;
modifier onlyLoto {
require(msg.sender == lotoAddress, "Loto only");
_;
}
function() external payable { }
function migrate() external {
require(msg.sender == owner || msg.sender == newVersionAddress);
require(newVersionAddress != address(0), "NewVersionAddress required");
newVersionAddress.transfer(address(this).balance);
}
function registerTicket(address payable gamer) external payable onlyLoto {
distribute();
uint8 i;
if (gamer == address(0x0)) {
for (; i < 4; i++)
if (block.number >= ends[i])
setJackpot(i);
}
else {
uint256 number = numberEnd + 1;
for (; i < 4; i++) {
if (block.number >= ends[i]) {
setJackpot(i);
numberStarts[i] = number;
}
else
if (numberStarts[i] == prevJackpots[starts[i]][i].start)
numberStarts[i] = number;
}
numberEnd = number;
tickets[number] = gamer;
emit Ticket(number);
}
}
function setLoto(address loto) external onlyOwner {
lotoAddress = loto;
}
function setNewVersion(address payable newVersion) external onlyOwner {
newVersionAddress = newVersion;
}
function payIn() external payable { distribute(); }
function settleJackpot() external {
for (uint8 i = 0; i < 4; i++)
if (block.number >= ends[i])
setJackpot(i);
uint256[4] memory payAmounts;
uint256[4] memory wins;
uint8[4] memory PARTS = [84, 12, 3, 1];
for (uint8 i = 0; i < 4; i++) {
uint256 start = starts[i];
if (block.number == start || (start < block.number - 256))
continue;
if (prevJackpots[start][i].processed == false && prevJackpots[start][i].start != 0) {
payAmounts[i] = amounts[i] * PRECISION / PARTS[i] / PRECISION;
amounts[i] -= payAmounts[i];
prevJackpots[start][i].processed = true;
uint48 modulo = uint48(bytes6(blockhash(start) << 29));
wins[i] = getNumber(prevJackpots[start][i].start, prevJackpots[start][i].end, modulo);
emit Jackpot(wins[i], prevJackpots[start][i].end - prevJackpots[start][i].start + 1, payAmounts[i], byte(uint8(1) << i));
}
}
for (uint8 i = 0; i < 4; i++)
if (payAmounts[i] > 0 && !getAddress(wins[i]).send(payAmounts[i]))
(new DeliverFunds).value(payAmounts[i])(getAddress(wins[i]));
}
function settleMissedJackpot(bytes32 hash, uint256 blockHeight) external onlyOwner {
for (uint8 i = 0; i < 4; i++)
if (block.number >= ends[i])
setJackpot(i);
if (blockHeight < block.number - 256) {
uint48 modulo = uint48(bytes6(hash << 29));
uint256[4] memory payAmounts;
uint256[4] memory wins;
uint8[4] memory PARTS = [84, 12, 3, 1];
for (uint8 i = 0; i < 4; i++) {
if (prevJackpots[blockHeight][i].processed == false && prevJackpots[blockHeight][i].start != 0) {
payAmounts[i] = amounts[i] * PRECISION / PARTS[i] / PRECISION;
amounts[i] -= payAmounts[i];
prevJackpots[blockHeight][i].processed = true;
wins[i] = getNumber(prevJackpots[blockHeight][i].start, prevJackpots[blockHeight][i].end, modulo);
emit Jackpot(wins[i], prevJackpots[blockHeight][i].end - prevJackpots[blockHeight][i].start + 1, payAmounts[i], byte(uint8(1) << i));
}
}
for (uint8 i = 0; i < 4; i++)
if (payAmounts[i] > 0 && !getAddress(wins[i]).send(payAmounts[i]))
(new DeliverFunds).value(payAmounts[i])(getAddress(wins[i]));
}
}
function paySuperprize(address payable winner) external onlyLoto {
uint256 superprizeAmount = amounts[0] + amounts[1] + amounts[2] + amounts[3];
amounts[0] = 0;
amounts[1] = 0;
amounts[2] = 0;
amounts[3] = 0;
emit Superprize(superprizeAmount, winner);
if (superprizeAmount > 0 && !winner.send(superprizeAmount))
(new DeliverFunds).value(superprizeAmount)(winner);
}
function setOldVersion(address payable oldAddress) external onlyOwner {
previousContract = EthexJackpot(oldAddress);
for (uint8 i = 0; i < 4; i++) {
starts[i] = previousContract.starts(i);
ends[i] = previousContract.ends(i);
numberStarts[i] = previousContract.numberStarts(i);
uint256 start;
uint256 end;
bool processed;
(start, end, processed) = previousContract.prevJackpots(starts[i], i);
prevJackpots[starts[i]][i] = Segment(start, end, processed);
amounts[i] = previousContract.amounts(i);
}
numberEnd = previousContract.numberEnd();
firstNumber = numberEnd;
previousContract.migrate();
}
function getAddress(uint256 number) public returns (address payable) {
if (number <= firstNumber)
return previousContract.getAddress(number);
return tickets[number];
}
function setJackpot(uint8 jackpotType) private {
uint24[4] memory LENGTH = [5000, 35000, 150000, 450000];
prevJackpots[ends[jackpotType]][jackpotType].processed = prevJackpots[starts[jackpotType]][jackpotType].end == numberEnd;
starts[jackpotType] = ends[jackpotType];
ends[jackpotType] = starts[jackpotType] + LENGTH[jackpotType];
prevJackpots[starts[jackpotType]][jackpotType].start = numberStarts[jackpotType];
prevJackpots[starts[jackpotType]][jackpotType].end = numberEnd;
}
function distribute() private {
uint256 distributedAmount = amounts[0] + amounts[1] + amounts[2] + amounts[3];
if (distributedAmount < address(this).balance) {
uint256 amount = (address(this).balance - distributedAmount) / 4;
amounts[0] += amount;
amounts[1] += amount;
amounts[2] += amount;
amounts[3] += amount;
}
}
function getNumber(uint256 startNumber, uint256 endNumber, uint48 modulo) private pure returns (uint256) {
return startNumber + modulo % (endNumber - startNumber + 1);
}
}
// File: contracts/EthexHouse.sol
pragma solidity 0.5.17;
/**
* (E)t)h)e)x) House 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 EthexHouse is Ownable {
function payIn() external payable { }
function withdraw() external onlyOwner {
owner.transfer(address(this).balance);
}
}
// File: contracts/EthexSuperprize.sol
pragma solidity 0.5.17;
/**
* (E)t)h)e)x) Superprize Contract
* This smart-contract is the part of Ethex Lottery fair game.
* See latest version at https://github.com/ethex-bet/ethex-lottery
* http://ethex.bet
*/
contract EthexSuperprize is Ownable {
struct Payout {
uint256 index;
uint256 amount;
uint256 block;
address payable winnerAddress;
uint256 betId;
}
Payout[] public payouts;
address public lotoAddress;
address payable public newVersionAddress;
EthexSuperprize public previousContract;
uint256 public hold;
event Superprize (
uint256 index,
uint256 amount,
address winner,
uint256 betId,
byte state
);
uint8 internal constant PARTS = 6;
uint256 internal constant PRECISION = 1 ether;
uint256 internal constant MONTHLY = 150000;
function() external payable { }
function initSuperprize(address payable winner, uint256 betId) external {
require(msg.sender == lotoAddress, "Loto only");
uint256 amount = address(this).balance - hold;
hold = address(this).balance;
uint256 sum;
uint256 temp;
for (uint256 i = 1; i < PARTS; i++) {
temp = amount * PRECISION * (i - 1 + 10) / 75 / PRECISION;
sum += temp;
payouts.push(Payout(i, temp, block.number + i * MONTHLY, winner, betId));
}
payouts.push(Payout(PARTS, amount - sum, block.number + PARTS * MONTHLY, winner, betId));
emit Superprize(0, amount, winner, betId, 0);
}
function paySuperprize() external onlyOwner {
if (payouts.length == 0)
return;
Payout[] memory payoutArray = new Payout[](payouts.length);
uint i = payouts.length;
while (i > 0) {
i--;
if (payouts[i].block <= block.number) {
emit Superprize(payouts[i].index, payouts[i].amount, payouts[i].winnerAddress, payouts[i].betId, 0x01);
hold -= payouts[i].amount;
}
payoutArray[i] = payouts[i];
payouts.pop();
}
for (i = 0; i < payoutArray.length; i++)
if (payoutArray[i].block > block.number)
payouts.push(payoutArray[i]);
for (i = 0; i < payoutArray.length; i++)
if (payoutArray[i].block <= block.number && !payoutArray[i].winnerAddress.send(payoutArray[i].amount))
(new DeliverFunds).value(payoutArray[i].amount)(payoutArray[i].winnerAddress);
}
function setOldVersion(address payable oldAddress) external onlyOwner {
previousContract = EthexSuperprize(oldAddress);
lotoAddress = previousContract.lotoAddress();
hold = previousContract.hold();
uint256 index;
uint256 amount;
uint256 betBlock;
address payable winner;
uint256 betId;
uint256 payoutsCount = previousContract.getPayoutsCount();
for (uint i = 0; i < payoutsCount; i++) {
(index, amount, betBlock, winner, betId) = previousContract.payouts(i);
payouts.push(Payout(index, amount, betBlock, winner, betId));
}
previousContract.migrate();
}
function setNewVersion(address payable newVersion) external onlyOwner {
newVersionAddress = newVersion;
}
function setLoto(address loto) external onlyOwner {
lotoAddress = loto;
}
function migrate() external {
require(msg.sender == owner || msg.sender == newVersionAddress);
require(newVersionAddress != address(0));
newVersionAddress.transfer(address(this).balance);
}
function getPayoutsCount() public view returns (uint256) { return payouts.length; }
}
// File: contracts/openzeppelin/GSN/Context.sol
pragma solidity ^0.5.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
// solhint-disable-previous-line no-empty-blocks
function _msgSender() internal view returns (address payable) {
return msg.sender;
}
function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File: contracts/openzeppelin/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: contracts/openzeppelin/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* NOTE: This is a feature of the next version of OpenZeppelin Contracts.
* @dev Get it via `npm install @openzeppelin/contracts@next`.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
* NOTE: This is a feature of the next version of OpenZeppelin Contracts.
* @dev Get it via `npm install @openzeppelin/contracts@next`.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* NOTE: This is a feature of the next version of OpenZeppelin Contracts.
* @dev Get it via `npm install @openzeppelin/contracts@next`.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// File: contracts/openzeppelin/access/Roles.sol
pragma solidity ^0.5.0;
/**
* @title Roles
* @dev Library for managing addresses assigned to a Role.
*/
library Roles {
struct Role {
mapping (address => bool) bearer;
}
/**
* @dev Give an account access to this role.
*/
function add(Role storage role, address account) internal {
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
/**
* @dev Remove an account's access to this role.
*/
function remove(Role storage role, address account) internal {
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
/**
* @dev Check if an account has this role.
* @return bool
*/
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
}
// File: contracts/openzeppelin/access/roles/DistributorRole.sol
pragma solidity ^0.5.0;
contract DistributorRole is Context {
using Roles for Roles.Role;
event DistributorAdded(address indexed account);
event DistributorRemoved(address indexed account);
Roles.Role private _distributors;
constructor () internal {
_addDistributor(_msgSender());
}
modifier onlyDistributor() {
require(isDistributor(_msgSender()), "DistributorRole: caller does not have the Distributor role");
_;
}
function isDistributor(address account) public view returns (bool) {
return _distributors.has(account);
}
function addDistributor(address account) public onlyDistributor {
_addDistributor(account);
}
function renounceDistributor() public {
_removeDistributor(_msgSender());
}
function _addDistributor(address account) internal {
_distributors.add(account);
emit DistributorAdded(account);
}
function _removeDistributor(address account) internal {
_distributors.remove(account);
emit DistributorRemoved(account);
}
}
// File: contracts/openzeppelin/token/ERC20/ERC20Distributable.sol
pragma solidity ^0.5.0;
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20Distributable is Context, IERC20, DistributorRole {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public onlyDistributor returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public onlyDistributor returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public onlyDistributor returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public onlyDistributor returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public onlyDistributor returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
}
// File: contracts/openzeppelin/token/ERC20/ERC20Detailed.sol
pragma solidity ^0.5.0;
/**
* @dev Optional functions from the ERC20 standard.
*/
contract ERC20Detailed is IERC20 {
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
* these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name, string memory symbol, uint8 decimals) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
}
// File: contracts/EthexFreeSpins.sol
pragma solidity 0.5.17;
contract EthexFreeSpins is Ownable, Context, ERC20Distributable, ERC20Detailed {
mapping (address => bool) private _migrated;
address payable public lotoAddress;
address payable public oldVersionAddress;
address payable public newVersionAddress;
uint256 public Rate;
constructor (uint256 rate) public ERC20Detailed("EthexFreeSpins", "EFS", 18) {
require(rate > 0, "Rate must be non zero");
Rate = rate;
}
function use(address account, uint256 amount) public {
require(amount >= Rate, "Amount must be greater then rate");
require(msg.sender == lotoAddress, "Loto only");
if (oldVersionAddress != address(0) && _migrated[account] == false) {
uint256 totalAmount = EthexFreeSpins(oldVersionAddress).totalBalanceOf(account);
_mint(account, totalAmount);
_migrated[account] = true;
}
_burn(account, amount);
lotoAddress.transfer(amount / Rate);
}
function removeDistributor(address account) external onlyOwner {
_removeDistributor(account);
}
function setLoto(address payable loto) external onlyOwner {
lotoAddress = loto;
}
function mint(address account) public payable {
_mint(account, msg.value * Rate);
}
function setOldVersion(address payable oldVersion) external onlyOwner {
oldVersionAddress = oldVersion;
}
function setNewVersion(address payable newVersion) external onlyOwner {
newVersionAddress = newVersion;
}
function migrate() external {
require(msg.sender == owner || msg.sender == newVersionAddress);
require(newVersionAddress != address(0), "NewVersionAddress required");
EthexFreeSpins(newVersionAddress).payIn.value(address(this).balance)();
}
function payIn() external payable { }
function totalBalanceOf(address account) public view returns (uint256) {
uint256 balance = balanceOf(account);
if (oldVersionAddress != address(0) && _migrated[account] == false)
return balance + EthexFreeSpins(oldVersionAddress).totalBalanceOf(account);
return balance;
}
}
// File: contracts/uniswap/IUniswapFactory.sol
pragma solidity ^0.5.0;
contract IUniswapFactory {
// Public Variables
address public exchangeTemplate;
uint256 public tokenCount;
// Create Exchange
function createExchange(address token) external returns (address exchange);
// Get Exchange and Token Info
function getExchange(address token) external view returns (address exchange);
function getToken(address exchange) external view returns (address token);
function getTokenWithId(uint256 tokenId) external view returns (address token);
// Never use
function initializeFactory(address template) external;
}
// File: contracts/uniswap/IUniswapExchange.sol
pragma solidity ^0.5.0;
contract IUniswapExchange {
// Address of ERC20 token sold on this exchange
function tokenAddress() external view returns (address token);
// Address of Uniswap Factory
function factoryAddress() external view returns (address factory);
// Provide Liquidity
function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256);
function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256);
// Get Prices
function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);
// Trade ETH to ERC20
function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256 tokens_bought);
function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256 tokens_bought);
function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256 eth_sold);
function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256 eth_sold);
// Trade ERC20 to ETH
function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256 eth_bought);
function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256 eth_bought);
function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256 tokens_sold);
function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256 tokens_sold);
// Trade ERC20 to ERC20
function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256 tokens_bought);
function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256 tokens_bought);
function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256 tokens_sold);
function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256 tokens_sold);
// Trade ERC20 to Custom Pool
function tokenToExchangeSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address exchange_addr) external returns (uint256 tokens_bought);
function tokenToExchangeTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address exchange_addr) external returns (uint256 tokens_bought);
function tokenToExchangeSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address exchange_addr) external returns (uint256 tokens_sold);
function tokenToExchangeTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address exchange_addr) external returns (uint256 tokens_sold);
// ERC20 comaptibility for liquidity tokens
bytes32 public name;
bytes32 public symbol;
uint256 public decimals;
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 value) external returns (bool);
function approve(address _spender, uint256 _value) external returns (bool);
function allowance(address _owner, address _spender) external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256);
function totalSupply() external view returns (uint256);
// Never use
function setup(address token_addr) external;
}
// File: contracts/EthexLoto.sol
pragma solidity 0.5.17;
/**
* (E)t)h)e)x) Loto Contract
* This smart-contract is the part of Ethex Lottery fair game.
* See latest version at https://github.com/ethex-bet/ethex-contacts
* http://ethex.bet
*/
contract EthexLoto is Ownable {
struct Bet {
uint256 blockNumber;
uint256 amount;
uint256 id;
bytes6 bet;
address payable gamer;
}
struct Transaction {
uint256 amount;
address payable gamer;
}
struct Superprize {
uint256 amount;
uint256 id;
}
mapping(uint256 => Bet) public betQueue;
uint256 public counters = 0x20000000000000001;
uint256 public holdBalance;
address payable public newVersionAddress;
address payable public jackpotAddress;
address payable public houseAddress;
address payable public superprizeAddress;
address payable public freeSpinsAddress;
address payable public uniswapAddress;
event TokenBetAdded (
uint256 tokenAmount,
uint256 ethAmount
);
event PlaceBet (
uint256 id
);
event PayoutBet (
uint256 amount,
uint256 id,
address gamer
);
event RefundBet (
uint256 amount,
uint256 id,
address gamer
);
uint256 internal constant MIN_BET = 0.01 ether;
uint256 internal constant PRECISION = 1 ether;
uint256 internal constant JACKPOT_PERCENT = 10;
uint256 internal constant HOUSE_EDGE = 10;
constructor(address payable jackpot, address payable house, address payable superprize, address payable freeSpins, address payable uniswap) public {
jackpotAddress = jackpot;
houseAddress = house;
superprizeAddress = superprize;
freeSpinsAddress = freeSpins;
uniswapAddress = uniswap;
for(uint i = 1; i <= 10; i++)
betQueue[i] = Bet(1, MIN_BET, 1, 0xffffffffffff, address(0x0));
}
function() external payable { }
function placeTokenBet(address tokenAddress, uint256 betMaxTokenAmount, uint256 betTargetEthAmount, uint256 swapDeadline, bytes6 bet) external {
require(betTargetEthAmount >= MIN_BET, "Bet amount should be greater or equal than minimal amount");
require(tokenAddress != address(0), "Token address is required");
if (tokenAddress == freeSpinsAddress)
{
placeFreeSpinBet(betTargetEthAmount, bet);
return;
}
IERC20 token = IERC20(tokenAddress);
IUniswapFactory uniswapFactory = IUniswapFactory(uniswapAddress);
address exchangeAddress = uniswapFactory.getExchange(tokenAddress);
require(exchangeAddress != address(0), "Token is not supported");
IUniswapExchange uniswap = IUniswapExchange(exchangeAddress);
uint256 tokensSold = uniswap.getTokenToEthOutputPrice(betTargetEthAmount);
require(betMaxTokenAmount >= tokensSold, "Swap requires more token then was allowed");
uint256 balance = token.balanceOf(msg.sender);
require(balance >= tokensSold, "Not enough tokens");
token.transferFrom(msg.sender, address(this), tokensSold);
token.approve(exchangeAddress, tokensSold);
uint256 converted = uniswap.tokenToEthSwapOutput(betTargetEthAmount, tokensSold, swapDeadline);
require(converted >= betTargetEthAmount, "Exchange result is smaller then requested");
placeBet(msg.sender, betTargetEthAmount, bet);
emit TokenBetAdded(tokensSold, betTargetEthAmount);
}
function placeFreeSpinBet(uint256 betAmount, bytes6 bet) public {
require(betAmount >= MIN_BET, "Bet amount should be greater or equal than minimal amount");
EthexFreeSpins freeSpinsToken = EthexFreeSpins(freeSpinsAddress);
uint256 freeSpinAmount = betAmount * freeSpinsToken.Rate();
uint256 freeSpinBalance = freeSpinsToken.balanceOf(msg.sender);
require(freeSpinBalance >= freeSpinAmount, "Not enough tokens");
freeSpinsToken.use(msg.sender, freeSpinAmount);
placeBet(msg.sender, betAmount, bet);
emit TokenBetAdded(freeSpinAmount, betAmount);
}
function placeBet(bytes6 bet) external payable {
require(tx.origin == msg.sender);
placeBet(msg.sender, msg.value, bet);
}
function placeBet(address payable player, uint256 amount, bytes6 bet) private {
require(amount >= MIN_BET, "Bet amount should be greater or equal than minimal amount");
uint256 coefficient;
uint8 markedCount;
uint256 holdAmount;
uint256 jackpotFee = amount * JACKPOT_PERCENT * PRECISION / 100 / PRECISION;
uint256 houseEdgeFee = amount * HOUSE_EDGE * PRECISION / 100 / PRECISION;
uint256 betAmount = amount - jackpotFee - houseEdgeFee;
(coefficient, markedCount, holdAmount) = getHold(betAmount, bet);
require(amount * (100 - JACKPOT_PERCENT - HOUSE_EDGE) * (coefficient * 8 - 15 * markedCount) <= 9000 ether * markedCount);
require(
amount * (800 * coefficient - (JACKPOT_PERCENT + HOUSE_EDGE) * (coefficient * 8 + 15 * markedCount)) <= 1500 * markedCount * (address(this).balance - holdBalance));
holdBalance += holdAmount;
emit PlaceBet(enqueue(betAmount, bet, player));
EthexJackpot(jackpotAddress).registerTicket.value(jackpotFee)(markedCount > 1 ? player : address(0x0));
EthexHouse(houseAddress).payIn.value(houseEdgeFee)();
}
function settleBets() external {
uint256 betCount;
uint256 first;
uint256 last;
(betCount, first, last) = getCounters();
if (first > last)
return;
uint256 i = 0;
uint256 length = getLength();
length = length > 10 ? 10 : length;
Transaction[] memory transactions = new Transaction[](length);
Superprize[] memory superprizes = new Superprize[](length);
uint256 hold = holdBalance;
uint256 balance = address(this).balance - hold;
for(; i < length; i++) {
if (betQueue[first].blockNumber >= block.number) {
length = i;
break;
}
else {
Bet memory bet = dequeue();
first++;
uint256 coefficient = 0;
uint8 markedCount = 0;
uint256 holdAmount = 0;
(coefficient, markedCount, holdAmount) = getHold(bet.amount, bet.bet);
hold -= holdAmount;
balance += holdAmount;
if (bet.blockNumber < block.number - 256) {
transactions[i] = Transaction(bet.amount, bet.gamer);
emit RefundBet(bet.amount, bet.id, bet.gamer);
balance -= bet.amount;
}
else {
bytes32 blockHash = blockhash(bet.blockNumber);
coefficient = 0;
uint8 matchesCount;
bool isSuperPrize = true;
for (uint8 j = 0; j < bet.bet.length; j++) {
if (bet.bet[j] > 0x13) {
isSuperPrize = false;
continue;
}
byte field;
if (j % 2 == 0)
field = blockHash[29 + j / 2] >> 4;
else
field = blockHash[29 + j / 2] & 0x0F;
if (bet.bet[j] < 0x10) {
if (field == bet.bet[j]) {
matchesCount++;
coefficient += 30;
}
else
isSuperPrize = false;
continue;
}
else
isSuperPrize = false;
if (bet.bet[j] == 0x10) {
if (field > 0x09 && field < 0x10) {
matchesCount++;
coefficient += 5;
}
continue;
}
if (bet.bet[j] == 0x11) {
if (field < 0x0A) {
matchesCount++;
coefficient += 3;
}
continue;
}
if (bet.bet[j] == 0x12) {
if (field < 0x0A && field & 0x01 == 0x01) {
matchesCount++;
coefficient += 6;
}
continue;
}
if (bet.bet[j] == 0x13) {
if (field < 0x0A && field & 0x01 == 0x0) {
matchesCount++;
coefficient += 6;
}
continue;
}
}
coefficient *= PRECISION * 8;
uint256 payoutAmount = bet.amount * coefficient / (PRECISION * 15 * markedCount);
transactions[i] = Transaction(payoutAmount, bet.gamer);
emit PayoutBet(payoutAmount, bet.id, bet.gamer);
balance -= payoutAmount;
if (isSuperPrize == true) {
superprizes[i].amount = balance;
superprizes[i].id = bet.id;
balance = 0;
}
}
holdBalance = hold;
}
}
for (i = 0; i < length; i++) {
if (transactions[i].amount > 0 && !transactions[i].gamer.send(transactions[i].amount))
(new DeliverFunds).value(transactions[i].amount)(transactions[i].gamer);
if (superprizes[i].id != 0) {
EthexSuperprize(superprizeAddress).initSuperprize(transactions[i].gamer, superprizes[i].id);
EthexJackpot(jackpotAddress).paySuperprize(transactions[i].gamer);
if (superprizes[i].amount > 0 && !transactions[i].gamer.send(superprizes[i].amount))
(new DeliverFunds).value(superprizes[i].amount)(transactions[i].gamer);
}
}
}
function migrate() external {
require(msg.sender == owner || msg.sender == newVersionAddress);
require(getLength() == 0, "There are pending bets");
require(newVersionAddress != address(0), "NewVersionAddress required");
newVersionAddress.transfer(address(this).balance);
}
function setJackpot(address payable jackpot) external onlyOwner {
jackpotAddress = jackpot;
}
function setSuperprize(address payable superprize) external onlyOwner {
superprizeAddress = superprize;
}
function setFreeSpins(address payable freeSpins) external onlyOwner {
freeSpinsAddress = freeSpins;
}
function setNewVersion(address payable newVersion) external onlyOwner {
newVersionAddress = newVersion;
}
function setOldVersion(address payable oldAddress) external onlyOwner {
counters = EthexLoto(oldAddress).counters();
EthexLoto(oldAddress).migrate();
}
function withdrawToken(IERC20 token, uint amount, address sendTo) external onlyOwner {
require(token.transfer(sendTo, amount));
}
function length() external view returns (uint256) { return getLength(); }
function enqueue(uint256 amount, bytes6 bet, address payable gamer) internal returns (uint256 betCount) {
uint256 first;
uint256 last;
(betCount, first, last) = getCounters();
last++;
betCount++;
betQueue[last] = Bet(block.number, amount, betCount, bet, gamer);
counters = betCount << 128 | (first << 64 | last);
}
function dequeue() internal returns (Bet memory bet) {
uint256 betCount;
uint256 first;
uint256 last;
(betCount, first, last) = getCounters();
require(last >= first);
bet = betQueue[first];
if (first == last)
counters = betCount << 128 | 0x20000000000000001;
else
counters = betCount << 128 | (first + 1 << 64 | last);
}
function getLength() internal view returns (uint256) {
uint256 betCount;
uint256 first;
uint256 last;
(betCount, first, last) = getCounters();
return 1 + last - first;
}
function getCounters() internal view returns (uint256 betCount, uint256 first, uint256 last) {
betCount = counters >> 128;
first = (counters & 0xffffffffffffffffffffffffffffffff) >> 64;
last = counters & 0xffffffffffffffff;
}
function getHold(uint256 amount, bytes6 bet) internal pure returns (uint256 coefficient, uint8 markedCount, uint256 holdAmount) {
for (uint8 i = 0; i < bet.length; i++) {
if (bet[i] > 0x13)
continue;
markedCount++;
if (bet[i] < 0x10) {
coefficient += 30;
continue;
}
if (bet[i] == 0x10) {
coefficient += 5;
continue;
}
if (bet[i] == 0x11) {
coefficient += 3;
continue;
}
if (bet[i] == 0x12) {
coefficient += 6;
continue;
}
if (bet[i] == 0x13) {
coefficient += 6;
continue;
}
}
holdAmount = amount * coefficient * 8 / 15 / markedCount;
}
}