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