false
false
0

Contract Address Details

0xA61C77758257D7374822836B62DeFf42ac1eA68C

Contract Name
Staking
Creator
0xc7d98cā€“7f3521 at 0x4cc521ā€“4d5877
Balance
0 CLO
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
16286041
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
Staking




Optimization enabled
true
Compiler version
v0.8.6+commit.11564f7e




Optimization runs
200
EVM Version
default




Verified at
2024-09-26T15:34:35.664979Z

Contract source code

// File: contracts/EnumerableSet.sol





pragma solidity ^0.8.0;



/**

 * @dev Library for managing

 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive

 * types.

 *

 * Sets have the following properties:

 *

 * - Elements are added, removed, and checked for existence in constant time

 * (O(1)).

 * - Elements are enumerated in O(n). No guarantees are made on the ordering.

 *

 * ```

 * contract Example {

 *     // Add the library methods

 *     using EnumerableSet for EnumerableSet.AddressSet;

 *

 *     // Declare a set state variable

 *     EnumerableSet.AddressSet private mySet;

 * }

 * ```

 *

 * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`

 * (`UintSet`) are supported.

 */

library EnumerableSet {



    struct AddressSet {

        // Storage of set values

        address[] _values;



        // Position of the value in the `values` array, plus 1 because index 0

        // means a value is not in the set.

        mapping (address => uint256) _indexes;

    }



    /**

     * @dev Add a value to a set. O(1).

     *

     * Returns true if the value was added to the set, that is if it was not

     * already present.

     */

    function add(AddressSet storage set, address value) internal returns (bool) {

        if (!contains(set, value)) {

            set._values.push(value);

            // The value is stored at length-1, but we add 1 to all indexes

            // and use 0 as a sentinel value

            set._indexes[value] = set._values.length;

            return true;

        } else {

            return false;

        }

    }



    /**

     * @dev Removes a value from a set. O(1).

     *

     * Returns true if the value was removed from the set, that is if it was

     * present.

     */

    function remove(AddressSet storage set, address value) internal returns (bool) {

        // We read and store the value's index to prevent multiple reads from the same storage slot

        uint256 valueIndex = set._indexes[value];



        if (valueIndex != 0) { // Equivalent to contains(set, value)

            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in

            // the array, and then remove the last element (sometimes called as 'swap and pop').

            // This modifies the order of the array, as noted in {at}.



            uint256 toDeleteIndex = valueIndex - 1;

            uint256 lastIndex = set._values.length - 1;



            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs

            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.



            address lastvalue = set._values[lastIndex];



            // Move the last value to the index where the value to delete is

            set._values[toDeleteIndex] = lastvalue;

            // Update the index for the moved value

            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based



            // Delete the slot where the moved value was stored

            set._values.pop();



            // Delete the index for the deleted slot

            delete set._indexes[value];



            return true;

        } else {

            return false;

        }

    }



    /**

     * @dev Returns true if the value is in the set. O(1).

     */

    function contains(AddressSet storage set, address value) internal view returns (bool) {

        return set._indexes[value] != 0;

    }



    /**

     * @dev Returns the number of values on the set. O(1).

     */

    function length(AddressSet storage set) internal view returns (uint256) {

        return set._values.length;

    }



   /**

    * @dev Returns the value stored at position `index` in the set. O(1).

    *

    * Note that there are no guarantees on the ordering of values inside the

    * array, and it may change when more values are added or removed.

    *

    * Requirements:

    *

    * - `index` must be strictly less than {length}.

    */

    function at(AddressSet storage set, uint256 index) internal view returns (address) {

        require(set._values.length > index, "EnumerableSet: index out of bounds");

        return set._values[index];

    }

}


// File: contracts/StakingBIC.sol


pragma solidity ^0.8.0;


interface IBEP20 {
    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function mint(address user, uint256 amount) external;
    function balanceOf(address account) external view returns (uint256);
}
abstract contract Ownable {
    address internal _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
/*  we use proxy, so owner will be set in initialize() function
    constructor () {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }
*/
    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == msg.sender, "Ownable: 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 virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

contract Staking is Ownable {
    using EnumerableSet for EnumerableSet.AddressSet;

    address public token;   // BIC token
    EnumerableSet.AddressSet usersList; // list of users with stake

    struct User {
        uint256 amount; // amount of deposit
        uint256 weightedTokens; // the user's weighted tokens balance. (weighted tokens = token amount * staked days)
        uint256 previousWeightedTokens;  // the user's weighted tokens balance for previous month.
        uint256 reward; // amount of reward that user may withdraw before ends of staking.
        uint64 unlockTime;   // when can be unstake
        uint64 updated;    // day when updated
        uint64 multiplier; // multiplier (with 2 decimals) applied to deposit ([0.25 .. 1])
        uint8 status;   // 0 - active, 1 - ended (weightedTokens > 0), 2 - ended (previousWeightedTokens > 0), 3 - ended, but not withdrawn
    }

    mapping(address => User[]) public users;  // users deposits array
    mapping(uint256 => uint256) public multiplier;      // staking days => multiplier (with 2 decimals)

    uint256 public withdrawWindow; // user may withdraw during this time when previous lock period ends.
    uint256 public prolongate;    // if user did not withdraw, money locked on next 30 days.
    uint256 public minimumAmount;   // minimum amount for staking

    // for TEST
    /*
    uint256 public withdrawWindow = 1 days; // user may withdraw during this time when previous lock period ends.
    uint256 public prolongate = 2 days;    // if user user did not withdraw, money locked on next 30 days.
    uint256 public minimumAmount = 1;   // minimum amount for staking
    */
    
    uint256 public totalStaked;
    uint256 public totalStakedWeight;
    uint256 public previousTotalStakedWeight;   // the total staked weight for previous month.
    int256  public amountPerWeightedToken;      // amount (with 18 decimals) of profit/loss per weighted token for previous month.
    int256  public monthResultAmount;  // result for previous month
    uint256 public processingStep; // 0 - processing complete, 1 - recalculate Weight, 2 - split result
    uint256 public processedDay;    // last day that was processed
    uint256 public precessedMonth;  // month when profit/loss was added. Admin can add profit/loss only once per month.
    uint256 public nextProcessUser;

    mapping (address => bool) public admins;
    event SetAdmin(address admin, bool isActive);
    event Stake(address user, uint256 amount, uint256 period, uint256 multiplier);
    event Unstake(address user, uint256 amount);
    event WithdrawUSD(address user, uint256 amount);
    event Processed(uint256 number, uint256 total, uint256 step);
    event RemoveUser(address user);
    event RemoveDeposit(address user, uint256 depositId);
    event MonthResult(int256 amount);
    event SplitResult(address user, uint256 depositId, int256 amount);
    event MonthBalances(uint256 totalStaked, uint256 totalBalance);

    // run only once from proxy
    function initialize(address newOwner, address _token) external {
        require(newOwner != address(0) && _owner == address(0)); // run only once
        _owner = newOwner;
        token = _token;
        emit OwnershipTransferred(address(0), msg.sender);
        withdrawWindow = 5 days; // user may withdraw during this time when previous lock period ends.
        prolongate = 30 days;    // if user did not withdraw, money locked on next 30 days.
        minimumAmount = 200 ether;   // minimum amount for staking
        multiplier[90] = 25;    // staking for 90 days = multiplier 0.25 
        multiplier[180] = 50;    // staking for 180 days = multiplier 0.5 
        multiplier[270] = 75;    // staking for 270 days = multiplier 0.75 
        multiplier[360] = 100;    // staking for 180 days = multiplier 1 
    }
    /**
    * @dev Throws if called by any account other than the admin.
    */
    modifier onlyAdmin() {
        require(admins[msg.sender] || owner() == msg.sender, "Caller is not admin");
        _;
    }

    function setAdmin(address admin, bool isActive) external onlyOwner {
        admins[admin] = isActive;
        emit SetAdmin(admin, isActive);
    }

    // set multiplier (with 2 decimals)
    function setMultiplier(uint256 _days, uint256 _multiplier) external onlyOwner {
        require(_multiplier <= 100, "Wrong multiplier");
        multiplier[_days] = _multiplier;
    }

    // set minimumAmount
    function setMinimumAmount(uint256 _minAmount) external onlyOwner {
        minimumAmount = _minAmount;
    }

    // set withdrawWindow in seconds
    function setWithdrawWindow(uint256 time) external onlyOwner {
        withdrawWindow = time;
    }
    // set prolongate in seconds
    function setProlongate(uint256 time) external onlyOwner {
        prolongate = time;
    }

    // get total number of users
    function getUsersNumber() external view returns(uint256) {
        return usersList.length();
    }

    // get total number of deposits of user
    function getUsersDepositsNumber(address user) external view returns(uint256) {
        return users[user].length;
    }

    // get deposits of user
    function getUsersDeposits(address user) external view returns(User[] memory) {
        return users[user];
    }

    // get deposits of user
    function getUsersDepositsByNumber(uint256 userNumber) external view returns(address, User[] memory) {
        address user = usersList.at(userNumber);
        return (user, users[user]);
    }

    function getTotalBalance() public view returns(uint256 balance) {
        uint256 lastUser = usersList.length();
        for(uint256 precessedUsers = 0; precessedUsers < lastUser; precessedUsers++) {
            address user = usersList.at(precessedUsers);
            uint256 deposits = users[user].length;            
            for (uint256 i = 0; i < deposits; i++) {
                balance += users[user][i].amount;
            }        
        }
    }

    // Step 0. Call this function 1st day of each month
    // Enter result for previous month
    function monthResult(int256 amount) external onlyOwner {
        require(processingStep == 0, "Previous result was not processed");
        _updateWeight();
        (,uint256 month,) = timestampToDate(block.timestamp);
        //month = block.timestamp / 1 days;  // for TEST
        require(precessedMonth != month, "Result may be added only once per month");
        precessedMonth = month;
        monthResultAmount = amount;
        require (int256(totalStaked) + amount >= 0, "totalStaked less then amount"); 
        if (amount > 0) {
            _safeTransferFrom(token, msg.sender, address(this), uint256(amount));
        }
        processingStep = 1;
        nextProcessUser = 0;
        emit MonthResult(amount);
    }

    // recalculate users weights and total weights to exclude expired staking.
    // Step 1. Call this function 1st day of each month
    // numberUsers - is number of users that will processed at one transaction. Repeat call this function until emit event with number == total.
    function recalculateWeight(uint256 numberUsers) external onlyAdmin {
        require(processingStep == 1, "Wrong step");
        uint256 currentDay = block.timestamp / 1 days;  // number of days that passed
        //uint256 currentDay = block.timestamp / 1 hours;  // for TEST
        uint256 precessedUsers = nextProcessUser;
        uint256 lastUser = usersList.length();
        while (precessedUsers < lastUser) {
            address user = usersList.at(precessedUsers);
            uint256 deposits = users[user].length;
            if (numberUsers >= deposits) {
                numberUsers -= deposits;
            } else {
                nextProcessUser = precessedUsers;
                emit Processed(precessedUsers, lastUser, processingStep);
                return;
            }
            for (uint256 i = 0; i < deposits; i++) {
                _updateUser(currentDay, users[user][i]);
            }        
            precessedUsers++;
        }
        processingStep = 2;
        nextProcessUser = 0;
        emit Processed(precessedUsers, lastUser, 1);

    }
    // call this function 1st day of each month to split month's result among users.
    // numberUsers - is number of users that will processed at one transaction. Repeat call this function until emit event with number == total.
    function splitResult(uint256 numberUsers) external onlyAdmin {
        require(processingStep == 2, "Wrong step");
        int256 _monthResult = monthResultAmount;
        uint256 lastUser = usersList.length();
        if (_monthResult == 0) {    // exit if no amount to split
            processingStep = 0;
            emit Processed(lastUser, lastUser, 2);
            return;
        }
        int256 _previousTotalStakedWeight = int256(previousTotalStakedWeight);
        int256 split;
        uint256 precessedUsers = nextProcessUser;
        while (precessedUsers < lastUser) {
            address user = usersList.at(precessedUsers);
            uint256 deposits = users[user].length;
            if (numberUsers >= deposits) {
                numberUsers -= deposits;
            } else {
                nextProcessUser = precessedUsers;
                totalStaked = uint256(int256(totalStaked) + split); // add to totalStaked split amount
                emit Processed(precessedUsers, lastUser, processingStep);
                return;
            }
            for (uint256 i = 0; i < deposits; i++) {
                int256 wight = int256(users[user][i].previousWeightedTokens);
                if (wight != 0) {
                    int256 stakedAmount = int256(users[user][i].amount);
                    int256 reward = _monthResult * wight * int64(users[user][i].multiplier) / (_previousTotalStakedWeight * 100);
                    split += reward;
                    if (reward > 0) users[user][i].reward += uint256(reward);
                    stakedAmount = stakedAmount + reward;
                    if (stakedAmount < 0) {
                        split = split - stakedAmount;  // unsplit negative values
                        stakedAmount = 0;
                    }
                    users[user][i].amount = uint256(stakedAmount);
                    users[user][i].previousWeightedTokens = 0;
                    emit SplitResult(user, i, reward);                    
                } 
            }        
            precessedUsers++;
        }
        totalStaked = uint256(int256(totalStaked) + split); // add to totalStaked split amount
        processingStep = 0;
        nextProcessUser = 0;
        uint256 bal = IBEP20(token).balanceOf(address(this));
        uint256 totalBalance = getTotalBalance();
        emit MonthBalances(totalStaked, totalBalance);
        if (totalBalance >= totalStaked && bal > totalBalance) // send rest of money to owner
            _safeTransfer(token, owner(), bal - totalBalance);
        emit Processed(precessedUsers, lastUser, 2);
    }

    // allow owner to unstake token's on user behalf without waiting unlock time.
    // user's deposit will be deleted.
    function unstakeAdmin(address user, uint256 depositId, bool withdrawUSD) external onlyOwner {
        uint256 currentDay = _updateWeight();  // number of days that passed
        User storage u = users[user][depositId];
        _updateUser(currentDay, u);
        uint256 totalAmount = u.amount;

        if (totalAmount != 0) {
            if (withdrawUSD) {
                _safeTransfer(token, owner(), totalAmount);
                emit WithdrawUSD(user, totalAmount);
            }
            else {
                _safeTransfer(token, user, totalAmount);
                emit Unstake(user, totalAmount);
            }
        }
        if (u.status == 0) totalStaked -= totalAmount;
        totalStakedWeight -= u.weightedTokens;
        previousTotalStakedWeight -= u.previousWeightedTokens;

        // delete deposit records
        uint256 lastDepositId = users[user].length - 1;
        if (lastDepositId == 0) {
            users[user].pop();  // delete last deposit
            usersList.remove(user); // remove user from list
            emit RemoveUser(user);
        } else {
            if (depositId != lastDepositId) users[user][depositId] = users[user][lastDepositId];
            users[user].pop();
            emit RemoveDeposit(user, depositId);
        }
    }

    // User stake amount of tokens. 
    // User can't withdraw tokens/USD at least 90 days after first deposit or 30 days after further deposits
    function stake(uint256 amount, uint256 period) external {
        require(amount >= minimumAmount, "Staked amount is less then minimum");
        uint256 mp = multiplier[period];
        require(mp != 0, "Wrong period");
        _safeTransferFrom(token, msg.sender, address(this), amount);
        _stake(msg.sender, amount, period, mp);
    }

    // Owner stake amount of tokens on user behalf. 
    // user - address of user's wallet
    // amount - amount of token to transfer on stake
    // period - number of days when tokens will be locked on stake
    // multiplier_ - multiplier (with 2 decimals) applied to deposit ([0.25 .. 1]). I.e. 25 means 0.25
    function stakeBehalf(address user, uint256 amount, uint256 period, uint256 multiplier_) external onlyOwner {
        require(multiplier_ <= 100, "Wrong multiplier");
        _safeTransferFrom(token, msg.sender, address(this), amount);
        _stake(user, amount, period, multiplier_);
    }

    // Withdraw rewards during allowed window.
    function withdrawRewards(uint256 depositId, uint256 amount, bool withdrawUSD) external {
        User storage u = users[msg.sender][depositId];
        uint256 unlockTime = u.unlockTime;
        if (block.timestamp < unlockTime) {
            uint256 _days = unlockTime - block.timestamp;
            _days = _days % prolongate; 
            require(_days > prolongate - withdrawWindow || _days == 0, "Out of withdraw window");
            uint256 reward = u.reward;
            require(amount <= reward && amount <= u.amount, "Not enough rewards");
            uint256 currentDay = _updateWeight();  // number of days that passed
            _updateUser(currentDay, u);
            u.reward = reward - amount;
            u.amount -= amount;
            if (u.status == 0) totalStaked -= amount;
            if (withdrawUSD) {
                _safeTransfer(token, owner(), amount);
                emit WithdrawUSD(msg.sender, amount);
            }
            else {
                _safeTransfer(token, msg.sender, amount);
                emit Unstake(msg.sender, amount);
            }            
        } else {
            _unstake(msg.sender, depositId, withdrawUSD);
        }
    }

    // unstake tokens to user wallet or withdraw USD (tokens will be send to owner())
    // User can't unstake/withdraw at least 90 days after first deposit or 30 days after further deposits
    function unstake(uint256 depositId, bool withdrawUSD) external {
        require(block.timestamp >= users[msg.sender][depositId].unlockTime, "Account locked");
        _unstake(msg.sender, depositId, withdrawUSD);
    }

    // Stake amount for selected period (in days)
    function _stake(address user, uint256 amount, uint256 period, uint256 multiplier_) internal {
        _updateWeight();
        require(users[user].length < 10, "Allowed 10 deposits per wallet");
        totalStaked += amount;
        usersList.add(user);
        users[user].push(
            User({
                amount: amount, 
                weightedTokens: 0,
                previousWeightedTokens: 0,
                reward: 0,
                unlockTime: uint64(block.timestamp + (period * 1 days)),
                updated: uint64(block.timestamp / 1 days),
                //unlockTime: uint64(block.timestamp + (period * 1 hours)), // for TEST
                //updated: uint64(block.timestamp / 1 hours), // for TEST
                multiplier: uint64(multiplier_),
                status: 0
            })
        );
        emit Stake(user, amount, period, multiplier_);
    }

    function _unstake(address user, uint256 depositId, bool withdrawUSD) internal {
        uint256 currentDay = _updateWeight();  // number of days that passed
        User storage u = users[user][depositId];
        _updateUser(currentDay, u);
        uint256 totalAmount = u.amount;
        u.amount = 0;
        u.reward = 0;
        if (totalAmount != 0) {
            if (withdrawUSD) {
                _safeTransfer(token, owner(), totalAmount);
                emit WithdrawUSD(user, totalAmount);
            }
            else {
                _safeTransfer(token, user, totalAmount);
                emit Unstake(user, totalAmount);
            }
        }

        if(u.previousWeightedTokens == 0 && u.weightedTokens == 0) {
            // delete deposit records
            uint256 lastDepositId = users[user].length - 1;
            if (lastDepositId == 0) {
                users[user].pop();  // delete last deposit
                usersList.remove(user); // remove user from list
                emit RemoveUser(user);
            } else {
                if (depositId != lastDepositId) users[user][depositId] = users[user][lastDepositId];
                users[user].pop();
                emit RemoveDeposit(user, depositId);
            }
        }
    }

    function _updateUser(uint256 currentDay, User storage u) internal {
        uint8 status = u.status;   // 0 - active, 1 - ended (weightedTokens > 0), 2 - ended (previousWeightedTokens > 0), 3 - ended, but not withdrawn
        if (status == 0) {
            uint256 amount = u.amount;
            uint256 weightedTokens = u.weightedTokens;
            uint256 unlockDay = u.unlockTime / 1 days;
            if (unlockDay <= currentDay) {
                // Stacking ends. 
                (uint256 previousStakedWeight, uint256 stakedWeight) = 
                    _calculateWeight(unlockDay, u.updated, amount, weightedTokens);
                u.updated = uint64(currentDay);
                u.weightedTokens = stakedWeight;
                if (previousStakedWeight != 0)
                    u.previousWeightedTokens = previousStakedWeight;
                if(stakedWeight != 0) {
                    u.status = 1;       // no active staked amount
                } else if (previousStakedWeight != 0) {
                    u.status = 2;
                } else {
                    u.status = 3;
                }
                totalStaked -= amount;
                // Fix previous weight.
                (previousStakedWeight, stakedWeight) = 
                    _calculateWeight(currentDay, unlockDay, amount, u.weightedTokens);
                totalStakedWeight -= stakedWeight;
                if (previousStakedWeight != 0)
                    previousTotalStakedWeight -= previousStakedWeight;
            } else {
                (uint256 previousStakedWeight, uint256 stakedWeight) = 
                    _calculateWeight(currentDay, u.updated, amount, weightedTokens);
                u.updated = uint64(currentDay);
                u.weightedTokens = stakedWeight;
                if (previousStakedWeight != 0)
                    u.previousWeightedTokens = previousStakedWeight;
            }
        } else if (status == 1 && u.updated < currentDay && u.previousWeightedTokens == 0) {
            u.previousWeightedTokens = u.weightedTokens;
            u.weightedTokens = 0;
            if (u.previousWeightedTokens != 0) {
                u.status = 2;
            } else {
                u.status = 3;
            }
        }
    }

    function _updateWeight() internal returns(uint256 currentDay) {
        currentDay = block.timestamp / 1 days;  // number of days that passed
        //currentDay = block.timestamp / 1 hours;  // for TEST
        if (currentDay > processedDay) {
            (uint256 previousStakedWeight, uint256 stakedWeight) = 
                _calculateWeight(currentDay, processedDay, totalStaked, totalStakedWeight);
            processedDay = currentDay;
            totalStakedWeight = stakedWeight;
            if (previousStakedWeight != 0)
                previousTotalStakedWeight = previousStakedWeight;
        }
    }

    function _calculateWeight(uint256 currentDay, uint256 lastUpdate, uint256 amount, uint256 currentStakedWeight) internal pure returns(uint256 previousStakedWeight, uint256 stakedWeight) {
        stakedWeight = currentStakedWeight;
        if (currentDay > lastUpdate) {
            uint256 interval = currentDay - lastUpdate;
            (,,uint day) = _daysToDate(currentDay);
            
            if (day <= interval) {  // new month starts
                uint256 previousInterval = interval - day + 1;
                previousStakedWeight = currentStakedWeight + (amount * previousInterval);
                interval = interval - previousInterval; // number of days passed in current month
                stakedWeight = amount * interval;  // start total staked weight from zero
            }
            else {
                stakedWeight = currentStakedWeight + (amount * interval);
            }
        }        
    }

// TEST ========================================================================================================
/*
    function TEST_calculateWeight(uint256 currentDay, uint256 lastUpdate, uint256 amount, uint256 currentStakedWeight) public pure returns(uint256 previousStakedWeight, uint256 stakedWeight) {
        stakedWeight = currentStakedWeight;
        if (currentDay > lastUpdate) {
            uint256 interval = currentDay - lastUpdate;
            uint day = (currentDay % 24) + 1;   // 1 hour = 1 day, 24 hours = 1 month
            
            if (day <= interval) {  // new month starts
                uint256 previousInterval = interval - day + 1;
                previousStakedWeight = currentStakedWeight + (amount * previousInterval);
                interval = interval - previousInterval; // number of days passed in current month
                stakedWeight = amount * interval;  // start total staked weight from zero
            }
            else {
                stakedWeight = currentStakedWeight + (amount * interval);
            }
        }        
    }
*/
// END TEST ====================================================================================================

    function _safeTransfer(address _token, address to, uint value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = _token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
    }

    function _safeTransferFrom(address _token, address from, address to, uint value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = _token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
    }

    /**
     * @dev Calculate number of days from 1/1/1970 to selected date.
     * @param year The year number
     * @param month The month number
     * @param day The day number
     * @return _days number of day.
     */
    function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) {
        require(year >= 1970);
        int _year = int(year);
        int _month = int(month);
        int _day = int(day);

        int __days = _day
          - 32075
          + 1461 * (_year + 4800 + (_month - 14) / 12) / 4
          + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12
          - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4
          - 2440588;

        _days = uint(__days);
    }

    /**
     * @dev Calculate timestamp (UNIX time) of selected date and time.
     * @param year The year number
     * @param month The month number
     * @param day The day number
     * @param hour number of hours
     * @param minute number of minutes
     * @param second number of seconds
     * @return timestamp UNIX time
     */
    function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) {
        timestamp = _daysFromDate(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;
    }

    function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) {
        int __days = int(_days);

        int L = __days + 68569 + 2440588;
        int N = 4 * L / 146097;
        L = L - (146097 * N + 3) / 4;
        int _year = 4000 * (L + 1) / 1461001;
        L = L - 1461 * _year / 4 + 31;
        int _month = 80 * L / 2447;
        int _day = L - 2447 * _month / 80;
        L = _month / 11;
        _month = _month + 2 - 12 * L;
        _year = 100 * (N - 49) + _year + L;

        year = uint(_year);
        month = uint(_month);
        day = uint(_day);
    }

    /**
     * @dev Calculate date from timestamp (UNIX time) .
     * @param timestamp UNIX time
     * @return year The year number
     * @return month The month number
     * @return day The day number
     */
    function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) {
        (year, month, day) = _daysToDate(timestamp / 86400);
    }
}
        

Contract ABI

[{"type":"event","name":"MonthBalances","inputs":[{"type":"uint256","name":"totalStaked","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalBalance","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"MonthResult","inputs":[{"type":"int256","name":"amount","internalType":"int256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Processed","inputs":[{"type":"uint256","name":"number","internalType":"uint256","indexed":false},{"type":"uint256","name":"total","internalType":"uint256","indexed":false},{"type":"uint256","name":"step","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RemoveDeposit","inputs":[{"type":"address","name":"user","internalType":"address","indexed":false},{"type":"uint256","name":"depositId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RemoveUser","inputs":[{"type":"address","name":"user","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SetAdmin","inputs":[{"type":"address","name":"admin","internalType":"address","indexed":false},{"type":"bool","name":"isActive","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"SplitResult","inputs":[{"type":"address","name":"user","internalType":"address","indexed":false},{"type":"uint256","name":"depositId","internalType":"uint256","indexed":false},{"type":"int256","name":"amount","internalType":"int256","indexed":false}],"anonymous":false},{"type":"event","name":"Stake","inputs":[{"type":"address","name":"user","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"period","internalType":"uint256","indexed":false},{"type":"uint256","name":"multiplier","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Unstake","inputs":[{"type":"address","name":"user","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawUSD","inputs":[{"type":"address","name":"user","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"admins","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int256","name":"","internalType":"int256"}],"name":"amountPerWeightedToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"balance","internalType":"uint256"}],"name":"getTotalBalance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"","internalType":"struct Staking.User[]","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"weightedTokens","internalType":"uint256"},{"type":"uint256","name":"previousWeightedTokens","internalType":"uint256"},{"type":"uint256","name":"reward","internalType":"uint256"},{"type":"uint64","name":"unlockTime","internalType":"uint64"},{"type":"uint64","name":"updated","internalType":"uint64"},{"type":"uint64","name":"multiplier","internalType":"uint64"},{"type":"uint8","name":"status","internalType":"uint8"}]}],"name":"getUsersDeposits","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"},{"type":"tuple[]","name":"","internalType":"struct Staking.User[]","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"weightedTokens","internalType":"uint256"},{"type":"uint256","name":"previousWeightedTokens","internalType":"uint256"},{"type":"uint256","name":"reward","internalType":"uint256"},{"type":"uint64","name":"unlockTime","internalType":"uint64"},{"type":"uint64","name":"updated","internalType":"uint64"},{"type":"uint64","name":"multiplier","internalType":"uint64"},{"type":"uint8","name":"status","internalType":"uint8"}]}],"name":"getUsersDepositsByNumber","inputs":[{"type":"uint256","name":"userNumber","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUsersDepositsNumber","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUsersNumber","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"newOwner","internalType":"address"},{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minimumAmount","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"monthResult","inputs":[{"type":"int256","name":"amount","internalType":"int256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int256","name":"","internalType":"int256"}],"name":"monthResultAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"multiplier","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nextProcessUser","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"precessedMonth","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previousTotalStakedWeight","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"processedDay","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"processingStep","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"prolongate","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recalculateWeight","inputs":[{"type":"uint256","name":"numberUsers","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAdmin","inputs":[{"type":"address","name":"admin","internalType":"address"},{"type":"bool","name":"isActive","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMinimumAmount","inputs":[{"type":"uint256","name":"_minAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMultiplier","inputs":[{"type":"uint256","name":"_days","internalType":"uint256"},{"type":"uint256","name":"_multiplier","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setProlongate","inputs":[{"type":"uint256","name":"time","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setWithdrawWindow","inputs":[{"type":"uint256","name":"time","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"splitResult","inputs":[{"type":"uint256","name":"numberUsers","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"stake","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"period","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"stakeBehalf","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"period","internalType":"uint256"},{"type":"uint256","name":"multiplier_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"token","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalStaked","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalStakedWeight","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unstake","inputs":[{"type":"uint256","name":"depositId","internalType":"uint256"},{"type":"bool","name":"withdrawUSD","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unstakeAdmin","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"uint256","name":"depositId","internalType":"uint256"},{"type":"bool","name":"withdrawUSD","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"weightedTokens","internalType":"uint256"},{"type":"uint256","name":"previousWeightedTokens","internalType":"uint256"},{"type":"uint256","name":"reward","internalType":"uint256"},{"type":"uint64","name":"unlockTime","internalType":"uint64"},{"type":"uint64","name":"updated","internalType":"uint64"},{"type":"uint64","name":"multiplier","internalType":"uint64"},{"type":"uint8","name":"status","internalType":"uint8"}],"name":"users","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawRewards","inputs":[{"type":"uint256","name":"depositId","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bool","name":"withdrawUSD","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"withdrawWindow","inputs":[]}]
              

Contract Creation Code

Verify & Publish
0x608060405234801561001057600080fd5b50613208806100206000396000f3fe608060405234801561001057600080fd5b50600436106102275760003560e01c80639652b12111610130578063e6fc015e116100b8578063eeb4a9c81161007c578063eeb4a9c8146104c4578063f2fde38b146104d7578063f7cb8124146104ea578063fc0c546a146104f3578063ff293df31461050657600080fd5b8063e6fc015e14610479578063ebda3b051461048c578063ed47ab5b14610495578063edcb208b146104a8578063ee79da19146104bb57600080fd5b8063bb0c8298116100ff578063bb0c829814610442578063bbe0d9f61461044b578063bc10b1721461045e578063cb028a8314610467578063e03aabe61461047057600080fd5b80639652b121146104145780639ebea88c1461041d578063aa8c532414610430578063ab2e58dd1461043957600080fd5b8063485cc955116101b35780636696f3ff116101825780636696f3ff146103c25780637b0472f0146103cb5780637fe91239146103de578063817b1cd2146103e65780638da5cb5b146103ef57600080fd5b8063485cc955146103295780634b0bddd21461033c57806354a02f9e1461034f5780635c141991146103af57600080fd5b806320114ac2116101fa57806320114ac21461029d57806328a6a2dd146102b0578063333271d0146102d057806334553147146102e3578063429b62e5146102f657600080fd5b806305e30f511461022c57806312b583491461025f57806313c02846146102675780631e0927e81461027c575b600080fd5b61024c61023a366004612d72565b60056020526000908152604090205481565b6040519081526020015b60405180910390f35b61024c61052f565b61027a610275366004612d72565b6105f0565b005b61028f61028a366004612d72565b6107d2565b604051610256929190612eee565b61027a6102ab366004612d72565b6108cf565b6102c36102be366004612c26565b610aea565b6040516102569190612f1a565b61027a6102de366004612dc9565b610bc1565b61027a6102f1366004612deb565b610c50565b610319610304366004612c26565b60126020526000908152604090205460ff1681565b6040519015158152602001610256565b61027a610337366004612c48565b610eaf565b61027a61034a366004612c7b565b610fea565b61036261035d366004612cb2565b61107f565b6040805198895260208901979097529587019490945260608601929092526001600160401b03908116608086015290811660a08501521660c083015260ff1660e082015261010001610256565b61027a6103bd366004612d72565b6110f6565b61024c60105481565b61027a6103d9366004612dc9565b611134565b61024c611205565b61024c60095481565b6000546001600160a01b03165b6040516001600160a01b039091168152602001610256565b61024c600d5481565b61027a61042b366004612da4565b611215565b61024c60075481565b61024c60115481565b61024c60085481565b61027a610459366004612d1c565b6112a2565b61024c600a5481565b61024c60065481565b61024c600f5481565b61027a610487366004612d72565b611349565b61024c600e5481565b61027a6104a3366004612cdc565b6118da565b61027a6104b6366004612d72565b611d7c565b61024c600c5481565b61027a6104d2366004612d72565b611dba565b61027a6104e5366004612c26565b611df8565b61024c600b5481565b6001546103fc906001600160a01b031681565b61024c610514366004612c26565b6001600160a01b031660009081526004602052604090205490565b60008061053b60025490565b905060005b818110156105eb576000610555600283611ef1565b6001600160a01b0381166000908152600460205260408120549192505b818110156105d5576001600160a01b03831660009081526004602052604090208054829081106105a4576105a461318e565b906000526020600020906005020160000154866105c19190612fa3565b9550806105cd8161311d565b915050610572565b50505080806105e39061311d565b915050610540565b505090565b336106036000546001600160a01b031690565b6001600160a01b0316146106325760405162461bcd60e51b815260040161062990612f2d565b60405180910390fd5b600e541561068c5760405162461bcd60e51b815260206004820152602160248201527f50726576696f757320726573756c7420776173206e6f742070726f63657373656044820152601960fa1b6064820152608401610629565b610694611f82565b5060006106a042611fcf565b509150508060105414156107065760405162461bcd60e51b815260206004820152602760248201527f526573756c74206d6179206265206164646564206f6e6c79206f6e63652070656044820152660e440dadedce8d60cb1b6064820152608401610629565b6010819055600d829055600954600090610721908490612f62565b121561076f5760405162461bcd60e51b815260206004820152601c60248201527f746f74616c5374616b6564206c657373207468656e20616d6f756e74000000006044820152606401610629565b600082131561079057600154610790906001600160a01b0316333085611ff5565b6001600e5560006011556040518281527f7e378eda7a53c9c96fb609c656ea4cddfd3f364b2fd5a0338fed559471da4fbe906020015b60405180910390a15050565b60006060816107e2600285611ef1565b90508060046000836001600160a01b03166001600160a01b0316815260200190815260200160002080805480602002602001604051908101604052809291908181526020016000905b828210156108bf576000848152602090819020604080516101008101825260058602909201805483526001808201548486015260028201549284019290925260038101546060840152600401546001600160401b038082166080850152600160401b8204811660a0850152600160801b82041660c0840152600160c01b900460ff1660e0830152908352909201910161082b565b5050505090509250925050915091565b3360009081526012602052604090205460ff16806109065750336108fb6000546001600160a01b031690565b6001600160a01b0316145b6109485760405162461bcd60e51b815260206004820152601360248201527221b0b63632b91034b9903737ba1030b236b4b760691b6044820152606401610629565b600e546001146109875760405162461bcd60e51b815260206004820152600a602482015269057726f6e6720737465760b41b6044820152606401610629565b60006109966201518042612fe9565b60115490915060006109a760025490565b90505b80821015610aa65760006109bf600284611ef1565b6001600160a01b0381166000908152600460205260409020549091508086106109f3576109ec8187613106565b9550610a31565b6011849055600e54604080518681526020810186905280820192909252516000805160206131b38339815191529181900360600190a1505050505050565b60005b81811015610a91576001600160a01b03831660009081526004602052604090208054610a7f91889184908110610a6c57610a6c61318e565b9060005260206000209060050201612125565b80610a898161311d565b915050610a34565b5083610a9c8161311d565b94505050506109aa565b6002600e55600060115560408051838152602081018390526001918101919091526000805160206131b3833981519152906060015b60405180910390a15050505b50565b6001600160a01b0381166000908152600460209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610bb6576000848152602090819020604080516101008101825260058602909201805483526001808201548486015260028201549284019290925260038101546060840152600401546001600160401b038082166080850152600160401b8204811660a0850152600160801b82041660c0840152600160c01b900460ff1660e08301529083529092019101610b22565b505050509050919050565b33610bd46000546001600160a01b031690565b6001600160a01b031614610bfa5760405162461bcd60e51b815260040161062990612f2d565b6064811115610c3e5760405162461bcd60e51b815260206004820152601060248201526f2bb937b7339036bab63a34b83634b2b960811b6044820152606401610629565b60009182526005602052604090912055565b336000908152600460205260408120805485908110610c7157610c7161318e565b6000918252602090912060059091020160048101549091506001600160401b031642811115610e9d576000610ca64283613106565b905060075481610cb69190613138565b9050600654600754610cc89190613106565b811180610cd3575080155b610d185760405162461bcd60e51b81526020600482015260166024820152754f7574206f662077697468647261772077696e646f7760501b6044820152606401610629565b6003830154808611801590610d2e575083548611155b610d6f5760405162461bcd60e51b81526020600482015260126024820152714e6f7420656e6f756768207265776172647360701b6044820152606401610629565b6000610d79611f82565b9050610d858186612125565b610d8f8783613106565b6003860155845487908690600090610da8908490613106565b90915550506004850154600160c01b900460ff16610dd8578660096000828254610dd29190613106565b90915550505b8515610e4457600154610e06906001600160a01b0316610e006000546001600160a01b031690565b8961238e565b60408051338152602081018990527fa859066e89895ffb9f2cdf2af7e7e84061fcb7ddd8657877c264737f25f8b655910160405180910390a1610e95565b600154610e5b906001600160a01b0316338961238e565b60408051338152602081018990527f85082129d87b2fe11527cb1b3b7a520aeb5aa6913f88a3d8757fe40d1db02fdd910160405180910390a15b505050610ea8565b610ea83386856124a2565b5050505050565b6001600160a01b03821615801590610ed057506000546001600160a01b0316155b610ed957600080fd5b600080546001600160a01b038085166001600160a01b031992831617835560018054918516919092161790556040513391907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350506206978060065562278d00600755680ad78ebc5ac6200000600855600560205260197faadf04b4a1bf84e121ab68e5c1cffab5aeb9be1a024ff9944a2a0bc1674866095560327fec79235ffe9302935718f9bddc23f2c5c6c950ae49f49d137c786213c361802755604b7fe78ea91a221fb67f10aba14ba22dc39f780130b5cf7d2295f57a7414e295968b5561016860005260647f5464d34515fb195718451914a9b5e2c4ac04dce5fd392ea5e3f190355ea2fb7e55565b33610ffd6000546001600160a01b031690565b6001600160a01b0316146110235760405162461bcd60e51b815260040161062990612f2d565b6001600160a01b038216600081815260126020908152604091829020805460ff19168515159081179091558251938452908301527f55a5194bc0174fcaf12b2978bef43911466bf63b34db8d1dd1a0d5dcd5c41bea91016107c6565b6004602052816000526040600020818154811061109b57600080fd5b60009182526020909120600590910201805460018201546002830154600384015460049094015492955090935091906001600160401b0380821691600160401b8104821691600160801b82041690600160c01b900460ff1688565b336111096000546001600160a01b031690565b6001600160a01b03161461112f5760405162461bcd60e51b815260040161062990612f2d565b600755565b6008548210156111915760405162461bcd60e51b815260206004820152602260248201527f5374616b656420616d6f756e74206973206c657373207468656e206d696e696d604482015261756d60f01b6064820152608401610629565b600081815260056020526040902054806111dc5760405162461bcd60e51b815260206004820152600c60248201526b15dc9bdb99c81c195c9a5bd960a21b6044820152606401610629565b6001546111f4906001600160a01b0316333086611ff5565b6112003384848461260e565b505050565b600061121060025490565b905090565b3360009081526004602052604090208054839081106112365761123661318e565b60009182526020909120600460059092020101546001600160401b03164210156112935760405162461bcd60e51b815260206004820152600e60248201526d1058d8dbdd5b9d081b1bd8dad95960921b6044820152606401610629565b61129e3383836124a2565b5050565b336112b56000546001600160a01b031690565b6001600160a01b0316146112db5760405162461bcd60e51b815260040161062990612f2d565b606481111561131f5760405162461bcd60e51b815260206004820152601060248201526f2bb937b7339036bab63a34b83634b2b960811b6044820152606401610629565b600154611337906001600160a01b0316333086611ff5565b6113438484848461260e565b50505050565b3360009081526012602052604090205460ff16806113805750336113756000546001600160a01b031690565b6001600160a01b0316145b6113c25760405162461bcd60e51b815260206004820152601360248201527221b0b63632b91034b9903737ba1030b236b4b760691b6044820152606401610629565b600e546002146114015760405162461bcd60e51b815260206004820152600a602482015269057726f6e6720737465760b41b6044820152606401610629565b600d54600061140f60025490565b90508161144e576000600e55604080518281526020810183905260028183015290516000805160206131b38339815191529181900360600190a1505050565b600b546011546000905b8381101561178157600061146d600283611ef1565b6001600160a01b0381166000908152600460205260409020549091508088106114a15761149a8189613106565b97506114f5565b60118390556009546114b4908590612f62565b600955600e546040805185815260208101899052908101919091526000805160206131b3833981519152906060015b60405180910390a15050505050505050565b60005b8181101561176c576001600160a01b038316600090815260046020526040812080548390811061152a5761152a61318e565b906000526020600020906005020160020154905080600014611759576001600160a01b03841660009081526004602052604081208054849081106115705761157061318e565b60009182526020822060059091020154915061158d896064613023565b6001600160a01b03871660009081526004602052604090208054869081106115b7576115b761318e565b6000918252602090912060059091020160040154600160801b90046001600160401b031660070b6115e8858e613023565b6115f29190613023565b6115fc9190612fbb565b90506116088189612f62565b97506000811315611666576001600160a01b03861660009081526004602052604090208054829190869081106116405761164061318e565b906000526020600020906005020160030160008282546116609190612fa3565b90915550505b6116708183612f62565b9150600082121561168c5761168582896130c7565b9750600091505b6001600160a01b03861660009081526004602052604090208054839190869081106116b9576116b961318e565b600091825260208083206005909202909101929092556001600160a01b03881681526004909152604081208054869081106116f6576116f661318e565b600091825260209182902060026005909202010191909155604080516001600160a01b038916815291820186905281018290527f4410f5289f71baff2baf30dad8844d6c4d5af1f55fd01dd890aec66584f1677c9060600160405180910390a150505b50806117648161311d565b9150506114f8565b50826117778161311d565b9350505050611458565b8160095461178f9190612f62565b6009556000600e81905560118190556001546040516370a0823160e01b81523060048201526001600160a01b03909116906370a082319060240160206040518083038186803b1580156117e157600080fd5b505afa1580156117f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118199190612d8b565b9050600061182561052f565b60095460408051918252602082018390529192507fd9cdec0295c8edc1c466e37dd37c5afeabaea2e083fe8aabc6b74cd6094182cb910160405180910390a1600954811015801561187557508082115b156118ab576001546118ab906001600160a01b031661189c6000546001600160a01b031690565b6118a68486613106565b61238e565b60408051848152602081018890526002918101919091526000805160206131b3833981519152906060016114e3565b336118ed6000546001600160a01b031690565b6001600160a01b0316146119135760405162461bcd60e51b815260040161062990612f2d565b600061191d611f82565b6001600160a01b0385166000908152600460205260408120805492935090918590811061194c5761194c61318e565b906000526020600020906005020190506119668282612125565b80548015611a3d5783156119e35760015461199c906001600160a01b03166119966000546001600160a01b031690565b8361238e565b604080516001600160a01b0388168152602081018390527fa859066e89895ffb9f2cdf2af7e7e84061fcb7ddd8657877c264737f25f8b655910160405180910390a1611a3d565b6001546119fa906001600160a01b0316878361238e565b604080516001600160a01b0388168152602081018390527f85082129d87b2fe11527cb1b3b7a520aeb5aa6913f88a3d8757fe40d1db02fdd910160405180910390a15b6004820154600160c01b900460ff16611a68578060096000828254611a629190613106565b90915550505b8160010154600a6000828254611a7e9190613106565b90915550506002820154600b8054600090611a9a908490613106565b90915550506001600160a01b038616600090815260046020526040812054611ac490600190613106565b905080611b80576001600160a01b0387166000908152600460205260409020805480611af257611af2613178565b600082815260208120600560001990930192830201818155600181018290556002808201839055600382019290925560040180546001600160c81b03191690559155611b3e9088612844565b506040516001600160a01b03881681527f0ad8e54ac59bf2d8a7a1474c1af503b593553cf4fcaaffdef04ab5249f89762b9060200160405180910390a1611d73565b808614611cc5576001600160a01b0387166000908152600460205260409020805482908110611bb157611bb161318e565b906000526020600020906005020160046000896001600160a01b03166001600160a01b031681526020019081526020016000208781548110611bf557611bf561318e565b6000918252602090912082546005909202019081556001808301549082015560028083015490820155600380830154908201556004918201805492909101805467ffffffffffffffff1981166001600160401b0394851690811783558354600160401b908190048616026001600160801b031990921617178082558254600160801b9081900490941690930267ffffffffffffffff60801b19841681178255915460ff600160c01b91829004160260ff60c01b1990921668ffffffffffffffffff60801b19909316929092171790555b6001600160a01b0387166000908152600460205260409020805480611cec57611cec613178565b600082815260208082206005600019949094019384020182815560018101839055600281018390556003810192909255600490910180546001600160c81b03191690559155604080516001600160a01b038a1681529182018890527f52da67f845146a7b07a837a7e32b5b74f6d3fafb17ec6c04f23acfc9e1aa9d1f910160405180910390a15b50505050505050565b33611d8f6000546001600160a01b031690565b6001600160a01b031614611db55760405162461bcd60e51b815260040161062990612f2d565b600655565b33611dcd6000546001600160a01b031690565b6001600160a01b031614611df35760405162461bcd60e51b815260040161062990612f2d565b600855565b33611e0b6000546001600160a01b031690565b6001600160a01b031614611e315760405162461bcd60e51b815260040161062990612f2d565b6001600160a01b038116611e965760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610629565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b81546000908210611f4f5760405162461bcd60e51b815260206004820152602260248201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604482015261647360f01b6064820152608401610629565b826000018281548110611f6457611f6461318e565b6000918252602090912001546001600160a01b031690505b92915050565b6000611f916201518042612fe9565b9050600f54811115611fcc57600080611fb283600f54600954600a54612986565b600f859055600a819055909250905081156105eb5750600b555b90565b60008080611fe8611fe36201518086612fe9565b612a25565b9196909550909350915050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916120599190612eb3565b6000604051808303816000865af19150503d8060008114612096576040519150601f19603f3d011682016040523d82523d6000602084013e61209b565b606091505b50915091508180156120c55750805115806120c55750808060200190518101906120c59190612d55565b61211d5760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401610629565b505050505050565b6004810154600160c01b900460ff168061230e5781546001830154600484015460009061215f9062015180906001600160401b0316612ffd565b6001600160401b0316905085811161229c5760048501546000908190612198908490600160401b90046001600160401b03168787612986565b60048901805467ffffffffffffffff60401b1916600160401b6001600160401b038d160217905560018901819055909250905081156121d957600287018290555b80156121f95760048701805460ff60c01b1916600160c01b17905561222f565b81156122195760048701805460ff60c01b1916600160c11b17905561222f565b60048701805460ff60c01b1916600360c01b1790555b84600960008282546122419190613106565b925050819055506122588884878a60010154612986565b809250819350505080600a60008282546122729190613106565b909155505081156122955781600b600082825461228f9190613106565b90915550505b505061211d565b600485015460009081906122c3908990600160401b90046001600160401b03168787612986565b60048901805467ffffffffffffffff60401b1916600160401b6001600160401b038d1602179055600189018190559092509050811561230457600287018290555b5050505050505050565b8060ff16600114801561233457506004820154600160401b90046001600160401b031683115b801561234257506002820154155b1561120057600182018054600284018190556000909155156123765750600401805460ff60c01b1916600160c11b17905550565b50600401805460ff60c01b1916600360c01b17905550565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916123ea9190612eb3565b6000604051808303816000865af19150503d8060008114612427576040519150601f19603f3d011682016040523d82523d6000602084013e61242c565b606091505b50915091508180156124565750805115806124565750808060200190518101906124569190612d55565b610ea85760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610629565b60006124ac611f82565b6001600160a01b038516600090815260046020526040812080549293509091859081106124db576124db61318e565b906000526020600020906005020190506124f58282612125565b80546000808355600383015580156125d05783156125765760015461252f906001600160a01b03166119966000546001600160a01b031690565b604080516001600160a01b0388168152602081018390527fa859066e89895ffb9f2cdf2af7e7e84061fcb7ddd8657877c264737f25f8b655910160405180910390a16125d0565b60015461258d906001600160a01b0316878361238e565b604080516001600160a01b0388168152602081018390527f85082129d87b2fe11527cb1b3b7a520aeb5aa6913f88a3d8757fe40d1db02fdd910160405180910390a15b60028201541580156125e457506001820154155b1561211d576001600160a01b038616600090815260046020526040812054611ac490600190613106565b612616611f82565b506001600160a01b038416600090815260046020526040902054600a1161267f5760405162461bcd60e51b815260206004820152601e60248201527f416c6c6f776564203130206465706f73697473207065722077616c6c657400006044820152606401610629565b82600960008282546126919190612fa3565b909155506126a29050600285612b98565b5060046000856001600160a01b03166001600160a01b0316815260200190815260200160002060405180610100016040528085815260200160008152602001600081526020016000815260200184620151806126fe91906130a8565b6127089042612fa3565b6001600160401b031681526020016127236201518042612fe9565b6001600160401b0390811682528481166020808401919091526000604093840181905285546001818101885596825290829020855160059092020190815590840151948101949094558282015160028501556060830151600385015560808301516004909401805460a085015160c086015160e09096015160ff16600160c01b0260ff60c01b19968516600160801b029690961668ffffffffffffffffff60801b19918516600160401b026001600160801b0319909316979094169690961717949094161791909117909155517ff556991011e831bcfac4f406d547e5e32cdd98267efab83935230d5f8d02c44690610adb9086908690869086906001600160a01b0394909416845260208401929092526040830152606082015260800190565b6001600160a01b0381166000908152600183016020526040812054801561297c576000612872600183613106565b855490915060009061288690600190613106565b9050600086600001828154811061289f5761289f61318e565b60009182526020909120015487546001600160a01b03909116915081908890859081106128ce576128ce61318e565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055612902836001612fa3565b6001600160a01b0382166000908152600189016020526040902055865487908061292e5761292e613178565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0388168252600189810190915260408220919091559450611f7c9350505050565b6000915050611f7c565b60008184861115612a1c57600061299d8688613106565b905060006129aa88612a25565b92505050818111612a025760006129c18284613106565b6129cc906001612fa3565b90506129d881886130a8565b6129e29087612fa3565b94506129ee8184613106565b92506129fa83886130a8565b935050612a19565b612a0c82876130a8565b612a169086612fa3565b92505b50505b94509492505050565b600080808381612a388262010bd9612f62565b612a459062253d8c612f62565b9050600062023ab1612a58836004613023565b612a629190612fbb565b90506004612a738262023ab1613023565b612a7e906003612f62565b612a889190612fbb565b612a9290836130c7565b9150600062164b09612aa5846001612f62565b612ab190610fa0613023565b612abb9190612fbb565b90506004612acb826105b5613023565b612ad59190612fbb565b612adf90846130c7565b612aea90601f612f62565b9250600061098f612afc856050613023565b612b069190612fbb565b905060006050612b188361098f613023565b612b229190612fbb565b612b2c90866130c7565b9050612b39600b83612fbb565b9450612b4685600c613023565b612b51836002612f62565b612b5b91906130c7565b91508483612b6a6031876130c7565b612b75906064613023565b612b7f9190612f62565b612b899190612f62565b9a919950975095505050505050565b6001600160a01b0381166000908152600183016020526040812054612c0257508154600180820184556000848152602080822090930180546001600160a01b0319166001600160a01b03861690811790915585549082528286019093526040902091909155611f7c565b506000611f7c565b80356001600160a01b0381168114612c2157600080fd5b919050565b600060208284031215612c3857600080fd5b612c4182612c0a565b9392505050565b60008060408385031215612c5b57600080fd5b612c6483612c0a565b9150612c7260208401612c0a565b90509250929050565b60008060408385031215612c8e57600080fd5b612c9783612c0a565b91506020830135612ca7816131a4565b809150509250929050565b60008060408385031215612cc557600080fd5b612cce83612c0a565b946020939093013593505050565b600080600060608486031215612cf157600080fd5b612cfa84612c0a565b9250602084013591506040840135612d11816131a4565b809150509250925092565b60008060008060808587031215612d3257600080fd5b612d3b85612c0a565b966020860135965060408601359560600135945092505050565b600060208284031215612d6757600080fd5b8151612c41816131a4565b600060208284031215612d8457600080fd5b5035919050565b600060208284031215612d9d57600080fd5b5051919050565b60008060408385031215612db757600080fd5b823591506020830135612ca7816131a4565b60008060408385031215612ddc57600080fd5b50508035926020909101359150565b600080600060608486031215612e0057600080fd5b83359250602084013591506040840135612d11816131a4565b600081518084526020808501945080840160005b83811015612ea857815180518852838101518489015260408082015190890152606080820151908901526080808201516001600160401b03908116918a019190915260a0808301518216908a015260c0808301519091169089015260e09081015160ff16908801526101009096019590820190600101612e2d565b509495945050505050565b6000825160005b81811015612ed45760208186018101518583015201612eba565b81811115612ee3576000828501525b509190910192915050565b6001600160a01b0383168152604060208201819052600090612f1290830184612e19565b949350505050565b602081526000612c416020830184612e19565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600080821280156001600160ff1b0384900385131615612f8457612f8461314c565b600160ff1b8390038412811615612f9d57612f9d61314c565b50500190565b60008219821115612fb657612fb661314c565b500190565b600082612fca57612fca613162565b600160ff1b821460001984141615612fe457612fe461314c565b500590565b600082612ff857612ff8613162565b500490565b60006001600160401b038084168061301757613017613162565b92169190910492915050565b60006001600160ff1b03818413828413808216868404861116156130495761304961314c565b600160ff1b60008712828116878305891216156130685761306861314c565b600087129250878205871284841616156130845761308461314c565b8785058712818416161561309a5761309a61314c565b505050929093029392505050565b60008160001904831182151516156130c2576130c261314c565b500290565b60008083128015600160ff1b8501841216156130e5576130e561314c565b6001600160ff1b03840183138116156131005761310061314c565b50500390565b6000828210156131185761311861314c565b500390565b60006000198214156131315761313161314c565b5060010190565b60008261314757613147613162565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b8015158114610ae757600080fdfec534675d962a9c4b4a6eef4e934d46b14162000192fa31cf0c29cd81fdb8c2eea26469706673582212203615127cfb50b2103ce43dd3e3ed360634fb9eeb6a94f16787e198965f3f7eca64736f6c63430008060033

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106102275760003560e01c80639652b12111610130578063e6fc015e116100b8578063eeb4a9c81161007c578063eeb4a9c8146104c4578063f2fde38b146104d7578063f7cb8124146104ea578063fc0c546a146104f3578063ff293df31461050657600080fd5b8063e6fc015e14610479578063ebda3b051461048c578063ed47ab5b14610495578063edcb208b146104a8578063ee79da19146104bb57600080fd5b8063bb0c8298116100ff578063bb0c829814610442578063bbe0d9f61461044b578063bc10b1721461045e578063cb028a8314610467578063e03aabe61461047057600080fd5b80639652b121146104145780639ebea88c1461041d578063aa8c532414610430578063ab2e58dd1461043957600080fd5b8063485cc955116101b35780636696f3ff116101825780636696f3ff146103c25780637b0472f0146103cb5780637fe91239146103de578063817b1cd2146103e65780638da5cb5b146103ef57600080fd5b8063485cc955146103295780634b0bddd21461033c57806354a02f9e1461034f5780635c141991146103af57600080fd5b806320114ac2116101fa57806320114ac21461029d57806328a6a2dd146102b0578063333271d0146102d057806334553147146102e3578063429b62e5146102f657600080fd5b806305e30f511461022c57806312b583491461025f57806313c02846146102675780631e0927e81461027c575b600080fd5b61024c61023a366004612d72565b60056020526000908152604090205481565b6040519081526020015b60405180910390f35b61024c61052f565b61027a610275366004612d72565b6105f0565b005b61028f61028a366004612d72565b6107d2565b604051610256929190612eee565b61027a6102ab366004612d72565b6108cf565b6102c36102be366004612c26565b610aea565b6040516102569190612f1a565b61027a6102de366004612dc9565b610bc1565b61027a6102f1366004612deb565b610c50565b610319610304366004612c26565b60126020526000908152604090205460ff1681565b6040519015158152602001610256565b61027a610337366004612c48565b610eaf565b61027a61034a366004612c7b565b610fea565b61036261035d366004612cb2565b61107f565b6040805198895260208901979097529587019490945260608601929092526001600160401b03908116608086015290811660a08501521660c083015260ff1660e082015261010001610256565b61027a6103bd366004612d72565b6110f6565b61024c60105481565b61027a6103d9366004612dc9565b611134565b61024c611205565b61024c60095481565b6000546001600160a01b03165b6040516001600160a01b039091168152602001610256565b61024c600d5481565b61027a61042b366004612da4565b611215565b61024c60075481565b61024c60115481565b61024c60085481565b61027a610459366004612d1c565b6112a2565b61024c600a5481565b61024c60065481565b61024c600f5481565b61027a610487366004612d72565b611349565b61024c600e5481565b61027a6104a3366004612cdc565b6118da565b61027a6104b6366004612d72565b611d7c565b61024c600c5481565b61027a6104d2366004612d72565b611dba565b61027a6104e5366004612c26565b611df8565b61024c600b5481565b6001546103fc906001600160a01b031681565b61024c610514366004612c26565b6001600160a01b031660009081526004602052604090205490565b60008061053b60025490565b905060005b818110156105eb576000610555600283611ef1565b6001600160a01b0381166000908152600460205260408120549192505b818110156105d5576001600160a01b03831660009081526004602052604090208054829081106105a4576105a461318e565b906000526020600020906005020160000154866105c19190612fa3565b9550806105cd8161311d565b915050610572565b50505080806105e39061311d565b915050610540565b505090565b336106036000546001600160a01b031690565b6001600160a01b0316146106325760405162461bcd60e51b815260040161062990612f2d565b60405180910390fd5b600e541561068c5760405162461bcd60e51b815260206004820152602160248201527f50726576696f757320726573756c7420776173206e6f742070726f63657373656044820152601960fa1b6064820152608401610629565b610694611f82565b5060006106a042611fcf565b509150508060105414156107065760405162461bcd60e51b815260206004820152602760248201527f526573756c74206d6179206265206164646564206f6e6c79206f6e63652070656044820152660e440dadedce8d60cb1b6064820152608401610629565b6010819055600d829055600954600090610721908490612f62565b121561076f5760405162461bcd60e51b815260206004820152601c60248201527f746f74616c5374616b6564206c657373207468656e20616d6f756e74000000006044820152606401610629565b600082131561079057600154610790906001600160a01b0316333085611ff5565b6001600e5560006011556040518281527f7e378eda7a53c9c96fb609c656ea4cddfd3f364b2fd5a0338fed559471da4fbe906020015b60405180910390a15050565b60006060816107e2600285611ef1565b90508060046000836001600160a01b03166001600160a01b0316815260200190815260200160002080805480602002602001604051908101604052809291908181526020016000905b828210156108bf576000848152602090819020604080516101008101825260058602909201805483526001808201548486015260028201549284019290925260038101546060840152600401546001600160401b038082166080850152600160401b8204811660a0850152600160801b82041660c0840152600160c01b900460ff1660e0830152908352909201910161082b565b5050505090509250925050915091565b3360009081526012602052604090205460ff16806109065750336108fb6000546001600160a01b031690565b6001600160a01b0316145b6109485760405162461bcd60e51b815260206004820152601360248201527221b0b63632b91034b9903737ba1030b236b4b760691b6044820152606401610629565b600e546001146109875760405162461bcd60e51b815260206004820152600a602482015269057726f6e6720737465760b41b6044820152606401610629565b60006109966201518042612fe9565b60115490915060006109a760025490565b90505b80821015610aa65760006109bf600284611ef1565b6001600160a01b0381166000908152600460205260409020549091508086106109f3576109ec8187613106565b9550610a31565b6011849055600e54604080518681526020810186905280820192909252516000805160206131b38339815191529181900360600190a1505050505050565b60005b81811015610a91576001600160a01b03831660009081526004602052604090208054610a7f91889184908110610a6c57610a6c61318e565b9060005260206000209060050201612125565b80610a898161311d565b915050610a34565b5083610a9c8161311d565b94505050506109aa565b6002600e55600060115560408051838152602081018390526001918101919091526000805160206131b3833981519152906060015b60405180910390a15050505b50565b6001600160a01b0381166000908152600460209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610bb6576000848152602090819020604080516101008101825260058602909201805483526001808201548486015260028201549284019290925260038101546060840152600401546001600160401b038082166080850152600160401b8204811660a0850152600160801b82041660c0840152600160c01b900460ff1660e08301529083529092019101610b22565b505050509050919050565b33610bd46000546001600160a01b031690565b6001600160a01b031614610bfa5760405162461bcd60e51b815260040161062990612f2d565b6064811115610c3e5760405162461bcd60e51b815260206004820152601060248201526f2bb937b7339036bab63a34b83634b2b960811b6044820152606401610629565b60009182526005602052604090912055565b336000908152600460205260408120805485908110610c7157610c7161318e565b6000918252602090912060059091020160048101549091506001600160401b031642811115610e9d576000610ca64283613106565b905060075481610cb69190613138565b9050600654600754610cc89190613106565b811180610cd3575080155b610d185760405162461bcd60e51b81526020600482015260166024820152754f7574206f662077697468647261772077696e646f7760501b6044820152606401610629565b6003830154808611801590610d2e575083548611155b610d6f5760405162461bcd60e51b81526020600482015260126024820152714e6f7420656e6f756768207265776172647360701b6044820152606401610629565b6000610d79611f82565b9050610d858186612125565b610d8f8783613106565b6003860155845487908690600090610da8908490613106565b90915550506004850154600160c01b900460ff16610dd8578660096000828254610dd29190613106565b90915550505b8515610e4457600154610e06906001600160a01b0316610e006000546001600160a01b031690565b8961238e565b60408051338152602081018990527fa859066e89895ffb9f2cdf2af7e7e84061fcb7ddd8657877c264737f25f8b655910160405180910390a1610e95565b600154610e5b906001600160a01b0316338961238e565b60408051338152602081018990527f85082129d87b2fe11527cb1b3b7a520aeb5aa6913f88a3d8757fe40d1db02fdd910160405180910390a15b505050610ea8565b610ea83386856124a2565b5050505050565b6001600160a01b03821615801590610ed057506000546001600160a01b0316155b610ed957600080fd5b600080546001600160a01b038085166001600160a01b031992831617835560018054918516919092161790556040513391907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350506206978060065562278d00600755680ad78ebc5ac6200000600855600560205260197faadf04b4a1bf84e121ab68e5c1cffab5aeb9be1a024ff9944a2a0bc1674866095560327fec79235ffe9302935718f9bddc23f2c5c6c950ae49f49d137c786213c361802755604b7fe78ea91a221fb67f10aba14ba22dc39f780130b5cf7d2295f57a7414e295968b5561016860005260647f5464d34515fb195718451914a9b5e2c4ac04dce5fd392ea5e3f190355ea2fb7e55565b33610ffd6000546001600160a01b031690565b6001600160a01b0316146110235760405162461bcd60e51b815260040161062990612f2d565b6001600160a01b038216600081815260126020908152604091829020805460ff19168515159081179091558251938452908301527f55a5194bc0174fcaf12b2978bef43911466bf63b34db8d1dd1a0d5dcd5c41bea91016107c6565b6004602052816000526040600020818154811061109b57600080fd5b60009182526020909120600590910201805460018201546002830154600384015460049094015492955090935091906001600160401b0380821691600160401b8104821691600160801b82041690600160c01b900460ff1688565b336111096000546001600160a01b031690565b6001600160a01b03161461112f5760405162461bcd60e51b815260040161062990612f2d565b600755565b6008548210156111915760405162461bcd60e51b815260206004820152602260248201527f5374616b656420616d6f756e74206973206c657373207468656e206d696e696d604482015261756d60f01b6064820152608401610629565b600081815260056020526040902054806111dc5760405162461bcd60e51b815260206004820152600c60248201526b15dc9bdb99c81c195c9a5bd960a21b6044820152606401610629565b6001546111f4906001600160a01b0316333086611ff5565b6112003384848461260e565b505050565b600061121060025490565b905090565b3360009081526004602052604090208054839081106112365761123661318e565b60009182526020909120600460059092020101546001600160401b03164210156112935760405162461bcd60e51b815260206004820152600e60248201526d1058d8dbdd5b9d081b1bd8dad95960921b6044820152606401610629565b61129e3383836124a2565b5050565b336112b56000546001600160a01b031690565b6001600160a01b0316146112db5760405162461bcd60e51b815260040161062990612f2d565b606481111561131f5760405162461bcd60e51b815260206004820152601060248201526f2bb937b7339036bab63a34b83634b2b960811b6044820152606401610629565b600154611337906001600160a01b0316333086611ff5565b6113438484848461260e565b50505050565b3360009081526012602052604090205460ff16806113805750336113756000546001600160a01b031690565b6001600160a01b0316145b6113c25760405162461bcd60e51b815260206004820152601360248201527221b0b63632b91034b9903737ba1030b236b4b760691b6044820152606401610629565b600e546002146114015760405162461bcd60e51b815260206004820152600a602482015269057726f6e6720737465760b41b6044820152606401610629565b600d54600061140f60025490565b90508161144e576000600e55604080518281526020810183905260028183015290516000805160206131b38339815191529181900360600190a1505050565b600b546011546000905b8381101561178157600061146d600283611ef1565b6001600160a01b0381166000908152600460205260409020549091508088106114a15761149a8189613106565b97506114f5565b60118390556009546114b4908590612f62565b600955600e546040805185815260208101899052908101919091526000805160206131b3833981519152906060015b60405180910390a15050505050505050565b60005b8181101561176c576001600160a01b038316600090815260046020526040812080548390811061152a5761152a61318e565b906000526020600020906005020160020154905080600014611759576001600160a01b03841660009081526004602052604081208054849081106115705761157061318e565b60009182526020822060059091020154915061158d896064613023565b6001600160a01b03871660009081526004602052604090208054869081106115b7576115b761318e565b6000918252602090912060059091020160040154600160801b90046001600160401b031660070b6115e8858e613023565b6115f29190613023565b6115fc9190612fbb565b90506116088189612f62565b97506000811315611666576001600160a01b03861660009081526004602052604090208054829190869081106116405761164061318e565b906000526020600020906005020160030160008282546116609190612fa3565b90915550505b6116708183612f62565b9150600082121561168c5761168582896130c7565b9750600091505b6001600160a01b03861660009081526004602052604090208054839190869081106116b9576116b961318e565b600091825260208083206005909202909101929092556001600160a01b03881681526004909152604081208054869081106116f6576116f661318e565b600091825260209182902060026005909202010191909155604080516001600160a01b038916815291820186905281018290527f4410f5289f71baff2baf30dad8844d6c4d5af1f55fd01dd890aec66584f1677c9060600160405180910390a150505b50806117648161311d565b9150506114f8565b50826117778161311d565b9350505050611458565b8160095461178f9190612f62565b6009556000600e81905560118190556001546040516370a0823160e01b81523060048201526001600160a01b03909116906370a082319060240160206040518083038186803b1580156117e157600080fd5b505afa1580156117f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118199190612d8b565b9050600061182561052f565b60095460408051918252602082018390529192507fd9cdec0295c8edc1c466e37dd37c5afeabaea2e083fe8aabc6b74cd6094182cb910160405180910390a1600954811015801561187557508082115b156118ab576001546118ab906001600160a01b031661189c6000546001600160a01b031690565b6118a68486613106565b61238e565b60408051848152602081018890526002918101919091526000805160206131b3833981519152906060016114e3565b336118ed6000546001600160a01b031690565b6001600160a01b0316146119135760405162461bcd60e51b815260040161062990612f2d565b600061191d611f82565b6001600160a01b0385166000908152600460205260408120805492935090918590811061194c5761194c61318e565b906000526020600020906005020190506119668282612125565b80548015611a3d5783156119e35760015461199c906001600160a01b03166119966000546001600160a01b031690565b8361238e565b604080516001600160a01b0388168152602081018390527fa859066e89895ffb9f2cdf2af7e7e84061fcb7ddd8657877c264737f25f8b655910160405180910390a1611a3d565b6001546119fa906001600160a01b0316878361238e565b604080516001600160a01b0388168152602081018390527f85082129d87b2fe11527cb1b3b7a520aeb5aa6913f88a3d8757fe40d1db02fdd910160405180910390a15b6004820154600160c01b900460ff16611a68578060096000828254611a629190613106565b90915550505b8160010154600a6000828254611a7e9190613106565b90915550506002820154600b8054600090611a9a908490613106565b90915550506001600160a01b038616600090815260046020526040812054611ac490600190613106565b905080611b80576001600160a01b0387166000908152600460205260409020805480611af257611af2613178565b600082815260208120600560001990930192830201818155600181018290556002808201839055600382019290925560040180546001600160c81b03191690559155611b3e9088612844565b506040516001600160a01b03881681527f0ad8e54ac59bf2d8a7a1474c1af503b593553cf4fcaaffdef04ab5249f89762b9060200160405180910390a1611d73565b808614611cc5576001600160a01b0387166000908152600460205260409020805482908110611bb157611bb161318e565b906000526020600020906005020160046000896001600160a01b03166001600160a01b031681526020019081526020016000208781548110611bf557611bf561318e565b6000918252602090912082546005909202019081556001808301549082015560028083015490820155600380830154908201556004918201805492909101805467ffffffffffffffff1981166001600160401b0394851690811783558354600160401b908190048616026001600160801b031990921617178082558254600160801b9081900490941690930267ffffffffffffffff60801b19841681178255915460ff600160c01b91829004160260ff60c01b1990921668ffffffffffffffffff60801b19909316929092171790555b6001600160a01b0387166000908152600460205260409020805480611cec57611cec613178565b600082815260208082206005600019949094019384020182815560018101839055600281018390556003810192909255600490910180546001600160c81b03191690559155604080516001600160a01b038a1681529182018890527f52da67f845146a7b07a837a7e32b5b74f6d3fafb17ec6c04f23acfc9e1aa9d1f910160405180910390a15b50505050505050565b33611d8f6000546001600160a01b031690565b6001600160a01b031614611db55760405162461bcd60e51b815260040161062990612f2d565b600655565b33611dcd6000546001600160a01b031690565b6001600160a01b031614611df35760405162461bcd60e51b815260040161062990612f2d565b600855565b33611e0b6000546001600160a01b031690565b6001600160a01b031614611e315760405162461bcd60e51b815260040161062990612f2d565b6001600160a01b038116611e965760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610629565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b81546000908210611f4f5760405162461bcd60e51b815260206004820152602260248201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604482015261647360f01b6064820152608401610629565b826000018281548110611f6457611f6461318e565b6000918252602090912001546001600160a01b031690505b92915050565b6000611f916201518042612fe9565b9050600f54811115611fcc57600080611fb283600f54600954600a54612986565b600f859055600a819055909250905081156105eb5750600b555b90565b60008080611fe8611fe36201518086612fe9565b612a25565b9196909550909350915050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916120599190612eb3565b6000604051808303816000865af19150503d8060008114612096576040519150601f19603f3d011682016040523d82523d6000602084013e61209b565b606091505b50915091508180156120c55750805115806120c55750808060200190518101906120c59190612d55565b61211d5760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401610629565b505050505050565b6004810154600160c01b900460ff168061230e5781546001830154600484015460009061215f9062015180906001600160401b0316612ffd565b6001600160401b0316905085811161229c5760048501546000908190612198908490600160401b90046001600160401b03168787612986565b60048901805467ffffffffffffffff60401b1916600160401b6001600160401b038d160217905560018901819055909250905081156121d957600287018290555b80156121f95760048701805460ff60c01b1916600160c01b17905561222f565b81156122195760048701805460ff60c01b1916600160c11b17905561222f565b60048701805460ff60c01b1916600360c01b1790555b84600960008282546122419190613106565b925050819055506122588884878a60010154612986565b809250819350505080600a60008282546122729190613106565b909155505081156122955781600b600082825461228f9190613106565b90915550505b505061211d565b600485015460009081906122c3908990600160401b90046001600160401b03168787612986565b60048901805467ffffffffffffffff60401b1916600160401b6001600160401b038d1602179055600189018190559092509050811561230457600287018290555b5050505050505050565b8060ff16600114801561233457506004820154600160401b90046001600160401b031683115b801561234257506002820154155b1561120057600182018054600284018190556000909155156123765750600401805460ff60c01b1916600160c11b17905550565b50600401805460ff60c01b1916600360c01b17905550565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916123ea9190612eb3565b6000604051808303816000865af19150503d8060008114612427576040519150601f19603f3d011682016040523d82523d6000602084013e61242c565b606091505b50915091508180156124565750805115806124565750808060200190518101906124569190612d55565b610ea85760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610629565b60006124ac611f82565b6001600160a01b038516600090815260046020526040812080549293509091859081106124db576124db61318e565b906000526020600020906005020190506124f58282612125565b80546000808355600383015580156125d05783156125765760015461252f906001600160a01b03166119966000546001600160a01b031690565b604080516001600160a01b0388168152602081018390527fa859066e89895ffb9f2cdf2af7e7e84061fcb7ddd8657877c264737f25f8b655910160405180910390a16125d0565b60015461258d906001600160a01b0316878361238e565b604080516001600160a01b0388168152602081018390527f85082129d87b2fe11527cb1b3b7a520aeb5aa6913f88a3d8757fe40d1db02fdd910160405180910390a15b60028201541580156125e457506001820154155b1561211d576001600160a01b038616600090815260046020526040812054611ac490600190613106565b612616611f82565b506001600160a01b038416600090815260046020526040902054600a1161267f5760405162461bcd60e51b815260206004820152601e60248201527f416c6c6f776564203130206465706f73697473207065722077616c6c657400006044820152606401610629565b82600960008282546126919190612fa3565b909155506126a29050600285612b98565b5060046000856001600160a01b03166001600160a01b0316815260200190815260200160002060405180610100016040528085815260200160008152602001600081526020016000815260200184620151806126fe91906130a8565b6127089042612fa3565b6001600160401b031681526020016127236201518042612fe9565b6001600160401b0390811682528481166020808401919091526000604093840181905285546001818101885596825290829020855160059092020190815590840151948101949094558282015160028501556060830151600385015560808301516004909401805460a085015160c086015160e09096015160ff16600160c01b0260ff60c01b19968516600160801b029690961668ffffffffffffffffff60801b19918516600160401b026001600160801b0319909316979094169690961717949094161791909117909155517ff556991011e831bcfac4f406d547e5e32cdd98267efab83935230d5f8d02c44690610adb9086908690869086906001600160a01b0394909416845260208401929092526040830152606082015260800190565b6001600160a01b0381166000908152600183016020526040812054801561297c576000612872600183613106565b855490915060009061288690600190613106565b9050600086600001828154811061289f5761289f61318e565b60009182526020909120015487546001600160a01b03909116915081908890859081106128ce576128ce61318e565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055612902836001612fa3565b6001600160a01b0382166000908152600189016020526040902055865487908061292e5761292e613178565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0388168252600189810190915260408220919091559450611f7c9350505050565b6000915050611f7c565b60008184861115612a1c57600061299d8688613106565b905060006129aa88612a25565b92505050818111612a025760006129c18284613106565b6129cc906001612fa3565b90506129d881886130a8565b6129e29087612fa3565b94506129ee8184613106565b92506129fa83886130a8565b935050612a19565b612a0c82876130a8565b612a169086612fa3565b92505b50505b94509492505050565b600080808381612a388262010bd9612f62565b612a459062253d8c612f62565b9050600062023ab1612a58836004613023565b612a629190612fbb565b90506004612a738262023ab1613023565b612a7e906003612f62565b612a889190612fbb565b612a9290836130c7565b9150600062164b09612aa5846001612f62565b612ab190610fa0613023565b612abb9190612fbb565b90506004612acb826105b5613023565b612ad59190612fbb565b612adf90846130c7565b612aea90601f612f62565b9250600061098f612afc856050613023565b612b069190612fbb565b905060006050612b188361098f613023565b612b229190612fbb565b612b2c90866130c7565b9050612b39600b83612fbb565b9450612b4685600c613023565b612b51836002612f62565b612b5b91906130c7565b91508483612b6a6031876130c7565b612b75906064613023565b612b7f9190612f62565b612b899190612f62565b9a919950975095505050505050565b6001600160a01b0381166000908152600183016020526040812054612c0257508154600180820184556000848152602080822090930180546001600160a01b0319166001600160a01b03861690811790915585549082528286019093526040902091909155611f7c565b506000611f7c565b80356001600160a01b0381168114612c2157600080fd5b919050565b600060208284031215612c3857600080fd5b612c4182612c0a565b9392505050565b60008060408385031215612c5b57600080fd5b612c6483612c0a565b9150612c7260208401612c0a565b90509250929050565b60008060408385031215612c8e57600080fd5b612c9783612c0a565b91506020830135612ca7816131a4565b809150509250929050565b60008060408385031215612cc557600080fd5b612cce83612c0a565b946020939093013593505050565b600080600060608486031215612cf157600080fd5b612cfa84612c0a565b9250602084013591506040840135612d11816131a4565b809150509250925092565b60008060008060808587031215612d3257600080fd5b612d3b85612c0a565b966020860135965060408601359560600135945092505050565b600060208284031215612d6757600080fd5b8151612c41816131a4565b600060208284031215612d8457600080fd5b5035919050565b600060208284031215612d9d57600080fd5b5051919050565b60008060408385031215612db757600080fd5b823591506020830135612ca7816131a4565b60008060408385031215612ddc57600080fd5b50508035926020909101359150565b600080600060608486031215612e0057600080fd5b83359250602084013591506040840135612d11816131a4565b600081518084526020808501945080840160005b83811015612ea857815180518852838101518489015260408082015190890152606080820151908901526080808201516001600160401b03908116918a019190915260a0808301518216908a015260c0808301519091169089015260e09081015160ff16908801526101009096019590820190600101612e2d565b509495945050505050565b6000825160005b81811015612ed45760208186018101518583015201612eba565b81811115612ee3576000828501525b509190910192915050565b6001600160a01b0383168152604060208201819052600090612f1290830184612e19565b949350505050565b602081526000612c416020830184612e19565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600080821280156001600160ff1b0384900385131615612f8457612f8461314c565b600160ff1b8390038412811615612f9d57612f9d61314c565b50500190565b60008219821115612fb657612fb661314c565b500190565b600082612fca57612fca613162565b600160ff1b821460001984141615612fe457612fe461314c565b500590565b600082612ff857612ff8613162565b500490565b60006001600160401b038084168061301757613017613162565b92169190910492915050565b60006001600160ff1b03818413828413808216868404861116156130495761304961314c565b600160ff1b60008712828116878305891216156130685761306861314c565b600087129250878205871284841616156130845761308461314c565b8785058712818416161561309a5761309a61314c565b505050929093029392505050565b60008160001904831182151516156130c2576130c261314c565b500290565b60008083128015600160ff1b8501841216156130e5576130e561314c565b6001600160ff1b03840183138116156131005761310061314c565b50500390565b6000828210156131185761311861314c565b500390565b60006000198214156131315761313161314c565b5060010190565b60008261314757613147613162565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b8015158114610ae757600080fdfec534675d962a9c4b4a6eef4e934d46b14162000192fa31cf0c29cd81fdb8c2eea26469706673582212203615127cfb50b2103ce43dd3e3ed360634fb9eeb6a94f16787e198965f3f7eca64736f6c63430008060033