false
false

Contract Address Details

0x57DE340887A671C9AccA4B3612D10Ad9e4460a7A

Contract Name
GaugeFactory
Creator
0x0348f8–a27d70 at 0x459a77–ddcebc
Balance
0 KCS
Tokens
Fetching tokens...
Transactions
2 Transactions
Transfers
0 Transfers
Gas Used
129,464
Last Balance Update
44808442
Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB 0xbbeaef148c783c202f700021d594352b7f52d156.
All metadata displayed below is from that contract. In order to verify current contract, click Verify & Publish button
Verify & Publish
Contract name:
GaugeFactory




Optimization enabled
true
Compiler version
v0.8.13+commit.abaa5c0e




Optimization runs
200
Verified at
2023-08-25T19:25:46.603514Z

Contract source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IGaugeFactory {
    function createGauge(address, address, address, bool, address[] memory) external returns (address);
}

// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

interface IBribe {
    function _deposit(uint amount, uint tokenId) external;
    function _withdraw(uint amount, uint tokenId) external;
    function getRewardForOwner(uint tokenId, address[] memory tokens) external;
    function notifyRewardAmount(address token, uint amount) external;
    function left(address token) external view returns (uint);
}

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function transfer(address recipient, uint amount) external returns (bool);
    function decimals() external view returns (uint8);
    function symbol() external view returns (string memory);
    function balanceOf(address) external view returns (uint);
    function transferFrom(address sender, address recipient, uint amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint);
    function approve(address spender, uint value) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

interface IGauge {
    function notifyRewardAmount(address token, uint amount) external;
    function getReward(address account, address[] memory tokens) external;
    function left(address token) external view returns (uint);
    function stake() external view returns (address);
}

interface IOptionToken {
    function mint(address _to, uint256 _amount) external;
    function exercise(
        uint256 _amount,
        uint256 _maxPaymentAmount,
        address _recipient
    ) external returns (uint256);
    function paymentToken() external returns (address);
    function underlyingToken() external returns (address);
    function getDiscountedPrice(uint256 _amount) external view returns (uint256);
}

interface IVoter {
    function _ve() external view returns (address);
    function governor() external view returns (address);
    function emergencyCouncil() external view returns (address);
    function attachTokenToGauge(uint _tokenId, address account) external;
    function detachTokenFromGauge(uint _tokenId, address account) external;
    function emitDeposit(uint _tokenId, address account, uint amount) external;
    function emitWithdraw(uint _tokenId, address account, uint amount) external;
    function isWhitelisted(address token) external view returns (bool);
    function notifyRewardAmount(uint amount) external;
    function distribute(address _gauge) external;
    function gauges(address) external view returns (address);
}

interface IVotingEscrow {

    struct Point {
        int128 bias;
        int128 slope; // # -dweight / dt
        uint256 ts;
        uint256 blk; // block
    }

    function token() external view returns (address);
    function team() external returns (address);
    function epoch() external view returns (uint);
    function point_history(uint loc) external view returns (Point memory);
    function user_point_history(uint tokenId, uint loc) external view returns (Point memory);
    function user_point_epoch(uint tokenId) external view returns (uint);

    function ownerOf(uint) external view returns (address);
    function isApprovedOrOwner(address, uint) external view returns (bool);
    function transferFrom(address, address, uint) external;

    function voting(uint tokenId) external;
    function abstain(uint tokenId) external;
    function attach(uint tokenId) external;
    function detach(uint tokenId) external;

    function checkpoint() external;
    function deposit_for(uint tokenId, uint value) external;
    function create_lock_for(uint, uint, address) external returns (uint);

    function balanceOfNFT(uint) external view returns (uint);
    function totalSupply() external view returns (uint);
}

// Gauges are used to incentivize pools, they emit reward tokens over 7 days for staked LP tokens
contract Gauge is IGauge {

    address public immutable stake; // the LP token that needs to be staked for rewards
    address public immutable _ve; // the ve token used for gauges
    address public immutable external_bribe;
    address public immutable voter;
    address public immutable kus;
    address public immutable gaugeFactory;
    address public oKus;

    uint public derivedSupply;
    mapping(address => uint) public derivedBalances;

    bool public isForPair;

    uint internal constant DURATION = 7 days; // rewards are released over 7 days
    uint internal constant PRECISION = 10 ** 18;
    uint internal constant MAX_REWARD_TOKENS = 4;

    // default snx staking contract implementation
    mapping(address => uint) public rewardRate;
    mapping(address => uint) public periodFinish;
    mapping(address => uint) public lastUpdateTime;
    mapping(address => uint) public rewardPerTokenStored;

    mapping(address => mapping(address => uint)) public lastEarn;
    mapping(address => mapping(address => uint)) public userRewardPerTokenStored;

    mapping(address => uint) public tokenIds;

    uint public totalSupply;
    mapping(address => uint) public balanceOf;

    address[] public rewards;
    mapping(address => bool) public isReward;

    /// @notice A checkpoint for marking balance
    struct Checkpoint {
        uint timestamp;
        uint balanceOf;
    }

    /// @notice A checkpoint for marking reward rate
    struct RewardPerTokenCheckpoint {
        uint timestamp;
        uint rewardPerToken;
    }

    /// @notice A checkpoint for marking supply
    struct SupplyCheckpoint {
        uint timestamp;
        uint supply;
    }

    /// @notice A record of balance checkpoints for each account, by index
    mapping (address => mapping (uint => Checkpoint)) public checkpoints;
    /// @notice The number of checkpoints for each account
    mapping (address => uint) public numCheckpoints;
    /// @notice A record of balance checkpoints for each token, by index
    mapping (uint => SupplyCheckpoint) public supplyCheckpoints;
    /// @notice The number of checkpoints
    uint public supplyNumCheckpoints;
    /// @notice A record of balance checkpoints for each token, by index
    mapping (address => mapping (uint => RewardPerTokenCheckpoint)) public rewardPerTokenCheckpoints;
    /// @notice The number of checkpoints for each token
    mapping (address => uint) public rewardPerTokenNumCheckpoints;

    uint public fees0;
    uint public fees1;

    event Deposit(address indexed from, uint tokenId, uint amount);
    event Withdraw(address indexed from, uint tokenId, uint amount);
    event NotifyReward(address indexed from, address indexed reward, uint amount);
    event ClaimRewards(address indexed from, address indexed reward, uint amount);
    event OKusSet(address indexed _oKus);

    constructor(address _stake, address _external_bribe, address  __ve, address _voter, address _oKus, address _gaugeFactory, bool _forPair, address[] memory _allowedRewardTokens) {
        stake = _stake;
        external_bribe = _external_bribe;
        _ve = __ve;
        voter = _voter;
        oKus = _oKus;
        gaugeFactory = _gaugeFactory;
        isForPair = _forPair;
        kus = IVotingEscrow(_ve).token();
        _safeApprove(kus, oKus, type(uint256).max);

        for (uint i; i < _allowedRewardTokens.length; i++) {
            if (_allowedRewardTokens[i] != address(0)) {
                isReward[_allowedRewardTokens[i]] = true;
                rewards.push(_allowedRewardTokens[i]);
            }
        }
    }

    // simple re-entrancy check
    uint internal _unlocked = 1;
    modifier lock() {
        require(_unlocked == 1);
        _unlocked = 2;
        _;
        _unlocked = 1;
    }

    /**
    * @notice Determine the prior balance for an account as of a block number
    * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
    * @param account The address of the account to check
    * @param timestamp The timestamp to get the balance at
    * @return The balance the account had as of the given block
    */
    function getPriorBalanceIndex(address account, uint timestamp) public view returns (uint) {
        uint nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }

        // First check most recent balance
        if (checkpoints[account][nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }

        // Next check implicit zero balance
        if (checkpoints[account][0].timestamp > timestamp) {
            return 0;
        }

        uint lower = 0;
        uint upper = nCheckpoints - 1;
        while (upper > lower) {
            uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            Checkpoint memory cp = checkpoints[account][center];
            if (cp.timestamp == timestamp) {
                return center;
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return lower;
    }

    function getPriorSupplyIndex(uint timestamp) public view returns (uint) {
        uint nCheckpoints = supplyNumCheckpoints;
        if (nCheckpoints == 0) {
            return 0;
        }

        // First check most recent balance
        if (supplyCheckpoints[nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }

        // Next check implicit zero balance
        if (supplyCheckpoints[0].timestamp > timestamp) {
            return 0;
        }

        uint lower = 0;
        uint upper = nCheckpoints - 1;
        while (upper > lower) {
            uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            SupplyCheckpoint memory cp = supplyCheckpoints[center];
            if (cp.timestamp == timestamp) {
                return center;
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return lower;
    }

    function getPriorRewardPerToken(address token, uint timestamp) public view returns (uint, uint) {
        uint nCheckpoints = rewardPerTokenNumCheckpoints[token];
        if (nCheckpoints == 0) {
            return (0,0);
        }

        // First check most recent balance
        if (rewardPerTokenCheckpoints[token][nCheckpoints - 1].timestamp <= timestamp) {
            return (rewardPerTokenCheckpoints[token][nCheckpoints - 1].rewardPerToken, rewardPerTokenCheckpoints[token][nCheckpoints - 1].timestamp);
        }

        // Next check implicit zero balance
        if (rewardPerTokenCheckpoints[token][0].timestamp > timestamp) {
            return (0,0);
        }

        uint lower = 0;
        uint upper = nCheckpoints - 1;
        while (upper > lower) {
            uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            RewardPerTokenCheckpoint memory cp = rewardPerTokenCheckpoints[token][center];
            if (cp.timestamp == timestamp) {
                return (cp.rewardPerToken, cp.timestamp);
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return (rewardPerTokenCheckpoints[token][lower].rewardPerToken, rewardPerTokenCheckpoints[token][lower].timestamp);
    }

    function _writeCheckpoint(address account, uint balance) internal {
        uint _timestamp = block.timestamp;
        uint _nCheckPoints = numCheckpoints[account];

        if (_nCheckPoints > 0 && checkpoints[account][_nCheckPoints - 1].timestamp == _timestamp) {
            checkpoints[account][_nCheckPoints - 1].balanceOf = balance;
        } else {
            checkpoints[account][_nCheckPoints] = Checkpoint(_timestamp, balance);
            numCheckpoints[account] = _nCheckPoints + 1;
        }
    }

    function _writeRewardPerTokenCheckpoint(address token, uint reward, uint timestamp) internal {
        uint _nCheckPoints = rewardPerTokenNumCheckpoints[token];

        if (_nCheckPoints > 0 && rewardPerTokenCheckpoints[token][_nCheckPoints - 1].timestamp == timestamp) {
            rewardPerTokenCheckpoints[token][_nCheckPoints - 1].rewardPerToken = reward;
        } else {
            rewardPerTokenCheckpoints[token][_nCheckPoints] = RewardPerTokenCheckpoint(timestamp, reward);
            rewardPerTokenNumCheckpoints[token] = _nCheckPoints + 1;
        }
    }

    function _writeSupplyCheckpoint() internal {
        uint _nCheckPoints = supplyNumCheckpoints;
        uint _timestamp = block.timestamp;

        if (_nCheckPoints > 0 && supplyCheckpoints[_nCheckPoints - 1].timestamp == _timestamp) {
            supplyCheckpoints[_nCheckPoints - 1].supply = derivedSupply;
        } else {
            supplyCheckpoints[_nCheckPoints] = SupplyCheckpoint(_timestamp, derivedSupply);
            supplyNumCheckpoints = _nCheckPoints + 1;
        }
    }

    function rewardsListLength() external view returns (uint) {
        return rewards.length;
    }

    // returns the last time the reward was modified or periodFinish if the reward has ended
    function lastTimeRewardApplicable(address token) public view returns (uint) {
        return Math.min(block.timestamp, periodFinish[token]);
    }

    function getReward(address account, address[] memory tokens) external lock {
        require(msg.sender == account || msg.sender == voter);
        _unlocked = 1;
        IVoter(voter).distribute(address(this));
        _unlocked = 2;

        for (uint i = 0; i < tokens.length; i++) {
            (rewardPerTokenStored[tokens[i]], lastUpdateTime[tokens[i]]) = _updateRewardPerToken(tokens[i], type(uint).max, true);

            uint _reward = earned(tokens[i], account);
            lastEarn[tokens[i]][account] = block.timestamp;
            userRewardPerTokenStored[tokens[i]][account] = rewardPerTokenStored[tokens[i]];
            if (_reward > 0) {
                if (tokens[i] == kus) {
                    try IOptionToken(oKus).mint(account, _reward){} catch {
                        _safeTransfer(tokens[i], account, _reward);
                    }
                } else {
                    _safeTransfer(tokens[i], account, _reward);
                }
            }

            emit ClaimRewards(msg.sender, tokens[i], _reward);
        }

        uint _derivedBalance = derivedBalances[account];
        derivedSupply -= _derivedBalance;
        _derivedBalance = derivedBalance(account);
        derivedBalances[account] = _derivedBalance;
        derivedSupply += _derivedBalance;

        _writeCheckpoint(account, derivedBalances[account]);
        _writeSupplyCheckpoint();
    }

    function rewardPerToken(address token) public view returns (uint) {
        if (derivedSupply == 0) {
            return rewardPerTokenStored[token];
        }
        return rewardPerTokenStored[token] + ((lastTimeRewardApplicable(token) - Math.min(lastUpdateTime[token], periodFinish[token])) * rewardRate[token] * PRECISION / derivedSupply);
    }

    function derivedBalance(address account) public view returns (uint) {
        return balanceOf[account];
    }

    function batchRewardPerToken(address token, uint maxRuns) external {
        (rewardPerTokenStored[token], lastUpdateTime[token])  = _batchRewardPerToken(token, maxRuns);
    }

    function _batchRewardPerToken(address token, uint maxRuns) internal returns (uint, uint) {
        uint _startTimestamp = lastUpdateTime[token];
        uint reward = rewardPerTokenStored[token];

        if (supplyNumCheckpoints == 0) {
            return (reward, _startTimestamp);
        }

        if (rewardRate[token] == 0) {
            return (reward, block.timestamp);
        }

        uint _startIndex = getPriorSupplyIndex(_startTimestamp);
        uint _endIndex = Math.min(supplyNumCheckpoints-1, maxRuns);

        for (uint i = _startIndex; i < _endIndex; i++) {
            SupplyCheckpoint memory sp0 = supplyCheckpoints[i];
            if (sp0.supply > 0) {
                SupplyCheckpoint memory sp1 = supplyCheckpoints[i+1];
                (uint _reward, uint _endTime) = _calcRewardPerToken(token, sp1.timestamp, sp0.timestamp, sp0.supply, _startTimestamp);
                reward += _reward;
                _writeRewardPerTokenCheckpoint(token, reward, _endTime);
                _startTimestamp = _endTime;
            }
        }

        return (reward, _startTimestamp);
    }

    function _calcRewardPerToken(address token, uint timestamp1, uint timestamp0, uint supply, uint startTimestamp) internal view returns (uint, uint) {
        uint endTime = Math.max(timestamp1, startTimestamp);
        return (((Math.min(endTime, periodFinish[token]) - Math.min(Math.max(timestamp0, startTimestamp), periodFinish[token])) * rewardRate[token] * PRECISION / supply), endTime);
    }

    /// @dev Update stored rewardPerToken values without the last one snapshot
    ///      If the contract will get "out of gas" error on users actions this will be helpful
    function batchUpdateRewardPerToken(address token, uint maxRuns) external {
      (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, maxRuns, false);
    }

    function _updateRewardForAllTokens() internal {
      uint length = rewards.length;
      for (uint i; i < length; i++) {
        address token = rewards[i];
        (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, type(uint).max, true);
      }
    }

    function _updateRewardPerToken(address token, uint maxRuns, bool actualLast) internal returns (uint, uint) {
        uint _startTimestamp = lastUpdateTime[token];
        uint reward = rewardPerTokenStored[token];

        if (supplyNumCheckpoints == 0) {
            return (reward, _startTimestamp);
        }

        if (rewardRate[token] == 0) {
            return (reward, block.timestamp);
        }

        uint _startIndex = getPriorSupplyIndex(_startTimestamp);
        uint _endIndex = Math.min(supplyNumCheckpoints - 1, maxRuns);

        if (_endIndex > 0) {
            for (uint i = _startIndex; i <= _endIndex - 1; i++) {
                SupplyCheckpoint memory sp0 = supplyCheckpoints[i];
                if (sp0.supply > 0) {
                    SupplyCheckpoint memory sp1 = supplyCheckpoints[i+1];
                    (uint _reward, uint _endTime) = _calcRewardPerToken(token, sp1.timestamp, sp0.timestamp, sp0.supply, _startTimestamp);
                    reward += _reward;
                    _writeRewardPerTokenCheckpoint(token, reward, _endTime);
                    _startTimestamp = _endTime;
                }
            }
        }

        // need to override the last value with actual numbers only on deposit/withdraw/claim/notify actions
        if (actualLast) {
            SupplyCheckpoint memory sp = supplyCheckpoints[_endIndex];
            if (sp.supply > 0) {
                (uint _reward,) = _calcRewardPerToken(token, lastTimeRewardApplicable(token), Math.max(sp.timestamp, _startTimestamp), sp.supply, _startTimestamp);
                reward += _reward;
                _writeRewardPerTokenCheckpoint(token, reward, block.timestamp);
                _startTimestamp = block.timestamp;
            }
        }

        return (reward, _startTimestamp);
    }

    // earned is an estimation, it won't be exact till the supply > rewardPerToken calculations have run
    function earned(address token, address account) public view returns (uint) {
        uint _startTimestamp = Math.max(lastEarn[token][account], rewardPerTokenCheckpoints[token][0].timestamp);
        if (numCheckpoints[account] == 0) {
            return 0;
        }

        uint _startIndex = getPriorBalanceIndex(account, _startTimestamp);
        uint _endIndex = numCheckpoints[account]-1;

        uint reward = 0;

        if (_endIndex > 0) {
            for (uint i = _startIndex; i <= _endIndex-1; i++) {
                Checkpoint memory cp0 = checkpoints[account][i];
                Checkpoint memory cp1 = checkpoints[account][i+1];
                (uint _rewardPerTokenStored0,) = getPriorRewardPerToken(token, cp0.timestamp);
                (uint _rewardPerTokenStored1,) = getPriorRewardPerToken(token, cp1.timestamp);
                reward += cp0.balanceOf * (_rewardPerTokenStored1 - _rewardPerTokenStored0) / PRECISION;
            }
        }

        Checkpoint memory cp = checkpoints[account][_endIndex];
        (uint _rewardPerTokenStored,) = getPriorRewardPerToken(token, cp.timestamp);
        reward += cp.balanceOf * (rewardPerToken(token) - Math.max(_rewardPerTokenStored, userRewardPerTokenStored[token][account])) / PRECISION;

        return reward;
    }

    function depositAll(uint tokenId) external {
        deposit(IERC20(stake).balanceOf(msg.sender), tokenId);
    }

    function deposit(uint amount, uint tokenId) public lock {
        require(amount > 0);
        _updateRewardForAllTokens();

        _safeTransferFrom(stake, msg.sender, address(this), amount);
        totalSupply += amount;
        balanceOf[msg.sender] += amount;

        if (tokenId > 0) {
            require(IVotingEscrow(_ve).ownerOf(tokenId) == msg.sender);
            if (tokenIds[msg.sender] == 0) {
                tokenIds[msg.sender] = tokenId;
                IVoter(voter).attachTokenToGauge(tokenId, msg.sender);
            }
            require(tokenIds[msg.sender] == tokenId);
        } else {
            tokenId = tokenIds[msg.sender];
        }

        uint _derivedBalance = derivedBalances[msg.sender];
        derivedSupply -= _derivedBalance;
        _derivedBalance = derivedBalance(msg.sender);
        derivedBalances[msg.sender] = _derivedBalance;
        derivedSupply += _derivedBalance;

        _writeCheckpoint(msg.sender, _derivedBalance);
        _writeSupplyCheckpoint();

        IVoter(voter).emitDeposit(tokenId, msg.sender, amount);
        emit Deposit(msg.sender, tokenId, amount);
    }

    function withdrawAll() external {
        withdraw(balanceOf[msg.sender]);
    }

    function withdraw(uint amount) public {
        uint tokenId = 0;
        if (amount == balanceOf[msg.sender]) {
            tokenId = tokenIds[msg.sender];
        }
        withdrawToken(amount, tokenId);
    }

    function withdrawToken(uint amount, uint tokenId) public lock {
        _updateRewardForAllTokens();

        totalSupply -= amount;
        balanceOf[msg.sender] -= amount;
        _safeTransfer(stake, msg.sender, amount);

        if (tokenId > 0) {
            require(tokenId == tokenIds[msg.sender]);
            tokenIds[msg.sender] = 0;
            IVoter(voter).detachTokenFromGauge(tokenId, msg.sender);
        } else {
            tokenId = tokenIds[msg.sender];
        }

        uint _derivedBalance = derivedBalances[msg.sender];
        derivedSupply -= _derivedBalance;
        _derivedBalance = derivedBalance(msg.sender);
        derivedBalances[msg.sender] = _derivedBalance;
        derivedSupply += _derivedBalance;

        _writeCheckpoint(msg.sender, derivedBalances[msg.sender]);
        _writeSupplyCheckpoint();

        IVoter(voter).emitWithdraw(tokenId, msg.sender, amount);
        emit Withdraw(msg.sender, tokenId, amount);
    }

    function left(address token) external view returns (uint) {
        if (block.timestamp >= periodFinish[token]) return 0;
        uint _remaining = periodFinish[token] - block.timestamp;
        return _remaining * rewardRate[token];
    }

    function notifyRewardAmount(address token, uint amount) external lock {
        require(token != stake);
        require(amount > 0);
        if (!isReward[token]) {
            require(IVoter(voter).isWhitelisted(token), "rewards tokens must be whitelisted");
            require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");
        }
        if (rewardRate[token] == 0) _writeRewardPerTokenCheckpoint(token, 0, block.timestamp);
        (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, type(uint).max, true);

        if (block.timestamp >= periodFinish[token]) {
            uint256 balanceBefore = IERC20(token).balanceOf(address(this));
            _safeTransferFrom(token, msg.sender, address(this), amount);
            uint256 balanceAfter = IERC20(token).balanceOf(address(this));
            amount = balanceAfter - balanceBefore;
            rewardRate[token] = amount / DURATION;
        } else {
            uint _remaining = periodFinish[token] - block.timestamp;
            uint _left = _remaining * rewardRate[token];
            require(amount > _left);
            uint256 balanceBefore = IERC20(token).balanceOf(address(this));
            _safeTransferFrom(token, msg.sender, address(this), amount);
            uint256 balanceAfter = IERC20(token).balanceOf(address(this));
            amount = balanceAfter - balanceBefore;
            rewardRate[token] = (amount + _left) / DURATION;
        }
        require(rewardRate[token] > 0);
        uint balance = IERC20(token).balanceOf(address(this));
        require(rewardRate[token] <= balance / DURATION, "Provided reward too high");
        periodFinish[token] = block.timestamp + DURATION;
        if (!isReward[token]) {
            isReward[token] = true;
            rewards.push(token);
        }

        emit NotifyReward(msg.sender, token, amount);
    }

    function swapOutRewardToken(uint i, address oldToken, address newToken) external {
        require(msg.sender == IVotingEscrow(_ve).team(), 'only team');
        require(rewards[i] == oldToken);
        isReward[oldToken] = false;
        isReward[newToken] = true;
        rewards[i] = newToken;
    }

    function setOKus(address _oKus) external {
        require(msg.sender == gaugeFactory, "not gauge factory");
        oKus = _oKus;
        _safeApprove(kus, _oKus, type(uint256).max);
        emit OKusSet(_oKus);
    }

    function _safeTransfer(address token, address to, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) =
        token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }

    function _safeTransferFrom(address token, address from, address to, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) =
        token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }

    function _safeApprove(address token, address spender, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) =
        token.call(abi.encodeWithSelector(IERC20.approve.selector, spender, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }
}

// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

contract GaugeFactory is IGaugeFactory, Ownable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER");

    address public last_gauge;
    address public oKus;

    event OKusSet(address indexed _oKus);
    event OKusUpdatedFor(address indexed _gauge);

    function createGauge(address _pool, address _external_bribe, address _ve, bool isPair, address[] memory allowedRewards) external returns (address) {
        last_gauge = address(new Gauge(_pool, _external_bribe, _ve, msg.sender, oKus, address(this), isPair, allowedRewards));
        if (oKus != address(0)) {
            IAccessControl(oKus).grantRole(MINTER_ROLE, last_gauge);
        }
        return last_gauge;
    }

    function setOKus(address _oKus) external onlyOwner {
        oKus = _oKus;
        emit OKusSet(_oKus);
    }

    function updateOKusFor(address _gauge) external onlyOwner {
        Gauge(_gauge).setOKus(oKus);
        emit OKusUpdatedFor(_gauge);
    }
}
        

Contract ABI

[{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"MINTER_ROLE","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"createGauge","inputs":[{"type":"address","name":"_pool","internalType":"address"},{"type":"address","name":"_external_bribe","internalType":"address"},{"type":"address","name":"_ve","internalType":"address"},{"type":"bool","name":"isPair","internalType":"bool"},{"type":"address[]","name":"allowedRewards","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"last_gauge","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"oKus","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setOKus","inputs":[{"type":"address","name":"_oKus","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateOKusFor","inputs":[{"type":"address","name":"_gauge","internalType":"address"}]},{"type":"event","name":"OKusSet","inputs":[{"type":"address","name":"_oKus","indexed":true}],"anonymous":false},{"type":"event","name":"OKusUpdatedFor","inputs":[{"type":"address","name":"_gauge","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","indexed":true},{"type":"address","name":"newOwner","indexed":true}],"anonymous":false}]
              

Contract Creation Code

Verify & Publish
0x608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61406e8061007e6000396000f3fe60806040523480156200001157600080fd5b50600436106200009f5760003560e01c80638da5cb5b116200006e5780638da5cb5b146200010c578063c44d623f146200011e578063d53913931462000135578063f2fde38b146200016c578063ff701934146200018357600080fd5b80631ede5a8e14620000a4578063715018a614620000d8578063730a8bdb14620000e4578063829a91e414620000f8575b600080fd5b620000bb620000b53660046200053c565b6200019a565b6040516001600160a01b0390911681526020015b60405180910390f35b620000e2620002be565b005b600154620000bb906001600160a01b031681565b600254620000bb906001600160a01b031681565b6000546001600160a01b0316620000bb565b620000e26200012f36600462000662565b620002d6565b6200015d7ff0887ba65ee2024ea881d91b74c2450ef19e1557f03bed3ea9f16b037cbe2dc981565b604051908152602001620000cf565b620000e26200017d36600462000662565b6200032a565b620000e26200019436600462000662565b620003ad565b600085858533600260009054906101000a90046001600160a01b0316308888604051620001c790620004fb565b620001da98979695949392919062000687565b604051809103906000f080158015620001f7573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039283161790556002541615620002a857600254600154604051632f2ff15d60e01b81527ff0887ba65ee2024ea881d91b74c2450ef19e1557f03bed3ea9f16b037cbe2dc960048201526001600160a01b039182166024820152911690632f2ff15d90604401600060405180830381600087803b1580156200028e57600080fd5b505af1158015620002a3573d6000803e3d6000fd5b505050505b506001546001600160a01b031695945050505050565b620002c86200044f565b620002d46000620004ab565b565b620002e06200044f565b600280546001600160a01b0319166001600160a01b0383169081179091556040517fb09a31c5640855b04448d4fc8825b1c52b6bfbf64d152e99317895439f30dce590600090a250565b620003346200044f565b6001600160a01b0381166200039f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b620003aa81620004ab565b50565b620003b76200044f565b60025460405163c44d623f60e01b81526001600160a01b0391821660048201529082169063c44d623f90602401600060405180830381600087803b158015620003ff57600080fd5b505af115801562000414573d6000803e3d6000fd5b50506040516001600160a01b03841692507fc48f89708ef0aece7c297583609062024cac183470610c0be14dbf67bfc127f89150600090a250565b6000546001600160a01b03163314620002d45760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000396565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61391b806200071e83390190565b80356001600160a01b03811681146200052157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a086880312156200055557600080fd5b620005608662000509565b945060206200057181880162000509565b9450620005816040880162000509565b9350606087013580151581146200059757600080fd5b9250608087013567ffffffffffffffff80821115620005b557600080fd5b818901915089601f830112620005ca57600080fd5b813581811115620005df57620005df62000526565b8060051b604051601f19603f8301168101818110858211171562000607576200060762000526565b60405291825284820192508381018501918c8311156200062657600080fd5b938501935b828510156200064f576200063f8562000509565b845293850193928501926200062b565b8096505050505050509295509295909350565b6000602082840312156200067557600080fd5b620006808262000509565b9392505050565b6001600160a01b03898116825288811660208084019190915288821660408401528782166060840152868216608084015285821660a084015284151560c084015261010060e08401819052845190840181905260009261012085019286810192855b8181101562000709578451841686529482019493820193600101620006e9565b50939e9d505050505050505050505050505056fe61014060405260016017553480156200001757600080fd5b506040516200391b3803806200391b8339810160408190526200003a916200035c565b6001600160a01b0388811660805287811660c05286811660a081905286821660e052600080546001600160a01b031916878416179055908416610120526003805460ff191684151517905560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa158015620000c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e79190620004b4565b6001600160a01b039081166101008190526000546200010a92166000196200021f565b60005b8151811015620002105760006001600160a01b0316828281518110620001375762000137620004d9565b60200260200101516001600160a01b031614620001fb576001600e6000848481518110620001695762000169620004d9565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600d828281518110620001bf57620001bf620004d9565b60209081029190910181015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b806200020781620004ef565b9150506200010d565b50505050505050505062000573565b6000836001600160a01b03163b116200023757600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b179052915160009283929087169162000295919062000517565b6000604051808303816000865af19150503d8060008114620002d4576040519150601f19603f3d011682016040523d82523d6000602084013e620002d9565b606091505b5091509150818015620003075750805115806200030757508080602001905181019062000307919062000555565b6200031157600080fd5b5050505050565b80516001600160a01b03811681146200033057600080fd5b919050565b805180151581146200033057600080fd5b634e487b7160e01b600052604160045260246000fd5b600080600080600080600080610100898b0312156200037a57600080fd5b620003858962000318565b9750602062000396818b0162000318565b9750620003a660408b0162000318565b9650620003b660608b0162000318565b9550620003c660808b0162000318565b9450620003d660a08b0162000318565b9350620003e660c08b0162000335565b60e08b01519093506001600160401b03808211156200040457600080fd5b818c0191508c601f8301126200041957600080fd5b8151818111156200042e576200042e62000346565b8060051b604051601f19603f8301168101818110858211171562000456576200045662000346565b60405291825284820192508381018501918f8311156200047557600080fd5b938501935b828510156200049e576200048e8562000318565b845293850193928501926200047a565b8096505050505050509295985092959890939650565b600060208284031215620004c757600080fd5b620004d28262000318565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200051057634e487b7160e01b600052601160045260246000fd5b5060010190565b6000825160005b818110156200053a57602081860181015185830152016200051e565b818111156200054a576000828501525b509190910192915050565b6000602082840312156200056857600080fd5b620004d28262000335565b60805160a05160c05160e05161010051610120516132e862000633600039600081816103820152611b6101526000818161071601528181610ee30152611be20152600081816104a001528181610c5601528181610c9b0152818161154501528181611e5501528181611f7a0152818161240a0152612522015260006103110152600081816105ca015281816112ad0152611d8901526000818161044e015281816114cb01528181611c5a01528181611d01015261238b01526132e86000f3fe608060405234801561001057600080fd5b50600436106102bb5760003560e01c8063853828b611610182578063d7da4bb0116100e9578063e8111a12116100a2578063f7412baf1161007c578063f7412baf1461078f578063fc97a303146107b6578063fd314098146107d6578063fdb483c7146107e957600080fd5b8063e8111a1214610760578063f122977714610769578063f301af421461077c57600080fd5b8063d7da4bb0146106e8578063da09d19d146106f1578063dfb05cb014610711578063e2bbb15814610738578063e57482131461074b578063e68863961461075857600080fd5b8063a495e5b51161013b578063a495e5b51461063b578063aa47965214610666578063b66503cf14610686578063c44d623f14610699578063c6f678bd146106ac578063d35e2544146106bf57600080fd5b8063853828b6146105bd5780638dd598fb146105c557806393f1c442146105ec5780639418f939146105f557806399bcc052146106085780639ce43f901461061b57600080fd5b80633ca068b61161022657806363fb415b116101df57806363fb415b1461052457806368fcee1a146105445780636fcfff451461055757806370a082311461057757806376f4be3614610597578063829a91e4146105aa57600080fd5b80633ca068b61461047057806346c96aac1461049b5780634c02a21c146104c25780634d5ce038146104cb5780635a45d052146104fe578063638634ee1461051157600080fd5b8063211dc32d11610278578063211dc32d146103ce578063221ca18c146103e15780632ce9aead146104015780632e1a7d4d1461042157806331279d3d146104365780633a4b66f11461044957600080fd5b806301316ddf146102c057806303fbf83a1461030c5780630cdfebfa1461034b5780630d52333c1461037d578063115c6f39146103a457806318160ddd146103c5575b600080fd5b6102f26102ce366004612f7b565b60136020908152600092835260408084209091529082529020805460019091015482565b604080519283526020830191909152015b60405180910390f35b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610303565b6102f2610359366004612f7b565b600f6020908152600092835260408084209091529082529020805460019091015482565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b76103b2366004612f7b565b6107fc565b604051908152602001610303565b6103b7600b5481565b6103b76103dc366004612fa7565b61096b565b6103b76103ef366004612fe0565b60046020526000908152604090205481565b6103b761040f366004612fe0565b60066020526000908152604090205481565b61043461042f366004612ffd565b610bef565b005b61043461044436600461302c565b610c25565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b761047e366004612fa7565b600960209081526000928352604080842090915290825290205481565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b760165481565b6104ee6104d9366004612fe0565b600e6020526000908152604090205460ff1681565b6040519015158152602001610303565b61043461050c366004612f7b565b6110f4565b6103b761051f366004612fe0565b61112c565b6103b7610532366004612fe0565b60026020526000908152604090205481565b610434610552366004612f7b565b611150565b6103b7610565366004612fe0565b60106020526000908152604090205481565b6103b7610585366004612fe0565b600c6020526000908152604090205481565b6103b76105a5366004612ffd565b61115c565b600054610333906001600160a01b031681565b610434611290565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b760155481565b610434610603366004613106565b6112ab565b6103b7610616366004612fe0565b61143c565b6103b7610629366004612fe0565b60076020526000908152604090205481565b6103b7610649366004612fa7565b600860209081526000928352604080842090915290825290205481565b6103b7610674366004612fe0565b60146020526000908152604090205481565b610434610694366004612f7b565b6114ad565b6104346106a7366004612fe0565b611b56565b6104346106ba366004612ffd565b611c41565b6103b76106cd366004612fe0565b6001600160a01b03166000908152600c602052604090205490565b6103b760015481565b6103b76106ff366004612fe0565b60056020526000908152604090205481565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b610434610746366004613148565b611cd6565b6003546104ee9060ff1681565b600d546103b7565b6103b760125481565b6103b7610777366004612fe0565b612024565b61033361078a366004612ffd565b6120e3565b6102f261079d366004612ffd565b6011602052600090815260409020805460019091015482565b6103b76107c4366004612fe0565b600a6020526000908152604090205481565b6102f26107e4366004612f7b565b61210d565b6104346107f7366004613148565b61232e565b6001600160a01b038216600090815260106020526040812054808203610826576000915050610965565b6001600160a01b0384166000908152600f60205260408120849161084b600185613180565b815260200190815260200160002060000154116108755761086d600182613180565b915050610965565b6001600160a01b0384166000908152600f602090815260408083208380529091529020548310156108aa576000915050610965565b6000806108b8600184613180565b90505b8181111561096057600060026108d18484613180565b6108db9190613197565b6108e59083613180565b6001600160a01b0388166000908152600f6020908152604080832084845282529182902082518084019093528054808452600190910154918301919091529192509087900361093a5750935061096592505050565b805187111561094b57819350610959565b610956600183613180565b92505b50506108bb565b509150505b92915050565b6001600160a01b0380831660008181526008602090815260408083209486168352938152838220549282526013815283822082805290529182205482916109b1916125be565b6001600160a01b038416600090815260106020526040812054919250036109dc576000915050610965565b60006109e884836107fc565b6001600160a01b03851660009081526010602052604081205491925090610a1190600190613180565b905060008115610b2357825b610a28600184613180565b8111610b21576001600160a01b0387166000818152600f602081815260408084208685528083528185208251808401909352805483526001908101548385015295855292909152928290610a7d9086906131b9565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000610abc8b846000015161210d565b5090506000610acf8c846000015161210d565b509050670de0b6b3a7640000610ae58383613180565b8560200151610af491906131d1565b610afe9190613197565b610b0890876131b9565b9550505050508080610b19906131f0565b915050610a1d565b505b6001600160a01b0386166000908152600f60209081526040808320858452825280832081518083019092528054808352600190910154928201929092529190610b6d908a9061210d565b506001600160a01b03808b166000908152600960209081526040808320938d1683529290522054909150670de0b6b3a764000090610bac9083906125be565b610bb58b612024565b610bbf9190613180565b8360200151610bce91906131d1565b610bd89190613197565b610be290846131b9565b9998505050505050505050565b336000908152600c60205260408120548203610c175750336000908152600a60205260409020545b610c21828261232e565b5050565b601754600114610c3457600080fd5b6002601755336001600160a01b0383161480610c785750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b610c8157600080fd5b60016017556040516363453ae160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906363453ae190602401600060405180830381600087803b158015610ce757600080fd5b505af1158015610cfb573d6000803e3d6000fd5b5050600260175550600090505b815181101561104d57610d38828281518110610d2657610d26613209565b602002602001015160001960016125d4565b60076000858581518110610d4e57610d4e613209565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600060066000878781518110610d8e57610d8e613209565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008491905055839190505550506000610de9838381518110610ddb57610ddb613209565b60200260200101518561096b565b90504260086000858581518110610e0257610e02613209565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000866001600160a01b03166001600160a01b031681526020019081526020016000208190555060076000848481518110610e6757610e67613209565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205460096000858581518110610ea657610ea6613209565b6020908102919091018101516001600160a01b039081168352828201939093526040918201600090812093891681529290529020558015610fd4577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316838381518110610f1d57610f1d613209565b60200260200101516001600160a01b031603610fbf576000546040516340c10f1960e01b81526001600160a01b03868116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b158015610f8157600080fd5b505af1925050508015610f92575060015b610fba57610fba838381518110610fab57610fab613209565b602002602001015185836127c4565b610fd4565b610fd4838381518110610fab57610fab613209565b828281518110610fe657610fe6613209565b60200260200101516001600160a01b0316336001600160a01b03167f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc98360405161103291815260200190565b60405180910390a35080611045816131f0565b915050610d08565b506001600160a01b038216600090815260026020526040812054600180549192839261107a908490613180565b9091555050506001600160a01b0382166000908152600c60209081526040808320546002909252822081905560018054919283926110b99084906131b9565b90915550506001600160a01b0383166000908152600260205260409020546110e29084906128c4565b6110ea6129cf565b5050600160175550565b6110fe8282612a74565b6001600160a01b03909316600090815260076020908152604080832060069092529091209390935590915550565b6001600160a01b038116600090815260056020526040812054610965904290612bd7565b6110fe828260006125d4565b6012546000908082036111725750600092915050565b8260116000611182600185613180565b815260200190815260200160002060000154116111ab576111a4600182613180565b9392505050565b6000805260116020527f4ad3b33220dddc71b994a52d72c06b10862965f7d926534c05c00fb7e819e7b7548310156111e65750600092915050565b6000806111f4600184613180565b90505b81811115611288576000600261120d8484613180565b6112179190613197565b6112219083613180565b600081815260116020908152604091829020825180840190935280548084526001909101549183019190915291925090879003611262575095945050505050565b805187111561127357819350611281565b61127e600183613180565b92505b50506111f7565b509392505050565b336000908152600c60205260409020546112a990610bef565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166385f2aef26040518163ffffffff1660e01b81526004016020604051808303816000875af115801561130b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132f919061321f565b6001600160a01b0316336001600160a01b0316146113805760405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b60448201526064015b60405180910390fd5b816001600160a01b0316600d848154811061139d5761139d613209565b6000918252602090912001546001600160a01b0316146113bc57600080fd5b6001600160a01b038083166000908152600e6020526040808220805460ff199081169091559284168252902080549091166001179055600d80548291908590811061140957611409613209565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505050565b6001600160a01b038116600090815260056020526040812054421061146357506000919050565b6001600160a01b038216600090815260056020526040812054611487904290613180565b6001600160a01b0384166000908152600460205260409020549091506111a490826131d1565b6017546001146114bc57600080fd5b60026017556001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116908316036114f957600080fd5b6000811161150657600080fd5b6001600160a01b0382166000908152600e602052604090205460ff1661165957604051633af32abf60e01b81526001600160a01b0383811660048301527f00000000000000000000000000000000000000000000000000000000000000001690633af32abf90602401602060405180830381865afa15801561158c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b0919061323c565b6116075760405162461bcd60e51b815260206004820152602260248201527f7265776172647320746f6b656e73206d7573742062652077686974656c697374604482015261195960f21b6064820152608401611377565b600d546004116116595760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e730000000000000000006044820152606401611377565b6001600160a01b03821660009081526004602052604081205490036116845761168482600042612be6565b6116928260001960016125d4565b6001600160a01b03841660009081526007602090815260408083206006835281842094909455939092556005909152205442106117e9576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611710573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611734919061325e565b905061174283333085612cd5565b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015611789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ad919061325e565b90506117b98282613180565b92506117c862093a8084613197565b6001600160a01b0385166000908152600460205260409020555061196a9050565b6001600160a01b03821660009081526005602052604081205461180d904290613180565b6001600160a01b0384166000908152600460205260408120549192509061183490836131d1565b905080831161184257600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ad919061325e565b90506118bb85333087612cd5565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015611902573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611926919061325e565b90506119328282613180565b945062093a8061194284876131b9565b61194c9190613197565b6001600160a01b038716600090815260046020526040902055505050505b6001600160a01b03821660009081526004602052604090205461198c57600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156119d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119f7919061325e565b9050611a0662093a8082613197565b6001600160a01b0384166000908152600460205260409020541115611a6d5760405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606401611377565b611a7a62093a80426131b9565b6001600160a01b038416600090815260056020908152604080832093909355600e9052205460ff16611b0c576001600160a01b0383166000818152600e60205260408120805460ff19166001908117909155600d805491820181559091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b03191690911790555b6040518281526001600160a01b0384169033907ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf508269060200160405180910390a35050600160175550565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611bc25760405162461bcd60e51b81526020600482015260116024820152706e6f7420676175676520666163746f727960781b6044820152606401611377565b600080546001600160a01b0319166001600160a01b038316179055611c0a7f000000000000000000000000000000000000000000000000000000000000000082600019612dcd565b6040516001600160a01b038216907fb09a31c5640855b04448d4fc8825b1c52b6bfbf64d152e99317895439f30dce590600090a250565b6040516370a0823160e01b8152336004820152611cd3907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ccd919061325e565b82611cd6565b50565b601754600114611ce557600080fd5b600260175581611cf457600080fd5b611cfc612e17565b611d287f0000000000000000000000000000000000000000000000000000000000000000333085612cd5565b81600b6000828254611d3a91906131b9565b9091555050336000908152600c602052604081208054849290611d5e9084906131b9565b90915550508015611ed2576040516331a9108f60e11b81526004810182905233906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa158015611dd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df4919061321f565b6001600160a01b031614611e0757600080fd5b336000908152600a60205260408120549003611eb257336000818152600a6020526040908190208390555163698473e360e01b81526004810183905260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063698473e390604401600060405180830381600087803b158015611e9957600080fd5b505af1158015611ead573d6000803e3d6000fd5b505050505b336000908152600a60205260409020548114611ecd57600080fd5b611ee4565b50336000908152600a60205260409020545b336000908152600260205260408120546001805491928392611f07908490613180565b9091555050336000908152600c6020908152604080832054600290925282208190556001805491935083929091611f3f9084906131b9565b90915550611f4f905033826128c4565b611f576129cf565b60405163530e389d60e11b815260048101839052336024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a61c713a90606401600060405180830381600087803b158015611fc657600080fd5b505af1158015611fda573d6000803e3d6000fd5b505060408051858152602081018790523393507f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159250015b60405180910390a25050600160175550565b600060015460000361204c57506001600160a01b031660009081526007602052604090205490565b6001546001600160a01b03831660009081526004602090815260408083205460068352818420546005909352922054670de0b6b3a7640000929161208f91612bd7565b6120988661112c565b6120a29190613180565b6120ac91906131d1565b6120b691906131d1565b6120c09190613197565b6001600160a01b03831660009081526007602052604090205461096591906131b9565b600d81815481106120f357600080fd5b6000918252602090912001546001600160a01b0316905081565b6001600160a01b038216600090815260146020526040812054819080820361213c576000809250925050612327565b6001600160a01b03851660009081526013602052604081208591612161600185613180565b815260200190815260200160002060000154116121fe576001600160a01b03851660009081526013602052604081209061219c600184613180565b81526020019081526020016000206001015460136000876001600160a01b03166001600160a01b0316815260200190815260200160002060006001846121e29190613180565b8152602001908152602001600020600001549250925050612327565b6001600160a01b0385166000908152601360209081526040808320838052909152902054841015612236576000809250925050612327565b600080612244600184613180565b90505b818111156122f6576000600261225d8484613180565b6122679190613197565b6122719083613180565b6001600160a01b03891660009081526013602090815260408083208484528252918290208251808401909352805480845260019091015491830191909152919250908890036122d0576020810151905190965094506123279350505050565b80518811156122e1578193506122ef565b6122ec600183613180565b92505b5050612247565b506001600160a01b038616600090815260136020908152604080832093835292905220600181015490549093509150505b9250929050565b60175460011461233d57600080fd5b600260175561234a612e17565b81600b600082825461235c9190613180565b9091555050336000908152600c602052604081208054849290612380908490613180565b909155506123b190507f000000000000000000000000000000000000000000000000000000000000000033846127c4565b801561246b57336000908152600a602052604090205481146123d257600080fd5b336000818152600a6020526040808220919091555163411b1f7760e01b81526004810183905260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063411b1f7790604401600060405180830381600087803b15801561244e57600080fd5b505af1158015612462573d6000803e3d6000fd5b5050505061247d565b50336000908152600a60205260409020545b3360009081526002602052604081205460018054919283926124a0908490613180565b9091555050336000908152600c60209081526040808320546002909252822081905560018054919350839290916124d89084906131b9565b9091555050336000818152600260205260409020546124f791906128c4565b6124ff6129cf565b604051633aa53b9160e21b815260048101839052336024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ea94ee4490606401600060405180830381600087803b15801561256e57600080fd5b505af1158015612582573d6000803e3d6000fd5b505060408051858152602081018790523393507ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568925001612012565b60008183116125cd57816111a4565b5090919050565b6001600160a01b038316600090815260066020908152604080832054600790925282205460125483929190830361260e57925090506127bc565b6001600160a01b03871660009081526004602052604081205490036126395792504291506127bc9050565b60006126448361115c565b90506000612660600160125461265a9190613180565b89612bd7565b9050801561273957815b612675600183613180565b81116127375760008181526011602090815260409182902082518084019093528054835260010154908201819052156127245760006011816126b88560016131b9565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000806127038e8460000151866000015187602001518d612e9b565b909250905061271282896131b9565b975061271f8e8983612be6565b975050505b508061272f816131f0565b91505061266a565b505b86156127b35760008181526011602090815260409182902082518084019093528054835260010154908201819052156127b15760006127928b61277b8d61112c565b8451612787908a6125be565b85602001518a612e9b565b50905061279f81866131b9565b94506127ac8b8642612be6565b429550505b505b50909350909150505b935093915050565b6000836001600160a01b03163b116127db57600080fd5b6040516001600160a01b03838116602483015260448201839052600091829186169063a9059cbb60e01b906064015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516128489190613277565b6000604051808303816000865af19150503d8060008114612885576040519150601f19603f3d011682016040523d82523d6000602084013e61288a565b606091505b50915091508180156128b45750805115806128b45750808060200190518101906128b4919061323c565b6128bd57600080fd5b5050505050565b6001600160a01b0382166000908152601060205260409020544290801580159061292257506001600160a01b0384166000908152600f60205260408120839161290e600185613180565b815260200190815260200160002060000154145b15612965576001600160a01b0384166000908152600f60205260408120849161294c600185613180565b81526020810191909152604001600020600101556129c9565b60408051808201825283815260208082018681526001600160a01b0388166000908152600f83528481208682529092529290209051815590516001918201556129af9082906131b9565b6001600160a01b0385166000908152601060205260409020555b50505050565b601254428115801590612a01575080601160006129ed600186613180565b815260200190815260200160002060000154145b15612a325760015460116000600185612a1a9190613180565b81526020810191909152604001600020600101555050565b604080518082018252828152600180546020808401918252600087815260119091529390932091518255915190820155612a6d9083906131b9565b6012555050565b6001600160a01b0382166000908152600660209081526040808320546007909252822054601254839291908303612aae5792509050612327565b6001600160a01b0386166000908152600460205260408120549003612ad95792504291506123279050565b6000612ae48361115c565b90506000612b006001601254612afa9190613180565b88612bd7565b9050815b81811015612bc8576000818152601160209081526040918290208251808401909352805483526001015490820181905215612bb5576000601181612b498560016131b9565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050600080612b948d8460000151866000015187602001518d612e9b565b9092509050612ba382896131b9565b9750612bb08d8983612be6565b975050505b5080612bc0816131f0565b915050612b04565b50919792965091945050505050565b60008183106125cd57816111a4565b6001600160a01b0383166000908152601460205260409020548015801590612c4257506001600160a01b03841660009081526013602052604081208391612c2e600185613180565b815260200190815260200160002060000154145b15612c6c576001600160a01b0384166000908152601360205260408120849161294c600185613180565b60408051808201825283815260208082018681526001600160a01b038816600090815260138352848120868252909252929020905181559051600191820155612cb69082906131b9565b6001600160a01b03851660009081526014602052604090205550505050565b6000846001600160a01b03163b11612cec57600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691612d509190613277565b6000604051808303816000865af19150503d8060008114612d8d576040519150601f19603f3d011682016040523d82523d6000602084013e612d92565b606091505b5091509150818015612dbc575080511580612dbc575080806020019051810190612dbc919061323c565b612dc557600080fd5b505050505050565b6000836001600160a01b03163b11612de457600080fd5b6040516001600160a01b03838116602483015260448201839052600091829186169063095ea7b360e01b9060640161280a565b600d5460005b81811015610c21576000600d8281548110612e3a57612e3a613209565b6000918252602090912001546001600160a01b03169050612e5f8160001960016125d4565b6001600160a01b039092166000908152600760209081526040808320600690925290912092909255905580612e93816131f0565b915050612e1d565b6000806000612eaa87856125be565b6001600160a01b0389166000908152600460205260409020549091508590670de0b6b3a764000090612efd612edf8a896125be565b6001600160a01b038d16600090815260056020526040902054612bd7565b6001600160a01b038c16600090815260056020526040902054612f21908690612bd7565b612f2b9190613180565b612f3591906131d1565b612f3f91906131d1565b612f499190613197565b9890975095505050505050565b6001600160a01b0381168114611cd357600080fd5b8035612f7681612f56565b919050565b60008060408385031215612f8e57600080fd5b8235612f9981612f56565b946020939093013593505050565b60008060408385031215612fba57600080fd5b8235612fc581612f56565b91506020830135612fd581612f56565b809150509250929050565b600060208284031215612ff257600080fd5b81356111a481612f56565b60006020828403121561300f57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561303f57600080fd5b823561304a81612f56565b915060208381013567ffffffffffffffff8082111561306857600080fd5b818601915086601f83011261307c57600080fd5b81358181111561308e5761308e613016565b8060051b604051601f19603f830116810181811085821117156130b3576130b3613016565b6040529182528482019250838101850191898311156130d157600080fd5b938501935b828510156130f6576130e785612f6b565b845293850193928501926130d6565b8096505050505050509250929050565b60008060006060848603121561311b57600080fd5b83359250602084013561312d81612f56565b9150604084013561313d81612f56565b809150509250925092565b6000806040838503121561315b57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6000828210156131925761319261316a565b500390565b6000826131b457634e487b7160e01b600052601260045260246000fd5b500490565b600082198211156131cc576131cc61316a565b500190565b60008160001904831182151516156131eb576131eb61316a565b500290565b6000600182016132025761320261316a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561323157600080fd5b81516111a481612f56565b60006020828403121561324e57600080fd5b815180151581146111a457600080fd5b60006020828403121561327057600080fd5b5051919050565b6000825160005b81811015613298576020818601810151858301520161327e565b818111156132a7576000828501525b50919091019291505056fea2646970667358221220e88dcc20dacdf0ee28ff5f9b17658539c6e0234e11e20f9df0bc372b541fc82064736f6c634300080d0033a2646970667358221220ed9dd6aeb254c9e9fbe08092bddd834cd0c4488c3d505417234414952cd73eec64736f6c634300080d0033

Deployed ByteCode

0x60806040523480156200001157600080fd5b50600436106200009f5760003560e01c80638da5cb5b116200006e5780638da5cb5b146200010c578063c44d623f146200011e578063d53913931462000135578063f2fde38b146200016c578063ff701934146200018357600080fd5b80631ede5a8e14620000a4578063715018a614620000d8578063730a8bdb14620000e4578063829a91e414620000f8575b600080fd5b620000bb620000b53660046200053c565b6200019a565b6040516001600160a01b0390911681526020015b60405180910390f35b620000e2620002be565b005b600154620000bb906001600160a01b031681565b600254620000bb906001600160a01b031681565b6000546001600160a01b0316620000bb565b620000e26200012f36600462000662565b620002d6565b6200015d7ff0887ba65ee2024ea881d91b74c2450ef19e1557f03bed3ea9f16b037cbe2dc981565b604051908152602001620000cf565b620000e26200017d36600462000662565b6200032a565b620000e26200019436600462000662565b620003ad565b600085858533600260009054906101000a90046001600160a01b0316308888604051620001c790620004fb565b620001da98979695949392919062000687565b604051809103906000f080158015620001f7573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039283161790556002541615620002a857600254600154604051632f2ff15d60e01b81527ff0887ba65ee2024ea881d91b74c2450ef19e1557f03bed3ea9f16b037cbe2dc960048201526001600160a01b039182166024820152911690632f2ff15d90604401600060405180830381600087803b1580156200028e57600080fd5b505af1158015620002a3573d6000803e3d6000fd5b505050505b506001546001600160a01b031695945050505050565b620002c86200044f565b620002d46000620004ab565b565b620002e06200044f565b600280546001600160a01b0319166001600160a01b0383169081179091556040517fb09a31c5640855b04448d4fc8825b1c52b6bfbf64d152e99317895439f30dce590600090a250565b620003346200044f565b6001600160a01b0381166200039f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b620003aa81620004ab565b50565b620003b76200044f565b60025460405163c44d623f60e01b81526001600160a01b0391821660048201529082169063c44d623f90602401600060405180830381600087803b158015620003ff57600080fd5b505af115801562000414573d6000803e3d6000fd5b50506040516001600160a01b03841692507fc48f89708ef0aece7c297583609062024cac183470610c0be14dbf67bfc127f89150600090a250565b6000546001600160a01b03163314620002d45760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000396565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61391b806200071e83390190565b80356001600160a01b03811681146200052157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a086880312156200055557600080fd5b620005608662000509565b945060206200057181880162000509565b9450620005816040880162000509565b9350606087013580151581146200059757600080fd5b9250608087013567ffffffffffffffff80821115620005b557600080fd5b818901915089601f830112620005ca57600080fd5b813581811115620005df57620005df62000526565b8060051b604051601f19603f8301168101818110858211171562000607576200060762000526565b60405291825284820192508381018501918c8311156200062657600080fd5b938501935b828510156200064f576200063f8562000509565b845293850193928501926200062b565b8096505050505050509295509295909350565b6000602082840312156200067557600080fd5b620006808262000509565b9392505050565b6001600160a01b03898116825288811660208084019190915288821660408401528782166060840152868216608084015285821660a084015284151560c084015261010060e08401819052845190840181905260009261012085019286810192855b8181101562000709578451841686529482019493820193600101620006e9565b50939e9d505050505050505050505050505056fe61014060405260016017553480156200001757600080fd5b506040516200391b3803806200391b8339810160408190526200003a916200035c565b6001600160a01b0388811660805287811660c05286811660a081905286821660e052600080546001600160a01b031916878416179055908416610120526003805460ff191684151517905560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa158015620000c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e79190620004b4565b6001600160a01b039081166101008190526000546200010a92166000196200021f565b60005b8151811015620002105760006001600160a01b0316828281518110620001375762000137620004d9565b60200260200101516001600160a01b031614620001fb576001600e6000848481518110620001695762000169620004d9565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600d828281518110620001bf57620001bf620004d9565b60209081029190910181015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b806200020781620004ef565b9150506200010d565b50505050505050505062000573565b6000836001600160a01b03163b116200023757600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b179052915160009283929087169162000295919062000517565b6000604051808303816000865af19150503d8060008114620002d4576040519150601f19603f3d011682016040523d82523d6000602084013e620002d9565b606091505b5091509150818015620003075750805115806200030757508080602001905181019062000307919062000555565b6200031157600080fd5b5050505050565b80516001600160a01b03811681146200033057600080fd5b919050565b805180151581146200033057600080fd5b634e487b7160e01b600052604160045260246000fd5b600080600080600080600080610100898b0312156200037a57600080fd5b620003858962000318565b9750602062000396818b0162000318565b9750620003a660408b0162000318565b9650620003b660608b0162000318565b9550620003c660808b0162000318565b9450620003d660a08b0162000318565b9350620003e660c08b0162000335565b60e08b01519093506001600160401b03808211156200040457600080fd5b818c0191508c601f8301126200041957600080fd5b8151818111156200042e576200042e62000346565b8060051b604051601f19603f8301168101818110858211171562000456576200045662000346565b60405291825284820192508381018501918f8311156200047557600080fd5b938501935b828510156200049e576200048e8562000318565b845293850193928501926200047a565b8096505050505050509295985092959890939650565b600060208284031215620004c757600080fd5b620004d28262000318565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200051057634e487b7160e01b600052601160045260246000fd5b5060010190565b6000825160005b818110156200053a57602081860181015185830152016200051e565b818111156200054a576000828501525b509190910192915050565b6000602082840312156200056857600080fd5b620004d28262000335565b60805160a05160c05160e05161010051610120516132e862000633600039600081816103820152611b6101526000818161071601528181610ee30152611be20152600081816104a001528181610c5601528181610c9b0152818161154501528181611e5501528181611f7a0152818161240a0152612522015260006103110152600081816105ca015281816112ad0152611d8901526000818161044e015281816114cb01528181611c5a01528181611d01015261238b01526132e86000f3fe608060405234801561001057600080fd5b50600436106102bb5760003560e01c8063853828b611610182578063d7da4bb0116100e9578063e8111a12116100a2578063f7412baf1161007c578063f7412baf1461078f578063fc97a303146107b6578063fd314098146107d6578063fdb483c7146107e957600080fd5b8063e8111a1214610760578063f122977714610769578063f301af421461077c57600080fd5b8063d7da4bb0146106e8578063da09d19d146106f1578063dfb05cb014610711578063e2bbb15814610738578063e57482131461074b578063e68863961461075857600080fd5b8063a495e5b51161013b578063a495e5b51461063b578063aa47965214610666578063b66503cf14610686578063c44d623f14610699578063c6f678bd146106ac578063d35e2544146106bf57600080fd5b8063853828b6146105bd5780638dd598fb146105c557806393f1c442146105ec5780639418f939146105f557806399bcc052146106085780639ce43f901461061b57600080fd5b80633ca068b61161022657806363fb415b116101df57806363fb415b1461052457806368fcee1a146105445780636fcfff451461055757806370a082311461057757806376f4be3614610597578063829a91e4146105aa57600080fd5b80633ca068b61461047057806346c96aac1461049b5780634c02a21c146104c25780634d5ce038146104cb5780635a45d052146104fe578063638634ee1461051157600080fd5b8063211dc32d11610278578063211dc32d146103ce578063221ca18c146103e15780632ce9aead146104015780632e1a7d4d1461042157806331279d3d146104365780633a4b66f11461044957600080fd5b806301316ddf146102c057806303fbf83a1461030c5780630cdfebfa1461034b5780630d52333c1461037d578063115c6f39146103a457806318160ddd146103c5575b600080fd5b6102f26102ce366004612f7b565b60136020908152600092835260408084209091529082529020805460019091015482565b604080519283526020830191909152015b60405180910390f35b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610303565b6102f2610359366004612f7b565b600f6020908152600092835260408084209091529082529020805460019091015482565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b76103b2366004612f7b565b6107fc565b604051908152602001610303565b6103b7600b5481565b6103b76103dc366004612fa7565b61096b565b6103b76103ef366004612fe0565b60046020526000908152604090205481565b6103b761040f366004612fe0565b60066020526000908152604090205481565b61043461042f366004612ffd565b610bef565b005b61043461044436600461302c565b610c25565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b761047e366004612fa7565b600960209081526000928352604080842090915290825290205481565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b760165481565b6104ee6104d9366004612fe0565b600e6020526000908152604090205460ff1681565b6040519015158152602001610303565b61043461050c366004612f7b565b6110f4565b6103b761051f366004612fe0565b61112c565b6103b7610532366004612fe0565b60026020526000908152604090205481565b610434610552366004612f7b565b611150565b6103b7610565366004612fe0565b60106020526000908152604090205481565b6103b7610585366004612fe0565b600c6020526000908152604090205481565b6103b76105a5366004612ffd565b61115c565b600054610333906001600160a01b031681565b610434611290565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b760155481565b610434610603366004613106565b6112ab565b6103b7610616366004612fe0565b61143c565b6103b7610629366004612fe0565b60076020526000908152604090205481565b6103b7610649366004612fa7565b600860209081526000928352604080842090915290825290205481565b6103b7610674366004612fe0565b60146020526000908152604090205481565b610434610694366004612f7b565b6114ad565b6104346106a7366004612fe0565b611b56565b6104346106ba366004612ffd565b611c41565b6103b76106cd366004612fe0565b6001600160a01b03166000908152600c602052604090205490565b6103b760015481565b6103b76106ff366004612fe0565b60056020526000908152604090205481565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b610434610746366004613148565b611cd6565b6003546104ee9060ff1681565b600d546103b7565b6103b760125481565b6103b7610777366004612fe0565b612024565b61033361078a366004612ffd565b6120e3565b6102f261079d366004612ffd565b6011602052600090815260409020805460019091015482565b6103b76107c4366004612fe0565b600a6020526000908152604090205481565b6102f26107e4366004612f7b565b61210d565b6104346107f7366004613148565b61232e565b6001600160a01b038216600090815260106020526040812054808203610826576000915050610965565b6001600160a01b0384166000908152600f60205260408120849161084b600185613180565b815260200190815260200160002060000154116108755761086d600182613180565b915050610965565b6001600160a01b0384166000908152600f602090815260408083208380529091529020548310156108aa576000915050610965565b6000806108b8600184613180565b90505b8181111561096057600060026108d18484613180565b6108db9190613197565b6108e59083613180565b6001600160a01b0388166000908152600f6020908152604080832084845282529182902082518084019093528054808452600190910154918301919091529192509087900361093a5750935061096592505050565b805187111561094b57819350610959565b610956600183613180565b92505b50506108bb565b509150505b92915050565b6001600160a01b0380831660008181526008602090815260408083209486168352938152838220549282526013815283822082805290529182205482916109b1916125be565b6001600160a01b038416600090815260106020526040812054919250036109dc576000915050610965565b60006109e884836107fc565b6001600160a01b03851660009081526010602052604081205491925090610a1190600190613180565b905060008115610b2357825b610a28600184613180565b8111610b21576001600160a01b0387166000818152600f602081815260408084208685528083528185208251808401909352805483526001908101548385015295855292909152928290610a7d9086906131b9565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000610abc8b846000015161210d565b5090506000610acf8c846000015161210d565b509050670de0b6b3a7640000610ae58383613180565b8560200151610af491906131d1565b610afe9190613197565b610b0890876131b9565b9550505050508080610b19906131f0565b915050610a1d565b505b6001600160a01b0386166000908152600f60209081526040808320858452825280832081518083019092528054808352600190910154928201929092529190610b6d908a9061210d565b506001600160a01b03808b166000908152600960209081526040808320938d1683529290522054909150670de0b6b3a764000090610bac9083906125be565b610bb58b612024565b610bbf9190613180565b8360200151610bce91906131d1565b610bd89190613197565b610be290846131b9565b9998505050505050505050565b336000908152600c60205260408120548203610c175750336000908152600a60205260409020545b610c21828261232e565b5050565b601754600114610c3457600080fd5b6002601755336001600160a01b0383161480610c785750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b610c8157600080fd5b60016017556040516363453ae160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906363453ae190602401600060405180830381600087803b158015610ce757600080fd5b505af1158015610cfb573d6000803e3d6000fd5b5050600260175550600090505b815181101561104d57610d38828281518110610d2657610d26613209565b602002602001015160001960016125d4565b60076000858581518110610d4e57610d4e613209565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600060066000878781518110610d8e57610d8e613209565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008491905055839190505550506000610de9838381518110610ddb57610ddb613209565b60200260200101518561096b565b90504260086000858581518110610e0257610e02613209565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000866001600160a01b03166001600160a01b031681526020019081526020016000208190555060076000848481518110610e6757610e67613209565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205460096000858581518110610ea657610ea6613209565b6020908102919091018101516001600160a01b039081168352828201939093526040918201600090812093891681529290529020558015610fd4577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316838381518110610f1d57610f1d613209565b60200260200101516001600160a01b031603610fbf576000546040516340c10f1960e01b81526001600160a01b03868116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b158015610f8157600080fd5b505af1925050508015610f92575060015b610fba57610fba838381518110610fab57610fab613209565b602002602001015185836127c4565b610fd4565b610fd4838381518110610fab57610fab613209565b828281518110610fe657610fe6613209565b60200260200101516001600160a01b0316336001600160a01b03167f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc98360405161103291815260200190565b60405180910390a35080611045816131f0565b915050610d08565b506001600160a01b038216600090815260026020526040812054600180549192839261107a908490613180565b9091555050506001600160a01b0382166000908152600c60209081526040808320546002909252822081905560018054919283926110b99084906131b9565b90915550506001600160a01b0383166000908152600260205260409020546110e29084906128c4565b6110ea6129cf565b5050600160175550565b6110fe8282612a74565b6001600160a01b03909316600090815260076020908152604080832060069092529091209390935590915550565b6001600160a01b038116600090815260056020526040812054610965904290612bd7565b6110fe828260006125d4565b6012546000908082036111725750600092915050565b8260116000611182600185613180565b815260200190815260200160002060000154116111ab576111a4600182613180565b9392505050565b6000805260116020527f4ad3b33220dddc71b994a52d72c06b10862965f7d926534c05c00fb7e819e7b7548310156111e65750600092915050565b6000806111f4600184613180565b90505b81811115611288576000600261120d8484613180565b6112179190613197565b6112219083613180565b600081815260116020908152604091829020825180840190935280548084526001909101549183019190915291925090879003611262575095945050505050565b805187111561127357819350611281565b61127e600183613180565b92505b50506111f7565b509392505050565b336000908152600c60205260409020546112a990610bef565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166385f2aef26040518163ffffffff1660e01b81526004016020604051808303816000875af115801561130b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132f919061321f565b6001600160a01b0316336001600160a01b0316146113805760405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b60448201526064015b60405180910390fd5b816001600160a01b0316600d848154811061139d5761139d613209565b6000918252602090912001546001600160a01b0316146113bc57600080fd5b6001600160a01b038083166000908152600e6020526040808220805460ff199081169091559284168252902080549091166001179055600d80548291908590811061140957611409613209565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505050565b6001600160a01b038116600090815260056020526040812054421061146357506000919050565b6001600160a01b038216600090815260056020526040812054611487904290613180565b6001600160a01b0384166000908152600460205260409020549091506111a490826131d1565b6017546001146114bc57600080fd5b60026017556001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116908316036114f957600080fd5b6000811161150657600080fd5b6001600160a01b0382166000908152600e602052604090205460ff1661165957604051633af32abf60e01b81526001600160a01b0383811660048301527f00000000000000000000000000000000000000000000000000000000000000001690633af32abf90602401602060405180830381865afa15801561158c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b0919061323c565b6116075760405162461bcd60e51b815260206004820152602260248201527f7265776172647320746f6b656e73206d7573742062652077686974656c697374604482015261195960f21b6064820152608401611377565b600d546004116116595760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e730000000000000000006044820152606401611377565b6001600160a01b03821660009081526004602052604081205490036116845761168482600042612be6565b6116928260001960016125d4565b6001600160a01b03841660009081526007602090815260408083206006835281842094909455939092556005909152205442106117e9576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611710573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611734919061325e565b905061174283333085612cd5565b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015611789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ad919061325e565b90506117b98282613180565b92506117c862093a8084613197565b6001600160a01b0385166000908152600460205260409020555061196a9050565b6001600160a01b03821660009081526005602052604081205461180d904290613180565b6001600160a01b0384166000908152600460205260408120549192509061183490836131d1565b905080831161184257600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ad919061325e565b90506118bb85333087612cd5565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015611902573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611926919061325e565b90506119328282613180565b945062093a8061194284876131b9565b61194c9190613197565b6001600160a01b038716600090815260046020526040902055505050505b6001600160a01b03821660009081526004602052604090205461198c57600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156119d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119f7919061325e565b9050611a0662093a8082613197565b6001600160a01b0384166000908152600460205260409020541115611a6d5760405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606401611377565b611a7a62093a80426131b9565b6001600160a01b038416600090815260056020908152604080832093909355600e9052205460ff16611b0c576001600160a01b0383166000818152600e60205260408120805460ff19166001908117909155600d805491820181559091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b03191690911790555b6040518281526001600160a01b0384169033907ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf508269060200160405180910390a35050600160175550565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611bc25760405162461bcd60e51b81526020600482015260116024820152706e6f7420676175676520666163746f727960781b6044820152606401611377565b600080546001600160a01b0319166001600160a01b038316179055611c0a7f000000000000000000000000000000000000000000000000000000000000000082600019612dcd565b6040516001600160a01b038216907fb09a31c5640855b04448d4fc8825b1c52b6bfbf64d152e99317895439f30dce590600090a250565b6040516370a0823160e01b8152336004820152611cd3907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ccd919061325e565b82611cd6565b50565b601754600114611ce557600080fd5b600260175581611cf457600080fd5b611cfc612e17565b611d287f0000000000000000000000000000000000000000000000000000000000000000333085612cd5565b81600b6000828254611d3a91906131b9565b9091555050336000908152600c602052604081208054849290611d5e9084906131b9565b90915550508015611ed2576040516331a9108f60e11b81526004810182905233906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa158015611dd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df4919061321f565b6001600160a01b031614611e0757600080fd5b336000908152600a60205260408120549003611eb257336000818152600a6020526040908190208390555163698473e360e01b81526004810183905260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063698473e390604401600060405180830381600087803b158015611e9957600080fd5b505af1158015611ead573d6000803e3d6000fd5b505050505b336000908152600a60205260409020548114611ecd57600080fd5b611ee4565b50336000908152600a60205260409020545b336000908152600260205260408120546001805491928392611f07908490613180565b9091555050336000908152600c6020908152604080832054600290925282208190556001805491935083929091611f3f9084906131b9565b90915550611f4f905033826128c4565b611f576129cf565b60405163530e389d60e11b815260048101839052336024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a61c713a90606401600060405180830381600087803b158015611fc657600080fd5b505af1158015611fda573d6000803e3d6000fd5b505060408051858152602081018790523393507f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159250015b60405180910390a25050600160175550565b600060015460000361204c57506001600160a01b031660009081526007602052604090205490565b6001546001600160a01b03831660009081526004602090815260408083205460068352818420546005909352922054670de0b6b3a7640000929161208f91612bd7565b6120988661112c565b6120a29190613180565b6120ac91906131d1565b6120b691906131d1565b6120c09190613197565b6001600160a01b03831660009081526007602052604090205461096591906131b9565b600d81815481106120f357600080fd5b6000918252602090912001546001600160a01b0316905081565b6001600160a01b038216600090815260146020526040812054819080820361213c576000809250925050612327565b6001600160a01b03851660009081526013602052604081208591612161600185613180565b815260200190815260200160002060000154116121fe576001600160a01b03851660009081526013602052604081209061219c600184613180565b81526020019081526020016000206001015460136000876001600160a01b03166001600160a01b0316815260200190815260200160002060006001846121e29190613180565b8152602001908152602001600020600001549250925050612327565b6001600160a01b0385166000908152601360209081526040808320838052909152902054841015612236576000809250925050612327565b600080612244600184613180565b90505b818111156122f6576000600261225d8484613180565b6122679190613197565b6122719083613180565b6001600160a01b03891660009081526013602090815260408083208484528252918290208251808401909352805480845260019091015491830191909152919250908890036122d0576020810151905190965094506123279350505050565b80518811156122e1578193506122ef565b6122ec600183613180565b92505b5050612247565b506001600160a01b038616600090815260136020908152604080832093835292905220600181015490549093509150505b9250929050565b60175460011461233d57600080fd5b600260175561234a612e17565b81600b600082825461235c9190613180565b9091555050336000908152600c602052604081208054849290612380908490613180565b909155506123b190507f000000000000000000000000000000000000000000000000000000000000000033846127c4565b801561246b57336000908152600a602052604090205481146123d257600080fd5b336000818152600a6020526040808220919091555163411b1f7760e01b81526004810183905260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063411b1f7790604401600060405180830381600087803b15801561244e57600080fd5b505af1158015612462573d6000803e3d6000fd5b5050505061247d565b50336000908152600a60205260409020545b3360009081526002602052604081205460018054919283926124a0908490613180565b9091555050336000908152600c60209081526040808320546002909252822081905560018054919350839290916124d89084906131b9565b9091555050336000818152600260205260409020546124f791906128c4565b6124ff6129cf565b604051633aa53b9160e21b815260048101839052336024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ea94ee4490606401600060405180830381600087803b15801561256e57600080fd5b505af1158015612582573d6000803e3d6000fd5b505060408051858152602081018790523393507ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568925001612012565b60008183116125cd57816111a4565b5090919050565b6001600160a01b038316600090815260066020908152604080832054600790925282205460125483929190830361260e57925090506127bc565b6001600160a01b03871660009081526004602052604081205490036126395792504291506127bc9050565b60006126448361115c565b90506000612660600160125461265a9190613180565b89612bd7565b9050801561273957815b612675600183613180565b81116127375760008181526011602090815260409182902082518084019093528054835260010154908201819052156127245760006011816126b88560016131b9565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000806127038e8460000151866000015187602001518d612e9b565b909250905061271282896131b9565b975061271f8e8983612be6565b975050505b508061272f816131f0565b91505061266a565b505b86156127b35760008181526011602090815260409182902082518084019093528054835260010154908201819052156127b15760006127928b61277b8d61112c565b8451612787908a6125be565b85602001518a612e9b565b50905061279f81866131b9565b94506127ac8b8642612be6565b429550505b505b50909350909150505b935093915050565b6000836001600160a01b03163b116127db57600080fd5b6040516001600160a01b03838116602483015260448201839052600091829186169063a9059cbb60e01b906064015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516128489190613277565b6000604051808303816000865af19150503d8060008114612885576040519150601f19603f3d011682016040523d82523d6000602084013e61288a565b606091505b50915091508180156128b45750805115806128b45750808060200190518101906128b4919061323c565b6128bd57600080fd5b5050505050565b6001600160a01b0382166000908152601060205260409020544290801580159061292257506001600160a01b0384166000908152600f60205260408120839161290e600185613180565b815260200190815260200160002060000154145b15612965576001600160a01b0384166000908152600f60205260408120849161294c600185613180565b81526020810191909152604001600020600101556129c9565b60408051808201825283815260208082018681526001600160a01b0388166000908152600f83528481208682529092529290209051815590516001918201556129af9082906131b9565b6001600160a01b0385166000908152601060205260409020555b50505050565b601254428115801590612a01575080601160006129ed600186613180565b815260200190815260200160002060000154145b15612a325760015460116000600185612a1a9190613180565b81526020810191909152604001600020600101555050565b604080518082018252828152600180546020808401918252600087815260119091529390932091518255915190820155612a6d9083906131b9565b6012555050565b6001600160a01b0382166000908152600660209081526040808320546007909252822054601254839291908303612aae5792509050612327565b6001600160a01b0386166000908152600460205260408120549003612ad95792504291506123279050565b6000612ae48361115c565b90506000612b006001601254612afa9190613180565b88612bd7565b9050815b81811015612bc8576000818152601160209081526040918290208251808401909352805483526001015490820181905215612bb5576000601181612b498560016131b9565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050600080612b948d8460000151866000015187602001518d612e9b565b9092509050612ba382896131b9565b9750612bb08d8983612be6565b975050505b5080612bc0816131f0565b915050612b04565b50919792965091945050505050565b60008183106125cd57816111a4565b6001600160a01b0383166000908152601460205260409020548015801590612c4257506001600160a01b03841660009081526013602052604081208391612c2e600185613180565b815260200190815260200160002060000154145b15612c6c576001600160a01b0384166000908152601360205260408120849161294c600185613180565b60408051808201825283815260208082018681526001600160a01b038816600090815260138352848120868252909252929020905181559051600191820155612cb69082906131b9565b6001600160a01b03851660009081526014602052604090205550505050565b6000846001600160a01b03163b11612cec57600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691612d509190613277565b6000604051808303816000865af19150503d8060008114612d8d576040519150601f19603f3d011682016040523d82523d6000602084013e612d92565b606091505b5091509150818015612dbc575080511580612dbc575080806020019051810190612dbc919061323c565b612dc557600080fd5b505050505050565b6000836001600160a01b03163b11612de457600080fd5b6040516001600160a01b03838116602483015260448201839052600091829186169063095ea7b360e01b9060640161280a565b600d5460005b81811015610c21576000600d8281548110612e3a57612e3a613209565b6000918252602090912001546001600160a01b03169050612e5f8160001960016125d4565b6001600160a01b039092166000908152600760209081526040808320600690925290912092909255905580612e93816131f0565b915050612e1d565b6000806000612eaa87856125be565b6001600160a01b0389166000908152600460205260409020549091508590670de0b6b3a764000090612efd612edf8a896125be565b6001600160a01b038d16600090815260056020526040902054612bd7565b6001600160a01b038c16600090815260056020526040902054612f21908690612bd7565b612f2b9190613180565b612f3591906131d1565b612f3f91906131d1565b612f499190613197565b9890975095505050505050565b6001600160a01b0381168114611cd357600080fd5b8035612f7681612f56565b919050565b60008060408385031215612f8e57600080fd5b8235612f9981612f56565b946020939093013593505050565b60008060408385031215612fba57600080fd5b8235612fc581612f56565b91506020830135612fd581612f56565b809150509250929050565b600060208284031215612ff257600080fd5b81356111a481612f56565b60006020828403121561300f57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561303f57600080fd5b823561304a81612f56565b915060208381013567ffffffffffffffff8082111561306857600080fd5b818601915086601f83011261307c57600080fd5b81358181111561308e5761308e613016565b8060051b604051601f19603f830116810181811085821117156130b3576130b3613016565b6040529182528482019250838101850191898311156130d157600080fd5b938501935b828510156130f6576130e785612f6b565b845293850193928501926130d6565b8096505050505050509250929050565b60008060006060848603121561311b57600080fd5b83359250602084013561312d81612f56565b9150604084013561313d81612f56565b809150509250925092565b6000806040838503121561315b57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6000828210156131925761319261316a565b500390565b6000826131b457634e487b7160e01b600052601260045260246000fd5b500490565b600082198211156131cc576131cc61316a565b500190565b60008160001904831182151516156131eb576131eb61316a565b500290565b6000600182016132025761320261316a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561323157600080fd5b81516111a481612f56565b60006020828403121561324e57600080fd5b815180151581146111a457600080fd5b60006020828403121561327057600080fd5b5051919050565b6000825160005b81811015613298576020818601810151858301520161327e565b818111156132a7576000828501525b50919091019291505056fea2646970667358221220e88dcc20dacdf0ee28ff5f9b17658539c6e0234e11e20f9df0bc372b541fc82064736f6c634300080d0033a2646970667358221220ed9dd6aeb254c9e9fbe08092bddd834cd0c4488c3d505417234414952cd73eec64736f6c634300080d0033