ETH Price: $2,145.91 (-1.99%)

Transaction Decoder

Block:
9054834 at Dec-05-2019 12:13:06 PM +UTC
Transaction Fee:
0.00066633 ETH $1.43
Gas Used:
66,633 Gas / 10 Gwei

Emitted Events:

54 MutualAlliance.LogWithdrawProfit( who=[Sender] 0x88e38daba08fef9f326fa68367fcc3b42205ca54, uid=83, amount=240000000000000000, time=1575547986, t=1 )

Account State Difference:

  Address   Before After State Difference Code
0x5370645B...7ad4B286c 2,931.34732 Eth2,931.10732 Eth0.24
0x88E38daB...42205CA54
2.14290632 Eth
Nonce: 34
2.38223999 Eth
Nonce: 35
0.23933367
(Ethermine)
308.98987354588771049 Eth308.99053987588771049 Eth0.00066633

Execution Trace

MutualAlliance.CALL( )
  • ETH 0.24 0x88e38daba08fef9f326fa68367fcc3b42205ca54.CALL( )
    pragma solidity ^0.5.0;
    
    contract UtilMutualAlliance{
        uint ethWei = 1 ether;
    
        function getLevel(uint value) internal view returns (uint) {
            if (value >= 1 * ethWei && value <= 5 * ethWei) {
                return 1;
            }
            if (value >= 6 * ethWei && value <= 14 * ethWei) {
                return 2;
            }
            if (value >= 15 * ethWei && value <= 20 * ethWei) {
                return 3;
            }
            return 0;
        }
    
        function getLineLevel(uint value) internal view returns (uint) {
            if (value >= 1 * ethWei && value <= 5 * ethWei) {
                return 1;
            }
            if (value >= 6 * ethWei && value <= 14 * ethWei) {
                return 2;
            }
            if (value >= 15 * ethWei) {
                return 3;
            }
            return 0;
        }
    
        function getSepScByLevel(uint level) internal pure returns(uint) {
            if (level == 1) {
                return 65;
            }
            if (level == 2) {
                return 85;
            }
            if (level == 3) {
                return 120;
            }
    
            return 0;
        }
    
        function getScByLevel(uint level, uint reInvestCount) internal pure returns (uint) {
            if (level == 1) {
                if (reInvestCount == 0) {
                    return 20;
                }
                if (reInvestCount == 1) {
                    return 25;
                }
                if (reInvestCount == 2) {
                    return 30;
                }
                if (reInvestCount == 3) {
                    return 35;
                }
                if (reInvestCount >= 4) {
                    return 50;
                }
            }
            if (level == 2) {
                if (reInvestCount == 0) {
                    return 30;
                }
                if (reInvestCount == 1) {
                    return 40;
                }
                if (reInvestCount == 2) {
                    return 50;
                }
                if (reInvestCount == 3) {
                    return 60;
                }
                if (reInvestCount >= 4) {
                    return 70;
                }
            }
            if (level == 3) {
                if (reInvestCount == 0) {
                    return 60;
                }
                if (reInvestCount == 1) {
                    return 70;
                }
                if (reInvestCount == 2) {
                    return 80;
                }
                if (reInvestCount == 3) {
                    return 90;
                }
                if (reInvestCount >= 4) {
                    return 100;
                }
            }
            return 0;
        }
    
        function getFireScByLevel(uint level) internal pure returns (uint) {
            if (level == 1) {
                return 3;
            }
            if (level == 2) {
                return 6;
            }
            if (level == 3) {
                return 10;
            }
            return 0;
        }
    
        function getDynamicFloor(uint level) internal pure returns (uint) {
            if (level == 1) {
                return 1;
            }
            if (level == 2) {
                return 2;
            }
            if (level == 3) {
                return 20;
            }
    
            return 0;
        }
    
        function getFloorIndex(uint floor) internal pure returns (uint) {
            if (floor == 1) {
                return 1;
            }
            if (floor == 2) {
                return 2;
            }
            if (floor == 3) {
                return 3;
            }
            if (floor >= 4 && floor <= 5) {
                return 4;
            }
            if (floor >= 6 && floor <= 10) {
                return 5;
            }
            if (floor >= 11) {
                return 6;
            }
    
            return 0;
        }
    
        function getRecommendScaleByLevelAndTim(uint level, uint floorIndex) internal pure returns (uint){
            if (level == 1 && floorIndex == 1) {
                return 20;
            }
            if (level == 2) {
                if (floorIndex == 1) {
                    return 30;
                }
                if (floorIndex == 2) {
                    return 20;
                }
            }
            if (level == 3) {
                if (floorIndex == 1) {
                    return 50;
                }
                if (floorIndex == 2) {
                    return 30;
                }
                if (floorIndex == 3) {
                    return 20;
                }
                if (floorIndex == 4) {
                    return 10;
                }
                if (floorIndex == 5) {
                    return 5;
                }
                if (floorIndex >= 6) {
                    return 2;
                }
            }
            return 0;
        }
    
        function isEmpty(string memory str) internal pure returns (bool) {
            if (bytes(str).length == 0) {
                return true;
            }
    
            return false;
        }
    }
    
    /*
     * @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) {
            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;
        }
    }
    
    /**
     * @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.
     *
     * 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 is Context {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor () internal {
            _owner = _msgSender();
            emit OwnershipTransferred(address(0), _owner);
        }
    
        /**
         * @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(isOwner(), "Ownable: caller is not the owner");
            _;
        }
    
        /**
         * @dev Returns true if the caller is the current owner.
         */
        function isOwner() public view returns (bool) {
            return _msgSender() == _owner;
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        /**
         * @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 {
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         */
        function _transferOwnership(address newOwner) internal {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    /**
     * @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];
        }
    }
    
    /**
     * @title WhitelistAdminRole
     * @dev WhitelistAdmins are responsible for assigning and removing Whitelisted accounts.
     */
    contract WhitelistAdminRole is Context, Ownable {
        using Roles for Roles.Role;
    
        event WhitelistAdminAdded(address indexed account);
        event WhitelistAdminRemoved(address indexed account);
    
        Roles.Role private _whitelistAdmins;
    
        constructor () internal {
            _addWhitelistAdmin(_msgSender());
        }
    
        modifier onlyWhitelistAdmin() {
            require(isWhitelistAdmin(_msgSender()) || isOwner(), "WhitelistAdminRole: caller does not have the WhitelistAdmin role");
            _;
        }
    
        function isWhitelistAdmin(address account) public view returns (bool) {
            return _whitelistAdmins.has(account);
        }
    
        function addWhitelistAdmin(address account) public onlyWhitelistAdmin {
            _addWhitelistAdmin(account);
        }
    
        function removeWhitelistAdmin(address account) public onlyOwner {
            _whitelistAdmins.remove(account);
            emit WhitelistAdminRemoved(account);
        }
    
        function renounceWhitelistAdmin() public {
            _removeWhitelistAdmin(_msgSender());
        }
    
        function _addWhitelistAdmin(address account) internal {
            _whitelistAdmins.add(account);
            emit WhitelistAdminAdded(account);
        }
    
        function _removeWhitelistAdmin(address account) internal {
            _whitelistAdmins.remove(account);
            emit WhitelistAdminRemoved(account);
        }
    }
    
    contract MutualAlliance is UtilMutualAlliance, WhitelistAdminRole {
    
        using SafeMath for *;
    
        string constant private name = "MutualAlliance Official";
    
        uint ethWei = 1 ether;
    
        address payable private devAddr = address(0x3fd4967d8C5079c2D37cbaac8014c1e9584Fe5Dd);
    
        address payable private loyal = address(0x0EF71a4b3b37dbAb581bEc482bcd0eE7429917A3);
    
        address payable private other = address(0x0040E7d9808e9D344158D7379E0b91b61B93CC9F);
    
        struct User {
            uint id;
            address userAddress;
            uint staticLevel;
            uint dynamicLevel;
            uint allInvest;
            uint freezeAmount;
            uint unlockAmount;
            uint unlockAmountRedeemTime;
            uint allStaticAmount;
            uint hisStaticAmount;
            uint dynamicWithdrawn;
            uint staticWithdrawn;
            Invest[] invests;
            uint staticFlag;
    
            mapping(uint => mapping(uint => uint)) dynamicProfits;
            uint reInvestCount;
            uint inviteAmount;
            uint solitaire;
            uint hisSolitaire;
        }
    
        struct UserGlobal {
            uint id;
            address userAddress;
            string inviteCode;
            string referrer;
        }
    
        struct Invest {
            address userAddress;
            uint investAmount;
            uint investTime;
            uint realityInvestTime;
            uint times;
            uint modeFlag;
            bool isSuspendedInvest;
        }
    
        uint coefficient = 10;
        uint startTime;
        uint baseTime;
        uint investCount = 0;
        mapping(uint => uint) rInvestCount;
        uint investMoney = 0;
        mapping(uint => uint) rInvestMoney;
        uint uid = 0;
        uint rid = 1;
        uint period = 3 days;
        uint suspendedTime = 0;
        uint suspendedDays = 0 days;
        uint lastInvestTime = 0;
        mapping(uint => mapping(address => User)) userRoundMapping;
        mapping(address => UserGlobal) userMapping;
        mapping(string => address) addressMapping;
        mapping(uint => address) public indexMapping;
        mapping(uint => uint) public everyDayInvestMapping;
        mapping(uint => uint[]) investAmountList;
        mapping(uint => uint) transformAmount;
        uint baseLimit = 300 * ethWei;
    
        /**
         * @dev Just a simply check to prevent contract
         * @dev this by calling method in constructor.
         */
        modifier isHuman() {
            address addr = msg.sender;
            uint codeLength;
    
            assembly {codeLength := extcodesize(addr)}
            require(codeLength == 0, "sorry humans only");
            require(tx.origin == msg.sender, "sorry, human only");
            _;
        }
    
        modifier isSuspended() {
            require(notSuspended(), "suspended");
            _;
        }
    
        event LogInvestIn(address indexed who, uint indexed uid, uint amount, uint time, uint investTime, string inviteCode, string referrer, uint t);
        event LogWithdrawProfit(address indexed who, uint indexed uid, uint amount, uint time, uint t);
        event LogRedeem(address indexed who, uint indexed uid, uint amount, uint now);
    
        constructor () public {
        }
    
        function() external payable {
        }
    
        function activeGame(uint time, uint _baseTime) external onlyWhitelistAdmin
        {
            require(time > now, "invalid game start time");
            startTime = time;
    
            if (baseTime == 0) {
                baseTime = _baseTime;
            }
        }
    
        function setCoefficient(uint coeff, uint _baseLimit) external onlyWhitelistAdmin
        {
            require(coeff > 0, "invalid coeff");
            coefficient = coeff;
            require(_baseLimit > 0, "invalue base limit");
            baseLimit = _baseLimit;
        }
    
        function gameStart() public view returns (bool) {
            return startTime != 0 && now > startTime;
        }
    
        function investIn(string memory inviteCode, string memory referrer, uint flag)
        public
        isHuman()
        payable
        {
            require(flag == 0 || flag == 1, "invalid flag");
            require(gameStart(), "game not start");
            require(msg.value >= 1 * ethWei && msg.value <= 20 * ethWei, "between 1 and 20");
            require(msg.value == msg.value.div(ethWei).mul(ethWei), "invalid msg value");
            uint investTime = getInvestTime(msg.value);
            uint investDay = getCurrentInvestDay(investTime);
            everyDayInvestMapping[investDay] = msg.value.add(everyDayInvestMapping[investDay]);
            calUserQueueingStatic(msg.sender);
    
            UserGlobal storage userGlobal = userMapping[msg.sender];
            if (userGlobal.id == 0) {
                require(!isEmpty(inviteCode), "empty invite code");
                address referrerAddr = getUserAddressByCode(referrer);
                require(uint(referrerAddr) != 0, "referer not exist");
                require(referrerAddr != msg.sender, "referrer can't be self");
                require(!isUsed(inviteCode), "invite code is used");
    
                registerUser(msg.sender, inviteCode, referrer);
            }
    
            User storage user = userRoundMapping[rid][msg.sender];
            if (uint(user.userAddress) != 0) {
                require(user.freezeAmount == 0 && user.unlockAmount == 0, "your invest not unlocked");
                user.allInvest = user.allInvest.add(msg.value);
                user.freezeAmount = msg.value;
                user.staticLevel = getLevel(msg.value);
                user.dynamicLevel = getLineLevel(msg.value);
            } else {
                user.id = userGlobal.id;
                user.userAddress = msg.sender;
                user.freezeAmount = msg.value;
                user.staticLevel = getLevel(msg.value);
                user.dynamicLevel = getLineLevel(msg.value);
                user.allInvest = msg.value;
                if (!isEmpty(userGlobal.referrer)) {
                    address referrerAddr = getUserAddressByCode(userGlobal.referrer);
                    if (referrerAddr != address(0)) {
                        userRoundMapping[rid][referrerAddr].inviteAmount++;
                    }
                }
            }
            Invest memory invest = Invest(msg.sender, msg.value, investTime, now, 0, flag, !notSuspended(investTime));
            user.invests.push(invest);
            lastInvestTime = investTime;
    
            investCount = investCount.add(1);
            investMoney = investMoney.add(msg.value);
            rInvestCount[rid] = rInvestCount[rid].add(1);
            rInvestMoney[rid] = rInvestMoney[rid].add(msg.value);
            
            if (user.staticLevel >= 3) {
                storeSolitaire(msg.sender);
            }
            investAmountList[rid].push(msg.value);
    
            storeDynamicPreProfits(msg.sender, getDayForProfits(investTime), flag);
    
            sendFeetoAdmin(msg.value);
            trySendTransform(msg.value);
    
            emit LogInvestIn(msg.sender, userGlobal.id, msg.value, now, investTime, userGlobal.inviteCode, userGlobal.referrer, 0);
        }
    
        function reInvestIn() external payable {
            require(gameStart(), "game not start");
            User storage user = userRoundMapping[rid][msg.sender];
            require(user.id > 0, "user haven't invest in round before");
            calStaticProfitInner(msg.sender);
            require(user.freezeAmount == 0, "user have had invest in round");
            require(user.unlockAmount > 0, "user must have unlockAmount");
            require(user.unlockAmount.add(msg.value) <= 20 * ethWei, "can not beyond 20 eth");
            require(user.unlockAmount.add(msg.value) == user.unlockAmount.add(msg.value).div(ethWei).mul(ethWei), "invalid msg value");
    
            bool isEnough;
            uint sendMoney;
            sendMoney = calDynamicProfits(msg.sender);
            if (sendMoney > 0) {
                (isEnough, sendMoney) = isEnoughBalance(sendMoney);
    
                if (sendMoney > 0) {
                    user.dynamicWithdrawn = user.dynamicWithdrawn.add(sendMoney);
                    sendMoneyToUser(msg.sender, sendMoney.mul(90).div(100));
                    sendMoneyToUser(loyal, sendMoney.mul(10).div(100));
                    emit LogWithdrawProfit(msg.sender, user.id, sendMoney, now, 2);
                }
                if (!isEnough) {
                    endRound();
                    return;
                }
            }
    
            uint reInvestAmount = user.unlockAmount.add(msg.value);
    
            uint investTime = now;
            calUserQueueingStatic(msg.sender);
    
            uint leastAmount = reInvestAmount.mul(4).div(100);
            (isEnough, sendMoney) = isEnoughBalance(leastAmount);
            if (!isEnough) {
                if (sendMoney > 0) {
                    sendMoneyToUser(msg.sender, sendMoney);
                }
                endRound();
                return;
            }
    
            user.unlockAmount = 0;
            user.allInvest = user.allInvest.add(reInvestAmount);
            user.freezeAmount = user.freezeAmount.add(reInvestAmount);
            user.staticLevel = getLevel(user.freezeAmount);
            user.dynamicLevel = getLineLevel(user.freezeAmount);
            user.reInvestCount = user.reInvestCount.add(1);
            user.unlockAmountRedeemTime = 0;
    
            uint flag = user.invests[user.invests.length-1].modeFlag;
            Invest memory invest = Invest(msg.sender, reInvestAmount, investTime, now, 0, flag, !notSuspended(investTime));
            user.invests.push(invest);
            if (investTime > lastInvestTime) {
                lastInvestTime = investTime;
            }
    
            investCount = investCount.add(1);
            investMoney = investMoney.add(reInvestAmount);
            rInvestCount[rid] = rInvestCount[rid].add(1);
            rInvestMoney[rid] = rInvestMoney[rid].add(reInvestAmount);
            if (user.staticLevel >= 3) {
                storeSolitaire(msg.sender);
            }
            investAmountList[rid].push(reInvestAmount);
            storeDynamicPreProfits(msg.sender, getDayForProfits(investTime), flag);
    
            sendFeetoAdmin(reInvestAmount);
            trySendTransform(reInvestAmount);
            emit LogInvestIn(msg.sender, user.id, reInvestAmount, now, investTime, userMapping[msg.sender].inviteCode, userMapping[msg.sender].referrer, 1);
        }
    
        function storeSolitaire(address user) private {
            uint len = investAmountList[rid].length;
            if (len != 0) {
                uint tmpTotalInvest;
                for (uint i = 1; i <= 20 && i <= len; i++) {
                    tmpTotalInvest = tmpTotalInvest.add(investAmountList[rid][len-i]);
                }
                uint reward = tmpTotalInvest.mul(1).div(10000).mul(6);
                if (reward > 0) {
                    userRoundMapping[rid][user].solitaire = userRoundMapping[rid][user].solitaire.add(reward);
                }
            }
        }
    
        function withdrawProfit()
        public
        isHuman()
        {
            require(gameStart(), "game not start");
            User storage user = userRoundMapping[rid][msg.sender];
            calStaticProfitInner(msg.sender);
    
            uint sendMoney = user.allStaticAmount;
    
            bool isEnough = false;
            uint resultMoney = 0;
            (isEnough, resultMoney) = isEnoughBalance(sendMoney);
            if (!isEnough) {
                endRound();
            }
    
            if (resultMoney > 0) {
                sendMoneyToUser(msg.sender, resultMoney);
                user.staticWithdrawn = user.staticWithdrawn.add(sendMoney);
                user.allStaticAmount = 0;
                emit LogWithdrawProfit(msg.sender, user.id, resultMoney, now, 1);
            }
        }
    
        function isEnoughBalance(uint sendMoney) private view returns (bool, uint){
            if (sendMoney >= address(this).balance) {
                return (false, address(this).balance);
            } else {
                return (true, sendMoney);
            }
        }
    
        function isEnoughBalanceToRedeem(uint sendMoney, uint reInvestCount, uint hisStaticAmount) private view returns (bool, uint){
            uint deductedStaticAmount = 0;
            if (reInvestCount >= 0 && reInvestCount <= 6) {
                deductedStaticAmount = hisStaticAmount.mul(5).div(10);
                sendMoney = sendMoney.sub(deductedStaticAmount);
            }
            if (reInvestCount > 6 && reInvestCount <= 18) {
                deductedStaticAmount = hisStaticAmount.mul(4).div(10);
                sendMoney = sendMoney.sub(deductedStaticAmount);
            }
            if (reInvestCount > 18 && reInvestCount <= 36) {
                deductedStaticAmount = hisStaticAmount.mul(3).div(10);
                sendMoney = sendMoney.sub(deductedStaticAmount);
            }
            if (reInvestCount >= 37) {
                deductedStaticAmount = hisStaticAmount.mul(1).div(10);
                sendMoney = sendMoney.sub(deductedStaticAmount);
            }
            if (sendMoney >= address(this).balance) {
                return (false, address(this).balance);
            } else {
                return (true, sendMoney);
            }
        }
    
        function sendMoneyToUser(address payable userAddress, uint money) private {
            userAddress.transfer(money);
        }
    
        function calStaticProfitInner(address payable userAddr) private returns (uint){
            User storage user = userRoundMapping[rid][userAddr];
            if (user.id == 0 || user.freezeAmount == 0) {
                return 0;
            }
            uint allStatic = 0;
            uint i = user.invests.length.sub(1);
            Invest storage invest = user.invests[i];
            uint scale;
            if (invest.modeFlag == 0) {
                scale = getScByLevel(user.staticLevel, user.reInvestCount);
            } else if (invest.modeFlag == 1) {
                scale = getSepScByLevel(user.staticLevel);
            }
            uint startDay = invest.investTime.sub(8 hours).div(1 days).mul(1 days);
            if (now.sub(8 hours) < startDay) {
                return 0;
            }
            uint staticGaps = now.sub(8 hours).sub(startDay).div(1 days);
    
            if (staticGaps > 6) {
                staticGaps = 6;
            }
            if (staticGaps > invest.times) {
                if (invest.isSuspendedInvest) {
                    allStatic = staticGaps.sub(invest.times).mul(scale).mul(invest.investAmount).div(10000).mul(2);
                    invest.times = staticGaps;
                } else {
                    allStatic = staticGaps.sub(invest.times).mul(scale).mul(invest.investAmount).div(10000);
                    invest.times = staticGaps;
                }
            }
    
            (uint unlockDay, uint unlockAmountRedeemTime) = getUnLockDay(invest.investTime);
    
            if (unlockDay >= 6 && user.freezeAmount != 0) {
                user.staticFlag = user.staticFlag.add(1);
                user.freezeAmount = user.freezeAmount.sub(invest.investAmount);
                user.unlockAmount = user.unlockAmount.add(invest.investAmount);
                user.unlockAmountRedeemTime = unlockAmountRedeemTime;
                user.staticLevel = getLevel(user.freezeAmount);
    
                if (user.solitaire > 0) {
                    userAddr.transfer(user.solitaire);
                    user.hisSolitaire = user.hisSolitaire.add(user.solitaire);
                    emit LogWithdrawProfit(userAddr, user.id, user.solitaire, now, 3);
                }
                user.solitaire = 0;
            }
    
            allStatic = allStatic.mul(coefficient).div(10);
            user.allStaticAmount = user.allStaticAmount.add(allStatic);
            user.hisStaticAmount = user.hisStaticAmount.add(allStatic);
            return user.allStaticAmount;
        }
    
        function getStaticProfits(address userAddr) public view returns(uint, uint, uint) {
            User memory user = userRoundMapping[rid][userAddr];
            if (user.id == 0 || user.invests.length == 0) {
                return (0, 0, 0);
            }
            if (user.freezeAmount == 0) {
                return (0, user.hisStaticAmount, user.staticWithdrawn);
            }
            uint allStatic = 0;
            uint i = user.invests.length.sub(1);
            Invest memory invest = user.invests[i];
            uint scale;
            if (invest.modeFlag == 0) {
                scale = getScByLevel(user.staticLevel, user.reInvestCount);
            } else if (invest.modeFlag == 1) {
                scale = getSepScByLevel(user.staticLevel);
            }
            uint startDay = invest.investTime.sub(8 hours).div(1 days).mul(1 days);
            if (now.sub(8 hours) < startDay) {
                return (0, user.hisStaticAmount, user.staticWithdrawn);
            }
            uint staticGaps = now.sub(8 hours).sub(startDay).div(1 days);
    
            if (staticGaps > 6) {
                staticGaps = 6;
            }
            if (staticGaps > invest.times) {
                if (invest.isSuspendedInvest) {
                    allStatic = staticGaps.sub(invest.times).mul(scale).mul(invest.investAmount).div(10000).mul(2);
                } else {
                    allStatic = staticGaps.sub(invest.times).mul(scale).mul(invest.investAmount).div(10000);
                }
            }
    
            allStatic = allStatic.mul(coefficient).div(10);
            return (
                user.allStaticAmount.add(allStatic),
                user.hisStaticAmount.add(allStatic),
                user.staticWithdrawn
            );
        }
    
        function storeDynamicPreProfits(address userAddr, uint investDay, uint modeFlag) private {
            uint freezeAmount = userRoundMapping[rid][userAddr].freezeAmount;
            if (freezeAmount >= 1 * ethWei) {
                uint scale;
                if (modeFlag == 0) {
                    scale = getScByLevel(userRoundMapping[rid][userAddr].staticLevel, userRoundMapping[rid][userAddr].reInvestCount);
                } else if (modeFlag == 1) {
                    scale = getSepScByLevel(userRoundMapping[rid][userAddr].staticLevel);
                }
                uint staticMoney = freezeAmount.mul(scale).div(10000);
                updateReferrerPreProfits(userMapping[userAddr].referrer, investDay, staticMoney);
            }
        }
    
        function updateReferrerPreProfits(string memory referrer, uint day, uint staticMoney) private {
            string memory tmpReferrer = referrer;
    
            for (uint i = 1; i <= 20; i++) {
                if (isEmpty(tmpReferrer)) {
                    break;
                }
                uint floorIndex = getFloorIndex(i);
                address tmpUserAddr = addressMapping[tmpReferrer];
                if (tmpUserAddr == address(0)) {
                    break;
                }
    
                for (uint j = 0; j < 6; j++) {
                    uint dayIndex = day.add(j);
                    uint currentMoney = userRoundMapping[rid][tmpUserAddr].dynamicProfits[floorIndex][dayIndex];
                    userRoundMapping[rid][tmpUserAddr].dynamicProfits[floorIndex][dayIndex] = currentMoney.add(staticMoney);
                }
                tmpReferrer = userMapping[tmpUserAddr].referrer;
            }
        }
    
        function calDynamicProfits(address user) public view returns (uint) {
            uint len = userRoundMapping[rid][user].invests.length;
            if (len == 0) {
                return 0;
            }
            uint userInvestDay = getDayForProfits(userRoundMapping[rid][user].invests[len - 1].investTime);
            uint totalProfits;
    
            uint floor;
            uint dynamicLevel = userRoundMapping[rid][user].dynamicLevel;
            floor = getDynamicFloor(dynamicLevel);
            if (floor > 20) {
                floor = 20;
            }
            uint floorCap = getFloorIndex(floor);
            uint fireSc = getFireScByLevel(dynamicLevel);
    
            for (uint i = 1; i <= floorCap; i++) {
                uint recommendSc = getRecommendScaleByLevelAndTim(dynamicLevel, i);
                for (uint j = 0; j < 6; j++) {
                    uint day = userInvestDay.add(j);
                    uint staticProfits = userRoundMapping[rid][user].dynamicProfits[i][day];
    
                    if (recommendSc != 0) {
                        uint tmpDynamicAmount = staticProfits.mul(fireSc).mul(recommendSc);
                        totalProfits = tmpDynamicAmount.div(10).div(100).add(totalProfits);
                    }
                }
            }
    
            return totalProfits;
        }
    
        function registerUserInfo(address user, string calldata inviteCode, string calldata referrer) external onlyOwner {
            registerUser(user, inviteCode, referrer);
        }
    
        function calUserQueueingStatic(address userAddress) private returns(uint) {
            User storage calUser = userRoundMapping[rid][userAddress];
    
            uint investLength = calUser.invests.length;
            if (investLength == 0) {
                return 0;
            }
    
            Invest memory invest = calUser.invests[investLength - 1];
            if (invest.investTime <= invest.realityInvestTime) {
                return 0;
            }
    
            uint staticGaps = getQueueingStaticGaps(invest.investTime, invest.realityInvestTime);
            if (staticGaps <= 0) {
                return 0;
            }
            uint staticAmount = invest.investAmount.mul(staticGaps).mul(5).div(10000);
            calUser.hisStaticAmount = calUser.hisStaticAmount.add(staticAmount);
            calUser.allStaticAmount = calUser.allStaticAmount.add(staticAmount);
            return staticAmount;
        }
    
        function getQueueingStaticGaps(uint investTime, uint realityInvestTime) private pure returns (uint){
            if(investTime <= realityInvestTime){
                return 0;
            }
            uint startDay = realityInvestTime.sub(8 hours).div(1 days).mul(1 days);
            uint staticGaps = investTime.sub(8 hours).sub(startDay).div(1 days);
            return staticGaps;
        }
    
        function redeem()
        public
        isHuman()
        isSuspended()
        {
            require(gameStart(), "game not start");
            User storage user = userRoundMapping[rid][msg.sender];
            require(user.id > 0, "user not exist");
            withdrawProfit();
            require(now >= user.unlockAmountRedeemTime, "redeem time non-arrival");
    
            uint sendMoney = user.unlockAmount;
            require(sendMoney != 0, "you don't have unlock money");
            uint reInvestCount = user.reInvestCount;
            uint hisStaticAmount = user.hisStaticAmount;
    
            bool isEnough = false;
            uint resultMoney = 0;
    
            uint index = user.invests.length - 1;
            if (user.invests[index].modeFlag == 0) {
                (isEnough, resultMoney) = isEnoughBalanceToRedeem(sendMoney, reInvestCount, hisStaticAmount);
            } else if (user.invests[index].modeFlag == 1) {
                require(reInvestCount == 4 || (reInvestCount>4 && (reInvestCount-4)%5 == 0), "reInvest time not enough");
                (isEnough, resultMoney) = isEnoughBalance(sendMoney);
            } else {
                revert("invalid flag");
            }
    
            if (!isEnough) {
                endRound();
            }
            if (resultMoney > 0) {
                sendMoneyToUser(msg.sender, resultMoney);
                user.unlockAmount = 0;
                user.staticLevel = getLevel(user.freezeAmount);
                user.dynamicLevel = 0;
                user.reInvestCount = 0;
                user.hisStaticAmount = 0;
                emit LogRedeem(msg.sender, user.id, resultMoney, now);
            }
        }
    
        function getUnLockDay(uint investTime) public view returns (uint unlockDay, uint unlockAmountRedeemTime){
            uint gameStartTime = startTime;
            if (gameStartTime <= 0 || investTime > now || investTime < gameStartTime) {
                return (0, 0);
            }
            unlockDay = now.sub(investTime).div(1 days);
            unlockAmountRedeemTime = 0;
            if (unlockDay < 6) {
                return (unlockDay, unlockAmountRedeemTime);
            }
            unlockAmountRedeemTime = investTime.add(uint(6).mul(1 days));
    
            uint stopTime = suspendedTime;
            if (stopTime == 0) {
                return (unlockDay, unlockAmountRedeemTime);
            }
    
            uint stopDays = suspendedDays;
            uint stopEndTime = stopTime.add(stopDays.mul(1 days));
            if (investTime < stopTime){
                if(unlockAmountRedeemTime >= stopEndTime){
                    unlockAmountRedeemTime = unlockAmountRedeemTime.add(stopDays.mul(1 days));
                }else if(unlockAmountRedeemTime < stopEndTime && unlockAmountRedeemTime > stopTime){
                    unlockAmountRedeemTime = stopEndTime.add(unlockAmountRedeemTime.sub(stopTime));
                }
            }
            if (investTime >= stopTime && investTime < stopEndTime){
                if(unlockAmountRedeemTime >= stopEndTime){
                    unlockAmountRedeemTime = unlockAmountRedeemTime.add(stopEndTime.sub(investTime));
                }else if(unlockAmountRedeemTime < stopEndTime && unlockAmountRedeemTime > stopTime){
                    unlockAmountRedeemTime = stopEndTime.add(uint(6).mul(1 days));
                }
            }
            return (unlockDay, unlockAmountRedeemTime);
        }
    
        function endRound() private {
            rid++;
            startTime = now.add(period).div(1 days).mul(1 days);
            coefficient = 10;
        }
    
        function isUsed(string memory code) public view returns (bool) {
            address user = getUserAddressByCode(code);
            return uint(user) != 0;
        }
    
        function getUserAddressByCode(string memory code) public view returns (address) {
            return addressMapping[code];
        }
    
        function sendFeetoAdmin(uint amount) private {
            devAddr.transfer(amount.div(25));
        }
    
        function trySendTransform(uint amount) private {
            if (transformAmount[rid] > 500 * ethWei) {
                return;
            }
            uint sendAmount = amount.div(100);
            transformAmount[rid] = transformAmount[rid].add(sendAmount);
            other.transfer(sendAmount);
        }
    
        function getGameInfo() public isHuman() view returns (uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint) {
            uint dayInvest;
            uint dayLimit;
            dayInvest = everyDayInvestMapping[getCurrentInvestDay(now)];
            dayLimit = getCurrentInvestLimit(now);
            return (
            rid,
            uid,
            startTime,
            investCount,
            investMoney,
            rInvestCount[rid],
            rInvestMoney[rid],
            coefficient,
            dayInvest,
            dayLimit,
            now,
            lastInvestTime
            );
        }
    
        function getUserInfo(address user, uint roundId) public isHuman() view returns (
            uint[19] memory ct, string memory inviteCode, string memory referrer
        ) {
    
            if (roundId == 0) {
                roundId = rid;
            }
    
            User memory userInfo = userRoundMapping[roundId][user];
    
            ct[0] = userInfo.id;
            ct[1] = userInfo.staticLevel;
            ct[2] = userInfo.dynamicLevel;
            ct[3] = userInfo.allInvest;
            Invest memory invest;
            if (userInfo.invests.length == 0) {
                ct[4] = 0;
            } else {
                invest = userInfo.invests[userInfo.invests.length-1];
                if (invest.modeFlag == 0) {
                    ct[4] = getScByLevel(userInfo.staticLevel, userInfo.reInvestCount);
                } else if(invest.modeFlag == 1) {
                    ct[4] = getSepScByLevel(userInfo.staticLevel);
                } else {
                    ct[4] = 0;
                }
            }
            ct[5] = userInfo.inviteAmount;
            ct[6] = userInfo.freezeAmount;
            ct[7] = userInfo.staticWithdrawn.add(userInfo.dynamicWithdrawn);
            ct[8] = userInfo.staticWithdrawn;
            ct[9] = userInfo.dynamicWithdrawn;
            uint canWithdrawn;
            uint hisWithdrawn;
            uint staticWithdrawn;
            (canWithdrawn, hisWithdrawn, staticWithdrawn) = getStaticProfits(user);
            ct[10] = canWithdrawn;
            ct[11] = calDynamicProfits(user);
            uint lockDay;
            uint redeemTime;
            (lockDay, redeemTime) = getUnLockDay(invest.investTime);
            ct[12] = lockDay;
            ct[13] = redeemTime;
            ct[14] = userInfo.reInvestCount;
            ct[15] = invest.modeFlag;
            ct[16] = userInfo.unlockAmount;
            ct[17] = invest.investTime;
            ct[18] = userInfo.hisSolitaire;
    
            inviteCode = userMapping[user].inviteCode;
            referrer = userMapping[user].referrer;
            return (
            ct,
            inviteCode,
            referrer
            );
        }
    
        function getInvestTime(uint amount) public view returns (uint){
            uint lastTime = lastInvestTime;
    
            uint investTime = 0;
    
            if (isLessThanLimit(amount, now)) {
                if (now < lastTime) {
                    investTime = lastTime.add(1 seconds);
                } else {
                    investTime = now;
                }
            } else {
                investTime = lastTime.add(1 seconds);
                if (!isLessThanLimit(amount, investTime)) {
                    investTime = getCurrentInvestDay(lastTime).mul(1 days).add(baseTime);
                }
            }
            return investTime;
        }
    
    
        function getDayForProfits(uint investTime) private pure returns (uint) {
            return investTime.sub(8 hours).div(1 days);
        }
    
        function getCurrentInvestLimit(uint investTime) public view returns (uint){
            uint currentDays = getCurrentInvestDay(investTime).sub(1);
            uint currentRound = currentDays.div(6).add(1);
            uint x = 3 ** (currentRound.sub(1));
            uint y = 2 ** (currentRound.sub(1));
            return baseLimit.mul(x).div(y);
        }
    
        function getCurrentInvestDay(uint investTime) public view returns (uint){
            uint gameStartTime = baseTime;
            if (gameStartTime == 0 || investTime < gameStartTime) {
                return 0;
            }
            uint currentInvestDay = investTime.sub(gameStartTime).div(1 days).add(1);
            return currentInvestDay;
        }
        function isLessThanLimit(uint amount, uint investTime) public view returns (bool){
            return getCurrentInvestLimit(investTime) >= amount.add(everyDayInvestMapping[getCurrentInvestDay(investTime)]);
        }
        function notSuspended() public view returns (bool) {
            uint sTime = suspendedTime;
            uint sDays = suspendedDays;
            return sTime == 0 || now < sTime || now >= sDays.mul(1 days).add(sTime);
        }
    
        function notSuspended(uint investTime) public view returns (bool) {
            uint sTime = suspendedTime;
            uint sDays = suspendedDays;
            return sTime == 0 || investTime < sTime || investTime >= sDays.mul(1 days).add(sTime);
        }
    
        function suspended(uint stopTime, uint stopDays) external onlyWhitelistAdmin {
            require(gameStart(), "game not start");
            require(stopTime > now, "stopTime shoule gt now");
            require(stopTime > lastInvestTime, "stopTime shoule gt lastInvestTime");
            suspendedTime = stopTime;
            suspendedDays = stopDays;
        }
    
        function getUserById(uint id) public view returns (address){
            return indexMapping[id];
        }
    
        function getAvailableReInvestInAmount(address userAddr) public view returns (uint){
            User memory user = userRoundMapping[rid][userAddr];
            if(user.freezeAmount == 0){
                return user.unlockAmount;
            }else{
                Invest memory invest = user.invests[user.invests.length - 1];
                (uint unlockDay, uint unlockAmountRedeemTime) = getUnLockDay(invest.investTime);
                if(unlockDay >= 6){
                    return invest.investAmount;
                }
            }
            return 0;
        }
    
        function getAvailableRedeemAmount(address userAddr) public view returns (uint){
            User memory user = userRoundMapping[rid][userAddr];
            if (now < user.unlockAmountRedeemTime) {
                return 0;
            }
            uint allUnlock = user.unlockAmount;
            if (user.freezeAmount > 0) {
                Invest memory invest = user.invests[user.invests.length - 1];
                (uint unlockDay, uint unlockAmountRedeemTime) = getUnLockDay(invest.investTime);
                if (unlockDay >= 6 && now >= unlockAmountRedeemTime) {
                    allUnlock = invest.investAmount;
                }
                if(invest.modeFlag == 1){
                    if(user.reInvestCount < 4 || (user.reInvestCount - 4)%5 != 0){
                        allUnlock = 0;
                    }
                }
            }
            return allUnlock;
        }
    
        function registerUser(address user, string memory inviteCode, string memory referrer) private {
            UserGlobal storage userGlobal = userMapping[user];
            if (userGlobal.id != 0) {
                userGlobal.userAddress = user;
                userGlobal.inviteCode = inviteCode;
                userGlobal.referrer = referrer;
                
                addressMapping[inviteCode] = user;
                indexMapping[uid] = user;
            } else {
                uid++;
                userGlobal.id = uid;
                userGlobal.userAddress = user;
                userGlobal.inviteCode = inviteCode;
                userGlobal.referrer = referrer;
                
                addressMapping[inviteCode] = user;
                indexMapping[uid] = user;
            }
            
        }
    }
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that revert on error
     */
    library SafeMath {
    
        /**
        * @dev Multiplies two numbers, reverts on 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-solidity/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b, "mul overflow");
    
            return c;
        }
    
        /**
        * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
        */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "div zero");
            // Solidity only automatically asserts when dividing by 0
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
            return c;
        }
    
        /**
        * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
        */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a, "lower sub bigger");
            uint256 c = a - b;
    
            return c;
        }
    
        /**
        * @dev Adds two numbers, reverts on overflow.
        */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "overflow");
    
            return c;
        }
    
        /**
        * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
        * reverts when dividing by zero.
        */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b != 0, "mod zero");
            return a % b;
        }
    }