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
All metadata displayed below is from that contract. In order to verify current contract, click Verify & Publish button
- 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
0x608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61406e8061007e6000396000f3fe60806040523480156200001157600080fd5b50600436106200009f5760003560e01c80638da5cb5b116200006e5780638da5cb5b146200010c578063c44d623f146200011e578063d53913931462000135578063f2fde38b146200016c578063ff701934146200018357600080fd5b80631ede5a8e14620000a4578063715018a614620000d8578063730a8bdb14620000e4578063829a91e414620000f8575b600080fd5b620000bb620000b53660046200053c565b6200019a565b6040516001600160a01b0390911681526020015b60405180910390f35b620000e2620002be565b005b600154620000bb906001600160a01b031681565b600254620000bb906001600160a01b031681565b6000546001600160a01b0316620000bb565b620000e26200012f36600462000662565b620002d6565b6200015d7ff0887ba65ee2024ea881d91b74c2450ef19e1557f03bed3ea9f16b037cbe2dc981565b604051908152602001620000cf565b620000e26200017d36600462000662565b6200032a565b620000e26200019436600462000662565b620003ad565b600085858533600260009054906101000a90046001600160a01b0316308888604051620001c790620004fb565b620001da98979695949392919062000687565b604051809103906000f080158015620001f7573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039283161790556002541615620002a857600254600154604051632f2ff15d60e01b81527ff0887ba65ee2024ea881d91b74c2450ef19e1557f03bed3ea9f16b037cbe2dc960048201526001600160a01b039182166024820152911690632f2ff15d90604401600060405180830381600087803b1580156200028e57600080fd5b505af1158015620002a3573d6000803e3d6000fd5b505050505b506001546001600160a01b031695945050505050565b620002c86200044f565b620002d46000620004ab565b565b620002e06200044f565b600280546001600160a01b0319166001600160a01b0383169081179091556040517fb09a31c5640855b04448d4fc8825b1c52b6bfbf64d152e99317895439f30dce590600090a250565b620003346200044f565b6001600160a01b0381166200039f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b620003aa81620004ab565b50565b620003b76200044f565b60025460405163c44d623f60e01b81526001600160a01b0391821660048201529082169063c44d623f90602401600060405180830381600087803b158015620003ff57600080fd5b505af115801562000414573d6000803e3d6000fd5b50506040516001600160a01b03841692507fc48f89708ef0aece7c297583609062024cac183470610c0be14dbf67bfc127f89150600090a250565b6000546001600160a01b03163314620002d45760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000396565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61391b806200071e83390190565b80356001600160a01b03811681146200052157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a086880312156200055557600080fd5b620005608662000509565b945060206200057181880162000509565b9450620005816040880162000509565b9350606087013580151581146200059757600080fd5b9250608087013567ffffffffffffffff80821115620005b557600080fd5b818901915089601f830112620005ca57600080fd5b813581811115620005df57620005df62000526565b8060051b604051601f19603f8301168101818110858211171562000607576200060762000526565b60405291825284820192508381018501918c8311156200062657600080fd5b938501935b828510156200064f576200063f8562000509565b845293850193928501926200062b565b8096505050505050509295509295909350565b6000602082840312156200067557600080fd5b620006808262000509565b9392505050565b6001600160a01b03898116825288811660208084019190915288821660408401528782166060840152868216608084015285821660a084015284151560c084015261010060e08401819052845190840181905260009261012085019286810192855b8181101562000709578451841686529482019493820193600101620006e9565b50939e9d505050505050505050505050505056fe61014060405260016017553480156200001757600080fd5b506040516200391b3803806200391b8339810160408190526200003a916200035c565b6001600160a01b0388811660805287811660c05286811660a081905286821660e052600080546001600160a01b031916878416179055908416610120526003805460ff191684151517905560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa158015620000c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e79190620004b4565b6001600160a01b039081166101008190526000546200010a92166000196200021f565b60005b8151811015620002105760006001600160a01b0316828281518110620001375762000137620004d9565b60200260200101516001600160a01b031614620001fb576001600e6000848481518110620001695762000169620004d9565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600d828281518110620001bf57620001bf620004d9565b60209081029190910181015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b806200020781620004ef565b9150506200010d565b50505050505050505062000573565b6000836001600160a01b03163b116200023757600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b179052915160009283929087169162000295919062000517565b6000604051808303816000865af19150503d8060008114620002d4576040519150601f19603f3d011682016040523d82523d6000602084013e620002d9565b606091505b5091509150818015620003075750805115806200030757508080602001905181019062000307919062000555565b6200031157600080fd5b5050505050565b80516001600160a01b03811681146200033057600080fd5b919050565b805180151581146200033057600080fd5b634e487b7160e01b600052604160045260246000fd5b600080600080600080600080610100898b0312156200037a57600080fd5b620003858962000318565b9750602062000396818b0162000318565b9750620003a660408b0162000318565b9650620003b660608b0162000318565b9550620003c660808b0162000318565b9450620003d660a08b0162000318565b9350620003e660c08b0162000335565b60e08b01519093506001600160401b03808211156200040457600080fd5b818c0191508c601f8301126200041957600080fd5b8151818111156200042e576200042e62000346565b8060051b604051601f19603f8301168101818110858211171562000456576200045662000346565b60405291825284820192508381018501918f8311156200047557600080fd5b938501935b828510156200049e576200048e8562000318565b845293850193928501926200047a565b8096505050505050509295985092959890939650565b600060208284031215620004c757600080fd5b620004d28262000318565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200051057634e487b7160e01b600052601160045260246000fd5b5060010190565b6000825160005b818110156200053a57602081860181015185830152016200051e565b818111156200054a576000828501525b509190910192915050565b6000602082840312156200056857600080fd5b620004d28262000335565b60805160a05160c05160e05161010051610120516132e862000633600039600081816103820152611b6101526000818161071601528181610ee30152611be20152600081816104a001528181610c5601528181610c9b0152818161154501528181611e5501528181611f7a0152818161240a0152612522015260006103110152600081816105ca015281816112ad0152611d8901526000818161044e015281816114cb01528181611c5a01528181611d01015261238b01526132e86000f3fe608060405234801561001057600080fd5b50600436106102bb5760003560e01c8063853828b611610182578063d7da4bb0116100e9578063e8111a12116100a2578063f7412baf1161007c578063f7412baf1461078f578063fc97a303146107b6578063fd314098146107d6578063fdb483c7146107e957600080fd5b8063e8111a1214610760578063f122977714610769578063f301af421461077c57600080fd5b8063d7da4bb0146106e8578063da09d19d146106f1578063dfb05cb014610711578063e2bbb15814610738578063e57482131461074b578063e68863961461075857600080fd5b8063a495e5b51161013b578063a495e5b51461063b578063aa47965214610666578063b66503cf14610686578063c44d623f14610699578063c6f678bd146106ac578063d35e2544146106bf57600080fd5b8063853828b6146105bd5780638dd598fb146105c557806393f1c442146105ec5780639418f939146105f557806399bcc052146106085780639ce43f901461061b57600080fd5b80633ca068b61161022657806363fb415b116101df57806363fb415b1461052457806368fcee1a146105445780636fcfff451461055757806370a082311461057757806376f4be3614610597578063829a91e4146105aa57600080fd5b80633ca068b61461047057806346c96aac1461049b5780634c02a21c146104c25780634d5ce038146104cb5780635a45d052146104fe578063638634ee1461051157600080fd5b8063211dc32d11610278578063211dc32d146103ce578063221ca18c146103e15780632ce9aead146104015780632e1a7d4d1461042157806331279d3d146104365780633a4b66f11461044957600080fd5b806301316ddf146102c057806303fbf83a1461030c5780630cdfebfa1461034b5780630d52333c1461037d578063115c6f39146103a457806318160ddd146103c5575b600080fd5b6102f26102ce366004612f7b565b60136020908152600092835260408084209091529082529020805460019091015482565b604080519283526020830191909152015b60405180910390f35b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610303565b6102f2610359366004612f7b565b600f6020908152600092835260408084209091529082529020805460019091015482565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b76103b2366004612f7b565b6107fc565b604051908152602001610303565b6103b7600b5481565b6103b76103dc366004612fa7565b61096b565b6103b76103ef366004612fe0565b60046020526000908152604090205481565b6103b761040f366004612fe0565b60066020526000908152604090205481565b61043461042f366004612ffd565b610bef565b005b61043461044436600461302c565b610c25565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b761047e366004612fa7565b600960209081526000928352604080842090915290825290205481565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b760165481565b6104ee6104d9366004612fe0565b600e6020526000908152604090205460ff1681565b6040519015158152602001610303565b61043461050c366004612f7b565b6110f4565b6103b761051f366004612fe0565b61112c565b6103b7610532366004612fe0565b60026020526000908152604090205481565b610434610552366004612f7b565b611150565b6103b7610565366004612fe0565b60106020526000908152604090205481565b6103b7610585366004612fe0565b600c6020526000908152604090205481565b6103b76105a5366004612ffd565b61115c565b600054610333906001600160a01b031681565b610434611290565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b760155481565b610434610603366004613106565b6112ab565b6103b7610616366004612fe0565b61143c565b6103b7610629366004612fe0565b60076020526000908152604090205481565b6103b7610649366004612fa7565b600860209081526000928352604080842090915290825290205481565b6103b7610674366004612fe0565b60146020526000908152604090205481565b610434610694366004612f7b565b6114ad565b6104346106a7366004612fe0565b611b56565b6104346106ba366004612ffd565b611c41565b6103b76106cd366004612fe0565b6001600160a01b03166000908152600c602052604090205490565b6103b760015481565b6103b76106ff366004612fe0565b60056020526000908152604090205481565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b610434610746366004613148565b611cd6565b6003546104ee9060ff1681565b600d546103b7565b6103b760125481565b6103b7610777366004612fe0565b612024565b61033361078a366004612ffd565b6120e3565b6102f261079d366004612ffd565b6011602052600090815260409020805460019091015482565b6103b76107c4366004612fe0565b600a6020526000908152604090205481565b6102f26107e4366004612f7b565b61210d565b6104346107f7366004613148565b61232e565b6001600160a01b038216600090815260106020526040812054808203610826576000915050610965565b6001600160a01b0384166000908152600f60205260408120849161084b600185613180565b815260200190815260200160002060000154116108755761086d600182613180565b915050610965565b6001600160a01b0384166000908152600f602090815260408083208380529091529020548310156108aa576000915050610965565b6000806108b8600184613180565b90505b8181111561096057600060026108d18484613180565b6108db9190613197565b6108e59083613180565b6001600160a01b0388166000908152600f6020908152604080832084845282529182902082518084019093528054808452600190910154918301919091529192509087900361093a5750935061096592505050565b805187111561094b57819350610959565b610956600183613180565b92505b50506108bb565b509150505b92915050565b6001600160a01b0380831660008181526008602090815260408083209486168352938152838220549282526013815283822082805290529182205482916109b1916125be565b6001600160a01b038416600090815260106020526040812054919250036109dc576000915050610965565b60006109e884836107fc565b6001600160a01b03851660009081526010602052604081205491925090610a1190600190613180565b905060008115610b2357825b610a28600184613180565b8111610b21576001600160a01b0387166000818152600f602081815260408084208685528083528185208251808401909352805483526001908101548385015295855292909152928290610a7d9086906131b9565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000610abc8b846000015161210d565b5090506000610acf8c846000015161210d565b509050670de0b6b3a7640000610ae58383613180565b8560200151610af491906131d1565b610afe9190613197565b610b0890876131b9565b9550505050508080610b19906131f0565b915050610a1d565b505b6001600160a01b0386166000908152600f60209081526040808320858452825280832081518083019092528054808352600190910154928201929092529190610b6d908a9061210d565b506001600160a01b03808b166000908152600960209081526040808320938d1683529290522054909150670de0b6b3a764000090610bac9083906125be565b610bb58b612024565b610bbf9190613180565b8360200151610bce91906131d1565b610bd89190613197565b610be290846131b9565b9998505050505050505050565b336000908152600c60205260408120548203610c175750336000908152600a60205260409020545b610c21828261232e565b5050565b601754600114610c3457600080fd5b6002601755336001600160a01b0383161480610c785750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b610c8157600080fd5b60016017556040516363453ae160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906363453ae190602401600060405180830381600087803b158015610ce757600080fd5b505af1158015610cfb573d6000803e3d6000fd5b5050600260175550600090505b815181101561104d57610d38828281518110610d2657610d26613209565b602002602001015160001960016125d4565b60076000858581518110610d4e57610d4e613209565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600060066000878781518110610d8e57610d8e613209565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008491905055839190505550506000610de9838381518110610ddb57610ddb613209565b60200260200101518561096b565b90504260086000858581518110610e0257610e02613209565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000866001600160a01b03166001600160a01b031681526020019081526020016000208190555060076000848481518110610e6757610e67613209565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205460096000858581518110610ea657610ea6613209565b6020908102919091018101516001600160a01b039081168352828201939093526040918201600090812093891681529290529020558015610fd4577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316838381518110610f1d57610f1d613209565b60200260200101516001600160a01b031603610fbf576000546040516340c10f1960e01b81526001600160a01b03868116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b158015610f8157600080fd5b505af1925050508015610f92575060015b610fba57610fba838381518110610fab57610fab613209565b602002602001015185836127c4565b610fd4565b610fd4838381518110610fab57610fab613209565b828281518110610fe657610fe6613209565b60200260200101516001600160a01b0316336001600160a01b03167f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc98360405161103291815260200190565b60405180910390a35080611045816131f0565b915050610d08565b506001600160a01b038216600090815260026020526040812054600180549192839261107a908490613180565b9091555050506001600160a01b0382166000908152600c60209081526040808320546002909252822081905560018054919283926110b99084906131b9565b90915550506001600160a01b0383166000908152600260205260409020546110e29084906128c4565b6110ea6129cf565b5050600160175550565b6110fe8282612a74565b6001600160a01b03909316600090815260076020908152604080832060069092529091209390935590915550565b6001600160a01b038116600090815260056020526040812054610965904290612bd7565b6110fe828260006125d4565b6012546000908082036111725750600092915050565b8260116000611182600185613180565b815260200190815260200160002060000154116111ab576111a4600182613180565b9392505050565b6000805260116020527f4ad3b33220dddc71b994a52d72c06b10862965f7d926534c05c00fb7e819e7b7548310156111e65750600092915050565b6000806111f4600184613180565b90505b81811115611288576000600261120d8484613180565b6112179190613197565b6112219083613180565b600081815260116020908152604091829020825180840190935280548084526001909101549183019190915291925090879003611262575095945050505050565b805187111561127357819350611281565b61127e600183613180565b92505b50506111f7565b509392505050565b336000908152600c60205260409020546112a990610bef565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166385f2aef26040518163ffffffff1660e01b81526004016020604051808303816000875af115801561130b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132f919061321f565b6001600160a01b0316336001600160a01b0316146113805760405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b60448201526064015b60405180910390fd5b816001600160a01b0316600d848154811061139d5761139d613209565b6000918252602090912001546001600160a01b0316146113bc57600080fd5b6001600160a01b038083166000908152600e6020526040808220805460ff199081169091559284168252902080549091166001179055600d80548291908590811061140957611409613209565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505050565b6001600160a01b038116600090815260056020526040812054421061146357506000919050565b6001600160a01b038216600090815260056020526040812054611487904290613180565b6001600160a01b0384166000908152600460205260409020549091506111a490826131d1565b6017546001146114bc57600080fd5b60026017556001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116908316036114f957600080fd5b6000811161150657600080fd5b6001600160a01b0382166000908152600e602052604090205460ff1661165957604051633af32abf60e01b81526001600160a01b0383811660048301527f00000000000000000000000000000000000000000000000000000000000000001690633af32abf90602401602060405180830381865afa15801561158c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b0919061323c565b6116075760405162461bcd60e51b815260206004820152602260248201527f7265776172647320746f6b656e73206d7573742062652077686974656c697374604482015261195960f21b6064820152608401611377565b600d546004116116595760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e730000000000000000006044820152606401611377565b6001600160a01b03821660009081526004602052604081205490036116845761168482600042612be6565b6116928260001960016125d4565b6001600160a01b03841660009081526007602090815260408083206006835281842094909455939092556005909152205442106117e9576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611710573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611734919061325e565b905061174283333085612cd5565b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015611789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ad919061325e565b90506117b98282613180565b92506117c862093a8084613197565b6001600160a01b0385166000908152600460205260409020555061196a9050565b6001600160a01b03821660009081526005602052604081205461180d904290613180565b6001600160a01b0384166000908152600460205260408120549192509061183490836131d1565b905080831161184257600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ad919061325e565b90506118bb85333087612cd5565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015611902573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611926919061325e565b90506119328282613180565b945062093a8061194284876131b9565b61194c9190613197565b6001600160a01b038716600090815260046020526040902055505050505b6001600160a01b03821660009081526004602052604090205461198c57600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156119d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119f7919061325e565b9050611a0662093a8082613197565b6001600160a01b0384166000908152600460205260409020541115611a6d5760405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606401611377565b611a7a62093a80426131b9565b6001600160a01b038416600090815260056020908152604080832093909355600e9052205460ff16611b0c576001600160a01b0383166000818152600e60205260408120805460ff19166001908117909155600d805491820181559091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b03191690911790555b6040518281526001600160a01b0384169033907ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf508269060200160405180910390a35050600160175550565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611bc25760405162461bcd60e51b81526020600482015260116024820152706e6f7420676175676520666163746f727960781b6044820152606401611377565b600080546001600160a01b0319166001600160a01b038316179055611c0a7f000000000000000000000000000000000000000000000000000000000000000082600019612dcd565b6040516001600160a01b038216907fb09a31c5640855b04448d4fc8825b1c52b6bfbf64d152e99317895439f30dce590600090a250565b6040516370a0823160e01b8152336004820152611cd3907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ccd919061325e565b82611cd6565b50565b601754600114611ce557600080fd5b600260175581611cf457600080fd5b611cfc612e17565b611d287f0000000000000000000000000000000000000000000000000000000000000000333085612cd5565b81600b6000828254611d3a91906131b9565b9091555050336000908152600c602052604081208054849290611d5e9084906131b9565b90915550508015611ed2576040516331a9108f60e11b81526004810182905233906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa158015611dd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df4919061321f565b6001600160a01b031614611e0757600080fd5b336000908152600a60205260408120549003611eb257336000818152600a6020526040908190208390555163698473e360e01b81526004810183905260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063698473e390604401600060405180830381600087803b158015611e9957600080fd5b505af1158015611ead573d6000803e3d6000fd5b505050505b336000908152600a60205260409020548114611ecd57600080fd5b611ee4565b50336000908152600a60205260409020545b336000908152600260205260408120546001805491928392611f07908490613180565b9091555050336000908152600c6020908152604080832054600290925282208190556001805491935083929091611f3f9084906131b9565b90915550611f4f905033826128c4565b611f576129cf565b60405163530e389d60e11b815260048101839052336024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a61c713a90606401600060405180830381600087803b158015611fc657600080fd5b505af1158015611fda573d6000803e3d6000fd5b505060408051858152602081018790523393507f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159250015b60405180910390a25050600160175550565b600060015460000361204c57506001600160a01b031660009081526007602052604090205490565b6001546001600160a01b03831660009081526004602090815260408083205460068352818420546005909352922054670de0b6b3a7640000929161208f91612bd7565b6120988661112c565b6120a29190613180565b6120ac91906131d1565b6120b691906131d1565b6120c09190613197565b6001600160a01b03831660009081526007602052604090205461096591906131b9565b600d81815481106120f357600080fd5b6000918252602090912001546001600160a01b0316905081565b6001600160a01b038216600090815260146020526040812054819080820361213c576000809250925050612327565b6001600160a01b03851660009081526013602052604081208591612161600185613180565b815260200190815260200160002060000154116121fe576001600160a01b03851660009081526013602052604081209061219c600184613180565b81526020019081526020016000206001015460136000876001600160a01b03166001600160a01b0316815260200190815260200160002060006001846121e29190613180565b8152602001908152602001600020600001549250925050612327565b6001600160a01b0385166000908152601360209081526040808320838052909152902054841015612236576000809250925050612327565b600080612244600184613180565b90505b818111156122f6576000600261225d8484613180565b6122679190613197565b6122719083613180565b6001600160a01b03891660009081526013602090815260408083208484528252918290208251808401909352805480845260019091015491830191909152919250908890036122d0576020810151905190965094506123279350505050565b80518811156122e1578193506122ef565b6122ec600183613180565b92505b5050612247565b506001600160a01b038616600090815260136020908152604080832093835292905220600181015490549093509150505b9250929050565b60175460011461233d57600080fd5b600260175561234a612e17565b81600b600082825461235c9190613180565b9091555050336000908152600c602052604081208054849290612380908490613180565b909155506123b190507f000000000000000000000000000000000000000000000000000000000000000033846127c4565b801561246b57336000908152600a602052604090205481146123d257600080fd5b336000818152600a6020526040808220919091555163411b1f7760e01b81526004810183905260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063411b1f7790604401600060405180830381600087803b15801561244e57600080fd5b505af1158015612462573d6000803e3d6000fd5b5050505061247d565b50336000908152600a60205260409020545b3360009081526002602052604081205460018054919283926124a0908490613180565b9091555050336000908152600c60209081526040808320546002909252822081905560018054919350839290916124d89084906131b9565b9091555050336000818152600260205260409020546124f791906128c4565b6124ff6129cf565b604051633aa53b9160e21b815260048101839052336024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ea94ee4490606401600060405180830381600087803b15801561256e57600080fd5b505af1158015612582573d6000803e3d6000fd5b505060408051858152602081018790523393507ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568925001612012565b60008183116125cd57816111a4565b5090919050565b6001600160a01b038316600090815260066020908152604080832054600790925282205460125483929190830361260e57925090506127bc565b6001600160a01b03871660009081526004602052604081205490036126395792504291506127bc9050565b60006126448361115c565b90506000612660600160125461265a9190613180565b89612bd7565b9050801561273957815b612675600183613180565b81116127375760008181526011602090815260409182902082518084019093528054835260010154908201819052156127245760006011816126b88560016131b9565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000806127038e8460000151866000015187602001518d612e9b565b909250905061271282896131b9565b975061271f8e8983612be6565b975050505b508061272f816131f0565b91505061266a565b505b86156127b35760008181526011602090815260409182902082518084019093528054835260010154908201819052156127b15760006127928b61277b8d61112c565b8451612787908a6125be565b85602001518a612e9b565b50905061279f81866131b9565b94506127ac8b8642612be6565b429550505b505b50909350909150505b935093915050565b6000836001600160a01b03163b116127db57600080fd5b6040516001600160a01b03838116602483015260448201839052600091829186169063a9059cbb60e01b906064015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516128489190613277565b6000604051808303816000865af19150503d8060008114612885576040519150601f19603f3d011682016040523d82523d6000602084013e61288a565b606091505b50915091508180156128b45750805115806128b45750808060200190518101906128b4919061323c565b6128bd57600080fd5b5050505050565b6001600160a01b0382166000908152601060205260409020544290801580159061292257506001600160a01b0384166000908152600f60205260408120839161290e600185613180565b815260200190815260200160002060000154145b15612965576001600160a01b0384166000908152600f60205260408120849161294c600185613180565b81526020810191909152604001600020600101556129c9565b60408051808201825283815260208082018681526001600160a01b0388166000908152600f83528481208682529092529290209051815590516001918201556129af9082906131b9565b6001600160a01b0385166000908152601060205260409020555b50505050565b601254428115801590612a01575080601160006129ed600186613180565b815260200190815260200160002060000154145b15612a325760015460116000600185612a1a9190613180565b81526020810191909152604001600020600101555050565b604080518082018252828152600180546020808401918252600087815260119091529390932091518255915190820155612a6d9083906131b9565b6012555050565b6001600160a01b0382166000908152600660209081526040808320546007909252822054601254839291908303612aae5792509050612327565b6001600160a01b0386166000908152600460205260408120549003612ad95792504291506123279050565b6000612ae48361115c565b90506000612b006001601254612afa9190613180565b88612bd7565b9050815b81811015612bc8576000818152601160209081526040918290208251808401909352805483526001015490820181905215612bb5576000601181612b498560016131b9565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050600080612b948d8460000151866000015187602001518d612e9b565b9092509050612ba382896131b9565b9750612bb08d8983612be6565b975050505b5080612bc0816131f0565b915050612b04565b50919792965091945050505050565b60008183106125cd57816111a4565b6001600160a01b0383166000908152601460205260409020548015801590612c4257506001600160a01b03841660009081526013602052604081208391612c2e600185613180565b815260200190815260200160002060000154145b15612c6c576001600160a01b0384166000908152601360205260408120849161294c600185613180565b60408051808201825283815260208082018681526001600160a01b038816600090815260138352848120868252909252929020905181559051600191820155612cb69082906131b9565b6001600160a01b03851660009081526014602052604090205550505050565b6000846001600160a01b03163b11612cec57600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691612d509190613277565b6000604051808303816000865af19150503d8060008114612d8d576040519150601f19603f3d011682016040523d82523d6000602084013e612d92565b606091505b5091509150818015612dbc575080511580612dbc575080806020019051810190612dbc919061323c565b612dc557600080fd5b505050505050565b6000836001600160a01b03163b11612de457600080fd5b6040516001600160a01b03838116602483015260448201839052600091829186169063095ea7b360e01b9060640161280a565b600d5460005b81811015610c21576000600d8281548110612e3a57612e3a613209565b6000918252602090912001546001600160a01b03169050612e5f8160001960016125d4565b6001600160a01b039092166000908152600760209081526040808320600690925290912092909255905580612e93816131f0565b915050612e1d565b6000806000612eaa87856125be565b6001600160a01b0389166000908152600460205260409020549091508590670de0b6b3a764000090612efd612edf8a896125be565b6001600160a01b038d16600090815260056020526040902054612bd7565b6001600160a01b038c16600090815260056020526040902054612f21908690612bd7565b612f2b9190613180565b612f3591906131d1565b612f3f91906131d1565b612f499190613197565b9890975095505050505050565b6001600160a01b0381168114611cd357600080fd5b8035612f7681612f56565b919050565b60008060408385031215612f8e57600080fd5b8235612f9981612f56565b946020939093013593505050565b60008060408385031215612fba57600080fd5b8235612fc581612f56565b91506020830135612fd581612f56565b809150509250929050565b600060208284031215612ff257600080fd5b81356111a481612f56565b60006020828403121561300f57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561303f57600080fd5b823561304a81612f56565b915060208381013567ffffffffffffffff8082111561306857600080fd5b818601915086601f83011261307c57600080fd5b81358181111561308e5761308e613016565b8060051b604051601f19603f830116810181811085821117156130b3576130b3613016565b6040529182528482019250838101850191898311156130d157600080fd5b938501935b828510156130f6576130e785612f6b565b845293850193928501926130d6565b8096505050505050509250929050565b60008060006060848603121561311b57600080fd5b83359250602084013561312d81612f56565b9150604084013561313d81612f56565b809150509250925092565b6000806040838503121561315b57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6000828210156131925761319261316a565b500390565b6000826131b457634e487b7160e01b600052601260045260246000fd5b500490565b600082198211156131cc576131cc61316a565b500190565b60008160001904831182151516156131eb576131eb61316a565b500290565b6000600182016132025761320261316a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561323157600080fd5b81516111a481612f56565b60006020828403121561324e57600080fd5b815180151581146111a457600080fd5b60006020828403121561327057600080fd5b5051919050565b6000825160005b81811015613298576020818601810151858301520161327e565b818111156132a7576000828501525b50919091019291505056fea2646970667358221220e88dcc20dacdf0ee28ff5f9b17658539c6e0234e11e20f9df0bc372b541fc82064736f6c634300080d0033a2646970667358221220ed9dd6aeb254c9e9fbe08092bddd834cd0c4488c3d505417234414952cd73eec64736f6c634300080d0033
Deployed ByteCode
0x60806040523480156200001157600080fd5b50600436106200009f5760003560e01c80638da5cb5b116200006e5780638da5cb5b146200010c578063c44d623f146200011e578063d53913931462000135578063f2fde38b146200016c578063ff701934146200018357600080fd5b80631ede5a8e14620000a4578063715018a614620000d8578063730a8bdb14620000e4578063829a91e414620000f8575b600080fd5b620000bb620000b53660046200053c565b6200019a565b6040516001600160a01b0390911681526020015b60405180910390f35b620000e2620002be565b005b600154620000bb906001600160a01b031681565b600254620000bb906001600160a01b031681565b6000546001600160a01b0316620000bb565b620000e26200012f36600462000662565b620002d6565b6200015d7ff0887ba65ee2024ea881d91b74c2450ef19e1557f03bed3ea9f16b037cbe2dc981565b604051908152602001620000cf565b620000e26200017d36600462000662565b6200032a565b620000e26200019436600462000662565b620003ad565b600085858533600260009054906101000a90046001600160a01b0316308888604051620001c790620004fb565b620001da98979695949392919062000687565b604051809103906000f080158015620001f7573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039283161790556002541615620002a857600254600154604051632f2ff15d60e01b81527ff0887ba65ee2024ea881d91b74c2450ef19e1557f03bed3ea9f16b037cbe2dc960048201526001600160a01b039182166024820152911690632f2ff15d90604401600060405180830381600087803b1580156200028e57600080fd5b505af1158015620002a3573d6000803e3d6000fd5b505050505b506001546001600160a01b031695945050505050565b620002c86200044f565b620002d46000620004ab565b565b620002e06200044f565b600280546001600160a01b0319166001600160a01b0383169081179091556040517fb09a31c5640855b04448d4fc8825b1c52b6bfbf64d152e99317895439f30dce590600090a250565b620003346200044f565b6001600160a01b0381166200039f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b620003aa81620004ab565b50565b620003b76200044f565b60025460405163c44d623f60e01b81526001600160a01b0391821660048201529082169063c44d623f90602401600060405180830381600087803b158015620003ff57600080fd5b505af115801562000414573d6000803e3d6000fd5b50506040516001600160a01b03841692507fc48f89708ef0aece7c297583609062024cac183470610c0be14dbf67bfc127f89150600090a250565b6000546001600160a01b03163314620002d45760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000396565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61391b806200071e83390190565b80356001600160a01b03811681146200052157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a086880312156200055557600080fd5b620005608662000509565b945060206200057181880162000509565b9450620005816040880162000509565b9350606087013580151581146200059757600080fd5b9250608087013567ffffffffffffffff80821115620005b557600080fd5b818901915089601f830112620005ca57600080fd5b813581811115620005df57620005df62000526565b8060051b604051601f19603f8301168101818110858211171562000607576200060762000526565b60405291825284820192508381018501918c8311156200062657600080fd5b938501935b828510156200064f576200063f8562000509565b845293850193928501926200062b565b8096505050505050509295509295909350565b6000602082840312156200067557600080fd5b620006808262000509565b9392505050565b6001600160a01b03898116825288811660208084019190915288821660408401528782166060840152868216608084015285821660a084015284151560c084015261010060e08401819052845190840181905260009261012085019286810192855b8181101562000709578451841686529482019493820193600101620006e9565b50939e9d505050505050505050505050505056fe61014060405260016017553480156200001757600080fd5b506040516200391b3803806200391b8339810160408190526200003a916200035c565b6001600160a01b0388811660805287811660c05286811660a081905286821660e052600080546001600160a01b031916878416179055908416610120526003805460ff191684151517905560408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa158015620000c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e79190620004b4565b6001600160a01b039081166101008190526000546200010a92166000196200021f565b60005b8151811015620002105760006001600160a01b0316828281518110620001375762000137620004d9565b60200260200101516001600160a01b031614620001fb576001600e6000848481518110620001695762000169620004d9565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600d828281518110620001bf57620001bf620004d9565b60209081029190910181015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b806200020781620004ef565b9150506200010d565b50505050505050505062000573565b6000836001600160a01b03163b116200023757600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b179052915160009283929087169162000295919062000517565b6000604051808303816000865af19150503d8060008114620002d4576040519150601f19603f3d011682016040523d82523d6000602084013e620002d9565b606091505b5091509150818015620003075750805115806200030757508080602001905181019062000307919062000555565b6200031157600080fd5b5050505050565b80516001600160a01b03811681146200033057600080fd5b919050565b805180151581146200033057600080fd5b634e487b7160e01b600052604160045260246000fd5b600080600080600080600080610100898b0312156200037a57600080fd5b620003858962000318565b9750602062000396818b0162000318565b9750620003a660408b0162000318565b9650620003b660608b0162000318565b9550620003c660808b0162000318565b9450620003d660a08b0162000318565b9350620003e660c08b0162000335565b60e08b01519093506001600160401b03808211156200040457600080fd5b818c0191508c601f8301126200041957600080fd5b8151818111156200042e576200042e62000346565b8060051b604051601f19603f8301168101818110858211171562000456576200045662000346565b60405291825284820192508381018501918f8311156200047557600080fd5b938501935b828510156200049e576200048e8562000318565b845293850193928501926200047a565b8096505050505050509295985092959890939650565b600060208284031215620004c757600080fd5b620004d28262000318565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200051057634e487b7160e01b600052601160045260246000fd5b5060010190565b6000825160005b818110156200053a57602081860181015185830152016200051e565b818111156200054a576000828501525b509190910192915050565b6000602082840312156200056857600080fd5b620004d28262000335565b60805160a05160c05160e05161010051610120516132e862000633600039600081816103820152611b6101526000818161071601528181610ee30152611be20152600081816104a001528181610c5601528181610c9b0152818161154501528181611e5501528181611f7a0152818161240a0152612522015260006103110152600081816105ca015281816112ad0152611d8901526000818161044e015281816114cb01528181611c5a01528181611d01015261238b01526132e86000f3fe608060405234801561001057600080fd5b50600436106102bb5760003560e01c8063853828b611610182578063d7da4bb0116100e9578063e8111a12116100a2578063f7412baf1161007c578063f7412baf1461078f578063fc97a303146107b6578063fd314098146107d6578063fdb483c7146107e957600080fd5b8063e8111a1214610760578063f122977714610769578063f301af421461077c57600080fd5b8063d7da4bb0146106e8578063da09d19d146106f1578063dfb05cb014610711578063e2bbb15814610738578063e57482131461074b578063e68863961461075857600080fd5b8063a495e5b51161013b578063a495e5b51461063b578063aa47965214610666578063b66503cf14610686578063c44d623f14610699578063c6f678bd146106ac578063d35e2544146106bf57600080fd5b8063853828b6146105bd5780638dd598fb146105c557806393f1c442146105ec5780639418f939146105f557806399bcc052146106085780639ce43f901461061b57600080fd5b80633ca068b61161022657806363fb415b116101df57806363fb415b1461052457806368fcee1a146105445780636fcfff451461055757806370a082311461057757806376f4be3614610597578063829a91e4146105aa57600080fd5b80633ca068b61461047057806346c96aac1461049b5780634c02a21c146104c25780634d5ce038146104cb5780635a45d052146104fe578063638634ee1461051157600080fd5b8063211dc32d11610278578063211dc32d146103ce578063221ca18c146103e15780632ce9aead146104015780632e1a7d4d1461042157806331279d3d146104365780633a4b66f11461044957600080fd5b806301316ddf146102c057806303fbf83a1461030c5780630cdfebfa1461034b5780630d52333c1461037d578063115c6f39146103a457806318160ddd146103c5575b600080fd5b6102f26102ce366004612f7b565b60136020908152600092835260408084209091529082529020805460019091015482565b604080519283526020830191909152015b60405180910390f35b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610303565b6102f2610359366004612f7b565b600f6020908152600092835260408084209091529082529020805460019091015482565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b76103b2366004612f7b565b6107fc565b604051908152602001610303565b6103b7600b5481565b6103b76103dc366004612fa7565b61096b565b6103b76103ef366004612fe0565b60046020526000908152604090205481565b6103b761040f366004612fe0565b60066020526000908152604090205481565b61043461042f366004612ffd565b610bef565b005b61043461044436600461302c565b610c25565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b761047e366004612fa7565b600960209081526000928352604080842090915290825290205481565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b760165481565b6104ee6104d9366004612fe0565b600e6020526000908152604090205460ff1681565b6040519015158152602001610303565b61043461050c366004612f7b565b6110f4565b6103b761051f366004612fe0565b61112c565b6103b7610532366004612fe0565b60026020526000908152604090205481565b610434610552366004612f7b565b611150565b6103b7610565366004612fe0565b60106020526000908152604090205481565b6103b7610585366004612fe0565b600c6020526000908152604090205481565b6103b76105a5366004612ffd565b61115c565b600054610333906001600160a01b031681565b610434611290565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b6103b760155481565b610434610603366004613106565b6112ab565b6103b7610616366004612fe0565b61143c565b6103b7610629366004612fe0565b60076020526000908152604090205481565b6103b7610649366004612fa7565b600860209081526000928352604080842090915290825290205481565b6103b7610674366004612fe0565b60146020526000908152604090205481565b610434610694366004612f7b565b6114ad565b6104346106a7366004612fe0565b611b56565b6104346106ba366004612ffd565b611c41565b6103b76106cd366004612fe0565b6001600160a01b03166000908152600c602052604090205490565b6103b760015481565b6103b76106ff366004612fe0565b60056020526000908152604090205481565b6103337f000000000000000000000000000000000000000000000000000000000000000081565b610434610746366004613148565b611cd6565b6003546104ee9060ff1681565b600d546103b7565b6103b760125481565b6103b7610777366004612fe0565b612024565b61033361078a366004612ffd565b6120e3565b6102f261079d366004612ffd565b6011602052600090815260409020805460019091015482565b6103b76107c4366004612fe0565b600a6020526000908152604090205481565b6102f26107e4366004612f7b565b61210d565b6104346107f7366004613148565b61232e565b6001600160a01b038216600090815260106020526040812054808203610826576000915050610965565b6001600160a01b0384166000908152600f60205260408120849161084b600185613180565b815260200190815260200160002060000154116108755761086d600182613180565b915050610965565b6001600160a01b0384166000908152600f602090815260408083208380529091529020548310156108aa576000915050610965565b6000806108b8600184613180565b90505b8181111561096057600060026108d18484613180565b6108db9190613197565b6108e59083613180565b6001600160a01b0388166000908152600f6020908152604080832084845282529182902082518084019093528054808452600190910154918301919091529192509087900361093a5750935061096592505050565b805187111561094b57819350610959565b610956600183613180565b92505b50506108bb565b509150505b92915050565b6001600160a01b0380831660008181526008602090815260408083209486168352938152838220549282526013815283822082805290529182205482916109b1916125be565b6001600160a01b038416600090815260106020526040812054919250036109dc576000915050610965565b60006109e884836107fc565b6001600160a01b03851660009081526010602052604081205491925090610a1190600190613180565b905060008115610b2357825b610a28600184613180565b8111610b21576001600160a01b0387166000818152600f602081815260408084208685528083528185208251808401909352805483526001908101548385015295855292909152928290610a7d9086906131b9565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000610abc8b846000015161210d565b5090506000610acf8c846000015161210d565b509050670de0b6b3a7640000610ae58383613180565b8560200151610af491906131d1565b610afe9190613197565b610b0890876131b9565b9550505050508080610b19906131f0565b915050610a1d565b505b6001600160a01b0386166000908152600f60209081526040808320858452825280832081518083019092528054808352600190910154928201929092529190610b6d908a9061210d565b506001600160a01b03808b166000908152600960209081526040808320938d1683529290522054909150670de0b6b3a764000090610bac9083906125be565b610bb58b612024565b610bbf9190613180565b8360200151610bce91906131d1565b610bd89190613197565b610be290846131b9565b9998505050505050505050565b336000908152600c60205260408120548203610c175750336000908152600a60205260409020545b610c21828261232e565b5050565b601754600114610c3457600080fd5b6002601755336001600160a01b0383161480610c785750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b610c8157600080fd5b60016017556040516363453ae160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906363453ae190602401600060405180830381600087803b158015610ce757600080fd5b505af1158015610cfb573d6000803e3d6000fd5b5050600260175550600090505b815181101561104d57610d38828281518110610d2657610d26613209565b602002602001015160001960016125d4565b60076000858581518110610d4e57610d4e613209565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600060066000878781518110610d8e57610d8e613209565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008491905055839190505550506000610de9838381518110610ddb57610ddb613209565b60200260200101518561096b565b90504260086000858581518110610e0257610e02613209565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000866001600160a01b03166001600160a01b031681526020019081526020016000208190555060076000848481518110610e6757610e67613209565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205460096000858581518110610ea657610ea6613209565b6020908102919091018101516001600160a01b039081168352828201939093526040918201600090812093891681529290529020558015610fd4577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316838381518110610f1d57610f1d613209565b60200260200101516001600160a01b031603610fbf576000546040516340c10f1960e01b81526001600160a01b03868116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b158015610f8157600080fd5b505af1925050508015610f92575060015b610fba57610fba838381518110610fab57610fab613209565b602002602001015185836127c4565b610fd4565b610fd4838381518110610fab57610fab613209565b828281518110610fe657610fe6613209565b60200260200101516001600160a01b0316336001600160a01b03167f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc98360405161103291815260200190565b60405180910390a35080611045816131f0565b915050610d08565b506001600160a01b038216600090815260026020526040812054600180549192839261107a908490613180565b9091555050506001600160a01b0382166000908152600c60209081526040808320546002909252822081905560018054919283926110b99084906131b9565b90915550506001600160a01b0383166000908152600260205260409020546110e29084906128c4565b6110ea6129cf565b5050600160175550565b6110fe8282612a74565b6001600160a01b03909316600090815260076020908152604080832060069092529091209390935590915550565b6001600160a01b038116600090815260056020526040812054610965904290612bd7565b6110fe828260006125d4565b6012546000908082036111725750600092915050565b8260116000611182600185613180565b815260200190815260200160002060000154116111ab576111a4600182613180565b9392505050565b6000805260116020527f4ad3b33220dddc71b994a52d72c06b10862965f7d926534c05c00fb7e819e7b7548310156111e65750600092915050565b6000806111f4600184613180565b90505b81811115611288576000600261120d8484613180565b6112179190613197565b6112219083613180565b600081815260116020908152604091829020825180840190935280548084526001909101549183019190915291925090879003611262575095945050505050565b805187111561127357819350611281565b61127e600183613180565b92505b50506111f7565b509392505050565b336000908152600c60205260409020546112a990610bef565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166385f2aef26040518163ffffffff1660e01b81526004016020604051808303816000875af115801561130b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132f919061321f565b6001600160a01b0316336001600160a01b0316146113805760405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b60448201526064015b60405180910390fd5b816001600160a01b0316600d848154811061139d5761139d613209565b6000918252602090912001546001600160a01b0316146113bc57600080fd5b6001600160a01b038083166000908152600e6020526040808220805460ff199081169091559284168252902080549091166001179055600d80548291908590811061140957611409613209565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505050565b6001600160a01b038116600090815260056020526040812054421061146357506000919050565b6001600160a01b038216600090815260056020526040812054611487904290613180565b6001600160a01b0384166000908152600460205260409020549091506111a490826131d1565b6017546001146114bc57600080fd5b60026017556001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116908316036114f957600080fd5b6000811161150657600080fd5b6001600160a01b0382166000908152600e602052604090205460ff1661165957604051633af32abf60e01b81526001600160a01b0383811660048301527f00000000000000000000000000000000000000000000000000000000000000001690633af32abf90602401602060405180830381865afa15801561158c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b0919061323c565b6116075760405162461bcd60e51b815260206004820152602260248201527f7265776172647320746f6b656e73206d7573742062652077686974656c697374604482015261195960f21b6064820152608401611377565b600d546004116116595760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e730000000000000000006044820152606401611377565b6001600160a01b03821660009081526004602052604081205490036116845761168482600042612be6565b6116928260001960016125d4565b6001600160a01b03841660009081526007602090815260408083206006835281842094909455939092556005909152205442106117e9576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611710573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611734919061325e565b905061174283333085612cd5565b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015611789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ad919061325e565b90506117b98282613180565b92506117c862093a8084613197565b6001600160a01b0385166000908152600460205260409020555061196a9050565b6001600160a01b03821660009081526005602052604081205461180d904290613180565b6001600160a01b0384166000908152600460205260408120549192509061183490836131d1565b905080831161184257600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ad919061325e565b90506118bb85333087612cd5565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015611902573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611926919061325e565b90506119328282613180565b945062093a8061194284876131b9565b61194c9190613197565b6001600160a01b038716600090815260046020526040902055505050505b6001600160a01b03821660009081526004602052604090205461198c57600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156119d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119f7919061325e565b9050611a0662093a8082613197565b6001600160a01b0384166000908152600460205260409020541115611a6d5760405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606401611377565b611a7a62093a80426131b9565b6001600160a01b038416600090815260056020908152604080832093909355600e9052205460ff16611b0c576001600160a01b0383166000818152600e60205260408120805460ff19166001908117909155600d805491820181559091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b03191690911790555b6040518281526001600160a01b0384169033907ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf508269060200160405180910390a35050600160175550565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611bc25760405162461bcd60e51b81526020600482015260116024820152706e6f7420676175676520666163746f727960781b6044820152606401611377565b600080546001600160a01b0319166001600160a01b038316179055611c0a7f000000000000000000000000000000000000000000000000000000000000000082600019612dcd565b6040516001600160a01b038216907fb09a31c5640855b04448d4fc8825b1c52b6bfbf64d152e99317895439f30dce590600090a250565b6040516370a0823160e01b8152336004820152611cd3907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ccd919061325e565b82611cd6565b50565b601754600114611ce557600080fd5b600260175581611cf457600080fd5b611cfc612e17565b611d287f0000000000000000000000000000000000000000000000000000000000000000333085612cd5565b81600b6000828254611d3a91906131b9565b9091555050336000908152600c602052604081208054849290611d5e9084906131b9565b90915550508015611ed2576040516331a9108f60e11b81526004810182905233906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa158015611dd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df4919061321f565b6001600160a01b031614611e0757600080fd5b336000908152600a60205260408120549003611eb257336000818152600a6020526040908190208390555163698473e360e01b81526004810183905260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063698473e390604401600060405180830381600087803b158015611e9957600080fd5b505af1158015611ead573d6000803e3d6000fd5b505050505b336000908152600a60205260409020548114611ecd57600080fd5b611ee4565b50336000908152600a60205260409020545b336000908152600260205260408120546001805491928392611f07908490613180565b9091555050336000908152600c6020908152604080832054600290925282208190556001805491935083929091611f3f9084906131b9565b90915550611f4f905033826128c4565b611f576129cf565b60405163530e389d60e11b815260048101839052336024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a61c713a90606401600060405180830381600087803b158015611fc657600080fd5b505af1158015611fda573d6000803e3d6000fd5b505060408051858152602081018790523393507f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159250015b60405180910390a25050600160175550565b600060015460000361204c57506001600160a01b031660009081526007602052604090205490565b6001546001600160a01b03831660009081526004602090815260408083205460068352818420546005909352922054670de0b6b3a7640000929161208f91612bd7565b6120988661112c565b6120a29190613180565b6120ac91906131d1565b6120b691906131d1565b6120c09190613197565b6001600160a01b03831660009081526007602052604090205461096591906131b9565b600d81815481106120f357600080fd5b6000918252602090912001546001600160a01b0316905081565b6001600160a01b038216600090815260146020526040812054819080820361213c576000809250925050612327565b6001600160a01b03851660009081526013602052604081208591612161600185613180565b815260200190815260200160002060000154116121fe576001600160a01b03851660009081526013602052604081209061219c600184613180565b81526020019081526020016000206001015460136000876001600160a01b03166001600160a01b0316815260200190815260200160002060006001846121e29190613180565b8152602001908152602001600020600001549250925050612327565b6001600160a01b0385166000908152601360209081526040808320838052909152902054841015612236576000809250925050612327565b600080612244600184613180565b90505b818111156122f6576000600261225d8484613180565b6122679190613197565b6122719083613180565b6001600160a01b03891660009081526013602090815260408083208484528252918290208251808401909352805480845260019091015491830191909152919250908890036122d0576020810151905190965094506123279350505050565b80518811156122e1578193506122ef565b6122ec600183613180565b92505b5050612247565b506001600160a01b038616600090815260136020908152604080832093835292905220600181015490549093509150505b9250929050565b60175460011461233d57600080fd5b600260175561234a612e17565b81600b600082825461235c9190613180565b9091555050336000908152600c602052604081208054849290612380908490613180565b909155506123b190507f000000000000000000000000000000000000000000000000000000000000000033846127c4565b801561246b57336000908152600a602052604090205481146123d257600080fd5b336000818152600a6020526040808220919091555163411b1f7760e01b81526004810183905260248101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063411b1f7790604401600060405180830381600087803b15801561244e57600080fd5b505af1158015612462573d6000803e3d6000fd5b5050505061247d565b50336000908152600a60205260409020545b3360009081526002602052604081205460018054919283926124a0908490613180565b9091555050336000908152600c60209081526040808320546002909252822081905560018054919350839290916124d89084906131b9565b9091555050336000818152600260205260409020546124f791906128c4565b6124ff6129cf565b604051633aa53b9160e21b815260048101839052336024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ea94ee4490606401600060405180830381600087803b15801561256e57600080fd5b505af1158015612582573d6000803e3d6000fd5b505060408051858152602081018790523393507ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568925001612012565b60008183116125cd57816111a4565b5090919050565b6001600160a01b038316600090815260066020908152604080832054600790925282205460125483929190830361260e57925090506127bc565b6001600160a01b03871660009081526004602052604081205490036126395792504291506127bc9050565b60006126448361115c565b90506000612660600160125461265a9190613180565b89612bd7565b9050801561273957815b612675600183613180565b81116127375760008181526011602090815260409182902082518084019093528054835260010154908201819052156127245760006011816126b88560016131b9565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000806127038e8460000151866000015187602001518d612e9b565b909250905061271282896131b9565b975061271f8e8983612be6565b975050505b508061272f816131f0565b91505061266a565b505b86156127b35760008181526011602090815260409182902082518084019093528054835260010154908201819052156127b15760006127928b61277b8d61112c565b8451612787908a6125be565b85602001518a612e9b565b50905061279f81866131b9565b94506127ac8b8642612be6565b429550505b505b50909350909150505b935093915050565b6000836001600160a01b03163b116127db57600080fd5b6040516001600160a01b03838116602483015260448201839052600091829186169063a9059cbb60e01b906064015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516128489190613277565b6000604051808303816000865af19150503d8060008114612885576040519150601f19603f3d011682016040523d82523d6000602084013e61288a565b606091505b50915091508180156128b45750805115806128b45750808060200190518101906128b4919061323c565b6128bd57600080fd5b5050505050565b6001600160a01b0382166000908152601060205260409020544290801580159061292257506001600160a01b0384166000908152600f60205260408120839161290e600185613180565b815260200190815260200160002060000154145b15612965576001600160a01b0384166000908152600f60205260408120849161294c600185613180565b81526020810191909152604001600020600101556129c9565b60408051808201825283815260208082018681526001600160a01b0388166000908152600f83528481208682529092529290209051815590516001918201556129af9082906131b9565b6001600160a01b0385166000908152601060205260409020555b50505050565b601254428115801590612a01575080601160006129ed600186613180565b815260200190815260200160002060000154145b15612a325760015460116000600185612a1a9190613180565b81526020810191909152604001600020600101555050565b604080518082018252828152600180546020808401918252600087815260119091529390932091518255915190820155612a6d9083906131b9565b6012555050565b6001600160a01b0382166000908152600660209081526040808320546007909252822054601254839291908303612aae5792509050612327565b6001600160a01b0386166000908152600460205260408120549003612ad95792504291506123279050565b6000612ae48361115c565b90506000612b006001601254612afa9190613180565b88612bd7565b9050815b81811015612bc8576000818152601160209081526040918290208251808401909352805483526001015490820181905215612bb5576000601181612b498560016131b9565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050600080612b948d8460000151866000015187602001518d612e9b565b9092509050612ba382896131b9565b9750612bb08d8983612be6565b975050505b5080612bc0816131f0565b915050612b04565b50919792965091945050505050565b60008183106125cd57816111a4565b6001600160a01b0383166000908152601460205260409020548015801590612c4257506001600160a01b03841660009081526013602052604081208391612c2e600185613180565b815260200190815260200160002060000154145b15612c6c576001600160a01b0384166000908152601360205260408120849161294c600185613180565b60408051808201825283815260208082018681526001600160a01b038816600090815260138352848120868252909252929020905181559051600191820155612cb69082906131b9565b6001600160a01b03851660009081526014602052604090205550505050565b6000846001600160a01b03163b11612cec57600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691612d509190613277565b6000604051808303816000865af19150503d8060008114612d8d576040519150601f19603f3d011682016040523d82523d6000602084013e612d92565b606091505b5091509150818015612dbc575080511580612dbc575080806020019051810190612dbc919061323c565b612dc557600080fd5b505050505050565b6000836001600160a01b03163b11612de457600080fd5b6040516001600160a01b03838116602483015260448201839052600091829186169063095ea7b360e01b9060640161280a565b600d5460005b81811015610c21576000600d8281548110612e3a57612e3a613209565b6000918252602090912001546001600160a01b03169050612e5f8160001960016125d4565b6001600160a01b039092166000908152600760209081526040808320600690925290912092909255905580612e93816131f0565b915050612e1d565b6000806000612eaa87856125be565b6001600160a01b0389166000908152600460205260409020549091508590670de0b6b3a764000090612efd612edf8a896125be565b6001600160a01b038d16600090815260056020526040902054612bd7565b6001600160a01b038c16600090815260056020526040902054612f21908690612bd7565b612f2b9190613180565b612f3591906131d1565b612f3f91906131d1565b612f499190613197565b9890975095505050505050565b6001600160a01b0381168114611cd357600080fd5b8035612f7681612f56565b919050565b60008060408385031215612f8e57600080fd5b8235612f9981612f56565b946020939093013593505050565b60008060408385031215612fba57600080fd5b8235612fc581612f56565b91506020830135612fd581612f56565b809150509250929050565b600060208284031215612ff257600080fd5b81356111a481612f56565b60006020828403121561300f57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561303f57600080fd5b823561304a81612f56565b915060208381013567ffffffffffffffff8082111561306857600080fd5b818601915086601f83011261307c57600080fd5b81358181111561308e5761308e613016565b8060051b604051601f19603f830116810181811085821117156130b3576130b3613016565b6040529182528482019250838101850191898311156130d157600080fd5b938501935b828510156130f6576130e785612f6b565b845293850193928501926130d6565b8096505050505050509250929050565b60008060006060848603121561311b57600080fd5b83359250602084013561312d81612f56565b9150604084013561313d81612f56565b809150509250925092565b6000806040838503121561315b57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6000828210156131925761319261316a565b500390565b6000826131b457634e487b7160e01b600052601260045260246000fd5b500490565b600082198211156131cc576131cc61316a565b500190565b60008160001904831182151516156131eb576131eb61316a565b500290565b6000600182016132025761320261316a565b5060010190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561323157600080fd5b81516111a481612f56565b60006020828403121561324e57600080fd5b815180151581146111a457600080fd5b60006020828403121561327057600080fd5b5051919050565b6000825160005b81811015613298576020818601810151858301520161327e565b818111156132a7576000828501525b50919091019291505056fea2646970667358221220e88dcc20dacdf0ee28ff5f9b17658539c6e0234e11e20f9df0bc372b541fc82064736f6c634300080d0033a2646970667358221220ed9dd6aeb254c9e9fbe08092bddd834cd0c4488c3d505417234414952cd73eec64736f6c634300080d0033