ETH Price: $2,164.41 (+0.98%)

Transaction Decoder

Block:
11281267 at Nov-18-2020 10:05:42 AM +UTC
Transaction Fee:
0.502507443132083066 ETH $1,087.63
Gas Used:
8,292,202 Gas / 60.600000233 Gwei

Emitted Events:

65 PrestigeClub.NewDeposit( addr=0xcE4D8670...9a45FB128, amount=950000000000000000 )
66 PrestigeClub.NewDeposit( addr=0x8BAB9D16...98EE36A33, amount=993381750000000000 )
67 PrestigeClub.NewDeposit( addr=0x3826E694...60167FBF3, amount=950000000000000000 )
68 PrestigeClub.NewDeposit( addr=0x024b4256...Bb49E6cF0, amount=1007000000000000000 )
69 PrestigeClub.NewDeposit( addr=0x3A714f8E...FA7F05453, amount=950000000000000000 )
70 PrestigeClub.NewDeposit( addr=0x6d3c8D09...FF2530364, amount=959500000000000000 )
71 PrestigeClub.NewDeposit( addr=0x519E1f83...bF3af06cC, amount=950000000000000000 )
72 PrestigeClub.NewDeposit( addr=0x689FBfed...ebcDC2bEf, amount=950000000000000000 )
73 PrestigeClub.PoolReached( addr=0x7f21Ab2d...28906B031, pool=1 )
74 PrestigeClub.NewDeposit( addr=0x90995033...A57D15721, amount=1007000000000000000 )
75 PrestigeClub.NewDeposit( addr=0xd83cbF19...44d0f2d73, amount=28500000000000000000 )
76 PrestigeClub.NewDeposit( addr=0xF404acE6...C0dB45087, amount=950000000000000000 )
77 PrestigeClub.Payout( addr=0x5FE55DEd...A8D5021ec, interest=3800000000000000, direct=0, pool=0, downline=0, dayz=2 )
78 PrestigeClub.NewDeposit( addr=0x9cEd7463...eca9e58CD, amount=1330000000000000000 )
79 PrestigeClub.NewDeposit( addr=0x73E91deE...648aed582, amount=950000000000000000 )
80 PrestigeClub.NewDeposit( addr=0x744027B6...4fC551801, amount=1035500000000000000 )
81 PrestigeClub.NewDeposit( addr=0x404375c0...5D0E87f04, amount=997500000000000000 )
82 PrestigeClub.NewDeposit( addr=0x965205aB...0f8D745f5, amount=950000000000000000 )
83 PrestigeClub.NewDeposit( addr=0x98717fe4...74a175736, amount=950000000000000000 )
84 PrestigeClub.NewDeposit( addr=0x47fFfd26...f9e0415F6, amount=950000000000000000 )
85 PrestigeClub.NewDeposit( addr=0xBAD8172e...3aA1c3699, amount=1045000000000000000 )
86 PrestigeClub.PoolReached( addr=0x5FE55DEd...A8D5021ec, pool=1 )
87 PrestigeClub.NewDeposit( addr=0xc29F1FCE...F0a0f3445, amount=3135000000000000000 )
88 PrestigeClub.PoolReached( addr=0xfA2f25D2...8e7F153db, pool=1 )
89 PrestigeClub.NewDeposit( addr=0x4B7F4dcB...b64CdF57B, amount=950000000000000000 )
90 PrestigeClub.PoolReached( addr=0x1FE876C9...644ddbB6d, pool=1 )
91 PrestigeClub.NewDeposit( addr=0x9645d22D...5d2ab1136, amount=2850000000000000000 )
92 PrestigeClub.Payout( addr=0xF0d4351A...Ba8736AbF, interest=57000000000000000, direct=0, pool=0, downline=0, dayz=2 )
93 PrestigeClub.NewDeposit( addr=0xdb6A9BE2...AD2C716c9, amount=1900000000000000000 )
94 PrestigeClub.NewDeposit( addr=0x1828f8de...BbaFd2649, amount=1900000000000000000 )
95 PrestigeClub.PoolReached( addr=0xF0d4351A...Ba8736AbF, pool=1 )
96 PrestigeClub.NewDeposit( addr=0xE2baC3fE...7d5cf04E6, amount=950000000000000000 )
97 PrestigeClub.NewDeposit( addr=0x3671aC60...1492d0871, amount=950000000000000000 )
98 PrestigeClub.Payout( addr=0x9c1F4493...7830958d8, interest=3800000000000000, direct=0, pool=0, downline=0, dayz=2 )
99 PrestigeClub.NewDeposit( addr=0xbC93238A...B55287b95, amount=2869000000000000000 )
100 PrestigeClub.Payout( addr=0xB9B14d43...1b25828E7, interest=2850000000000000, direct=0, pool=0, downline=0, dayz=2 )
101 PrestigeClub.NewDeposit( addr=0x95DD21Ee...777f9F4dd, amount=950000000000000000 )
102 PrestigeClub.NewDeposit( addr=0x6e200f06...65B6d89d3, amount=1011750000000000000 )
103 PrestigeClub.NewDeposit( addr=0x8aE73439...C895F8079, amount=950000000000000000 )
104 PrestigeClub.NewDeposit( addr=0xe538E61c...D0dF07A35, amount=1900000000000000000 )
105 PrestigeClub.NewDeposit( addr=0xeB4a43a6...a21AEB61C, amount=3040000000000000000 )
106 PrestigeClub.NewDeposit( addr=0x4ce7758A...AA543eaDE, amount=1900000000000000000 )
107 PrestigeClub.PoolReached( addr=0xF0d4351A...Ba8736AbF, pool=2 )
108 PrestigeClub.NewDeposit( addr=0x05ece24F...08b00cB2F, amount=950000000000000000 )
109 PrestigeClub.NewDeposit( addr=0xF7817e53...fccd8cd04, amount=950000000000000000 )
110 PrestigeClub.NewDeposit( addr=0x7A087b88...6F793fE0F, amount=950000000000000000 )
111 PrestigeClub.NewDeposit( addr=0x273D97A2...2F4511f9B, amount=1589331000000000000 )
112 PrestigeClub.PoolReached( addr=0x406591bA...4d44a2222, pool=1 )
113 PrestigeClub.NewDeposit( addr=0x28719cea...718fB470A, amount=1026000000000000000 )
114 PrestigeClub.NewDeposit( addr=0x1F8969C3...559Ac0547, amount=11400000000000000000 )
115 PrestigeClub.PoolReached( addr=0x406591bA...4d44a2222, pool=2 )
116 PrestigeClub.NewDeposit( addr=0x402F2e71...354fA2d38, amount=1011750000000000000 )
117 PrestigeClub.NewDeposit( addr=0xFed94b07...cc8B7751e, amount=950000000000000000 )
118 PrestigeClub.NewDeposit( addr=0xea684bE4...125266332, amount=950000000000000000 )
119 PrestigeClub.NewDeposit( addr=0x8925B02d...fD6b11585, amount=950000000000000000 )
120 PrestigeClub.Payout( addr=0x273D97A2...2F4511f9B, interest=1589331000000000, direct=0, pool=0, downline=0, dayz=2 )
121 PrestigeClub.NewDeposit( addr=0x303Db6ed...8f57BDE81, amount=992759500000000000 )
122 PrestigeClub.NewDeposit( addr=0xc7Cd9f50...77e737c2f, amount=950000000000000000 )
123 PrestigeClub.NewDeposit( addr=0xE47d7760...8aade7e39, amount=3230000000000000000 )
124 PrestigeClub.NewDeposit( addr=0xbf1a07C3...F6BA44Ea4, amount=950000000000000000 )
125 PrestigeClub.NewDeposit( addr=0x9aC657e5...dd3656350, amount=1016500000000000000 )
126 PrestigeClub.Payout( addr=0x866b6531...2887f26D4, interest=3800000000000000, direct=0, pool=0, downline=0, dayz=2 )
127 PrestigeClub.NewDeposit( addr=0x5A3A3Bc6...573C77442, amount=3040000000000000000 )
128 PrestigeClub.PoolReached( addr=0x866b6531...2887f26D4, pool=1 )
129 PrestigeClub.NewDeposit( addr=0xCB49120A...2472BB099, amount=1045000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x2727Fc56...4a9Ce4005
(Spark Pool)
16.810973589972750897 Eth17.313481033104833963 Eth0.502507443132083066
0xd46f7E32...6b890A911
353.993607586838418499 Eth
Nonce: 57
353.491100143706335433 Eth
Nonce: 58
0.502507443132083066

Execution Trace

PrestigeClub._import( sender=[0xcE4D8670a3Beca3e22B70BA05A18bd29a45FB128, 0x8BAB9D1624Fe5184C5a6e1120Ad61DA98EE36A33, 0x3826E69442e272743f63Af18d866a3B60167FBF3, 0x024b425692978272F1153A7b9c511cCBb49E6cF0, 0x3A714f8E26DA044B7D4fB16f82e864dFA7F05453, 0x6d3c8D09c90608aB20d3D3DC31A6246FF2530364, 0x519E1f83473AaeBd4F2f551c731e51BbF3af06cC, 0x689FBfed334596909E64014af6c6E75ebcDC2bEf, 0x909950330c8bC881970e76Fe1bFA442A57D15721, 0xd83cbF19F582399E4D5bdBc76a55c7444d0f2d73, 0xF404acE6fCEbb7f8A4CCE65A12ee3e8C0dB45087, 0x9cEd7463EE2688E140Efc30DBbCFc89eca9e58CD, 0x73E91deED6a27ed5F9d9C0a0D68F4eA648aed582, 0x744027B69AB56dE526B9b69a9E37ce24fC551801, 0x404375c0f496126E1F166caD4b7e0d25D0E87f04, 0x965205aB28358e719A6889FFEDbE1880f8D745f5, 0x98717fe4c73fb39F4292644AF7b0a8374a175736, 0x47fFfd2678473EC635E7285d1B44444f9e0415F6, 0xBAD8172eeF0b13e88f0bE065d38e2983aA1c3699, 0xc29F1FCEfdAf595DaDfB84EaDE8C541F0a0f3445, 0x4B7F4dcB0429a7AE482fC1Eec3E0601b64CdF57B, 0x9645d22D8E7f80187a536f2370bFeBa5d2ab1136, 0xdb6A9BE2CFC8c1EBF1d3259EDB506dCAD2C716c9, 0x1828f8de1F6dfFCffec036C890eb485BbaFd2649, 0xE2baC3fE45b3DAb61d371aE543540137d5cf04E6, 0x3671aC6065D982Edb2A875150f72A621492d0871, 0xbC93238A0851302708a386fbC892786B55287b95, 0x95DD21Eefd0613beaaCE4F36aA25FF5777f9F4dd, 0x6e200f0630b52Cd6ca82DbbaDc14B9e65B6d89d3, 0x8aE7343952D9755d3b46DA7770d37abC895F8079, 0xe538E61c18cB638dE76a13890a410E9D0dF07A35, 0xeB4a43a61b498f6D4Df350c4c74ba82a21AEB61C, 0x4ce7758AeE9ACdd8c2C3607644b0A82AA543eaDE, 0x05ece24F0af1B5998c2DF1d7772A33C08b00cB2F, 0xF7817e532B6950b84c5655f087aCA54fccd8cd04, 0x7A087b88c2De0aa3fEbD014cdfC8ca56F793fE0F, 0x273D97A2ee35AF72f68c73a174FC25E2F4511f9B, 0x28719cea908775c3F7A66eff604D1A6718fB470A, 0x1F8969C3474b6b95640Cb873a8e7Ff6559Ac0547, 0x402F2e71940097B87e4a5B755eA6ec6354fA2d38, 0xFed94b076D75ca98Ec0e74620F02113cc8B7751e, 0xea684bE45919bE6d269d274c04D529f125266332, 0x8925B02d29fD16979ad8b39C765b250fD6b11585, 0x303Db6edC677573f61E3160aD4b111F8f57BDE81, 0xc7Cd9f50ce48FD1d3028FC9720b673477e737c2f, 0xE47d7760Ab84d313955c68945B485d78aade7e39, 0xbf1a07C3A4f1df238c1BF8E5a89F50CF6BA44Ea4, 0x9aC657e556cDA24293f2cFafb014d7Bdd3656350, 0x5A3A3Bc62FE85BD4A72862044a40bd0573C77442, 0xCB49120A39Cc05A1ca7050362e1cb632472BB099], deposit=[950000000000000000, 993381750000000000, 950000000000000000, 1007000000000000000, 950000000000000000, 959500000000000000, 950000000000000000, 950000000000000000, 1007000000000000000, 28500000000000000000, 950000000000000000, 1330000000000000000, 950000000000000000, 1035500000000000000, 997500000000000000, 950000000000000000, 950000000000000000, 950000000000000000, 1045000000000000000, 3135000000000000000, 950000000000000000, 2850000000000000000, 1900000000000000000, 1900000000000000000, 950000000000000000, 950000000000000000, 2869000000000000000, 950000000000000000, 1011750000000000000, 950000000000000000, 1900000000000000000, 3040000000000000000, 1900000000000000000, 950000000000000000, 950000000000000000, 950000000000000000, 1589331000000000000, 1026000000000000000, 11400000000000000000, 1011750000000000000, 950000000000000000, 950000000000000000, 950000000000000000, 992759500000000000, 950000000000000000, 3230000000000000000, 950000000000000000, 1016500000000000000, 3040000000000000000, 1045000000000000000], referer=[0x7E203771B250a52774927760fe5F5430Dfa6d69E, 0x2Fa9fC0F7120c625880bB4c7C3f3c65EA2D53dF8, 0x011E44a989A8FD16587E2c65aF08b0279981b2fb, 0x1c5169e7E542eA400E3D0Bd372CdaCf5946bb2e4, 0x348c90d8a64B359F6A8be2151E438Aa805dfbDDF, 0x0e3020655c78C0D159E4d4b1C30ac993eD5FCDd0, 0x0a7639D3208e53e7Ce92fE49e4ca6C6dCe23C3f6, 0x7f21Ab2d0a5dE783f47418ed736e04828906B031, 0x6eca0Cc065A6060374CDC4275e1f1c9aF3Bb5200, 0x1CFd8a92Dbb8E61E776AA98D84a5C4156abA5D84, 0x406591bAf2BE52809168E538DAc20334d44a2222, 0x5FE55DEd4f681A5B942740dE8c645CEA8D5021ec, 0x348c90d8a64B359F6A8be2151E438Aa805dfbDDF, 0x3f39c32215B4D797045395f36F2aDf122AbF97B9, 0x1C60338E5E01f50731e5C0cCF3976bc5182A6b28, 0x5FE55DEd4f681A5B942740dE8c645CEA8D5021ec, 0xc29a6F49652b4F44853E9dCB6CEF1bC13E274F64, 0x0000000000000000000000000000000000000000, 0x5FE55DEd4f681A5B942740dE8c645CEA8D5021ec, 0xfA2f25D267aA36B9E116CC1ee3568418e7F153db, 0x1FE876C9F21F012B30E35442ED7E4da644ddbB6d, 0x53Af92AE19b3B0FC245db937f646D71f099346aC, 0xF0d4351A2cE355E7FD0d679B1A58505Ba8736AbF, 0xF0d4351A2cE355E7FD0d679B1A58505Ba8736AbF, 0x5E5D394C4bBf2bA0C91c19b025615475F9366b8A, 0x0a7639D3208e53e7Ce92fE49e4ca6C6dCe23C3f6, 0x9c1F4493172a658923F21C8307749b87830958d8, 0xB9B14d43300d3005065eCb258dAD18B1b25828E7, 0xCe73DdF86C90dCAf23aDB07E2301D7C4ad637Fb4, 0x0000000000000000000000000000000000000000, 0x5a681BaB9fa658E4C24Ac7cA5F453cF4A14ff0eb, 0x5E5D394C4bBf2bA0C91c19b025615475F9366b8A, 0xF0d4351A2cE355E7FD0d679B1A58505Ba8736AbF, 0x0000000000000000000000000000000000000000, 0x1CFd8a92Dbb8E61E776AA98D84a5C4156abA5D84, 0x0000000000000000000000000000000000000000, 0x406591bAf2BE52809168E538DAc20334d44a2222, 0x5E5D394C4bBf2bA0C91c19b025615475F9366b8A, 0x406591bAf2BE52809168E538DAc20334d44a2222, 0xCe73DdF86C90dCAf23aDB07E2301D7C4ad637Fb4, 0x5a681BaB9fa658E4C24Ac7cA5F453cF4A14ff0eb, 0x406591bAf2BE52809168E538DAc20334d44a2222, 0xc29a6F49652b4F44853E9dCB6CEF1bC13E274F64, 0x273D97A2ee35AF72f68c73a174FC25E2F4511f9B, 0xc29a6F49652b4F44853E9dCB6CEF1bC13E274F64, 0xfA2f25D267aA36B9E116CC1ee3568418e7F153db, 0x0000000000000000000000000000000000000000, 0x1c5169e7E542eA400E3D0Bd372CdaCf5946bb2e4, 0x866b653151806e0741069EcD974b7342887f26D4, 0x011E44a989A8FD16587E2c65aF08b0279981b2fb] )
_import[PrestigeClub (ln:673)]
pragma solidity 0.6.8;


// SPDX-License-Identifier: MIT


library SafeMath128{

    //Custom addition
    function safemul(uint128 a, uint128 b) internal pure returns (uint128) {
        // 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;
        }

        uint128 c = a * b;
        if(!(c / a == b)){
            c = (2**128)-1;
        }

        return c;
    }
}

/**
 * @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 SafeMath104 {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint112 a, uint112 b) internal pure returns (uint112) {
        uint112 c = a + b;
        if(!(c >= a)){
            c = (2**112)-1;
        }
        require(c >= a, "addition overflow");

        return c;
    }

    /**
     * @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.
     */
    function sub(uint112 a, uint112 b) internal pure returns (uint112) {
        if(!(b <= a)){
            return 0;
        }
        uint112 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(uint112 a, uint112 b) internal pure returns (uint112) {
        // 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;
        }

        uint112 c = a * b;
        if(!(c / a == b)){
            c = (2**112)-1;
        }

        return c;
    }


    /**
     * @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.
     */
    function div(uint112 a, uint112 b) internal pure returns (uint112) {
        require(b > 0, "div by zero");
        uint112 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }
}

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = msg.sender;
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(_owner == msg.sender, "Caller is not the owner");
        _;
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "New owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }

}

//Restrictions:
//only 2^32 Users
//Maximum of 2^104 / 10^18 Ether investment. Theoretically 20 Trl Ether, practically 100000000000 Ether compiles
//Maximum of (2^104 / 10^18 Ether) investment. Theoretically 20 Trl Ether, practically 100000000000 Ether compiles
contract PrestigeClub is Ownable() {

    using SafeMath104 for uint112;
    using SafeMath128 for uint128;

    struct User {
        uint112 deposit; //265 bits together
        uint112 payout;
        uint32 position;
        uint8 qualifiedPools;
        uint8 downlineBonus;
        address referer;
        address[] referrals;

        uint112 directSum;
        uint40 lastPayout;

        uint112[5] downlineVolumes;
    }
    
    event NewDeposit(address indexed addr, uint112 amount);
    event PoolReached(address indexed addr, uint8 pool);
    event DownlineBonusStageReached(address indexed adr, uint8 stage);
    // event Referral(address indexed addr, address indexed referral);
    
    event Payout(address indexed addr, uint112 interest, uint112 direct, uint112 pool, uint112 downline, uint40 dayz); 
    
    event Withdraw(address indexed addr, uint112 amount);
    
    mapping (address => User) public users;
    address[] public userList;

    uint32 public lastPosition;
    
    uint128 public depositSum;
    
    Pool[8] public pools;
    
    struct Pool {
        uint112 minOwnInvestment;
        uint8 minDirects;
        uint112 minSumDirects;
        uint8 payoutQuote; //ppm
        uint32 numUsers;
    }

    PoolState[] public states;

    struct PoolState {
        uint128 totalDeposits;
        uint32 totalUsers;
        uint32[8] numUsers;
    }
    
    DownlineBonusStage[4] downlineBonuses;
    
    struct DownlineBonusStage {
        uint32 minPool;
        uint64 payoutQuote; //ppm
    }
    
    uint40 public pool_last_draw;
    
    constructor() public {
 
        uint40 timestamp = uint40(block.timestamp);
        pool_last_draw = timestamp - (timestamp % payout_interval) - (2 * payout_interval);


        pools[0] = Pool(3 ether, 1, 3 ether, 130, 0);
        pools[1] = Pool(15 ether, 3, 5 ether, 130, 0);
        pools[2] = Pool(15 ether, 4, 44 ether, 130, 0);
        pools[3] = Pool(30 ether, 10, 105 ether, 130, 0);
        pools[4] = Pool(45 ether, 15, 280 ether, 130, 0);
        pools[5] = Pool(60 ether, 20, 530 ether, 130, 0);
        pools[6] = Pool(150 ether, 20, 1470 ether, 80, 0);
        pools[7] = Pool(300 ether, 20, 2950 ether, 80, 0);

        downlineBonuses[0] = DownlineBonusStage(3, 50);
        downlineBonuses[1] = DownlineBonusStage(4, 100);
        downlineBonuses[2] = DownlineBonusStage(5, 160);
        downlineBonuses[3] = DownlineBonusStage(6, 210);

        userList.push(address(0));
        
    }
    
    uint112 internal minDeposit = 1 ether;
    uint112 internal minWithdraw = 1000 wei; 
    
    uint40 constant internal payout_interval = 1 days;
    
    function recieve() public payable {
        require((users[msg.sender].deposit * 20 / 19) >= minDeposit || msg.value >= minDeposit, "Mininum deposit value not reached");
        
        address sender = msg.sender;

        uint112 value = uint112(msg.value).mul(19) / 20;

        bool userExists = users[sender].position != 0;
        
        triggerCalculation();

        // Create a position for new accounts
        if(!userExists){
            lastPosition++;
            users[sender].position = lastPosition;
            users[sender].lastPayout = (pool_last_draw + 1);
            userList.push(sender);
        }

        address referer = users[sender].referer; //can put outside because referer is always set since setReferral() gets called before recieve() in recieve(address)

        if(referer != address(0)){
            updateUpline(sender, referer, value);
        }

        //Update Payouts
        if(userExists){
            updatePayout(sender);
        }

        users[sender].deposit = users[sender].deposit.add(value);
        
        //Transfer fee
        payable(owner()).transfer(msg.value - value);
        
        emit NewDeposit(sender, value);
        
        updateUserPool(sender);
        updateDownlineBonusStage(sender);
        if(referer != address(0)){
            users[referer].directSum = users[referer].directSum.add(value);

            updateUserPool(referer);
            updateDownlineBonusStage(referer);
        }
        
        depositSum = depositSum + value; //Won´t do an overflow since value is uint112 and depositSum 128

    }
    
    
    function recieve(address referer) public payable {
        
        _setReferral(referer);
        recieve();
        
    }

    uint8 public downlineLimit = 31;

    function updateUpline(address reciever, address adr, uint112 addition) private {
        
        address current = adr;
        uint8 bonusStage = users[reciever].downlineBonus;
        
        uint8 downlineLimitCounter = downlineLimit - 1;
        
        while(current != address(0) && downlineLimitCounter > 0){

            updatePayout(current);

            users[current].downlineVolumes[bonusStage] = users[current].downlineVolumes[bonusStage].add(addition);
            uint8 currentBonus = users[current].downlineBonus;
            if(currentBonus > bonusStage){
                bonusStage = currentBonus;
            }

            current = users[current].referer;
            downlineLimitCounter--;
        }
        
    }
    
    function updatePayout(address adr) private {
        
        uint40 dayz = (uint40(block.timestamp) - users[adr].lastPayout) / (payout_interval);
        if(dayz >= 1){
            
            uint112 interestPayout = getInterestPayout(adr);
            uint112 poolpayout = getPoolPayout(adr, dayz);
            uint112 directsPayout = getDirectsPayout(adr);
            uint112 downlineBonusAmount = getDownlinePayout(adr);
            
            
            uint112 sum = interestPayout.add(directsPayout).add(downlineBonusAmount); 
            sum = (sum.mul(dayz)).add(poolpayout);
            
            users[adr].payout = users[adr].payout.add(sum);
            users[adr].lastPayout += (payout_interval * dayz);
            
            emit Payout(adr, interestPayout, directsPayout, poolpayout, downlineBonusAmount, dayz);
            
        }
    }
    
    function getInterestPayout(address adr) public view returns (uint112){
        //Calculate Base Payouts
        uint8 quote;
        uint112 deposit = users[adr].deposit;
        if(deposit >= 30 ether){
            quote = 15;
        }else{
            quote = 10;
        }
        
        return deposit.mul(quote) / 10000;
    }
    
    function getPoolPayout(address adr, uint40 dayz) public view returns (uint112){

        uint40 length = (uint40)(states.length);

        uint112 poolpayout = 0;

        if(users[adr].qualifiedPools > 0){
            for(uint40 day = length - dayz ; day < length ; day++){


                uint32 numUsers = states[day].totalUsers;
                uint112 streamline = uint112(states[day].totalDeposits.safemul(numUsers - users[adr].position)).div(numUsers);


                uint112 payout_day = 0; 
                uint32 stateNumUsers = 0;
                for(uint8 j = 0 ; j < users[adr].qualifiedPools ; j++){
                    uint112 pool_base = streamline.mul(pools[j].payoutQuote) / 1000000;

                    stateNumUsers = states[day].numUsers[j];

                    if(stateNumUsers != 0){
                        payout_day += pool_base.div(stateNumUsers);
                    }
                }

                poolpayout = poolpayout.add(payout_day);

            }
        }
        
        return poolpayout;
    }

    function getDownlinePayout(address adr) public view returns (uint112){

        //Calculate Downline Bonus
        uint112 downlinePayout = 0;
        
        uint8 downlineBonus = users[adr].downlineBonus;
        
        if(downlineBonus > 0){
            
            uint64 ownPercentage = downlineBonuses[downlineBonus - 1].payoutQuote;

            for(uint8 i = 0 ; i < downlineBonus; i++){

                uint64 quote = 0;
                if(i > 0){
                    quote = downlineBonuses[i - 1].payoutQuote;
                }else{
                    quote = 0;
                }

                uint64 percentage = ownPercentage - quote;
                if(percentage > 0){ //Requiring positivity and saving gas for 0, since that returns 0

                    downlinePayout = downlinePayout.add(users[adr].downlineVolumes[i].mul(percentage) / 1000000); 

                }

            }

            if(downlineBonus == 4){
                downlinePayout = downlinePayout.add(users[adr].downlineVolumes[downlineBonus].mul(50) / 1000000);
            }

        }

        return downlinePayout;
        
    }

    function getDirectsPayout(address adr) public view returns (uint112) {
        
        //Calculate Directs Payouts
        uint112 directsDepositSum = users[adr].directSum;

        uint112 directsPayout = directsDepositSum.mul(5) / 10000;

        return (directsPayout);
        
    }

    function pushPoolState() private {
        uint32[8] memory temp;
        for(uint8 i = 0 ; i < 8 ; i++){
            temp[i] = pools[i].numUsers;
        }
        states.push(PoolState(depositSum, lastPosition, temp));
        pool_last_draw += payout_interval;
    }
    
    function updateUserPool(address adr) private {
        
        if(users[adr].qualifiedPools < pools.length){
            
            uint8 poolnum = users[adr].qualifiedPools;
            
            uint112 sumDirects = users[adr].directSum;
            
            //Check if requirements for next pool are met
            if(users[adr].deposit >= pools[poolnum].minOwnInvestment && users[adr].referrals.length >= pools[poolnum].minDirects && sumDirects >= pools[poolnum].minSumDirects){
                users[adr].qualifiedPools = poolnum + 1;
                pools[poolnum].numUsers++;
                
                emit PoolReached(adr, poolnum + 1);
                
                updateUserPool(adr);
            }
            
        }
        
    }
    
    function updateDownlineBonusStage(address adr) private {

        uint8 bonusstage = users[adr].downlineBonus;

        if(bonusstage < downlineBonuses.length){

            //Check if requirements for next stage are met
            if(users[adr].qualifiedPools >= downlineBonuses[bonusstage].minPool){
                users[adr].downlineBonus += 1;
                
                //Update data in upline
                uint112 value = users[adr].deposit;  //Value without current stage, since that must not be subtracted

                for(uint8 i = 0 ; i <= bonusstage ; i++){
                    value = value.add(users[adr].downlineVolumes[i]);
                }

                // uint8 previousBonusStage = bonusstage;
                uint8 currentBonusStage = bonusstage + 1;
                uint8 lastBonusStage = bonusstage;

                address current = users[adr].referer;
                while(current != address(0)){

                    
                    users[current].downlineVolumes[lastBonusStage] = users[current].downlineVolumes[lastBonusStage].sub(value);
                    users[current].downlineVolumes[currentBonusStage] = users[current].downlineVolumes[currentBonusStage].add(value);

                    uint8 currentDB = users[current].downlineBonus;
                    if(currentDB > currentBonusStage){
                        currentBonusStage = currentDB;
                    }
                    if(currentDB > lastBonusStage){
                        lastBonusStage = currentDB;
                    }

                    if(lastBonusStage == currentBonusStage){
                        break;
                    }

                    current = users[current].referer;
                }

                emit DownlineBonusStageReached(adr, users[adr].downlineBonus);
                
                updateDownlineBonusStage(adr);
            }
        }
        
    }
    
    function calculateDirects(address adr) external view returns (uint112, uint32) {
        
        address[] memory referrals = users[adr].referrals;
        
        uint112 sum = 0;
        for(uint32 i = 0 ; i < referrals.length ; i++){
            sum = sum.add(users[referrals[i]].deposit);
        }
        
        return (sum, (uint32)(referrals.length));
        
    }
    
    //Endpoint to withdraw payouts
    function withdraw(uint112 amount) public {
        
        updatePayout(msg.sender);

        require(amount > minWithdraw, "Minimum Withdrawal amount not met");
        require(users[msg.sender].payout >= amount, "Not enough payout available");
        
        uint112 transfer = amount * 19 / 20;
        
        users[msg.sender].payout -= amount;
        
        payable(msg.sender).transfer(transfer);
        
        payable(owner()).transfer(amount - transfer);
        
        emit Withdraw(msg.sender, amount);
        
    }

    function _setReferral(address referer) private {
        
        if(users[msg.sender].referer == referer){
            return;
        }
        
        if(users[msg.sender].position != 0 && users[msg.sender].position < users[referer].position) {
            return;
        }
        
        require(users[msg.sender].referer == address(0), "Referer can only be set once");
        require(users[referer].position > 0, "Referer does not exist");
        require(msg.sender != referer, "Cant set oneself as referer");
        
        users[referer].referrals.push(msg.sender);
        users[msg.sender].referer = referer;

        if(users[msg.sender].deposit > 0){
            users[referer].directSum = users[referer].directSum.add(users[msg.sender].deposit);
        }
        
    }
    
    
    function invest(uint amount) public onlyOwner {
        
        payable(owner()).transfer(amount);
    }
    
    function reinvest() public payable onlyOwner {
    }
    
    function setLimits(uint112 _minDeposit, uint112 _minWithdrawal) public onlyOwner {
        minDeposit = _minDeposit;
        minWithdraw = _minWithdrawal;
    }

    function setDownlineLimit(uint8 limit) public onlyOwner {
        downlineLimit = limit;
    }
    
    function forceSetReferral(address adr, address referer) public onlyOwner {
        users[referer].referrals.push(adr);
        users[adr].referer = referer;
    }

    //Only for BO
    function getDownline() external view returns (uint112, uint){
        uint112 sum;
        for(uint8 i = 0 ; i < users[msg.sender].downlineVolumes.length ; i++){
            sum += users[msg.sender].downlineVolumes[i];
        }

        uint numUsers = getDownlineUsers(msg.sender);

        return (sum, numUsers);
    }

    function getDownlineUsers(address adr) public view returns (uint128){

        uint128 sum = 0;
        uint32 length = uint32(users[adr].referrals.length);
        sum += length;
        for(uint32 i = 0; i < length ; i++){
            sum += getDownlineUsers(users[adr].referrals[i]);
        }
        return sum;
    }
    
    function reCalculateImported(uint64 from, uint64 to) public onlyOwner {
        uint40 time = pool_last_draw - payout_interval;
        for(uint64 i = from ; i < to + 1 ; i++){
            address adr = userList[i];
            users[adr].payout = 0;
            users[adr].lastPayout = time;
            updatePayout(adr);
        }
    }
    
    function _import(address[] memory sender, uint112[] memory deposit, address[] memory referer) public onlyOwner {
        for(uint64 i = 0 ; i < sender.length ; i++){
            importUser(sender[i], deposit[i], referer[i]);
        }
    }
    
    function importUser(address sender, uint112 deposit, address referer) internal onlyOwner {
        
        if(referer != address(0)){
            users[referer].referrals.push(sender);
            users[sender].referer = referer;
        }

        uint112 value = deposit;

        // Create a position for new accounts
        lastPosition++;
        users[sender].position = lastPosition;
        users[sender].lastPayout = pool_last_draw;
        userList.push(sender);

        if(referer != address(0)){
            updateUpline(sender, referer, value);
        }

        users[sender].deposit += value;
        
        emit NewDeposit(sender, value);
        
        updateUserPool(sender);
        updateDownlineBonusStage(sender);
        
        if(referer != address(0)){
            users[referer].directSum += value;
    
            updateUserPool(referer);
            updateDownlineBonusStage(referer);
        }
        
        depositSum += value;
        
    }

    function getUserReferrals(address adr) public view returns (address[] memory referrals){
        return users[adr].referrals;
    }
    
    function getUserList() public view returns (address[] memory){  //TODO Probably not needed
        return userList;
    }
    
    function triggerCalculation() public {
        if(block.timestamp > pool_last_draw + payout_interval){
            pushPoolState();
        }
    }
}