Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
VoterV5
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
// Package Imports
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// Local Imports
import {IVoterV5_Logic} from "./IVoterV5_Logic.sol";
import {IBribe} from "./IBribe.sol";
import {IGauge} from "../interfaces/IGauge.sol";
import {IOptionTokenV3} from "../OptionToken/IOptionTokenV3.sol";
import {IMinter} from "../interfaces/IMinter.sol";
import {IPermissionsRegistry} from "../interfaces/IPermissionsRegistry.sol";
import {IVoterV5_GaugeLogic} from "./VoterV5_GaugeLogic.sol";
import {Constants} from "../Constants.sol";
import {VoterV5_Storage} from "./VoterV5_Storage.sol";
import {IVotingEscrowV2} from "./VotingEscrow/IVotingEscrowV2.sol";
import {DelegateCallLib} from "../libraries/DelegateCallLib.sol";
/**
* @title VoterV5
* @notice VoterV5 allows users to vote on gauges/pools through a VotingEscrow position and receive rewards.
* @dev
* - DOES NOT support fee-on-transfer tokens for the `base` reward token or Option Tokens.
* - WARN `setOptionsToken` can change the distribution token if invoked before the EPOCH flip. It is highly recommended
* to call this function AFTER the EPOCH flip to avoid any confusion.
*/
contract VoterV5 is IVoterV5_Logic, VoterV5_Storage, ReentrancyGuardUpgradeable {
using SafeERC20 for IERC20;
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
EVENTS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
event GaugeCreated(
address indexed gauge,
address creator,
address internal_bribe,
address indexed external_bribe,
address indexed pool
);
event GaugeKilled(address indexed gauge);
event GaugeRevived(address indexed gauge);
event Voted(address indexed voter, uint256 weight);
event Abstained(address voter, uint256 weight);
event NotifyReward(address indexed sender, address indexed reward, uint256 amount);
event DistributeReward(address indexed sender, address indexed gauge, uint256 amount);
event RefundReward(address indexed gauge, uint256 amount);
event Whitelisted(address indexed whitelister, address indexed token);
event Blacklisted(address indexed blacklister, address indexed token);
event WhitelistedPool(address indexed whitelister, address indexed pool);
event BlacklistedPool(address indexed blacklister, address indexed pool);
event Attach(address indexed owner, address indexed gauge, uint256 tokenId);
event Detach(address indexed owner, address indexed gauge, uint256 tokenId);
event SetMinter(address indexed old, address indexed latest);
event SetOptions(address indexed old, address indexed latest);
event SetGaugeLogic(address indexed old, address indexed latest);
event HitRefreshApprovalLimit(uint256 fromGaugeIndex, uint256 toGaugeIndex);
event SetDepositor(address indexed old, bool enabled);
event SetBribeFactory(address indexed old, address indexed latest);
event SetPairFactory(address indexed old, address indexed latest);
event SetPermissionRegistry(address indexed old, address indexed latest);
event SetGaugeFactory(address indexed old, address indexed latest);
event SetBribeFor(bool isInternal, address indexed old, address indexed latest, address indexed gauge);
event SetVoteDelay(uint256 old, uint256 latest);
event AddFactories(address indexed pairfactory, address indexed gaugefactory);
event FactoryEnabled(uint256 indexed gaugeType, address indexed pairFactory, address indexed gaugeFactory);
event FactoryDisabled(uint256 indexed gaugeType, address indexed pairFactory, address indexed gaugeFactory);
event FactoryReplaced(
uint256 indexed gaugeType,
address indexed oldPairFactory,
address indexed oldGaugeFactory,
address newPairFactory,
address newGaugeFactory
);
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
ERRORS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
error AlreadyInitialized();
error InvalidProtocolName();
error NotVoterAdmin();
error NotGovernance();
error NotAuthorized();
error InvalidDelay();
error InvalidTokenAddress();
error DelayAlreadySet();
error AlreadyGaugeFactory();
error NotGaugeLogic();
error NotGaugeFactory();
error NotFactory();
error NotGauge();
error GaugeAlreadyKilled();
error GaugeAlreadyAlive();
error ExceedsMaxGauges();
error CannotReviveGaugeInSameEpoch();
error NotApprovedOrOwner();
error InsufficientVotingPower();
error VoteDelayNotMet();
error VotedAlready();
error LengthMismatch();
error ZeroAddress();
error NotContract();
error InvalidGaugeType();
error InconsistentFactoryPair();
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
CONSTRUCTOR & INITIALIZER
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
constructor() {
/// @dev Protecting the initializer on the implementation contract is important.
/// A malicious actor could potentially self-destruct the implementation by setting a malicious gaugeLogic contract.
_disableInitializers();
}
function initialize(
address __ve,
address _pairFactory,
address _gaugeFactory,
address _bribes,
address _gaugeLogic,
string memory _protocolName
) public initializer {
__ReentrancyGuard_init();
_ve = __ve;
base = address(IVotingEscrowV2(__ve).token());
_factories.push(_pairFactory);
isFactory[_pairFactory]++;
_gaugeFactories.push(_gaugeFactory);
isGaugeFactory[_gaugeFactory]++;
bribefactory = _bribes;
minter = msg.sender;
permissionRegistry = msg.sender;
if (bytes(_protocolName).length == 0) {
revert InvalidProtocolName();
}
protocolName = _protocolName;
VOTE_DELAY = 0;
DURATION = Constants.EPOCH;
MAX_VOTE_DELAY = Constants.EPOCH;
initflag = false;
_setGaugeLogic(_gaugeLogic);
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
MODIFIERS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @dev Using function instead of modifier to save gas
function VoterAdmin() private view {
if (!IPermissionsRegistry(permissionRegistry).hasRole("VOTER_ADMIN", msg.sender)) {
revert NotVoterAdmin();
}
}
/// @dev Using function instead of modifier to save gas
function Governance() private view {
if (!IPermissionsRegistry(permissionRegistry).hasRole("GOVERNANCE", msg.sender)) {
revert NotGovernance();
}
}
/// @notice initialize the voter contract
/// @param _tokens array of tokens to whitelist
/// @param _minter the minter of the protocol token
function _init(address[] memory _tokens, address _permissionsRegistry, address _minter, address _oToken) external {
if (msg.sender != minter && !IPermissionsRegistry(permissionRegistry).hasRole("VOTER_ADMIN", msg.sender)) {
revert NotAuthorized();
}
if (initflag) {
revert AlreadyInitialized();
}
for (uint256 i = 0; i < _tokens.length; i++) {
_updateWhitelistToken(_tokens[i], true);
}
minter = _minter;
permissionRegistry = _permissionsRegistry;
if (_oToken != address(0)) {
oToken = _oToken;
/// @dev base must be approved to mint option token.
IERC20(base).approve(oToken, type(uint256).max);
}
isGaugeDepositor[oToken] = true;
initflag = true;
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
VoterAdmin
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice set vote delay in seconds
function setVoteDelay(uint256 _delay) external {
VoterAdmin();
if (_delay == VOTE_DELAY) {
revert DelayAlreadySet();
}
if (_delay > MAX_VOTE_DELAY) {
revert InvalidDelay();
}
emit SetVoteDelay(VOTE_DELAY, _delay);
VOTE_DELAY = _delay;
}
/// @notice Set a new Minter
function setMinter(address _minter) external {
VoterAdmin();
if (_minter == address(0)) {
revert ZeroAddress();
}
if (_minter.code.length == 0) {
revert NotContract();
}
emit SetMinter(minter, _minter);
minter = _minter;
}
/// @notice Set options token
/// @dev The base token can become the gauge reward token by setting oToken to address(0)
function setOptionsToken(address _oToken) external {
VoterAdmin();
/// @dev oToken can be set to address(0) to revert to base token
if (_oToken != address(0) && _oToken.code.length == 0) {
revert InvalidTokenAddress();
}
// Revoke old Options token approval
address oldToken = oToken;
if (oldToken != address(0)) IERC20(base).approve(oldToken, 0);
isGaugeDepositor[oldToken] = false;
// Set new Options token. Revert to `base` if _oToken is address(0)
oToken = _oToken;
emit SetOptions(oToken, _oToken);
/// @dev If oToken == address(0), the base token approves will be updated in refreshApprovals()
if(_oToken != address(0)) {
isGaugeDepositor[oToken] = true;
IERC20(base).approve(oToken, type(uint256).max);
}
uint256 stop = pools.length;
/// @dev this is to avoid gas limits. If this happens the function will need to be manually ran externally
if(stop > 100) {
stop = 100;
emit HitRefreshApprovalLimit(0, stop);
}
if (pools.length > 0) refreshApprovals(0, stop, oldToken);
}
/// @notice revoke oldOtoken approval and include new one
/// @param start start index point of the pools array
/// @param finish finish index point of the pools array
/// @param _oldOtoken token to revoke
/// @dev this function is manually used in case we have too many pools and gasLimit is reached
function refreshApprovals(uint256 start, uint256 finish, address _oldOtoken) public nonReentrant {
VoterAdmin();
for (uint256 x = start; x < finish; x++) {
_refreshApproval(gauges[pools[x]], _oldOtoken);
}
}
function _refreshApproval(address _gauge, address _oldOtoken) internal {
if (_oldOtoken != address(0)) {
IERC20(_oldOtoken).approve(_gauge, 0);
}
if (oToken == address(0)) {
IERC20(base).approve(_gauge, type(uint256).max);
} else {
IERC20(base).approve(_gauge, 0);
IERC20(oToken).approve(_gauge, type(uint256).max);
}
}
/// @notice Set depositor that can deposit locked token on behalf of user token
function setGaugeDepositor(address _depositor, bool _enabled) external {
VoterAdmin();
isGaugeDepositor[_depositor] = _enabled;
emit SetDepositor(_depositor, _enabled);
}
/// @notice Set a new Bribe Factory
function setBribeFactory(address _bribeFactory) external {
VoterAdmin();
if (_bribeFactory.code.length == 0) revert NotContract();
if (_bribeFactory == address(0)) revert ZeroAddress();
emit SetBribeFactory(bribefactory, _bribeFactory);
bribefactory = _bribeFactory;
}
/// @notice Set a new PermissionRegistry
function setPermissionsRegistry(address _permissionRegistry) external {
VoterAdmin();
if (_permissionRegistry.code.length == 0) revert NotContract();
if (_permissionRegistry == address(0)) revert ZeroAddress();
emit SetPermissionRegistry(permissionRegistry, _permissionRegistry);
permissionRegistry = _permissionRegistry;
}
/// @notice Set a new bribes for a given gauge
function setNewBribes(address _gauge, address _internal, address _external) external {
VoterAdmin();
if (!isGauge[_gauge]) revert NotGauge();
if (_gauge.code.length == 0) revert NotContract();
_setInternalBribe(_gauge, _internal);
_setExternalBribe(_gauge, _external);
}
/// @notice Set a new internal bribe for a given gauge
function setInternalBribeFor(address _gauge, address _internal) external {
VoterAdmin();
if (!isGauge[_gauge]) revert NotGauge();
_setInternalBribe(_gauge, _internal);
}
/// @notice Set a new External bribe for a given gauge
function setExternalBribeFor(address _gauge, address _external) external {
VoterAdmin();
if (!isGauge[_gauge]) revert NotGauge();
_setExternalBribe(_gauge, _external);
}
function _setInternalBribe(address _gauge, address _internal) private {
if (_internal.code.length == 0) revert NotContract();
emit SetBribeFor(true, internal_bribes[_gauge], _internal, _gauge);
internal_bribes[_gauge] = _internal;
}
function _setExternalBribe(address _gauge, address _external) private {
if (_external.code.length == 0) revert NotContract();
emit SetBribeFor(false, external_bribes[_gauge], _external, _gauge);
external_bribes[_gauge] = _external;
}
/// @notice Get factory configuration for gauge type
function getFactoriesForGaugeType(uint256 _gaugeType)
external view returns (address dexFactory, address gaugeFactory, bool isConfigured)
{
if (!gaugeLogic.isValidGaugeType(_gaugeType) || _gaugeType >= _factories.length) {
return (address(0), address(0), false);
}
dexFactory = _factories[_gaugeType];
gaugeFactory = _gaugeFactories[_gaugeType];
isConfigured = (dexFactory != address(0) && gaugeFactory != address(0));
}
/// @notice Set factory pair for a specific gauge type
/// @param _gaugeType The gauge type enum index to configure
/// @param _pairFactory DEX factory address (address(0) to disable)
/// @param _gaugeFactory Gauge factory address (address(0) to disable)
function setFactory(
uint256 _gaugeType,
address _pairFactory,
address _gaugeFactory
) external override {
VoterAdmin();
// Validation
if (!gaugeLogic.isValidGaugeType(_gaugeType)) revert InvalidGaugeType();
if (_pairFactory != address(0) && _pairFactory.code.length == 0) revert NotContract();
if (_gaugeFactory != address(0) && _gaugeFactory.code.length == 0) revert NotContract();
if (_pairFactory == address(0) && _gaugeFactory != address(0)) revert InconsistentFactoryPair();
if (_pairFactory != address(0) && _gaugeFactory == address(0)) revert InconsistentFactoryPair();
// Expand arrays if needed
while (_factories.length <= _gaugeType) {
_factories.push(address(0));
_gaugeFactories.push(address(0));
}
// Get old values for cleanup and events
address oldPairFactory = _factories[_gaugeType];
address oldGaugeFactory = _gaugeFactories[_gaugeType];
bool wasEnabled = (oldPairFactory != address(0) && oldGaugeFactory != address(0));
bool willBeEnabled = (_pairFactory != address(0) && _gaugeFactory != address(0));
// Cleanup old factory tracking
if (oldPairFactory != address(0)) {
isFactory[oldPairFactory]--;
}
if (oldGaugeFactory != address(0)) {
if (isGaugeFactory[oldGaugeFactory] > 0) {
isGaugeFactory[oldGaugeFactory]--;
}
}
// Set new factories
_factories[_gaugeType] = _pairFactory;
_gaugeFactories[_gaugeType] = _gaugeFactory;
// Update new factory tracking
if (_pairFactory != address(0)) {
/// @dev Enforced that these are updated together
isFactory[_pairFactory]++;
isGaugeFactory[_gaugeFactory]++;
}
// Emit appropriate events
if (!wasEnabled && willBeEnabled) {
emit FactoryEnabled(_gaugeType, _pairFactory, _gaugeFactory);
} else if (wasEnabled && !willBeEnabled) {
emit FactoryDisabled(_gaugeType, oldPairFactory, oldGaugeFactory);
} else if (wasEnabled && willBeEnabled) {
emit FactoryReplaced(_gaugeType, oldPairFactory, oldGaugeFactory, _pairFactory, _gaugeFactory);
}
// Note: if (!wasEnabled && !willBeEnabled) - no event needed for no-op
}
/// @notice Set the gauge logic contract
/// @dev MUST support IVoterV5_GaugeLogic interface
function setGaugeLogic(address _gaugeLogic) external {
VoterAdmin();
_setGaugeLogic(_gaugeLogic);
}
function _setGaugeLogic(address _gaugeLogic) private {
if (!IVoterV5_GaugeLogic(_gaugeLogic).supportsInterface(type(IVoterV5_GaugeLogic).interfaceId)) {
revert NotGaugeLogic();
}
emit SetGaugeLogic(address(gaugeLogic), _gaugeLogic);
gaugeLogic = IVoterV5_GaugeLogic(_gaugeLogic);
}
function setProtocolName(string memory _protocolName) external {
VoterAdmin();
if (bytes(_protocolName).length == 0) {
revert InvalidProtocolName();
}
protocolName = _protocolName;
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GOVERNANCE
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
//--------------------------------------------------------------------------------
// Whitelist/Blacklist for Reward Token
//--------------------------------------------------------------------------------
/// @notice Whitelist/Blacklist tokens for gauge creation
function updateWhitelistToken(address[] memory _tokens, bool _whitelist) external {
Governance();
for (uint256 i = 0; i < _tokens.length; i++) {
_updateWhitelistToken(_tokens[i], _whitelist);
}
}
function _updateWhitelistToken(address _token, bool _whitelist) private {
if (_token.code.length == 0) revert NotContract();
isWhitelisted[_token] = _whitelist;
if (_whitelist) {
emit Whitelisted(msg.sender, _token);
} else {
emit Blacklisted(msg.sender, _token);
}
}
//--------------------------------------------------------------------------------
// Whitelist/Blacklist for Hypervisor Pools
//--------------------------------------------------------------------------------
/// @notice Whitelist/Blacklist pools for gauge creation
function updateWhitelistPool(address[] memory _pools, bool _whitelist) external {
Governance();
for (uint256 i = 0; i < _pools.length; i++) {
_updateWhitelistPool(_pools[i], _whitelist);
}
}
function _updateWhitelistPool(address _pool, bool _whitelist) private {
if (_pool.code.length == 0) revert NotContract();
isWhitelistedPool[_pool] = _whitelist;
if (_whitelist) {
emit WhitelistedPool(msg.sender, _pool);
} else {
emit BlacklistedPool(msg.sender, _pool);
}
}
//--------------------------------------------------------------------------------
// Gauge Kill/Revive
//--------------------------------------------------------------------------------
/// @notice Kill a malicious gauge
/// @param _gauge gauge to kill
function killGauge(address _gauge) external {
Governance();
if (!isAlive[_gauge]) revert GaugeAlreadyKilled();
// disable allowance
IERC20(base).approve(_gauge, 0);
if (oToken != address(0)) {
IERC20(oToken).approve(_gauge, 0);
}
// Return claimable back to minter
uint256 _claimable = _getRewardSharesForGaugeAndUpdateSupplyIndex(_gauge);
if (_claimable > 0) {
IERC20(base).safeTransfer(minter, _claimable); // transfer back to minter to prevent stuck rewards
emit RefundReward(_gauge, _claimable);
}
isAlive[_gauge] = false;
uint256 _time = _epochTimestamp(); // get start of current epoch
totalWeightsPerEpoch[_time] -= weightsPerEpoch[_time][poolForGauge[_gauge]];
weightsPerEpoch[_time][poolForGauge[_gauge]] = 0;
gaugeKilledEpoch[_gauge] = _time;
emit GaugeKilled(_gauge);
}
/// @notice Revive a malicious gauge
/// @param _gauge gauge to revive
function reviveGauge(address _gauge) external {
Governance();
if (isAlive[_gauge]) revert GaugeAlreadyAlive();
if (!isGauge[_gauge]) revert NotGauge();
/// @dev Gauges revived in the same epoch break vote accounting with regards to totalWeightsPerEpoch and _reset()
if (gaugeKilledEpoch[_gauge] >= _epochTimestamp()) revert CannotReviveGaugeInSameEpoch();
isAlive[_gauge] = true;
gaugesDistributionTimestamp[_gauge] = _epochTimestamp();
supplyIndex[_gauge] = index; // Reset the supply index for backward compatibility
// reset allowance
if (oToken != address(0)) {
IERC20(oToken).approve(_gauge, type(uint256).max);
} else {
IERC20(base).approve(_gauge, type(uint256).max);
}
emit GaugeRevived(_gauge);
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
USER INTERACTION
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice Reset the votes of a given TokenID
function reset() external nonReentrant {
address _voter = msg.sender;
_voteDelay(_voter);
_reset(_voter);
lastVoted[_voter] = block.timestamp;
}
function _reset(address _voter) internal {
address[] storage _poolVote = poolVote[_voter];
uint256 _poolVoteCnt = _poolVote.length;
uint256 _totalWeight = 0;
uint256 _time = _epochTimestamp();
bool votedInEpoch = lastVoted[_voter] > _time;
for (uint256 i = 0; i < _poolVoteCnt; i++) {
address _pool = _poolVote[i];
uint256 _votes = votes[_voter][_pool];
if (_votes != 0) {
// if user last vote is < than epochTimestamp then votes are 0! IF not underflow occur
// killGauge resets the weightsPerEpoch, so we don't need to do it here if killed
/// @dev Killed gauges need to wait until the next epoch to be revived to prevent underflow
if (votedInEpoch && isAlive[gauges[_pool]]) weightsPerEpoch[_time][_pool] -= _votes;
votes[_voter][_pool] -= _votes;
// if is alive remove _votes, else don't because we already done it in killGauge()
if (isAlive[gauges[_pool]]) _totalWeight += _votes;
emit Abstained(_voter, _votes);
IBribe(internal_bribes[gauges[_pool]]).withdraw(uint256(_votes), _voter);
IBribe(external_bribes[gauges[_pool]]).withdraw(uint256(_votes), _voter);
}
}
// if user last vote is < than epochTimestamp then _totalWeight is 0! IF not underflow occur
if (lastVoted[_voter] < _time) _totalWeight = 0;
totalWeightsPerEpoch[_time] -= _totalWeight;
delete poolVote[_voter];
}
/// @notice Recast the saved votes of a given TokenID
function poke() external nonReentrant {
address _voter = msg.sender;
_voteDelay(_voter);
address[] memory _poolVote = poolVote[_voter];
uint256 _poolCnt = _poolVote.length;
uint256[] memory _weights = new uint256[](_poolCnt);
for (uint256 i = 0; i < _poolCnt; i++) {
_weights[i] = votes[_voter][_poolVote[i]];
}
_vote(_voter, _poolVote, _weights);
lastVoted[_voter] = block.timestamp;
}
/// @notice Vote for pools
/// @param _poolVote array of LPs addresses to vote (eg.: [sAMM usdc-usdt , sAMM busd-usdt, vAMM wbnb-the ,...])
/// @param _voteProportions array of vote proportions for each LPs (eg.: [10, 90, 45,...])
function vote(address[] calldata _poolVote, uint256[] calldata _voteProportions) external nonReentrant {
_voteDelay(msg.sender);
if (_poolVote.length != _voteProportions.length) revert LengthMismatch();
_vote(msg.sender, _poolVote, _voteProportions);
lastVoted[msg.sender] = block.timestamp;
}
function _vote(address _voter, address[] memory _poolVote, uint256[] memory _voteProportions) internal {
_reset(_voter);
uint256 _poolCnt = _poolVote.length;
uint256 _time = _epochTimestamp();
uint256 _weight = IVotingEscrowV2(_ve).getPastVotes(_voter, _time);
uint256 _totalVoteProportions = 0;
uint256 _totalWeight = 0;
uint256 _usedWeight = 0;
for (uint i = 0; i < _poolCnt; i++) {
if (isAlive[gauges[_poolVote[i]]]) _totalVoteProportions += _voteProportions[i];
}
for (uint256 i = 0; i < _poolCnt; i++) {
address _pool = _poolVote[i];
address _gauge = gauges[_pool];
if (isGauge[_gauge] && isAlive[_gauge]) {
uint256 _poolWeight = (_voteProportions[i] * _weight) / _totalVoteProportions;
if (votes[_voter][_pool] != 0) revert VotedAlready();
if (_poolWeight == 0) revert InsufficientVotingPower();
poolVote[_voter].push(_pool);
weightsPerEpoch[_time][_pool] += _poolWeight;
votes[_voter][_pool] += _poolWeight;
_usedWeight += _poolWeight;
_totalWeight += _poolWeight;
emit Voted(msg.sender, _poolWeight);
IBribe(internal_bribes[_gauge]).deposit(uint256(_poolWeight), _voter);
IBribe(external_bribes[_gauge]).deposit(uint256(_poolWeight), _voter);
}
}
if (_weight < _totalWeight) revert InsufficientVotingPower();
totalWeightsPerEpoch[_time] += _totalWeight;
}
/// @notice check if user can vote
function _voteDelay(address _voter) internal view {
if (!(block.timestamp > lastVoted[_voter] + VOTE_DELAY && block.timestamp > _epochTimestamp())) {
revert VoteDelayNotMet();
}
}
/* -----------------------------------------------------------------------------
USER CLAIM GAUGE HELPERS
----------------------------------------------------------------------------- */
/// @notice claim LP gauge rewards
function claimRewards(address[] memory _gauges) external override {
_claimRewardsFor(_gauges, msg.sender);
}
/// @notice claim LP gauge rewards for a given address
function claimRewardsFor(address[] memory _gauges, address _claimFor) external override {
if (!IVotingEscrowV2(_ve).isClaimApprovedForAll(_claimFor, msg.sender)) revert NotApprovedOrOwner();
_claimRewardsFor(_gauges, _claimFor);
}
function _claimRewardsFor(address[] memory _gauges, address _claimFor) internal {
for (uint256 i = 0; i < _gauges.length; i++) {
IGauge(_gauges[i]).getReward(_claimFor);
}
}
function claimRewardTokens(address[] memory _gauges, address[][] memory _tokens) external override {
_claimRewardTokensFor(_gauges, _tokens, msg.sender);
}
function claimRewardTokensFor(address[] memory _gauges, address[][] memory _tokens, address _claimFor) external override {
if (!IVotingEscrowV2(_ve).isClaimApprovedForAll(_claimFor, msg.sender)) revert NotApprovedOrOwner();
_claimRewardTokensFor(_gauges, _tokens, _claimFor);
}
function _claimRewardTokensFor(address[] memory _gauges, address[][] memory _tokens, address _claimFor) internal {
for (uint256 i = 0; i < _gauges.length; i++) {
IGauge(_gauges[i]).getReward(_claimFor, _tokens[i]);
}
}
/// @notice claim bribes rewards given a TokenID
function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 _tokenId) external override {
if (!IVotingEscrowV2(_ve).isApprovedClaimOrOwner(msg.sender, _tokenId)) revert NotApprovedOrOwner();
if (_bribes.length != _tokens.length) revert LengthMismatch();
for (uint256 i = 0; i < _bribes.length; i++) {
IBribe(_bribes[i]).getRewardForOwner(_tokenId, _tokens[i]);
}
}
/// @notice claim fees rewards given a TokenID
function claimFees(address[] memory _fees, address[][] memory _tokens, uint256 _tokenId) external override {
if (!IVotingEscrowV2(_ve).isApprovedClaimOrOwner(msg.sender, _tokenId)) revert NotApprovedOrOwner();
if (_fees.length != _tokens.length) revert LengthMismatch();
for (uint256 i = 0; i < _fees.length; i++) {
IBribe(_fees[i]).getRewardForOwner(_tokenId, _tokens[i]);
}
}
/// @notice claim bribes rewards given an address
function claimBribes(address[] memory _bribes, address[][] memory _tokens) external override {
if (_bribes.length != _tokens.length) revert LengthMismatch();
for (uint256 i = 0; i < _bribes.length; i++) {
IBribe(_bribes[i]).getRewardForAddress(msg.sender, _tokens[i]);
}
}
/// @notice claim fees rewards given an address
function claimFees(address[] memory _fees, address[][] memory _tokens) external override {
if (_fees.length != _tokens.length) revert LengthMismatch();
for (uint256 i = 0; i < _fees.length; i++) {
IBribe(_fees[i]).getRewardForAddress(msg.sender, _tokens[i]);
}
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GAUGE CREATION
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice create multiple gauges
function createGauges(
address[] memory _pool,
uint256[] memory _gaugeTypes
) external nonReentrant returns (address[] memory, address[] memory, address[] memory) {
if (_pool.length != _gaugeTypes.length) revert LengthMismatch();
if (_pool.length > 10) revert ExceedsMaxGauges();
address[] memory _gauge = new address[](_pool.length);
address[] memory _int = new address[](_pool.length);
address[] memory _ext = new address[](_pool.length);
uint256 i = 0;
for (i; i < _pool.length; i++) {
(_gauge[i], _int[i], _ext[i]) = _createGauge(_pool[i], _gaugeTypes[i]);
}
return (_gauge, _int, _ext);
}
/// @notice create a gauge
function createGauge(
address _pool,
uint256 _gaugeType
) external nonReentrant returns (address _gauge, address _internal_bribe, address _external_bribe) {
(_gauge, _internal_bribe, _external_bribe) = _createGauge(_pool, _gaugeType);
}
/// @notice create a gauge
/// @param _pool LP address
/// @param _gaugeType the type of the gauge you want to create
/// @dev Logic offloaded to VoterV5_GaugeLogic contract to bring contract size into 24kb range.
/// See gaugeLogic contract for more details.
function _createGauge(
address _pool,
uint256 _gaugeType
) internal returns (address _gauge, address _internal_bribe, address _external_bribe) {
(bool success, bytes memory initialResult) = address(gaugeLogic).delegatecall(
abi.encodeWithSelector(IVoterV5_GaugeLogic.createGauge.selector, _pool, _gaugeType)
);
bytes memory result = DelegateCallLib.handleDelegateCallResult(success, initialResult);
// Decode the result
(_gauge, _internal_bribe, _external_bribe) = abi.decode(result, (address, address, address));
emit GaugeCreated(_gauge, msg.sender, _internal_bribe, _external_bribe, _pool);
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
VIEW FUNCTIONS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice view the total length of the pools
function length() external view returns (uint256) {
return pools.length;
}
/// @notice view the total length of the voted pools given a tokenId
function poolVoteLength(address voter) external view returns (uint256) {
return poolVote[voter].length;
}
function factories() external view returns (address[] memory) {
return _factories;
}
function factoryLength() external view returns (uint256) {
return _factories.length;
}
function gaugeFactories() external view returns (address[] memory) {
return _gaugeFactories;
}
function gaugeFactoriesLength() external view returns (uint256) {
return _gaugeFactories.length;
}
function weights(address _pool) public view returns (uint256) {
uint256 _time = _epochTimestamp();
return weightsPerEpoch[_time][_pool];
}
function weightsAt(address _pool, uint256 _time) public view returns (uint256) {
return weightsPerEpoch[_time][_pool];
}
function totalWeight() public view returns (uint256) {
uint256 _time = _epochTimestamp();
return totalWeightsPerEpoch[_time];
}
function totalWeightAt(uint256 _time) public view returns (uint256) {
return totalWeightsPerEpoch[_time];
}
/// @notice Returns the start time of the current epoch
function _epochTimestamp() public view returns (uint256) {
return IMinter(minter).active_period();
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
DISTRIBUTION
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice notify reward amount for gauge
/// @dev Called by the minter once after an epoch flip.
/// - DOES NOT support fee on transfer tokens
/// @param amount amount to distribute
function notifyRewardAmount(uint256 amount) external {
if (msg.sender != minter) revert NotAuthorized();
uint256 _previousEpoch = _epochTimestamp() - Constants.EPOCH;
uint256 _totalWeight = totalWeightAt(_previousEpoch); // minter call notify after updates active_period, loads votes - 1 week
uint256 _ratio = 0;
if (_totalWeight > 0) _ratio = (amount * 1e18) / _totalWeight; // 1e18 adjustment is removed during claim
if (_ratio > 0) {
index += _ratio;
}
emit NotifyReward(msg.sender, base, amount);
IERC20(base).safeTransferFrom(msg.sender, address(this), amount);
}
/// @notice distribute the LP Fees to the internal bribes
/// @param _gauges gauge address where to claim the fees
/// @dev the gauge is the owner of the LPs so it has to claim
function distributeFees(address[] memory _gauges) external {
for (uint256 i = 0; i < _gauges.length; i++) {
if (isGauge[_gauges[i]] && isAlive[_gauges[i]]) {
IGauge(_gauges[i]).claimFees();
}
}
}
/// @notice Distribute the emission for ALL gauges
/// @dev Generally called once after EPOCH flip
function distributeAll() external nonReentrant {
IMinter(minter).update_period();
uint256 x = 0;
uint256 stop = pools.length;
for (x; x < stop; x++) {
_distribute(gauges[pools[x]]);
}
}
/// @notice distribute the emission for N gauges
/// @param start start index point of the pools array
/// @param finish finish index point of the pools array
/// @dev this function is used in case we have too many pools and gasLimit is reached
/// - Generally called once after EPOCH flip
function distribute(uint256 start, uint256 finish) public nonReentrant {
IMinter(minter).update_period();
for (uint256 x = start; x < finish; x++) {
_distribute(gauges[pools[x]]);
}
}
/// @notice distribute reward only for given gauges
/// @dev this function is used in case some distribution fails
/// - Generally called once after EPOCH flip
function distribute(address[] memory _gauges) external nonReentrant {
IMinter(minter).update_period();
for (uint256 x = 0; x < _gauges.length; x++) {
_distribute(_gauges[x]);
}
}
/// @notice distribute available emissions for a given gauge
function _distribute(address _gauge) internal {
uint256 _claimable = _getRewardSharesForGaugeAndUpdateSupplyIndex(_gauge); // claimable is zero if already updated or killed
// distribute only if claimable is > 0 and gauge is alive
if (_claimable > 0) {
if (isAlive[_gauge]) {
/// @dev approvals set to MAX_UINT256 in _createGauge()
if (oToken != address(0)) {
IOptionTokenV3(oToken).mint(address(this), _claimable);
IGauge(_gauge).notifyRewardAmount(oToken, _claimable);
} else {
IGauge(_gauge).notifyRewardAmount(base, _claimable);
}
emit DistributeReward(msg.sender, _gauge, _claimable);
} else {
IERC20(base).safeTransfer(minter, _claimable);
emit RefundReward(_gauge, _claimable);
}
}
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
HELPERS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice Update Gauge reward index per vote weight and return accumulated rewards
function _getRewardSharesForGaugeAndUpdateSupplyIndex(address _gauge) private returns (uint256 rewardSharesForGauge) {
uint256 _gaugeRewardSupplyIndex = supplyIndex[_gauge];
uint256 _globalRewardIndex = index;
supplyIndex[_gauge] = _globalRewardIndex; // update _gauge current position to global position
uint256 _previousEpoch = _epochTimestamp() - Constants.EPOCH;
uint256 _previousEpochPoolWeight = weightsPerEpoch[_previousEpoch][poolForGauge[_gauge]];
if (_previousEpochPoolWeight > 0) {
uint256 _delta = _globalRewardIndex - _gaugeRewardSupplyIndex; // see if there is any difference that need to be accrued
if (_delta > 0) {
rewardSharesForGauge = (_previousEpochPoolWeight * _delta) / 1e18; // add accrued difference for each supplied token
}
}
}
function ve() external view returns (address) {
return _ve;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title The interface for the Algebra Factory
* @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraFactory {
/**
* @notice Emitted when the owner of the factory is changed
* @param newOwner The owner after the owner was changed
*/
event Owner(address indexed newOwner);
/**
* @notice Emitted when the vault address is changed
* @param newVaultAddress The vault address after the address was changed
*/
event VaultAddress(address indexed newVaultAddress);
/**
* @notice Emitted when a pool is created
* @param token0 The first token of the pool by address sort order
* @param token1 The second token of the pool by address sort order
* @param pool The address of the created pool
*/
event Pool(address indexed token0, address indexed token1, address pool);
/**
* @notice Emitted when the farming address is changed
* @param newFarmingAddress The farming address after the address was changed
*/
event FarmingAddress(address indexed newFarmingAddress);
event FeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
);
/**
* @notice Returns the current owner of the factory
* @dev Can be changed by the current owner via setOwner
* @return The address of the factory owner
*/
function owner() external view returns (address);
/**
* @notice Returns the current poolDeployerAddress
* @return The address of the poolDeployer
*/
function poolDeployer() external view returns (address);
/**
* @dev Is retrieved from the pools to restrict calling
* certain functions not by a tokenomics contract
* @return The tokenomics contract address
*/
function farmingAddress() external view returns (address);
function vaultAddress() external view returns (address);
/**
* @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
* @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
* @param tokenA The contract address of either token0 or token1
* @param tokenB The contract address of the other token
* @return pool The pool address
*/
function poolByPair(address tokenA, address tokenB) external view returns (address pool);
/**
* @notice Creates a pool for the given two tokens and fee
* @param tokenA One of the two tokens in the desired pool
* @param tokenB The other of the two tokens in the desired pool
* @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
* from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
* are invalid.
* @return pool The address of the newly created pool
*/
function createPool(address tokenA, address tokenB) external returns (address pool);
/**
* @notice Updates the owner of the factory
* @dev Must be called by the current owner
* @param _owner The new owner of the factory
*/
function setOwner(address _owner) external;
/**
* @dev updates tokenomics address on the factory
* @param _farmingAddress The new tokenomics contract address
*/
function setFarmingAddress(address _farmingAddress) external;
/**
* @dev updates vault address on the factory
* @param _vaultAddress The new vault contract address
*/
function setVaultAddress(address _vaultAddress) external;
/**
* @notice Changes initial fee configuration for new pools
* @dev changes coefficients for sigmoids: α / (1 + e^( (β-x) / γ))
* alpha1 + alpha2 + baseFee (max possible fee) must be <= type(uint16).max
* gammas must be > 0
* @param alpha1 max value of the first sigmoid
* @param alpha2 max value of the second sigmoid
* @param beta1 shift along the x-axis for the first sigmoid
* @param beta2 shift along the x-axis for the second sigmoid
* @param gamma1 horizontal stretch factor for the first sigmoid
* @param gamma2 horizontal stretch factor for the second sigmoid
* @param volumeBeta shift along the x-axis for the outer volume-sigmoid
* @param volumeGamma horizontal stretch factor the outer volume-sigmoid
* @param baseFee minimum possible fee
*/
function setBaseFeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../IERC721Upgradeable.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721EnumerableUpgradeable is IERC721Upgradeable {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721Upgradeable is IERC165Upgradeable {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165Upgradeable {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @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;
}// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @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);
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title The interface for the Uniswap V3 Factory
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
interface IUniswapV3Factory {
/// @notice Emitted when the owner of the factory is changed
/// @param oldOwner The owner before the owner was changed
/// @param newOwner The owner after the owner was changed
event OwnerChanged(address indexed oldOwner, address indexed newOwner);
/// @notice Emitted when a pool is created
/// @param token0 The first token of the pool by address sort order
/// @param token1 The second token of the pool by address sort order
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks
/// @param pool The address of the created pool
event PoolCreated(
address indexed token0,
address indexed token1,
uint24 indexed fee,
int24 tickSpacing,
address pool
);
/// @notice Emitted when a new fee amount is enabled for pool creation via the factory
/// @param fee The enabled fee, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);
/// @notice Returns the current owner of the factory
/// @dev Can be changed by the current owner via setOwner
/// @return The address of the factory owner
function owner() external view returns (address);
/// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
/// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
/// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
/// @return The tick spacing
function feeAmountTickSpacing(uint24 fee) external view returns (int24);
/// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @return pool The pool address
function getPool(
address tokenA,
address tokenB,
uint24 fee
) external view returns (address pool);
/// @notice Creates a pool for the given two tokens and fee
/// @param tokenA One of the two tokens in the desired pool
/// @param tokenB The other of the two tokens in the desired pool
/// @param fee The desired fee for the pool
/// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
/// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
/// are invalid.
/// @return pool The address of the newly created pool
function createPool(
address tokenA,
address tokenB,
uint24 fee
) external returns (address pool);
/// @notice Updates the owner of the factory
/// @dev Must be called by the current owner
/// @param _owner The new owner of the factory
function setOwner(address _owner) external;
/// @notice Enables a fee amount with the given tickSpacing
/// @dev Fee amounts may never be removed once enabled
/// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
/// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
library Constants {
uint48 constant EPOCH = 1 weeks;
}/// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { PoolKey } from "./PoolKey.sol";
// import {IImmutableState} from "@uniswap/v4-periphery/src/interfaces/IImmutableState.sol";
interface IImmutableState {
/// @notice The Uniswap v4 PoolManager contract
function poolManager() external view returns (address /*IPoolManager*/);
}
/**
* @title IMultiPositionManager
* @author Gamma
* @notice This interface is used to interact with Gamma ALM strategies which use UniswapV4 pools as concentrated liquidity positions.
*/
interface IMultiPositionManager is IERC20, IImmutableState {
struct Position {
PoolKey poolKey;
int24 lowerTick;
int24 upperTick;
}
struct PositionData {
uint128 liquidity;
uint256 amount0;
uint256 amount1;
}
function getPositions() external view returns (
Position[] memory,
PositionData[] memory
);
function basePositionsLength() external view returns (uint256);
function token0() external view returns (IERC20);
function token1() external view returns (IERC20);
function getTotalAmounts() external view returns (
uint256 total0,
uint256 total1,
uint256 totalFee0,
uint256 totalFee1
);
function currentTicks() external view returns (int24[] memory);
function rebalance(
Position[] memory baseRanges,
uint128[] memory liquidities,
int24 limitWidth,
uint256[2][] memory inMin,
uint256[2][] memory outMin,
int24 aimTick,
uint24 tickOffset
) external;
function compound(
uint128[] memory liquidities,
uint256[2][] memory inMin,
int24 aimTick,
uint24 tickOffset
) external;
function claimFee() external;
function setWhitelist(address _whitelist) external;
function setFeeRecipient(address _feeRecipient) external;
function setFee(uint16 fee) external;
// function setTickOffset(uint24 offset) external;
function deposit(
uint256 deposit0Desired,
uint256 deposit1Desired,
address to,
address from
) external payable returns (uint256, uint256, uint256);
function zeroBurnAll() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {PoolKey} from "./PoolKey.sol";
type PoolId is bytes32;
/// @notice Library for computing the ID of a pool
library PoolIdLibrary {
/// @notice Returns value equal to keccak256(abi.encode(poolKey))
function toId(PoolKey memory poolKey) internal pure returns (PoolId poolId) {
assembly ("memory-safe") {
// 0xa0 represents the total size of the poolKey struct (5 slots of 32 bytes)
poolId := keccak256(poolKey, 0xa0)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {PoolIdLibrary} from "./PoolIdLibrary.sol";
// import {Currency} from "./Currency.sol";
// import {IHooks} from "../interfaces/IHooks.sol";
using PoolIdLibrary for PoolKey global;
/// @notice Returns the key for identifying a pool
struct PoolKey {
/// @notice The lower currency of the pool, sorted numerically
address currency0; // NOTE: Should be Currency
/// @notice The higher currency of the pool, sorted numerically
address currency1; // NOTE: Should be Currency
/// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1, the pool has a dynamic fee and must be exactly equal to 0x800000
uint24 fee;
/// @notice Ticks that involve positions must be a multiple of tick spacing
int24 tickSpacing;
/// @notice The hooks of the pool
address hooks; // NOTE: Should be IHooks
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IBribeFactory {
function createInternalBribe(address[] memory) external returns (address);
function createExternalBribe(address[] memory) external returns (address);
function createBribe(address _owner,address _token0,address _token1, string memory _type) external returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
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);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IGauge {
function notifyRewardAmount(address token, uint amount) external;
function getReward(address account, address[] memory tokens) external;
function getReward(address account) external;
function claimFees() external returns (uint claimed0, uint claimed1);
function rewardRate(address _pair) external view returns (uint);
function balanceOf(address _account) external view returns (uint);
function isForPair() external view returns (bool);
function totalSupply() external view returns (uint);
function earned(address token, address account) external view returns (uint);
function stakeToken() external view returns (address);
function setDistribution(address _distro) external;
function addRewardToken(address _rewardToken) external;
function removeRewardToken(address _rewardToken) external;
function updateRewardToken() external;
function activateEmergencyMode() external;
function stopEmergencyMode() external;
function setInternalBribe(address intbribe) external;
function setGaugeRewarder(address _gr) external;
function setFeeVault(address _feeVault) external;
function depositWithLock(address account, uint256 amount, uint256 _lockDuration) external;
function sweepTokens(address[] memory tokens, uint256[] memory amounts, address to) external;
function initialize(address _rewardToken,address _ve,address _stakeToken,address _distribution, address _internal_bribe, address _external_bribe, bool _isForPair) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IGaugeFactory {
function createGauge(address, address, address, address, bool, address[] memory) external returns (address);
function createGaugeV2(address _rewardToken,address _ve,address _token,address _distribution, address _internal_bribe, address _external_bribe, bool _isPair) external returns (address) ;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IHypervisor {
function pool() external view returns(address);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IMinter {
function update_period() external returns (uint256);
function check() external view returns(bool);
function period() external view returns(uint256);
function active_period() external view returns(uint256);
function WEEK() external view returns(uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IPair {
function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1);
function claimFees() external returns (uint, uint);
function tokens() external view returns (address, address);
function token0() external view returns (address);
function token1() external view returns (address);
function fees() external view returns (address);
function transferFrom(address src, address dst, uint amount) external returns (bool);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function burn(address to) external returns (uint amount0, uint amount1);
function mint(address to) external returns (uint liquidity);
function getReserves() external view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast);
function getAmountOut(uint amountIn, address tokenIn) external view returns (uint);
function name() external view returns(string memory);
function symbol() external view returns(string memory);
function totalSupply() external view returns (uint);
function decimals() external view returns (uint8);
function claimable0(address _user) external view returns (uint);
function claimable1(address _user) external view returns (uint);
function isStable() external view returns(bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IPairFactory {
function allPairsLength() external view returns (uint);
function isPair(address pair) external view returns (bool);
function getFee(bool) external view returns (uint);
function allPairs(uint index) external view returns (address);
function feeManager() external view returns (address);
function pairCodeHash() external pure returns (bytes32);
function getPair(address tokenA, address token, bool stable) external view returns (address);
function createPair(address tokenA, address tokenB, bool stable) external returns (address pair);
function getInitializable() external view returns (address, address, bool);
function MAX_REFERRAL_FEE() external view returns(uint);
function dibs() external view returns(address);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IPairInfo {
function token0() external view returns(address);
function reserve0() external view returns(uint);
function decimals0() external view returns(uint);
function token1() external view returns(address);
function reserve1() external view returns(uint);
function decimals1() external view returns(uint);
function isPair(address _pair) external view returns(bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IPermissionsRegistry {
function adminMultisig() external view returns (address);
function teamMultisig() external view returns (address);
function emergencyCouncil() external view returns (address);
/// @notice Check if an address has a bytes role
function hasRole(bytes memory role, address caller) external view returns (bool);
/// @notice Check if an address has a role
function hasRoleString(string memory role, address _user) external view returns(bool);
/// @notice Read roles and return array of role strings
function rolesToString() external view returns(string[] memory __roles);
/// @notice Read roles return an array of roles in bytes
function roles() external view returns(bytes[] memory);
/// @notice Read the number of roles
function rolesLength() external view returns(uint);
/// @notice Return addresses for a given role
function roleToAddresses(string memory role) external view returns(address[] memory _addresses);
/// @notice Return roles for a given address
function addressToRole(address _user) external view returns(string[] memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IVersionable {
function VERSION() external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library DelegateCallLib {
/**
* @dev Handles the result of a delegatecall and reverts with the revert reason if the call failed.
* @param success The success flag returned by the delegatecall.
* @param result The result bytes returned by the delegatecall.
* @return The result bytes if the delegatecall was successful.
*/
function handleDelegateCallResult(bool success, bytes memory result) internal pure returns (bytes memory) {
if (success) {
return result;
} else {
// If the result length is less than 68, then the transaction failed silently (without a revert reason)
if (result.length < 68) revert("delegatecall failed without a revert reason");
assembly {
// Slice the sighash to remove the function selector
result := add(result, 0x04)
}
// All that remains is the revert string
revert(abi.decode(result, (string)));
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IUniswapV3Factory} from '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol';
/// @title UniswapV3Helper
/// @notice Helper library for UniswapV3 pool validation and fee tier management
/// @dev Used to abstract UniswapV3-specific logic from gauge creation
library UniswapV3Helper {
/// @notice Standard UniswapV3 fee tiers (in basis points)
uint24 public constant FEE_LOW = 500; // 0.05%
uint24 public constant FEE_MEDIUM = 3000; // 0.30%
uint24 public constant FEE_HIGH = 10000; // 1.00%
/// @notice Validates that a pool exists in UniswapV3 factory for given tokens
/// @param _factory The UniswapV3Factory address
/// @param _tokenA Token A address
/// @param _tokenB Token B address
/// @param _expectedPool The expected pool address to validate
/// @return isValid True if the pool exists in the factory for any standard fee tier
function validatePoolExists(
address _factory,
address _tokenA,
address _tokenB,
address _expectedPool
) internal view returns (bool isValid) {
// Check if the pool matches any of the standard fee tiers
uint24[3] memory fees = [FEE_LOW, FEE_MEDIUM, FEE_HIGH];
for (uint256 i = 0; i < fees.length; i++) {
address factoryPool = IUniswapV3Factory(_factory).getPool(_tokenA, _tokenB, fees[i]);
if (_expectedPool == factoryPool && factoryPool != address(0)) {
return true;
}
}
return false;
}
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.0;
interface IDynamicTwapOracle {
/**
* @notice Get the address of the pool
* @return The address of the pool
*/
function pool() external view returns (address);
/**
* @notice Get the address of the first token in the pool
* @return The address of the first token
*/
function token0() external view returns (address);
/**
* @notice Get the address of the second token in the pool
* @return The address of the second token
*/
function token1() external view returns (address);
/**
* @notice Estimate the output amount of a trade
* @param tokenIn The address of the input token
* @param amountIn The amount of the input token
* @param secondsAgo The number of seconds ago to start the TWAP
* @return amountOut The estimated output amount
*/
function estimateAmountOut(
address tokenIn,
uint128 amountIn,
uint32 secondsAgo
) external view returns (uint amountOut);
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.13;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
interface IOption is IAccessControl {
function paymentToken() external view returns (IERC20);
function getPaymentAmount(uint256 _amount, bytes calldata _data) external view returns (uint256);
function exercise(uint256 _amount, address sender, bytes calldata _data) external returns (uint256);
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.13;
interface IOptionFeeDistributor {
function distribute(address token, uint256 amount) external;
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.13;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {IDynamicTwapOracle} from "./DynamicTwapOracle/IDynamicTwapOracle.sol";
import {IOptionFeeDistributor} from "./IOptionFeeDistributor.sol";
import {IPair} from "../interfaces/IPair.sol";
import {IOption} from "./IOption.sol";
interface IOptionTokenV3 is IERC20, IAccessControl {
function ADMIN_ROLE() external view returns (bytes32);
function MINTER_ROLE() external view returns (bytes32);
function PAUSER_ROLE() external view returns (bytes32);
function paymentToken() external view returns (IERC20);
function UNDERLYING_TOKEN() external view returns (IERC20);
function voter() external view returns (address);
function mint(address _to, uint256 _amount) external;
function exercise(uint256 _amount, uint256 _maxPaymentAmount, address _recipient) external returns (uint256);
function exercise(uint256 _amount, uint256 _maxPaymentAmount, address _recipient, uint256 _deadline) external returns (uint256);
function exerciseVe(uint256 _amount, uint256 _maxPaymentAmount, address _recipient, uint256 _discount, uint256 _deadline) external returns (uint256, uint256);
function exerciseLp(uint256 _amount, uint256 _maxPaymentAmount, uint256 _maxLPAmount, address _recipient, uint256 _discount, uint256 _deadline) external returns (uint256, uint256);
function exerciseExternal(IOption _option, uint256 _amount, uint256 _deadline, bytes calldata _data) external returns (uint256);
function getVotingEscrow() external view returns (address votingEscrow);
function getLockDurationForVeDiscount(uint256 _discount) external view returns (uint256 duration);
function getSlopeInterceptForVeDiscount() external view returns (int256 slope, int256 intercept);
function togglePermissionedMint() external;
function toggleOption(address option, bool enabled) external;
function getDiscountedPrice(uint256 _amount) external view returns (uint256);
function getDiscountedPrice(uint256 _amount, uint256 _discount) external view returns (uint256);
function getLockDurationForLpDiscount(uint256 _amount) external view returns (uint256);
function getPaymentTokenAmountForExerciseLp(
uint256 _amount,
uint256 _discount
) external view returns (uint256, uint256);
function getSlopeInterceptForLpDiscount() external view returns (int256, int256);
function getTimeWeightedAveragePrice(uint256 _amount) external view returns (uint256);
function setTwapOracleAndPaymentToken(IDynamicTwapOracle _twapOracle, address _paymentToken) external;
function setPairAndPaymentToken(IPair _pair, address _paymentToken) external;
function setFeeDistributor(IOptionFeeDistributor _feeDistributor) external;
function setDiscount(uint256 _discount) external;
function setVeDiscount(uint256 _veDiscount) external;
function setMinLPDiscount(uint256 _lpMinDiscount) external;
function setMaxLPDiscount(uint256 _lpMaxDiscount) external;
function setLockDurationForMaxLpDiscount(uint256 _duration) external;
function setLockDurationForMinLpDiscount(uint256 _duration) external;
function setTwapSeconds(uint32 _twapSeconds) external;
function burn(uint256 _amount) external;
function updateGauge() external;
function setGauge(address _gauge) external;
function setRouter(address _router) external;
function unPause() external;
function pause() external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
interface IBribe is IERC165 {
function deposit(uint amount, address account) external;
function withdraw(uint amount, address account) external;
function getRewardForOwner(uint tokenId, address[] memory tokens) external;
function getRewardForAddress(address _owner, address[] memory tokens) external;
function notifyRewardAmount(address token, uint amount) external;
function addRewardToken(address _rewardsToken) external;
function addRewardTokens(address[] memory _rewardsToken) external;
function setVoter(address _Voter) external;
function setMinter(address _Voter) external;
function setOwner(address _Voter) external;
function emergencyRecoverERC20(address tokenAddress, uint256 tokenAmount) external;
function recoverERC20AndUpdateData(address tokenAddress, uint256 tokenAmount) external;
}
interface IBribe_Init is IBribe {
function initialize(address _owner, address _voter, address _bribeFactory, string memory _type) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IVoterV5_ClaimHelper
* @notice Interface to claim rewards from LP gauges and bribes from VoterV5
*/
interface IVoterV5_ClaimHelper {
/// @notice claim LP gauge rewards
function claimRewards(address[] memory _gauges) external;
/// @notice claim LP gauge rewards for a given address
function claimRewardsFor(address[] memory _gauges, address _claimFor) external;
/// @notice claim specific reward tokens from LP gauges
function claimRewardTokens(address[] memory _gauges, address[][] memory _tokens) external;
/// @notice claim specific reward tokens from LP gauges for a given address
function claimRewardTokensFor(address[] memory _gauges, address[][] memory _tokens, address _claimFor) external;
/// @notice claim bribes rewards given a TokenID
function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 _tokenId) external;
/// @notice claim fees rewards given a TokenID
function claimFees(address[] memory _fees, address[][] memory _tokens, uint256 _tokenId) external;
/// @notice claim bribes rewards given an address
function claimBribes(address[] memory _bribes, address[][] memory _tokens) external;
/// @notice claim fees rewards given an address
function claimFees(address[] memory _fees, address[][] memory _tokens) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
/// @title IVoterV5_GaugeLogic
interface IVoterV5_GaugeLogic is IERC165 {
function createGauge(
address _pool,
uint256 _gaugeType
) external returns (address _gauge, address _internal_bribe, address _external_bribe);
function isValidGaugeType(uint256 _gaugeType) external pure returns (bool);
function MAX_GAUGE_TYPE() external pure returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IVoterV5_GaugeLogic} from "./VoterV5_GaugeLogic.sol";
import {IVoterV5_ClaimHelper} from "./IVoterV5_ClaimHelper.sol";
import {IBribe} from "./IBribe.sol";
import {IGauge} from "../interfaces/IGauge.sol";
import {IOptionTokenV3} from "../OptionToken/IOptionTokenV3.sol";
import {IMinter} from "../interfaces/IMinter.sol";
import {IPermissionsRegistry} from "../interfaces/IPermissionsRegistry.sol";
import {IVotingEscrowV2} from "./VotingEscrow/IVotingEscrowV2.sol";
/// @title IVoterV5_Logic
/// @notice Interface to manage the functionality of the VoterV5 contract
/// @custom:version 2.0.0
/// - Replace addFactory, removeFactory, replaceFactory with setFactory to support GaugeType enum
interface IVoterV5_Logic is IVoterV5_ClaimHelper {
// Initialization
function initialize(
address __ve,
address _pairFactory,
address _gaugeFactory,
address _bribes,
address _gaugeLogic,
string memory _protocolName
) external;
function _init(address[] memory _tokens, address _permissionsRegistry, address _minter, address _oToken) external;
// Role Management
function setVoteDelay(uint256 _delay) external;
function setMinter(address _minter) external;
function setOptionsToken(address _oToken) external;
function refreshApprovals(uint256 start, uint256 finish, address _oldOtoken) external;
function setGaugeDepositor(address _depositor, bool _enabled) external;
function setBribeFactory(address _bribeFactory) external;
function setPermissionsRegistry(address _permissionRegistry) external;
function setNewBribes(address _gauge, address _internal, address _external) external;
function setInternalBribeFor(address _gauge, address _internal) external;
function setExternalBribeFor(address _gauge, address _external) external;
function setFactory(uint256 _gaugeType, address _pairFactory, address _gaugeFactory) external;
// Governance
function updateWhitelistToken(address[] memory _tokens, bool _whitelist) external;
function updateWhitelistPool(address[] memory _pools, bool _whitelist) external;
function killGauge(address _gauge) external;
function reviveGauge(address _gauge) external;
// User Interaction
function reset() external;
function poke() external;
function vote(address[] calldata _poolVote, uint256[] calldata _weights) external;
// Gauge Management
function createGauges(
address[] memory _pool,
uint256[] memory _gaugeTypes
) external returns (address[] memory, address[] memory, address[] memory);
function createGauge(
address _pool,
uint256 _gaugeType
) external returns (address _gauge, address _internal_bribe, address _external_bribe);
// View Functions
function length() external view returns (uint256);
function poolVoteLength(address voter) external view returns (uint256);
function factories() external view returns (address[] memory);
function factoryLength() external view returns (uint256);
function gaugeFactories() external view returns (address[] memory);
function gaugeFactoriesLength() external view returns (uint256);
function weights(address _pool) external view returns (uint256);
function weightsAt(address _pool, uint256 _time) external view returns (uint256);
function totalWeight() external view returns (uint256);
function totalWeightAt(uint256 _time) external view returns (uint256);
function _epochTimestamp() external view returns (uint256);
function ve() external view returns (address);
// Distribution
function notifyRewardAmount(uint256 amount) external;
function distributeFees(address[] memory _gauges) external;
function distributeAll() external;
function distribute(uint256 start, uint256 finish) external;
function distribute(address[] memory _gauges) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IVoterV5_GaugeLogic} from "./IVoterV5_GaugeLogic.sol";
/// @title IVoterV5_Storage
/// @notice Interface for accessing public and external variables of the VoterV5_Storage contract
interface IVoterV5_Storage {
/// @notice Returns the address of the ve token
function _ve() external view returns (address);
/// @notice Returns the address of the base token
function base() external view returns (address);
/// @notice Returns the address of the option token
function oToken() external view returns (address);
/// @notice Returns the address of the bribe factory
function bribefactory() external view returns (address);
/// @notice Returns the address of the minter
function minter() external view returns (address);
/// @notice Returns the address of the permission registry
function permissionRegistry() external view returns (address);
/// @notice Returns the address of a pool at a given index
/// @param index The index of the pool in the pools array
function pools(uint256 index) external view returns (address);
/// @notice Returns the global gauge index
function index() external view returns (uint256);
/// @notice Returns the delay between votes in seconds
function VOTE_DELAY() external view returns (uint256);
/// @notice Returns the maximum vote delay allowed
function MAX_VOTE_DELAY() external view returns (uint256);
/// @notice Returns the claimable amount for a given account
/// @param account The address of the account
function claimable(address account) external view returns (uint256);
/// @notice Returns the gauge address for a given pool
/// @param pool The address of the pool
function gauges(address pool) external view returns (address);
/// @notice Returns the last distribution timestamp for a given gauge
/// @param gauge The address of the gauge
function gaugesDistributionTimestamp(address gauge) external view returns (uint256);
/// @notice Returns the pool address for a given gauge
/// @param gauge The address of the gauge
function poolForGauge(address gauge) external view returns (address);
/// @notice Returns the internal bribe address for a given gauge
/// @param gauge The address of the gauge
function internal_bribes(address gauge) external view returns (address);
/// @notice Returns the external bribe address for a given gauge
/// @param gauge The address of the gauge
function external_bribes(address gauge) external view returns (address);
/// @notice Returns the votes for a given NFT and pool
/// @param nft The address of the NFT
/// @param pool The address of the pool
function votes(address nft, address pool) external view returns (uint256);
/// @notice Returns the pool address at a given index for a given NFT
/// @param nft The address of the NFT
/// @param index The index of the pool in the poolVote array
function poolVote(address nft, uint256 index) external view returns (address);
/// @notice Returns the timestamp of the last vote for a given NFT
/// @param nft The address of the NFT
function lastVoted(address nft) external view returns (uint256);
/// @notice Returns whether a given address is a gauge
/// @param gauge The address of the gauge
function isGauge(address gauge) external view returns (bool);
/// @notice Returns whether a given token is whitelisted
/// @param token The address of the token
function isWhitelisted(address token) external view returns (bool);
/// @notice Returns whether a given pool is whitelisted
/// @param token The address of the pool token
function isWhitelistedPool(address token) external view returns (bool);
/// @notice Returns whether a given gauge is alive
/// @param gauge The address of the gauge
function isAlive(address gauge) external view returns (bool);
/// @notice Returns the factory status of a given address
/// @param factory The address of the factory
function isFactory(address factory) external view returns (uint8);
/// @notice Returns whether a given address is a gauge factory
/// @param gaugeFactory The address of the gauge factory
/// @dev in 5.4.1, this returns a uint8 instead of a bool
/// This allows the same gauge factory to be used across multiple gauge types.
/// Storage-safe: bool and uint8 both occupy 1 byte, existing true/false values
/// become 1/0 counters seamlessly during upgrades.
function isGaugeFactory(address gaugeFactory) external view returns (uint8);
/// @notice Returns whether a given address is a gauge depositor
/// @param gaugeFactory The address of the gauge factory
function isGaugeDepositor(address gaugeFactory) external view returns (bool);
/// @notice Returns the address of the gauge logic contract
function gaugeLogic() external view returns (IVoterV5_GaugeLogic);
/// @notice Returns the epoch timestamp when a given gauge was killed
/// @param gauge The address of the gauge
function gaugeKilledEpoch(address gauge) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IVoterV5_GaugeLogic, IERC165} from "./IVoterV5_GaugeLogic.sol";
import {IERC20} from "../interfaces/IERC20.sol";
import {IAlgebraFactory} from "@cryptoalgebra/v1-core/contracts/interfaces/IAlgebraFactory.sol";
import {IBribeFactory} from "../interfaces/IBribeFactory.sol";
import {IGaugeFactory} from "../interfaces/IGaugeFactory.sol";
import {IPermissionsRegistry} from "../interfaces/IPermissionsRegistry.sol";
import {IHypervisor} from "../interfaces/IHypervisor.sol";
import {IMultiPositionManager} from "../dex/uniswap-v4/IMultiPositionManager.sol";
import {IPairFactory} from "../interfaces/IPairFactory.sol";
import {IPairInfo} from "../interfaces/IPairInfo.sol";
import {VoterV5_Storage} from "./VoterV5_Storage.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {UniswapV3Helper} from "../libraries/UniswapV3Helper.sol";
/**
* @notice Table showing GaugeType configurations
* ┌──────────────────────┬──────────────────────────┬────────────────────────┐
* │ GaugeType │ DEX Factory │ Gauge Factory │
* ├──────────────────────┼──────────────────────────┼────────────────────────┤
* │ PAIR_CLASSIC │ PairFactoryUpgradable │ GaugeFactoryV2 │
* │ ALM_ALGEBRA_V1 │ AlgebraFactory │ GaugeFactoryV2_CL │
* │ ALM_ICHI_UNISWAP_V3 │ UniswapV3Factory │ GaugeFactoryV2_CL │
* │ ALM_GAMMA_UNISWAP_V4 │ UniswapV4PoolManager │ GaugeFactoryV2_CL │
* └──────────────────────┴──────────────────────────┴────────────────────────┘
*/
/// @notice Enum representing different gauge types
/// @dev Make sure to use the correct gaugeType or gauge creation will fail
enum GaugeType {
PAIR_CLASSIC, // 0: Classic Stable/Volatile pair
ALM_ALGEBRA_V1, // 1: Ichi/Gamma concentrated liquidity for Algebra
ALM_ICHI_UNISWAP_V3, // 2: Ichi concentrated liquidity for UniswapV3
ALM_GAMMA_UNISWAP_V4 // 3: Gamma concentrated liquidity for UniswapV4
}
/// @title VoterV5_GaugeLogic
/// @notice This contract contains the logic for creating gauges in the VoterV5 system. It is used to save contract
/// size in VoterV5 by separating out expensive logic.
/// @dev This contract MUST be called from VoterV5 through delegatecall().
contract VoterV5_GaugeLogic is IVoterV5_GaugeLogic, VoterV5_Storage, ERC165 {
/**
* @notice changelog
* - 1.1.0: Add Support for UniswapV3 and UniswapV4 Gauges
*/
string public constant VERSION_GAUGE_LOGIC = "1.1.0";
/// @notice Maximum valid gauge type enum value
uint256 public constant MAX_GAUGE_TYPE = uint256(type(GaugeType).max);
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IVoterV5_GaugeLogic).interfaceId || super.supportsInterface(interfaceId);
}
/// @notice Validate if gauge type is within enum bounds (pure function)
function isValidGaugeType(uint256 _gaugeType) public pure returns (bool) {
return _gaugeType <= MAX_GAUGE_TYPE;
}
struct _CreateGauge_LocalVars {
address tokenA;
address tokenB;
address rewardToken;
address dexFactory;
address gaugeFactory;
address internal_bribe;
address external_bribe;
bool isPair;
}
/// @notice create a gauge
/// @param _pool LP address, which varies based on gauge type:
/// - For PAIR_CLASSIC: The actual pair contract address (similar to UniswapV2 pairs)
/// - For ALM strategies (GAMMA): The strategy contract address that manages the position
/// @param _gaugeType enum GaugeType, the type of gauge to create. The associated factory (_factories[_gaugeType])
/// represents different things based on type:
/// - For PAIR_CLASSIC: Deploy a Gauge for Solidly Classic Pairs
/// - For ALM_GAMMA_ALGEBRA: Deploy a Gauge for Gamma + Algebra ALM strategies
/// - For ALM_GAMMA_UNISWAP_V3: Deploy a Gauge for Gamma + UniswapV3 ALM strategies
/// - For ALM_GAMMA_UNISWAP_V4: Deploy a Gauge for Gamma + UniswapV4 ALM strategies
function createGauge(
address _pool,
uint256 _gaugeType
) external override returns (address _gauge, address _internal_bribe, address _external_bribe) {
// Enhanced validation
require(_gaugeType <= MAX_GAUGE_TYPE, "Invalid gauge type enum");
require(_gaugeType < _factories.length, "Gauge type not configured");
require(gauges[_pool] == address(0), "!exists");
require(_pool.code.length > 0, "!contract");
_CreateGauge_LocalVars memory vars;
vars.dexFactory = _factories[_gaugeType];
vars.gaugeFactory = _gaugeFactories[_gaugeType];
require(vars.dexFactory != address(0), "dex factory not set");
require(vars.gaugeFactory != address(0), "gauge factory not set");
(vars.tokenA) = IPairInfo(_pool).token0();
(vars.tokenB) = IPairInfo(_pool).token1();
if (_gaugeType == uint256(GaugeType.PAIR_CLASSIC)) {
/**
* @dev Classic Stable/Volatile pair
*/
vars.isPair = IPairFactory(vars.dexFactory).isPair(_pool);
} else if (_gaugeType == uint256(GaugeType.ALM_ALGEBRA_V1)) {
/**
* @dev ICHI/GAMMA + Algebra ALM strategy
*/
address _pool_factory = IAlgebraFactory(vars.dexFactory).poolByPair(vars.tokenA, vars.tokenB);
address _pool_hyper = IHypervisor(_pool).pool();
require(_pool_hyper == _pool_factory, "wrong tokens");
vars.isPair = true;
} else if (_gaugeType == uint256(GaugeType.ALM_ICHI_UNISWAP_V3)) {
/**
* @dev ICHI + Uniswap V3 ALM strategy
*/
address _pool_hyper = IHypervisor(_pool).pool();
bool isValidPool = UniswapV3Helper.validatePoolExists(vars.dexFactory, vars.tokenA, vars.tokenB, _pool_hyper);
require(isValidPool, "wrong tokens");
vars.isPair = true;
} else if (_gaugeType == uint256(GaugeType.ALM_GAMMA_UNISWAP_V4)) {
/**
* @dev GAMMA + Uniswap V4 ALM strategy
*/
address _poolManager = IMultiPositionManager(_pool).poolManager();
require(_poolManager == vars.dexFactory, "!poolManager");
/// @dev IMultiPositionManager supports token0() and token1()
// vars.tokenA = address(IMultiPositionManager(_pool).token0());
// vars.tokenB = address(IMultiPositionManager(_pool).token1());
vars.isPair = true;
}
/// @dev Gov can create for any pool, even non-lynex pairs
if (!IPermissionsRegistry(permissionRegistry).hasRole("GOVERNANCE", msg.sender)) {
require(vars.isPair, "!_pool");
if(_gaugeType != uint256(GaugeType.PAIR_CLASSIC)) {
/// @dev Assume this is an ALM strategy
require(isWhitelistedPool[_pool], "Only whitelisted strategies");
}
if(_gaugeType == uint256(GaugeType.ALM_GAMMA_UNISWAP_V4)) {
/// @dev UniswapV4 supports native tokens at address(0)
if(vars.tokenA != address(0)) {
require(isWhitelisted[vars.tokenA], "!whitelistedA");
}
} else {
require(vars.tokenA != address(0), "!pair.tokenA");
require(isWhitelisted[vars.tokenA], "!whitelistedA");
}
require(isWhitelisted[vars.tokenB], "!whitelistedB");
require(vars.tokenB != address(0), "!pair.tokenB");
}
/// -----------------------------------------------------------------------
/// Setup Bribes
/// -----------------------------------------------------------------------
// create internal and external bribe
address _owner = IPermissionsRegistry(permissionRegistry).teamMultisig();
string memory _type = string.concat(protocolName, " LP Fees: ", IERC20(_pool).symbol());
vars.internal_bribe = IBribeFactory(bribefactory).createBribe(_owner, vars.tokenA, vars.tokenB, _type);
_type = string.concat(protocolName, " Bribes: ", IERC20(_pool).symbol());
vars.external_bribe = IBribeFactory(bribefactory).createBribe(_owner, vars.tokenA, vars.tokenB, _type);
/// -----------------------------------------------------------------------
/// Setup Gauge
/// -----------------------------------------------------------------------
vars.rewardToken = oToken != address(0) ? oToken : base;
_gauge = IGaugeFactory(vars.gaugeFactory).createGaugeV2(
vars.rewardToken,
_ve,
_pool,
address(this), // distribution address
vars.internal_bribe,
vars.external_bribe,
vars.isPair
);
// approve spending for protocol token, this is set back to zero if gauge is killed
IERC20(vars.rewardToken).approve(_gauge, type(uint256).max);
/// -----------------------------------------------------------------------
/// Save Gauge Data
/// -----------------------------------------------------------------------
internal_bribes[_gauge] = vars.internal_bribe;
external_bribes[_gauge] = vars.external_bribe;
gauges[_pool] = _gauge;
poolForGauge[_gauge] = _pool;
isGauge[_gauge] = true;
isAlive[_gauge] = true;
pools.push(_pool);
// update supplyIndex (gaugeRewardsPerVoteWeight) gauge => index (globalRewardsPerVoteWeight)
supplyIndex[_gauge] = index; // new gauges are set to the default global state
return (_gauge, vars.internal_bribe, vars.external_bribe);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IVoterV5_Storage} from "./IVoterV5_Storage.sol";
import {IVoterV5_GaugeLogic} from "./IVoterV5_GaugeLogic.sol";
import {IVersionable} from "../interfaces/IVersionable.sol";
/// @title VoterV5_Storage
/// @notice This contract contains the storage variables for VoterV5.
/// @dev This contract is used to ensure both VoterV5 and VoterV5_GaugeLogic have access to the same storage variables
/// in the correct slots. They MUST both extend this.
/// - The storage layout for all version 5.x contracts MUST remain compatible for upgradeability.
contract VoterV5_Storage is IVoterV5_Storage, IVersionable {
/// @notice The current version of the contract
/// - 5.2.0: Add "protocolName", refactor to "oToken"
/// - 5.2.1: Change from require to revert CustomErrors
/// - 5.3.1: Add claim helper functions in VoterV5
/// - 5.4.0: GaugeType improvement, use setFactory to configure factories with GaugeType
/// - 5.4.1: Change isGaugeFactory mapping from bool to uint8 for reference counting.
/// This allows the same gauge factory to be used across multiple gauge types.
/// Storage-safe: bool and uint8 both occupy 1 byte, existing true/false values
/// become 1/0 counters seamlessly during upgrades.
string public constant override VERSION = "5.4.1";
bool internal initflag;
address public _ve; // ve token that governs these contracts
address[] internal _factories; // Array with all the pair factories
address public base; // underlying protocol token
address public oToken; // option protocol token
address[] internal _gaugeFactories; // array with all the gauge factories
address public bribefactory; // bribe factory (internal and external)
address public minter; // minter mints protocol tokens each epoch
address public permissionRegistry; // registry to check accesses
address[] public pools; // all pools viable for incentives
uint256 public index; // (globalRewardsPerVoteWeight) gauge index
uint256 internal DURATION; // rewards are released over 1 epoch
uint256 public VOTE_DELAY; // delay between votes in seconds
uint256 public MAX_VOTE_DELAY; // Max vote delay allowed
mapping(address => uint256) internal supplyIndex; /// (gaugeRewardsPerVoteWeight) gauge => index
mapping(address => uint256) public claimable; /// @dev deprecated, but leaving for upgradeability
mapping(address => address) public gauges; // pool => gauge
mapping(address => uint256) public gaugesDistributionTimestamp; // gauge => last Distribution Time
mapping(address => address) public poolForGauge; // gauge => pool
mapping(address => address) public internal_bribes; // gauge => internal bribe (only fees)
mapping(address => address) public external_bribes; // gauge => external bribe (real bribes)
mapping(address => mapping(address => uint256)) public votes; // nft => pool => votes
mapping(address => address[]) public poolVote; // nft => pools
mapping(uint256 => mapping(address => uint256)) internal weightsPerEpoch; // timestamp => pool => weights
mapping(uint256 => uint256) internal totalWeightsPerEpoch; // timestamp => total weights
mapping(address => uint256) public lastVoted; // nft => timestamp of last vote
mapping(address => bool) public isGauge; // gauge => boolean [is a gauge?]
mapping(address => bool) public isWhitelisted; // token => boolean [is an allowed token?]
mapping(address => bool) public isWhitelistedPool; // token => boolean [is an allowed token?]
mapping(address => bool) public isAlive; // gauge => boolean [is the gauge alive?]
mapping(address => uint8) public isFactory; // factory => boolean [the pair factory exists?]
mapping(address => uint8) public isGaugeFactory; // g.factory=> usage count [how many gauge types use this factory]
mapping(address => bool) public isGaugeDepositor; // g.factory=> boolean [the gauge factory exists?]
IVoterV5_GaugeLogic public gaugeLogic; // gauge logic contract
/**
* @dev May 2024: The state variables below this have been added in VoterV5 and the gap variable reduced accordingly.
*/
mapping(address => uint256) public gaugeKilledEpoch; // gauge => timestamp [epoch when gauge was killed]
string public protocolName; // name of the protocol for gauge creation
// Reserved space for future state variables
uint256[48] private __gap;
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
/**
* @title Non-Fungible Vesting Token Standard.
* @notice A non-fungible token standard used to vest ERC-20 tokens over a vesting release curve
* scheduled using timestamps.
* @dev Because this standard relies on timestamps for the vesting schedule, it's important to keep track of the
* tokens claimed per Vesting NFT so that a user cannot withdraw more tokens than allotted for a specific Vesting NFT.
* @custom:interface-id 0xbd3a202b
*/
interface IERC5725Upgradeable is IERC721Upgradeable {
/**
* This event is emitted when the payout is claimed through the claim function.
* @param tokenId the NFT tokenId of the assets being claimed.
* @param recipient The address which is receiving the payout.
* @param claimAmount The amount of tokens being claimed.
*/
event PayoutClaimed(uint256 indexed tokenId, address indexed recipient, uint256 claimAmount);
/**
* This event is emitted when an `owner` sets an address to manage token claims for all tokens.
* @param owner The address setting a manager to manage all tokens.
* @param spender The address being permitted to manage all tokens.
* @param approved A boolean indicating whether the spender is approved to claim for all tokens.
*/
event ClaimApprovalForAll(address indexed owner, address indexed spender, bool approved);
/**
* This event is emitted when an `owner` sets an address to manage token claims for a `tokenId`.
* @param owner The `owner` of `tokenId`.
* @param spender The address being permitted to manage a tokenId.
* @param tokenId The unique identifier of the token being managed.
* @param approved A boolean indicating whether the spender is approved to claim for `tokenId`.
*/
event ClaimApproval(address indexed owner, address indexed spender, uint256 indexed tokenId, bool approved);
/**
* @notice Claim the pending payout for the NFT.
* @dev MUST grant the claimablePayout value at the time of claim being called to `msg.sender`.
* MUST revert if not called by the token owner or approved users.
* MUST emit PayoutClaimed.
* SHOULD revert if there is nothing to claim.
* @param tokenId The NFT token id.
*/
function claim(uint256 tokenId) external;
/**
* @notice Number of tokens for the NFT which have been claimed at the current timestamp.
* @param tokenId The NFT token id.
* @return payout The total amount of payout tokens claimed for this NFT.
*/
function claimedPayout(uint256 tokenId) external view returns (uint256 payout);
/**
* @notice Number of tokens for the NFT which can be claimed at the current timestamp.
* @dev It is RECOMMENDED that this is calculated as the `vestedPayout()` subtracted from `payoutClaimed()`.
* @param tokenId The NFT token id.
* @return payout The amount of unlocked payout tokens for the NFT which have not yet been claimed.
*/
function claimablePayout(uint256 tokenId) external view returns (uint256 payout);
/**
* @notice Total amount of tokens which have been vested at the current timestamp.
* This number also includes vested tokens which have been claimed.
* @dev It is RECOMMENDED that this function calls `vestedPayoutAtTime`
* with `block.timestamp` as the `timestamp` parameter.
* @param tokenId The NFT token id.
* @return payout Total amount of tokens which have been vested at the current timestamp.
*/
function vestedPayout(uint256 tokenId) external view returns (uint256 payout);
/**
* @notice Total amount of vested tokens at the provided timestamp.
* This number also includes vested tokens which have been claimed.
* @dev `timestamp` MAY be both in the future and in the past.
* Zero MUST be returned if the timestamp is before the token was minted.
* @param tokenId The NFT token id.
* @param timestamp The timestamp to check on, can be both in the past and the future.
* @return payout Total amount of tokens which have been vested at the provided timestamp.
*/
function vestedPayoutAtTime(uint256 tokenId, uint256 timestamp) external view returns (uint256 payout);
/**
* @notice Number of tokens for an NFT which are currently vesting.
* @dev The sum of vestedPayout and vestingPayout SHOULD always be the total payout.
* @param tokenId The NFT token id.
* @return payout The number of tokens for the NFT which are vesting until a future date.
*/
function vestingPayout(uint256 tokenId) external view returns (uint256 payout);
/**
* @notice The start and end timestamps for the vesting of the provided NFT.
* MUST return the timestamp where no further increase in vestedPayout occurs for `vestingEnd`.
* @param tokenId The NFT token id.
* @return vestingStart The beginning of the vesting as a unix timestamp.
* @return vestingEnd The ending of the vesting as a unix timestamp.
*/
function vestingPeriod(uint256 tokenId) external view returns (uint256 vestingStart, uint256 vestingEnd);
/**
* @notice Token which is used to pay out the vesting claims.
* @param tokenId The NFT token id.
* @return token The token which is used to pay out the vesting claims.
*/
function payoutToken(uint256 tokenId) external view returns (address token);
/**
* @notice Sets a global `operator` with permission to manage all tokens owned by the current `msg.sender`.
* @param operator The address to let manage all tokens.
* @param approved A boolean indicating whether the spender is approved to claim for all tokens.
*/
function setClaimApprovalForAll(address operator, bool approved) external;
/**
* @notice Sets a tokenId `operator` with permission to manage a single `tokenId` owned by the `msg.sender`.
* @param operator The address to let manage a single `tokenId`.
* @param tokenId the `tokenId` to be managed.
* @param approved A boolean indicating whether the spender is approved to claim for all tokens.
*/
function setClaimApproval(address operator, bool approved, uint256 tokenId) external;
/**
* @notice Returns true if `owner` has set `operator` to manage all `tokenId`s.
* @param owner The owner allowing `operator` to manage all `tokenId`s.
* @param operator The address who is given permission to spend tokens on behalf of the `owner`.
*/
function isClaimApprovedForAll(address owner, address operator) external view returns (bool isClaimApproved);
/**
* @notice Returns the operating address for a `tokenId`.
* If `tokenId` is not managed, then returns the zero address.
* @param tokenId The NFT `tokenId` to query for a `tokenId` manager.
*/
function getClaimApproved(uint256 tokenId) external view returns (address operator);
}
interface IERC5725_ExtendedApproval is IERC5725Upgradeable {
/**
* @notice Returns true if `operator` is allowed to transfer the `tokenId` NFT.
* @param operator The address to check if it is approved for the transfer
* @param tokenId The token id to check if the operator is approved for
*/
function isApprovedOrOwner(address operator, uint tokenId) external view returns (bool);
/**
* @notice Returns true if `operator` is allowed to claim for the provided tokenId
* @param operator The address to check if it is approved for the claim or owner of the token
* @param tokenId The token id to check if the operator is approved for
*/
function isApprovedClaimOrOwner(address operator, uint256 tokenId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;
/**
* @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
*
* _Available since v4.5._
*/
interface IVotes {
/**
* @dev Emitted when an account changes their delegate.
*/
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/**
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
*/
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
/**
* @dev Returns the current amount of votes that `account` has.
*/
function getVotes(address account) external view returns (uint256);
/**
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*/
function getPastVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*/
function getPastTotalSupply(uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the delegate that `account` has chosen.
*/
function delegates(address account) external view returns (address);
/**
* @dev Delegates votes from the sender to `delegatee`.
*/
function delegate(address delegatee) external;
/**
* @notice Removed from the interface to avoid signature conflicts.
* @dev Delegates votes from signer to `delegatee`.
*/
// function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IVotes} from "./interfaces/IVotes.sol";
import {Checkpoints} from "./libraries/Checkpoints.sol";
import {IERC5725_ExtendedApproval} from "./erc5725/IERC5725Upgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";
import {IVersionable} from "../../interfaces/IVersionable.sol";
/**
* @title Voting Escrow V2 Interface for Upgrades
*/
interface IVotingEscrowV2 is IVotes, IERC5725_ExtendedApproval, IERC721EnumerableUpgradeable, IVersionable {
struct LockDetails {
uint256 amount; /// @dev amount of tokens locked
uint256 startTime; /// @dev when locking started
uint256 endTime; /// @dev when locking ends
bool isPermanent; /// @dev if its a permanent lock
}
/// -----------------------------------------------------------------------
/// Events
/// -----------------------------------------------------------------------
event SupplyUpdated(uint256 oldSupply, uint256 newSupply);
/// @notice Lock events
event LockCreated(uint256 indexed tokenId, address indexed to, uint256 value, uint256 unlockTime, bool isPermanent);
event LockUpdated(uint256 indexed tokenId, uint256 value, uint256 unlockTime, bool isPermanent);
event LockMerged(
uint256 indexed fromTokenId,
uint256 indexed toTokenId,
uint256 totalValue,
uint256 unlockTime,
bool isPermanent
);
event LockSplit(uint256[] splitWeights, uint256 indexed _tokenId);
event LockDurationExtended(uint256 indexed tokenId, uint256 newUnlockTime, bool isPermanent);
event LockAmountIncreased(uint256 indexed tokenId, uint256 value);
event UnlockPermanent(uint256 indexed tokenId, address indexed sender, uint256 unlockTime);
/// @notice Delegate events
event LockDelegateChanged(
uint256 indexed tokenId,
address indexed delegator,
address fromDelegate,
address indexed toDelegate
);
/// -----------------------------------------------------------------------
/// Errors
/// -----------------------------------------------------------------------
error AlreadyVoted();
error InvalidNonce();
error InvalidDelegatee();
error InvalidSignature();
error InvalidSignatureS();
error InvalidWeights();
error LockDurationNotInFuture();
error LockDurationTooLong();
error LockExpired();
error LockNotExpired();
error LockHoldsValue();
error LockModifiedDelay();
error NotPermanentLock();
error PermanentLock();
error PermanentLockMismatch();
error SameNFT();
error SignatureExpired();
error ZeroAmount();
error NotLockOwner();
function supply() external view returns (uint);
function token() external view returns (IERC20Upgradeable);
function totalNftsMinted() external view returns (uint256);
function balanceOfNFT(uint256 _tokenId) external view returns (uint256);
function balanceOfNFTAt(uint256 _tokenId, uint256 _timestamp) external view returns (uint256);
function delegates(uint256 tokenId, uint48 timestamp) external view returns (address);
function lockDetails(uint256 tokenId) external view returns (LockDetails calldata);
function getPastEscrowPoint(
uint256 _tokenId,
uint256 _timePoint
) external view returns (Checkpoints.Point memory, uint48);
function getFirstEscrowPoint(uint256 _tokenId) external view returns (Checkpoints.Point memory, uint48);
function checkpoint() external;
function increaseAmount(uint256 _tokenId, uint256 _value) external;
function createLockFor(
uint256 _value,
uint256 _lockDuration,
address _to,
bool _permanent
) external returns (uint256);
function createDelegatedLockFor(
uint256 _value,
uint256 _lockDuration,
address _to,
address _delegatee,
bool _permanent
) external returns (uint256);
function split(uint256[] memory _weights, uint256 _tokenId) external;
function merge(uint256 _from, uint256 _to) external;
function burn(uint256 _tokenId) external;
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// This file was derived from OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/Checkpoints.sol)
pragma solidity 0.8.13;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
* time, and later looking up past values by block number. See {Votes} as an example.
*
* To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
* checkpoint for the current transaction block using the {push} function.
*/
library Checkpoints {
struct Trace {
Checkpoint[] _checkpoints;
}
/**
* @dev Struct to keep track of the voting power over time.
*/
struct Point {
/// @dev The voting power at a specific time
/// - MUST never be negative.
int128 bias;
/// @dev The rate at which the voting power decreases over time.
int128 slope;
/// @dev The value of tokens which do not decrease over time, representing permanent voting power
/// - MUST never be negative.
int128 permanent;
}
struct Checkpoint {
uint48 _key;
Point _value;
}
/**
* @dev A value was attempted to be inserted on a past checkpoint.
*/
error CheckpointUnorderedInsertions();
/**
* @dev Pushes a (`key`, `value`) pair into a Trace so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
* library.
*/
function push(Trace storage self, uint48 key, Point memory value) internal returns (Point memory, Point memory) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace storage self, uint48 key) internal view returns (Point memory) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? blankPoint() : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(
Trace storage self,
uint48 key
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
exists = pos != 0;
_value = exists ? _unsafeAccess(self._checkpoints, pos - 1)._value : blankPoint();
_key = exists ? _unsafeAccess(self._checkpoints, pos - 1)._key : 0;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(
Trace storage self,
uint48 key
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
exists = pos != 0;
_value = exists ? _unsafeAccess(self._checkpoints, pos - 1)._value : blankPoint();
_key = exists ? _unsafeAccess(self._checkpoints, pos - 1)._key : 0;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace storage self) internal view returns (Point memory) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? blankPoint() : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(
Trace storage self
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, blankPoint());
} else {
Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function firstCheckpoint(
Trace storage self
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, blankPoint());
} else {
Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, 0);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace storage self, uint48 pos) internal view returns (Checkpoint memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(
Checkpoint[] storage self,
uint48 key,
Point memory value
) private returns (Point memory, Point memory) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
Checkpoint memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertions();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(Checkpoint({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(Checkpoint({_key: key, _value: value}));
return (blankPoint(), value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private view returns (Checkpoint storage result) {
return self[pos];
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _realUnsafeAccess(
Checkpoint[] storage self,
uint256 pos
) private pure returns (Checkpoint storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
function blankPoint() internal pure returns (Point memory) {
return Point({bias: 0, slope: 0, permanent: 0});
}
struct TraceAddress {
CheckpointAddress[] _checkpoints;
}
struct CheckpointAddress {
uint48 _key;
address _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a TraceAddress so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
* library.
*/
function push(TraceAddress storage self, uint48 key, address value) internal returns (address, address) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? address(0) : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(TraceAddress storage self) internal view returns (address) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(
TraceAddress storage self
) internal view returns (bool exists, uint48 _key, address _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, address(0));
} else {
CheckpointAddress memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(TraceAddress storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(TraceAddress storage self, uint48 pos) internal view returns (CheckpointAddress memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(CheckpointAddress[] storage self, uint48 key, address value) private returns (address, address) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
CheckpointAddress memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertions();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(CheckpointAddress({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(CheckpointAddress({_key: key, _value: value}));
return (address(0), value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
CheckpointAddress[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
CheckpointAddress[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
CheckpointAddress[] storage self,
uint256 pos
) private pure returns (CheckpointAddress storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyGaugeFactory","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"CannotReviveGaugeInSameEpoch","type":"error"},{"inputs":[],"name":"DelayAlreadySet","type":"error"},{"inputs":[],"name":"ExceedsMaxGauges","type":"error"},{"inputs":[],"name":"GaugeAlreadyAlive","type":"error"},{"inputs":[],"name":"GaugeAlreadyKilled","type":"error"},{"inputs":[],"name":"InconsistentFactoryPair","type":"error"},{"inputs":[],"name":"InsufficientVotingPower","type":"error"},{"inputs":[],"name":"InvalidDelay","type":"error"},{"inputs":[],"name":"InvalidGaugeType","type":"error"},{"inputs":[],"name":"InvalidProtocolName","type":"error"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"NotApprovedOrOwner","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"NotContract","type":"error"},{"inputs":[],"name":"NotFactory","type":"error"},{"inputs":[],"name":"NotGauge","type":"error"},{"inputs":[],"name":"NotGaugeFactory","type":"error"},{"inputs":[],"name":"NotGaugeLogic","type":"error"},{"inputs":[],"name":"NotGovernance","type":"error"},{"inputs":[],"name":"NotVoterAdmin","type":"error"},{"inputs":[],"name":"VoteDelayNotMet","type":"error"},{"inputs":[],"name":"VotedAlready","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"uint256","name":"weight","type":"uint256"}],"name":"Abstained","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pairfactory","type":"address"},{"indexed":true,"internalType":"address","name":"gaugefactory","type":"address"}],"name":"AddFactories","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Attach","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"blacklister","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"Blacklisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"blacklister","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"BlacklistedPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Detach","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DistributeReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gaugeType","type":"uint256"},{"indexed":true,"internalType":"address","name":"pairFactory","type":"address"},{"indexed":true,"internalType":"address","name":"gaugeFactory","type":"address"}],"name":"FactoryDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gaugeType","type":"uint256"},{"indexed":true,"internalType":"address","name":"pairFactory","type":"address"},{"indexed":true,"internalType":"address","name":"gaugeFactory","type":"address"}],"name":"FactoryEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gaugeType","type":"uint256"},{"indexed":true,"internalType":"address","name":"oldPairFactory","type":"address"},{"indexed":true,"internalType":"address","name":"oldGaugeFactory","type":"address"},{"indexed":false,"internalType":"address","name":"newPairFactory","type":"address"},{"indexed":false,"internalType":"address","name":"newGaugeFactory","type":"address"}],"name":"FactoryReplaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"address","name":"internal_bribe","type":"address"},{"indexed":true,"internalType":"address","name":"external_bribe","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"GaugeCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"}],"name":"GaugeKilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"}],"name":"GaugeRevived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fromGaugeIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toGaugeIndex","type":"uint256"}],"name":"HitRefreshApprovalLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NotifyReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RefundReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"latest","type":"address"}],"name":"SetBribeFactory","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isInternal","type":"bool"},{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"latest","type":"address"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"}],"name":"SetBribeFor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"SetDepositor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"latest","type":"address"}],"name":"SetGaugeFactory","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"latest","type":"address"}],"name":"SetGaugeLogic","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"latest","type":"address"}],"name":"SetMinter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"latest","type":"address"}],"name":"SetOptions","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"latest","type":"address"}],"name":"SetPairFactory","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"latest","type":"address"}],"name":"SetPermissionRegistry","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"old","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"latest","type":"uint256"}],"name":"SetVoteDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"uint256","name":"weight","type":"uint256"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"whitelister","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"Whitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"whitelister","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"WhitelistedPool","type":"event"},{"inputs":[],"name":"MAX_VOTE_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTE_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_epochTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"address","name":"_permissionsRegistry","type":"address"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"address","name":"_oToken","type":"address"}],"name":"_init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_ve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"base","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bribefactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_bribes","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"claimBribes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_bribes","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"}],"name":"claimBribes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"}],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_gauges","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"}],"name":"claimRewardTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_gauges","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"},{"internalType":"address","name":"_claimFor","type":"address"}],"name":"claimRewardTokensFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_gauges","type":"address[]"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_gauges","type":"address[]"},{"internalType":"address","name":"_claimFor","type":"address"}],"name":"claimRewardsFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint256","name":"_gaugeType","type":"uint256"}],"name":"createGauge","outputs":[{"internalType":"address","name":"_gauge","type":"address"},{"internalType":"address","name":"_internal_bribe","type":"address"},{"internalType":"address","name":"_external_bribe","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pool","type":"address[]"},{"internalType":"uint256[]","name":"_gaugeTypes","type":"uint256[]"}],"name":"createGauges","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_gauges","type":"address[]"}],"name":"distribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"finish","type":"uint256"}],"name":"distribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributeAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_gauges","type":"address[]"}],"name":"distributeFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"external_bribes","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factories","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gaugeFactories","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gaugeFactoriesLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gaugeKilledEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gaugeLogic","outputs":[{"internalType":"contract IVoterV5_GaugeLogic","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gauges","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gaugesDistributionTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gaugeType","type":"uint256"}],"name":"getFactoriesForGaugeType","outputs":[{"internalType":"address","name":"dexFactory","type":"address"},{"internalType":"address","name":"gaugeFactory","type":"address"},{"internalType":"bool","name":"isConfigured","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"__ve","type":"address"},{"internalType":"address","name":"_pairFactory","type":"address"},{"internalType":"address","name":"_gaugeFactory","type":"address"},{"internalType":"address","name":"_bribes","type":"address"},{"internalType":"address","name":"_gaugeLogic","type":"address"},{"internalType":"string","name":"_protocolName","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"internal_bribes","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isAlive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isFactory","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isGauge","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isGaugeDepositor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isGaugeFactory","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWhitelistedPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_gauge","type":"address"}],"name":"killGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastVoted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"length","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"oToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permissionRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poke","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"poolForGauge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolVote","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"voter","type":"address"}],"name":"poolVoteLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"finish","type":"uint256"},{"internalType":"address","name":"_oldOtoken","type":"address"}],"name":"refreshApprovals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gauge","type":"address"}],"name":"reviveGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_bribeFactory","type":"address"}],"name":"setBribeFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gauge","type":"address"},{"internalType":"address","name":"_external","type":"address"}],"name":"setExternalBribeFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gaugeType","type":"uint256"},{"internalType":"address","name":"_pairFactory","type":"address"},{"internalType":"address","name":"_gaugeFactory","type":"address"}],"name":"setFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"},{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setGaugeDepositor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gaugeLogic","type":"address"}],"name":"setGaugeLogic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gauge","type":"address"},{"internalType":"address","name":"_internal","type":"address"}],"name":"setInternalBribeFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"setMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gauge","type":"address"},{"internalType":"address","name":"_internal","type":"address"},{"internalType":"address","name":"_external","type":"address"}],"name":"setNewBribes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oToken","type":"address"}],"name":"setOptionsToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_permissionRegistry","type":"address"}],"name":"setPermissionsRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_protocolName","type":"string"}],"name":"setProtocolName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"setVoteDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_time","type":"uint256"}],"name":"totalWeightAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"},{"internalType":"bool","name":"_whitelist","type":"bool"}],"name":"updateWhitelistPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"bool","name":"_whitelist","type":"bool"}],"name":"updateWhitelistToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_poolVote","type":"address[]"},{"internalType":"uint256[]","name":"_voteProportions","type":"uint256[]"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"votes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"}],"name":"weights","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint256","name":"_time","type":"uint256"}],"name":"weightsAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b506200001c62000022565b620000e3565b605354610100900460ff16156200008f5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60535460ff90811614620000e1576053805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b615ca180620000f36000396000f3fe608060405234801561001057600080fd5b50600436106104805760003560e01c80639a61df8911610257578063cad1b90611610146578063eae40f26116100c3578063f9f031df11610087578063f9f031df14610b99578063fca3b5aa14610bac578063fe5b38e414610bbf578063ffa1ad7414610bc7578063ffe5195b14610beb57600080fd5b8063eae40f2614610b1f578063ef69952814610b48578063f2312ab014610b5b578063f6898d7a14610b7e578063f8803bb614610b9157600080fd5b8063dcd9e47a1161010a578063dcd9e47a14610a87578063df7ae62e14610ac4578063e128aace14610ad7578063e567e86914610aea578063e6da045714610aff57600080fd5b8063cad1b90614610a1b578063cdcaa13b14610a46578063d1a8e08c14610a59578063d826f88f14610a6c578063daa168bd14610a7457600080fd5b8063ae9c9e3f116101d4578063b9a09fd511610198578063b9a09fd5146109a6578063c2b79e98146109cf578063c48f5af4146109e2578063c527ee1f146109f5578063c991866d14610a0857600080fd5b8063ae9c9e3f14610952578063b0f5027814610965578063b52a315114610978578063b55a5c1c14610980578063b7c89f9c1461099357600080fd5b8063a7cac8461161021b578063a7cac846146108cd578063a9b5aa7e146108e0578063aa79979b146108f3578063ac4afa3814610916578063ae21c4cb1461092957600080fd5b80639a61df89146108615780639a83ed3b146108815780639f06247b146108945780639f1252df146108a7578063a3d9ce44146108ba57600080fd5b80634f3ec075116103735780636c4f2e38116102f05780638daeaa5a116102b45780638daeaa5a146107f05780638dd598fb1461081957806390e934161461083157806396c82e5714610846578063992a79331461084e57600080fd5b80636c4f2e381461079b5780636f816a20146107a45780637625391a146107b75780637715ee75146107ca5780638b6fc247146107dd57600080fd5b80635d130ec6116103375780635d130ec61461072c5780636138889b1461073f578063636056f214610752578063657021fb14610765578063666256aa1461078857600080fd5b80634f3ec075146106a35780635001f3b5146106b6578063533d3e71146106c957806353be568d146106e957806355f168d51461070957600080fd5b806333d4e07c11610401578063436596c4116103c5578063436596c414610623578063470f49851461062b578063473775fe14610633578063486b7dcc1461066e5780634d68ce441461068157600080fd5b806333d4e07c146105a757806338752a9d146105ba5780633af32abf146105cd5780633c6b16ab146105f0578063402914f51461060357600080fd5b80631a32aad6116104485780631a32aad6146105505780631f7b6d32146105635780631f850716146105755780632986c0e51461058b57806333832a6a1461059457600080fd5b806306d6a1b21461048557806307546172146104cb5780630f04ba67146104de5780631703e5f9146105135780631817835814610546575b600080fd5b6104ae610493366004615006565b6011602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6006546104ae906001600160a01b031681565b6105016104ec366004615006565b601d6020526000908152604090205460ff1681565b60405160ff90911681526020016104c2565b610536610521366004615006565b601c6020526000908152604090205460ff1681565b60405190151581526020016104c2565b61054e610bf4565b005b6003546104ae906001600160a01b031681565b6008545b6040519081526020016104c2565b60005461010090046001600160a01b03166104ae565b61056760095481565b61054e6105a2366004615006565b610d8a565b61054e6105b5366004615115565b610e40565b6005546104ae906001600160a01b031681565b6105366105db366004615006565b601a6020526000908152604090205460ff1681565b61054e6105fe366004615166565b610e8e565b610567610611366004615006565b600e6020526000908152604090205481565b61054e610f89565b600154610567565b610646610641366004615166565b611082565b604080516001600160a01b0394851681529390921660208401521515908201526060016104c2565b61054e61067c366004615115565b611191565b61069461068f36600461517f565b6111da565b6040516104c29392919061527d565b61054e6106b13660046152c0565b6113dd565b6002546104ae906001600160a01b031681565b6105676106d7366004615006565b60216020526000908152604090205481565b6105676106f7366004615166565b60009081526017602052604090205490565b610536610717366004615006565b601b6020526000908152604090205460ff1681565b61054e61073a36600461537a565b611958565b61054e61074d366004615412565b611c9e565b610567610760366004615446565b611d6e565b610501610773366004615006565b601e6020526000908152604090205460ff1681565b61054e6107963660046154f1565b611d98565b610567600c5481565b61054e6107b23660046155a8565b611f02565b61054e6107c5366004615613565b611fc0565b61054e6107d83660046154f1565b612087565b61054e6107eb366004615166565b6121f1565b6105676107fe366004615006565b6001600160a01b031660009081526015602052604090205490565b6000546104ae9061010090046001600160a01b031681565b610839612282565b6040516104c29190615635565b6105676122e4565b61054e61085c366004615006565b612304565b61056761086f366004615006565b60186020526000908152604090205481565b61054e61088f366004615648565b6125a2565b61054e6108a2366004615006565b612624565b61054e6108b5366004615676565b61286e565b61054e6108c83660046156d9565b612879565b6105676108db366004615006565b6128b6565b61054e6108ee366004615006565b6128ed565b610536610901366004615006565b60196020526000908152604090205460ff1681565b6104ae610924366004615166565b6129a3565b6104ae610937366004615006565b6013602052600090815260409020546001600160a01b031681565b6020546104ae906001600160a01b031681565b61054e61097336600461570d565b6129cd565b600454610567565b6007546104ae906001600160a01b031681565b61054e6109a136600461573d565b612a4d565b6104ae6109b4366004615006565b600f602052600090815260409020546001600160a01b031681565b61054e6109dd366004615676565b612a98565b6104ae6109f0366004615446565b612b6e565b61054e610a03366004615412565b612ba6565b61054e610a16366004615676565b612cca565b610567610a2936600461573d565b601460209081526000928352604080842090915290825290205481565b61054e610a5436600461576b565b612da0565b61054e610a67366004615006565b612e08565b61054e613040565b61054e610a8236600461573d565b61307f565b610a9a610a95366004615446565b6130ca565b604080516001600160a01b03948516815292841660208401529216918101919091526060016104c2565b61054e610ad2366004615799565b6130f2565b61054e610ae5366004615006565b6132f6565b610af2613307565b6040516104c29190615839565b610567610b0d366004615006565b60106020526000908152604090205481565b6104ae610b2d366004615006565b6012602052600090815260409020546001600160a01b031681565b61054e610b5636600461586c565b613395565b610536610b69366004615006565b601f6020526000908152604090205460ff1681565b61054e610b8c3660046158d8565b613437565b6105676134d8565b61054e610ba7366004615412565b61354b565b61054e610bba366004615006565b613555565b61083961360b565b610af260405180604001604052806005815260200164352e342e3160d81b81525081565b610567600b5481565b610bfc61366b565b33610c06816136c4565b6001600160a01b038116600090815260156020908152604080832080548251818502810185019093528083529192909190830182828015610c7057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c52575b505050505090506000815190506000816001600160401b03811115610c9757610c9761502a565b604051908082528060200260200182016040528015610cc0578160200160208202803683370190505b50905060005b82811015610d55576001600160a01b03851660009081526014602052604081208551909190869084908110610cfd57610cfd61591e565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054828281518110610d3857610d3861591e565b602090810291909101015280610d4d8161594a565b915050610cc6565b50610d6184848361371b565b5050506001600160a01b03166000908152601860205260409020429055610d886001605455565b565b610d92613bf0565b806001600160a01b03163b600003610dbd57604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b038116610de45760405163d92e233d60e01b815260040160405180910390fd5b6007546040516001600160a01b038084169216907f19fff1a4baaea69caec27e064bdfc0cb61d642370694fffdc0d38a568eb89b7390600090a3600780546001600160a01b0319166001600160a01b0392909216919091179055565b610e48613c7e565b60005b8251811015610e8957610e77838281518110610e6957610e6961591e565b602002602001015183613d2a565b80610e818161594a565b915050610e4b565b505050565b6006546001600160a01b03163314610eb95760405163ea8e4eb560e01b815260040160405180910390fd5b600062093a80610ec76134d8565b610ed19190615963565b6000818152601760205260408120549192508115610f095781610efc85670de0b6b3a764000061597a565b610f069190615999565b90505b8015610f27578060096000828254610f2191906159bb565b90915550505b6002546040518581526001600160a01b039091169033907ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf508269060200160405180910390a3600254610f83906001600160a01b0316333087613df6565b50505050565b610f9161366b565b600660009054906101000a90046001600160a01b03166001600160a01b031663ed29fc116040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061100a91906159d3565b506008546000905b8082101561107657611064600f6000600885815481106110345761103461591e565b60009182526020808320909101546001600160a01b03908116845290830193909352604090910190205416613e61565b8161106e8161594a565b925050611012565b5050610d886001605455565b6020546040516319d5af4160e31b815260048101839052600091829182916001600160a01b03169063cead7a0890602401602060405180830381865afa1580156110d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f491906159ec565b158061110257506001548410155b156111155750600091508190508061118a565b600184815481106111285761112861591e565b600091825260209091200154600480546001600160a01b03909216945090859081106111565761115661591e565b6000918252602090912001546001600160a01b03908116925083161580159061118757506001600160a01b03821615155b90505b9193909250565b611199613c7e565b60005b8251811015610e89576111c88382815181106111ba576111ba61591e565b602002602001015183614076565b806111d28161594a565b91505061119c565b60608060606111e761366b565b835185511461120c576040516001621398b960e31b0319815260040160405180910390fd5b600a8551111561122f57604051630f2b9ca360e31b815260040160405180910390fd5b600085516001600160401b0381111561124a5761124a61502a565b604051908082528060200260200182016040528015611273578160200160208202803683370190505b509050600086516001600160401b038111156112915761129161502a565b6040519080825280602002602001820160405280156112ba578160200160208202803683370190505b509050600087516001600160401b038111156112d8576112d861502a565b604051908082528060200260200182016040528015611301578160200160208202803683370190505b50905060005b88518110156113c45761134c8982815181106113255761132561591e565b602002602001015189838151811061133f5761133f61591e565b6020026020010151614142565b86848151811061135e5761135e61591e565b602002602001018685815181106113775761137761591e565b602002602001018686815181106113905761139061591e565b6001600160a01b039485166020918202929092010152928216909252919091169052806113bc8161594a565b915050611307565b50919450925090506113d66001605455565b9250925092565b6113e5613bf0565b6020546040516319d5af4160e31b8152600481018590526001600160a01b039091169063cead7a0890602401602060405180830381865afa15801561142e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061145291906159ec565b61146f57604051633ec8776b60e01b815260040160405180910390fd5b6001600160a01b0382161580159061148f57506001600160a01b0382163b155b156114ad57604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b038116158015906114cd57506001600160a01b0381163b155b156114eb57604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b03821615801561150a57506001600160a01b03811615155b1561152857604051638b44eb4d60e01b815260040160405180910390fd5b6001600160a01b0382161580159061154757506001600160a01b038116155b1561156557604051638b44eb4d60e01b815260040160405180910390fd5b5b60015483106115e7576001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b03199081169091556004805492830181556000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90910180549091169055611566565b6000600184815481106115fc576115fc61591e565b6000918252602082200154600480546001600160a01b03909216935090869081106116295761162961591e565b60009182526020822001546001600160a01b03908116925083161580159061165957506001600160a01b03821615155b905060006001600160a01b0386161580159061167d57506001600160a01b03851615155b90506001600160a01b038416156116cf576001600160a01b0384166000908152601d60205260408120805460ff16916116b583615a09565b91906101000a81548160ff021916908360ff160217905550505b6001600160a01b03831615611740576001600160a01b0383166000908152601e602052604090205460ff1615611740576001600160a01b0383166000908152601e60205260408120805460ff169161172683615a09565b91906101000a81548160ff021916908360ff160217905550505b85600188815481106117545761175461591e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555084600488815481106117965761179661591e565b600091825260209091200180546001600160a01b0319166001600160a01b03928316179055861615611845576001600160a01b0386166000908152601d60205260408120805460ff16916117e983615a26565b82546101009290920a60ff8181021990931691831602179091556001600160a01b0387166000908152601e602052604081208054909216925061182b83615a26565b91906101000a81548160ff021916908360ff160217905550505b811580156118505750805b1561189b57846001600160a01b0316866001600160a01b0316887fe3fc081d6f052556cd57e83799d0056060f5b8ee94a9f035d6f84310e0afe4cc60405160405180910390a461194f565b8180156118a6575080155b156118f157826001600160a01b0316846001600160a01b0316887fce7a57c8921d9c54bc89feca7fd76c0e0fd5f0e77a1c8333484ac3944b4b871260405160405180910390a461194f565b8180156118fb5750805b1561194f57604080516001600160a01b038881168252878116602083015280861692908716918a917f842bae1770fbb1ce6402c4c0606f3a37381914d8fa33e1c4e2ad0f51a60234a6910160405180910390a45b50505050505050565b605354610100900460ff16158080156119785750605354600160ff909116105b806119925750303b158015611992575060535460ff166001145b6119fa5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6053805460ff191660011790558015611a1d576053805461ff0019166101001790555b611a2561427c565b86600060016101000a8154816001600160a01b0302191690836001600160a01b03160217905550866001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aae9190615a45565b600280546001600160a01b039283166001600160a01b031991821617909155600180548082019091557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601805492891692909116821790556000908152601d60205260408120805460ff1691611b2383615a26565b82546101009290920a60ff81810219909316918316021790915560048054600181019091557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b0319166001600160a01b0389169081179091556000908152601e6020526040812080549092169250611ba483615a26565b825460ff9182166101009390930a928302919092021990911617905550600580546001600160a01b0386166001600160a01b03199182161790915560068054821633908117909155600780549092161790558151600003611c1857604051635e2eba9760e01b815260040160405180910390fd5b8151611c2b906022906020850190614f3e565b506000600b81905562093a80600a819055600c55805460ff19169055611c50836142ab565b801561194f576053805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050505050565b611ca661366b565b600660009054906101000a90046001600160a01b03166001600160a01b031663ed29fc116040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611cfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d1f91906159d3565b5060005b8151811015611d6057611d4e828281518110611d4157611d4161591e565b6020026020010151613e61565b80611d588161594a565b915050611d23565b50611d6b6001605455565b50565b60008181526016602090815260408083206001600160a01b03861684529091529020545b92915050565b600054604051636bec739760e01b81526101009091046001600160a01b031690636bec739790611dce9033908590600401615a62565b602060405180830381865afa158015611deb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0f91906159ec565b611e2c5760405163390cdd9b60e21b815260040160405180910390fd5b8151835114611e51576040516001621398b960e31b0319815260040160405180910390fd5b60005b8351811015610f8357838181518110611e6f57611e6f61591e565b60200260200101516001600160a01b031663a7852afa83858481518110611e9857611e9861591e565b60200260200101516040518363ffffffff1660e01b8152600401611ebd929190615a7b565b600060405180830381600087803b158015611ed757600080fd5b505af1158015611eeb573d6000803e3d6000fd5b505050508080611efa9061594a565b915050611e54565b611f0a61366b565b611f13336136c4565b828114611f36576040516001621398b960e31b0319815260040160405180910390fd5b611fa4338585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080890282810182019093528882529093508892508791829185019084908082843760009201919091525061371b92505050565b336000908152601860205260409020429055610f836001605455565b611fc861366b565b600660009054906101000a90046001600160a01b03166001600160a01b031663ed29fc116040518163ffffffff1660e01b81526004016020604051808303816000875af115801561201d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204191906159d3565b50815b8181101561207857612066600f6000600884815481106110345761103461591e565b806120708161594a565b915050612044565b506120836001605455565b5050565b600054604051636bec739760e01b81526101009091046001600160a01b031690636bec7397906120bd9033908590600401615a62565b602060405180830381865afa1580156120da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120fe91906159ec565b61211b5760405163390cdd9b60e21b815260040160405180910390fd5b8151835114612140576040516001621398b960e31b0319815260040160405180910390fd5b60005b8351811015610f835783818151811061215e5761215e61591e565b60200260200101516001600160a01b031663a7852afa838584815181106121875761218761591e565b60200260200101516040518363ffffffff1660e01b81526004016121ac929190615a7b565b600060405180830381600087803b1580156121c657600080fd5b505af11580156121da573d6000803e3d6000fd5b5050505080806121e99061594a565b915050612143565b6121f9613bf0565b600b54810361221e576040516001626a738d60e11b0319815260040160405180910390fd5b600c54811115612241576040516327df2edd60e11b815260040160405180910390fd5b600b5460408051918252602082018390527f17fb843f25faf2431510bc4a71e4898ccc1a28a3e985450e509670d14be2eaa1910160405180910390a1600b55565b606060048054806020026020016040519081016040528092919081815260200182805480156122da57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116122bc575b5050505050905090565b6000806122ef6134d8565b60009081526017602052604090205492915050565b61230c613c7e565b6001600160a01b0381166000908152601c602052604090205460ff1661234557604051633f88da5160e21b815260040160405180910390fd5b60025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390612378908490600090600401615a62565b6020604051808303816000875af1158015612397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bb91906159ec565b506003546001600160a01b0316156124455760035460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390612400908490600090600401615a62565b6020604051808303816000875af115801561241f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061244391906159ec565b505b600061245082614393565b905080156124b957600654600254612475916001600160a01b03918216911683614443565b816001600160a01b03167f17b30dc851ebb8ff6aaaf985b0588fb1f53a24b553db88d364af4c3f259b2af5826040516124b091815260200190565b60405180910390a25b6001600160a01b0382166000908152601c60205260408120805460ff191690556124e16134d8565b60008181526016602090815260408083206001600160a01b0380891685526011845282852054168452825280832054848452601790925282208054939450909290919061252f908490615963565b909155505060008181526016602090815260408083206001600160a01b038088168086526011855283862054909116855290835281842084905580845260219092528083208490555190917f04a5d3f5d80d22d9345acc80618f4a4e7e663cf9e1aed23b57d975acec002ba791a2505050565b6125aa61366b565b6125b2613bf0565b825b8281101561261957612607600f6000600884815481106125d6576125d661591e565b60009182526020808320909101546001600160a01b0390811684529083019390935260409091019020541683614462565b806126118161594a565b9150506125b4565b50610e896001605455565b61262c613c7e565b6001600160a01b0381166000908152601c602052604090205460ff1615612666576040516368368ceb60e01b815260040160405180910390fd5b6001600160a01b03811660009081526019602052604090205460ff1661269f576040516304639b6160e11b815260040160405180910390fd5b6126a76134d8565b6001600160a01b038216600090815260216020526040902054106126de5760405163760d387760e01b815260040160405180910390fd5b6001600160a01b0381166000908152601c60205260409020805460ff191660011790556127096134d8565b6001600160a01b03808316600090815260106020908152604080832094909455600954600d9091529290209190915560035416156127be5760035460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061277590849060001990600401615a62565b6020604051808303816000875af1158015612794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b891906159ec565b50612837565b60025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b3906127f290849060001990600401615a62565b6020604051808303816000875af1158015612811573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283591906159ec565b505b6040516001600160a01b038216907fed18e9faa3dccfd8aa45f69c4de40546b2ca9cccc4538a2323531656516db1aa90600090a250565b612083828233614617565b612881613bf0565b80516000036128a357604051635e2eba9760e01b815260040160405180910390fd5b8051612083906022906020840190614f3e565b6000806128c16134d8565b60009081526016602090815260408083206001600160a01b039096168352949052929092205492915050565b6128f5613bf0565b806001600160a01b03163b60000361292057604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b0381166129475760405163d92e233d60e01b815260040160405180910390fd5b6005546040516001600160a01b038084169216907f03ab94e51ce1ec8483c05c7425f6ebc27289fbd4a9c8b6ba0c2fe3b8b01765d490600090a3600580546001600160a01b0319166001600160a01b0392909216919091179055565b600881815481106129b357600080fd5b6000918252602090912001546001600160a01b0316905081565b6129d5613bf0565b6001600160a01b03831660009081526019602052604090205460ff16612a0e576040516304639b6160e11b815260040160405180910390fd5b826001600160a01b03163b600003612a3957604051636f7c43f160e01b815260040160405180910390fd5b612a4383836146c8565b610e898382614779565b612a55613bf0565b6001600160a01b03821660009081526019602052604090205460ff16612a8e576040516304639b6160e11b815260040160405180910390fd5b61208382826146c8565b8051825114612abd576040516001621398b960e31b0319815260040160405180910390fd5b60005b8251811015610e8957828181518110612adb57612adb61591e565b60200260200101516001600160a01b031663db0ea98433848481518110612b0457612b0461591e565b60200260200101516040518363ffffffff1660e01b8152600401612b29929190615a94565b600060405180830381600087803b158015612b4357600080fd5b505af1158015612b57573d6000803e3d6000fd5b505050508080612b669061594a565b915050612ac0565b60156020528160005260406000208181548110612b8a57600080fd5b6000918252602090912001546001600160a01b03169150829050565b60005b81518110156120835760196000838381518110612bc857612bc861591e565b6020908102919091018101516001600160a01b031682528101919091526040016000205460ff168015612c345750601c6000838381518110612c0c57612c0c61591e565b6020908102919091018101516001600160a01b031682528101919091526040016000205460ff165b15612cb857818181518110612c4b57612c4b61591e565b60200260200101516001600160a01b031663d294f0936040518163ffffffff1660e01b815260040160408051808303816000875af1158015612c91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cb59190615ab8565b50505b80612cc28161594a565b915050612ba9565b8051825114612cef576040516001621398b960e31b0319815260040160405180910390fd5b60005b8251811015610e8957828181518110612d0d57612d0d61591e565b60200260200101516001600160a01b031663db0ea98433848481518110612d3657612d3661591e565b60200260200101516040518363ffffffff1660e01b8152600401612d5b929190615a94565b600060405180830381600087803b158015612d7557600080fd5b505af1158015612d89573d6000803e3d6000fd5b505050508080612d989061594a565b915050612cf2565b612da8613bf0565b6001600160a01b0382166000818152601f6020908152604091829020805460ff191685151590811790915591519182527fb1252fa67b4c05afbdaadfae34890b205103c0212a9e062b3978e8cd573631a991015b60405180910390a25050565b612e10613bf0565b6001600160a01b03811615801590612e3057506001600160a01b0381163b155b15612e4e57604051630f58058360e11b815260040160405180910390fd5b6003546001600160a01b03168015612ed85760025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390612e93908490600090600401615a62565b6020604051808303816000875af1158015612eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ed691906159ec565b505b6001600160a01b038181166000908152601f6020526040808220805460ff19169055600380546001600160a01b03191693861693841790555182917fa078441d4124bc8d06fb66724c308c14f635a5a0d2bb1f422400e094b5cac40c91a36001600160a01b03821615612fe157600380546001600160a01b039081166000908152601f602052604090819020805460ff191660011790556002549254905163095ea7b360e01b81529282169263095ea7b392612f9c92169060001990600401615a62565b6020604051808303816000875af1158015612fbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fdf91906159ec565b505b600854606481111561302c57506040805160008152606460208201819052917f54cf3663cc24a77872b6e7edff8268ac45778eda977c143530389972902aa0a9910160405180910390a15b60085415610e8957610e89600082846125a2565b61304861366b565b33613052816136c4565b61305b81614827565b6001600160a01b03166000908152601860205260409020429055610d886001605455565b613087613bf0565b6001600160a01b03821660009081526019602052604090205460ff166130c0576040516304639b6160e11b815260040160405180910390fd5b6120838282614779565b60008060006130d761366b565b6130e18585614142565b919450925090506113d66001605455565b6006546001600160a01b0316331480159061317b5750600754604051634448e1eb60e01b81526001600160a01b0390911690634448e1eb90613138903390600401615adc565b602060405180830381865afa158015613155573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061317991906159ec565b155b156131995760405163ea8e4eb560e01b815260040160405180910390fd5b60005460ff16156131bc5760405162dc149f60e41b815260040160405180910390fd5b60005b84518110156131fe576131ec8582815181106131dd576131dd61591e565b60200260200101516001614076565b806131f68161594a565b9150506131bf565b50600680546001600160a01b038085166001600160a01b0319928316179092556007805486841692169190911790558116156132c157600380546001600160a01b0319166001600160a01b0383811691821790925560025460405163095ea7b360e01b815292169163095ea7b39161327c9160001990600401615a62565b6020604051808303816000875af115801561329b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bf91906159ec565b505b50506003546001600160a01b03166000908152601f602052604081208054600160ff1991821681179092558254161790555050565b6132fe613bf0565b611d6b816142ab565b6022805461331490615b12565b80601f016020809104026020016040519081016040528092919081815260200182805461334090615b12565b801561338d5780601f106133625761010080835404028352916020019161338d565b820191906000526020600020905b81548152906001019060200180831161337057829003601f168201915b505050505081565b6000546040516328ba294360e21b81526001600160a01b0383811660048301523360248301526101009092049091169063a2e8a50c90604401602060405180830381865afa1580156133eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061340f91906159ec565b61342c5760405163390cdd9b60e21b815260040160405180910390fd5b610e89838383614617565b6000546040516328ba294360e21b81526001600160a01b0383811660048301523360248301526101009092049091169063a2e8a50c90604401602060405180830381865afa15801561348d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134b191906159ec565b6134ce5760405163390cdd9b60e21b815260040160405180910390fd5b6120838282614b72565b60065460408051631a2732c160e31b815290516000926001600160a01b03169163d13996089160048083019260209291908290030181865afa158015613522573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061354691906159d3565b905090565b611d6b8133614b72565b61355d613bf0565b6001600160a01b0381166135845760405163d92e233d60e01b815260040160405180910390fd5b806001600160a01b03163b6000036135af57604051636f7c43f160e01b815260040160405180910390fd5b6006546040516001600160a01b038084169216907fe490d3138e32f1f66ef3971a3c73c7f7704ba0c1d1000f1e2c3df6fc0376610b90600090a3600680546001600160a01b0319166001600160a01b0392909216919091179055565b606060018054806020026020016040519081016040528092919081815260200182805480156122da576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116122bc575050505050905090565b6002605454036136bd5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016119f1565b6002605455565b600b546001600160a01b0382166000908152601860205260409020546136ea91906159bb565b421180156136fe57506136fb6134d8565b42115b611d6b57604051632add46eb60e01b815260040160405180910390fd5b61372483614827565b815160006137306134d8565b60008054604051630748d63560e31b815292935090916101009091046001600160a01b031690633a46b1a89061376c9089908690600401615a62565b602060405180830381865afa158015613789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137ad91906159d3565b90506000806000805b8681101561385557601c6000600f60008c85815181106137d8576137d861591e565b6020908102919091018101516001600160a01b0390811683528282019390935260409182016000908120549093168452830193909352910190205460ff16156138435787818151811061382d5761382d61591e565b60200260200101518461384091906159bb565b93505b8061384d8161594a565b9150506137b6565b5060005b86811015613b995760008982815181106138755761387561591e565b6020908102919091018101516001600160a01b038082166000908152600f84526040808220549092168082526019909452205490925060ff1680156138d257506001600160a01b0381166000908152601c602052604090205460ff165b15613b8457600086888c86815181106138ed576138ed61591e565b60200260200101516138ff919061597a565b6139099190615999565b6001600160a01b03808f166000908152601460209081526040808320938816835292905220549091501561395057604051631d9fc79760e11b815260040160405180910390fd5b806000036139715760405163cabeb65560e01b815260040160405180910390fd5b6001600160a01b038d8116600090815260156020908152604080832080546001810182559084528284200180546001600160a01b03191694881694851790558c83526016825280832093835292905290812080548392906139d39084906159bb565b90915550506001600160a01b03808e16600090815260146020908152604080832093871683529290529081208054839290613a0f9084906159bb565b90915550613a1f905081866159bb565b9450613a2b81876159bb565b60405182815290965033907f4d99b957a2bc29a30ebd96a7be8e68fe50a3c701db28a91436490b7d53870ca49060200160405180910390a26001600160a01b0380831660009081526012602052604090819020549051636e553f6560e01b8152600481018490528f83166024820152911690636e553f6590604401600060405180830381600087803b158015613ac057600080fd5b505af1158015613ad4573d6000803e3d6000fd5b5050505060136000836001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166001600160a01b0316636e553f65828f6040518363ffffffff1660e01b8152600401613b509291909182526001600160a01b0316602082015260400190565b600060405180830381600087803b158015613b6a57600080fd5b505af1158015613b7e573d6000803e3d6000fd5b50505050505b50508080613b919061594a565b915050613859565b5081841015613bbb5760405163cabeb65560e01b815260040160405180910390fd5b60008581526017602052604081208054849290613bd99084906159bb565b9091555050505050505050505050565b6001605455565b600754604051634448e1eb60e01b81526001600160a01b0390911690634448e1eb90613c20903390600401615adc565b602060405180830381865afa158015613c3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c6191906159ec565b610d885760405163cbe39e7f60e01b815260040160405180910390fd5b60075460408051634448e1eb60e01b81526004810191909152600a604482015269474f5645524e414e434560b01b60648201523360248201526001600160a01b0390911690634448e1eb90608401602060405180830381865afa158015613ce9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d0d91906159ec565b610d8857604051632d5be4cb60e21b815260040160405180910390fd5b816001600160a01b03163b600003613d5557604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b0382166000908152601b60205260409020805460ff19168215801591909117909155613dbc576040516001600160a01b0383169033907f88e52751b2db8161caf8700d6127c81ae036b0c7bd22fdbcfeff5d7f79cdcae590600090a35050565b6040516001600160a01b0383169033907f44ae20f34ded1b62036f5645208c7c8b0c79b5c9c659ae5019c3b12679ed86ed90600090a35050565b6040516001600160a01b0380851660248301528316604482015260648101829052610f839085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614c0a565b6000613e6c82614393565b90508015612083576001600160a01b0382166000908152601c602052604090205460ff161561401e576003546001600160a01b031615613f73576003546040516340c10f1960e01b81526001600160a01b03909116906340c10f1990613ed89030908590600401615a62565b600060405180830381600087803b158015613ef257600080fd5b505af1158015613f06573d6000803e3d6000fd5b505060035460405163b66503cf60e01b81526001600160a01b03808716945063b66503cf9350613f3c9216908590600401615a62565b600060405180830381600087803b158015613f5657600080fd5b505af1158015613f6a573d6000803e3d6000fd5b50505050613fda565b60025460405163b66503cf60e01b81526001600160a01b038481169263b66503cf92613fa792909116908590600401615a62565b600060405180830381600087803b158015613fc157600080fd5b505af1158015613fd5573d6000803e3d6000fd5b505050505b6040518181526001600160a01b0383169033907f4fa9693cae526341d334e2862ca2413b2e503f1266255f9e0869fb36e6d89b179060200160405180910390a35050565b60065460025461403b916001600160a01b03918216911683614443565b816001600160a01b03167f17b30dc851ebb8ff6aaaf985b0588fb1f53a24b553db88d364af4c3f259b2af582604051612dfc91815260200190565b816001600160a01b03163b6000036140a157604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b0382166000908152601a60205260409020805460ff19168215801591909117909155614108576040516001600160a01b0383169033907f6661a7108aecd07864384529117d96c319c1163e3010c01390f6b704726e07de90600090a35050565b6040516001600160a01b0383169033907fd36871fdf6981136f3ac0564927005901eda06f7a9dff1e8b2a1d7846b8ebb5090600090a35050565b60205460405160009182918291829182916001600160a01b031690636e6cf23d60e11b90614176908a908a90602401615a62565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516141b49190615b4c565b600060405180830381855af49150503d80600081146141ef576040519150601f19603f3d011682016040523d82523d6000602084013e6141f4565b606091505b509150915060006142058383614cdf565b90508080602001905181019061421b9190615b68565b604080513381526001600160a01b038085166020830152949a50929850909650828b169280881692908a16917fa4d97e9e7c65249b4cd01acb82add613adea98af32daf092366982f0a0d4e453910160405180910390a45050509250925092565b605354610100900460ff166142a35760405162461bcd60e51b81526004016119f190615baa565b610d88614d87565b6040516301ffc9a760e01b81526344707c4760e01b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa1580156142f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061431a91906159ec565b6143375760405163f233911760e01b815260040160405180910390fd5b6020546040516001600160a01b038084169216907f1bdeec301704e428e5c1929a6f66bfbd6661886a1ee802affbec12e4798b9c0690600090a3602080546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166000908152600d60205260408120805460095491829055908262093a806143c26134d8565b6143cc9190615963565b60008181526016602090815260408083206001600160a01b03808b1685526011845282852054168452909152902054909150801561443a5760006144108585615963565b9050801561443857670de0b6b3a764000061442b828461597a565b6144359190615999565b95505b505b50505050919050565b610e898363a9059cbb60e01b8484604051602401613e2a929190615a62565b6001600160a01b038116156144e55760405163095ea7b360e01b81526001600160a01b0382169063095ea7b3906144a0908590600090600401615a62565b6020604051808303816000875af11580156144bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144e391906159ec565b505b6003546001600160a01b031661456c5760025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061452990859060001990600401615a62565b6020604051808303816000875af1158015614548573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8991906159ec565b60025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061459f908590600090600401615a62565b6020604051808303816000875af11580156145be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145e291906159ec565b5060035460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061452990859060001990600401615a62565b60005b8351811015610f83578381815181106146355761463561591e565b60200260200101516001600160a01b03166331279d3d8385848151811061465e5761465e61591e565b60200260200101516040518363ffffffff1660e01b8152600401614683929190615a94565b600060405180830381600087803b15801561469d57600080fd5b505af11580156146b1573d6000803e3d6000fd5b5050505080806146c09061594a565b91505061461a565b806001600160a01b03163b6000036146f357604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b03828116600081815260126020908152604091829020549151600181529293858116939216917fddf0ec8cba6dad18edc75774c452fa4201ff17ec1761486329f3321936ce66d0910160405180910390a46001600160a01b03918216600090815260126020526040902080546001600160a01b03191691909216179055565b806001600160a01b03163b6000036147a457604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b03828116600081815260136020908152604080832054905192835292938581169316917fddf0ec8cba6dad18edc75774c452fa4201ff17ec1761486329f3321936ce66d0910160405180910390a46001600160a01b03918216600090815260136020526040902080546001600160a01b03191691909216179055565b6001600160a01b0381166000908152601560205260408120805490918061484c6134d8565b6001600160a01b038616600090815260186020526040812054919250908210905b84811015614b005760008682815481106148895761488961591e565b60009182526020808320909101546001600160a01b038b81168452601483526040808520919092168085529252909120549091508015614aeb578380156148f757506001600160a01b038083166000908152600f60209081526040808320549093168252601c9052205460ff165b156149345760008581526016602090815260408083206001600160a01b03861684529091528120805483929061492e908490615963565b90915550505b6001600160a01b03808a1660009081526014602090815260408083209386168352929052908120805483929061496b908490615963565b90915550506001600160a01b038083166000908152600f60209081526040808320549093168252601c9052205460ff16156149ad576149aa81876159bb565b95505b7f433fa8b9e8e2cb00bd714503252e8bfd144f0f318459a2ea8b39c1ae2536171789826040516149de929190615a62565b60405180910390a16001600160a01b038281166000908152600f602090815260408083205484168352601290915290819020549051627b8a6760e11b8152600481018490528b8316602482015291169062f714ce90604401600060405180830381600087803b158015614a5057600080fd5b505af1158015614a64573d6000803e3d6000fd5b505050506001600160a01b038281166000908152600f602090815260408083205484168352601390915290819020549051627b8a6760e11b8152600481018490528b8316602482015291169062f714ce90604401600060405180830381600087803b158015614ad257600080fd5b505af1158015614ae6573d6000803e3d6000fd5b505050505b50508080614af89061594a565b91505061486d565b506001600160a01b038616600090815260186020526040902054821115614b2657600092505b60008281526017602052604081208054859290614b44908490615963565b90915550506001600160a01b0386166000908152601560205260408120614b6a91614fc2565b505050505050565b60005b8251811015610e8957828181518110614b9057614b9061591e565b6020908102919091010151604051630c00007b60e41b81526001600160a01b0384811660048301529091169063c00007b090602401600060405180830381600087803b158015614bdf57600080fd5b505af1158015614bf3573d6000803e3d6000fd5b505050508080614c029061594a565b915050614b75565b6000614c5f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614dae9092919063ffffffff16565b9050805160001480614c80575080806020019051810190614c8091906159ec565b610e895760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016119f1565b60608215614cee575080611d92565b604482511015614d545760405162461bcd60e51b815260206004820152602b60248201527f64656c656761746563616c6c206661696c656420776974686f7574206120726560448201526a3b32b93a103932b0b9b7b760a91b60648201526084016119f1565b60048201915081806020019051810190614d6e9190615bf5565b60405162461bcd60e51b81526004016119f19190615839565b605354610100900460ff16613be95760405162461bcd60e51b81526004016119f190615baa565b6060614dbd8484600085614dc5565b949350505050565b606082471015614e265760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016119f1565b600080866001600160a01b03168587604051614e429190615b4c565b60006040518083038185875af1925050503d8060008114614e7f576040519150601f19603f3d011682016040523d82523d6000602084013e614e84565b606091505b5091509150614e9587838387614ea0565b979650505050505050565b60608315614f0f578251600003614f08576001600160a01b0385163b614f085760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016119f1565b5081614dbd565b614dbd8383815115614f245781518083602001fd5b8060405162461bcd60e51b81526004016119f19190615839565b828054614f4a90615b12565b90600052602060002090601f016020900481019282614f6c5760008555614fb2565b82601f10614f8557805160ff1916838001178555614fb2565b82800160010185558215614fb2579182015b82811115614fb2578251825591602001919060010190614f97565b50614fbe929150614fdc565b5090565b5080546000825590600052602060002090810190611d6b91905b5b80821115614fbe5760008155600101614fdd565b6001600160a01b0381168114611d6b57600080fd5b60006020828403121561501857600080fd5b813561502381614ff1565b9392505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156150685761506861502a565b604052919050565b60006001600160401b038211156150895761508961502a565b5060051b60200190565b600082601f8301126150a457600080fd5b813560206150b96150b483615070565b615040565b82815260059290921b840181019181810190868411156150d857600080fd5b8286015b848110156150fc5780356150ef81614ff1565b83529183019183016150dc565b509695505050505050565b8015158114611d6b57600080fd5b6000806040838503121561512857600080fd5b82356001600160401b0381111561513e57600080fd5b61514a85828601615093565b925050602083013561515b81615107565b809150509250929050565b60006020828403121561517857600080fd5b5035919050565b6000806040838503121561519257600080fd5b82356001600160401b03808211156151a957600080fd5b6151b586838701615093565b93506020915081850135818111156151cc57600080fd5b85019050601f810186136151df57600080fd5b80356151ed6150b482615070565b81815260059190911b8201830190838101908883111561520c57600080fd5b928401925b8284101561522a57833582529284019290840190615211565b80955050505050509250929050565b600081518084526020808501945080840160005b838110156152725781516001600160a01b03168752958201959082019060010161524d565b509495945050505050565b6060815260006152906060830186615239565b82810360208401526152a28186615239565b905082810360408401526152b68185615239565b9695505050505050565b6000806000606084860312156152d557600080fd5b8335925060208401356152e781614ff1565b915060408401356152f781614ff1565b809150509250925092565b60006001600160401b0382111561531b5761531b61502a565b50601f01601f191660200190565b600082601f83011261533a57600080fd5b81356153486150b482615302565b81815284602083860101111561535d57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c0878903121561539357600080fd5b863561539e81614ff1565b955060208701356153ae81614ff1565b945060408701356153be81614ff1565b935060608701356153ce81614ff1565b925060808701356153de81614ff1565b915060a08701356001600160401b038111156153f957600080fd5b61540589828a01615329565b9150509295509295509295565b60006020828403121561542457600080fd5b81356001600160401b0381111561543a57600080fd5b614dbd84828501615093565b6000806040838503121561545957600080fd5b823561546481614ff1565b946020939093013593505050565b600082601f83011261548357600080fd5b813560206154936150b483615070565b82815260059290921b840181019181810190868411156154b257600080fd5b8286015b848110156150fc5780356001600160401b038111156154d55760008081fd5b6154e38986838b0101615093565b8452509183019183016154b6565b60008060006060848603121561550657600080fd5b83356001600160401b038082111561551d57600080fd5b61552987838801615093565b9450602086013591508082111561553f57600080fd5b5061554c86828701615472565b925050604084013590509250925092565b60008083601f84011261556f57600080fd5b5081356001600160401b0381111561558657600080fd5b6020830191508360208260051b85010111156155a157600080fd5b9250929050565b600080600080604085870312156155be57600080fd5b84356001600160401b03808211156155d557600080fd5b6155e18883890161555d565b909650945060208701359150808211156155fa57600080fd5b506156078782880161555d565b95989497509550505050565b6000806040838503121561562657600080fd5b50508035926020909101359150565b6020815260006150236020830184615239565b60008060006060848603121561565d57600080fd5b833592506020840135915060408401356152f781614ff1565b6000806040838503121561568957600080fd5b82356001600160401b03808211156156a057600080fd5b6156ac86838701615093565b935060208501359150808211156156c257600080fd5b506156cf85828601615472565b9150509250929050565b6000602082840312156156eb57600080fd5b81356001600160401b0381111561570157600080fd5b614dbd84828501615329565b60008060006060848603121561572257600080fd5b833561572d81614ff1565b925060208401356152e781614ff1565b6000806040838503121561575057600080fd5b823561575b81614ff1565b9150602083013561515b81614ff1565b6000806040838503121561577e57600080fd5b823561578981614ff1565b9150602083013561515b81615107565b600080600080608085870312156157af57600080fd5b84356001600160401b038111156157c557600080fd5b6157d187828801615093565b94505060208501356157e281614ff1565b925060408501356157f281614ff1565b9150606085013561580281614ff1565b939692955090935050565b60005b83811015615828578181015183820152602001615810565b83811115610f835750506000910152565b602081526000825180602084015261585881604085016020870161580d565b601f01601f19169190910160400192915050565b60008060006060848603121561588157600080fd5b83356001600160401b038082111561589857600080fd5b6158a487838801615093565b945060208601359150808211156158ba57600080fd5b506158c786828701615472565b92505060408401356152f781614ff1565b600080604083850312156158eb57600080fd5b82356001600160401b0381111561590157600080fd5b61590d85828601615093565b925050602083013561515b81614ff1565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820161595c5761595c615934565b5060010190565b60008282101561597557615975615934565b500390565b600081600019048311821515161561599457615994615934565b500290565b6000826159b657634e487b7160e01b600052601260045260246000fd5b500490565b600082198211156159ce576159ce615934565b500190565b6000602082840312156159e557600080fd5b5051919050565b6000602082840312156159fe57600080fd5b815161502381615107565b600060ff821680615a1c57615a1c615934565b6000190192915050565b600060ff821660ff8103615a3c57615a3c615934565b60010192915050565b600060208284031215615a5757600080fd5b815161502381614ff1565b6001600160a01b03929092168252602082015260400190565b828152604060208201526000614dbd6040830184615239565b6001600160a01b0383168152604060208201819052600090614dbd90830184615239565b60008060408385031215615acb57600080fd5b505080516020909101519092909150565b6040808252600b908201526a2b27aa22a92fa0a226a4a760a91b60608201526001600160a01b0391909116602082015260800190565b600181811c90821680615b2657607f821691505b602082108103615b4657634e487b7160e01b600052602260045260246000fd5b50919050565b60008251615b5e81846020870161580d565b9190910192915050565b600080600060608486031215615b7d57600080fd5b8351615b8881614ff1565b6020850151909350615b9981614ff1565b60408501519092506152f781614ff1565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600060208284031215615c0757600080fd5b81516001600160401b03811115615c1d57600080fd5b8201601f81018413615c2e57600080fd5b8051615c3c6150b482615302565b818152856020838501011115615c5157600080fd5b615c6282602083016020860161580d565b9594505050505056fea264697066735822122086e1cd468c84e2b446a6800656904d8ee61182a14559fc951003393267d69a6f64736f6c634300080d0033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106104805760003560e01c80639a61df8911610257578063cad1b90611610146578063eae40f26116100c3578063f9f031df11610087578063f9f031df14610b99578063fca3b5aa14610bac578063fe5b38e414610bbf578063ffa1ad7414610bc7578063ffe5195b14610beb57600080fd5b8063eae40f2614610b1f578063ef69952814610b48578063f2312ab014610b5b578063f6898d7a14610b7e578063f8803bb614610b9157600080fd5b8063dcd9e47a1161010a578063dcd9e47a14610a87578063df7ae62e14610ac4578063e128aace14610ad7578063e567e86914610aea578063e6da045714610aff57600080fd5b8063cad1b90614610a1b578063cdcaa13b14610a46578063d1a8e08c14610a59578063d826f88f14610a6c578063daa168bd14610a7457600080fd5b8063ae9c9e3f116101d4578063b9a09fd511610198578063b9a09fd5146109a6578063c2b79e98146109cf578063c48f5af4146109e2578063c527ee1f146109f5578063c991866d14610a0857600080fd5b8063ae9c9e3f14610952578063b0f5027814610965578063b52a315114610978578063b55a5c1c14610980578063b7c89f9c1461099357600080fd5b8063a7cac8461161021b578063a7cac846146108cd578063a9b5aa7e146108e0578063aa79979b146108f3578063ac4afa3814610916578063ae21c4cb1461092957600080fd5b80639a61df89146108615780639a83ed3b146108815780639f06247b146108945780639f1252df146108a7578063a3d9ce44146108ba57600080fd5b80634f3ec075116103735780636c4f2e38116102f05780638daeaa5a116102b45780638daeaa5a146107f05780638dd598fb1461081957806390e934161461083157806396c82e5714610846578063992a79331461084e57600080fd5b80636c4f2e381461079b5780636f816a20146107a45780637625391a146107b75780637715ee75146107ca5780638b6fc247146107dd57600080fd5b80635d130ec6116103375780635d130ec61461072c5780636138889b1461073f578063636056f214610752578063657021fb14610765578063666256aa1461078857600080fd5b80634f3ec075146106a35780635001f3b5146106b6578063533d3e71146106c957806353be568d146106e957806355f168d51461070957600080fd5b806333d4e07c11610401578063436596c4116103c5578063436596c414610623578063470f49851461062b578063473775fe14610633578063486b7dcc1461066e5780634d68ce441461068157600080fd5b806333d4e07c146105a757806338752a9d146105ba5780633af32abf146105cd5780633c6b16ab146105f0578063402914f51461060357600080fd5b80631a32aad6116104485780631a32aad6146105505780631f7b6d32146105635780631f850716146105755780632986c0e51461058b57806333832a6a1461059457600080fd5b806306d6a1b21461048557806307546172146104cb5780630f04ba67146104de5780631703e5f9146105135780631817835814610546575b600080fd5b6104ae610493366004615006565b6011602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6006546104ae906001600160a01b031681565b6105016104ec366004615006565b601d6020526000908152604090205460ff1681565b60405160ff90911681526020016104c2565b610536610521366004615006565b601c6020526000908152604090205460ff1681565b60405190151581526020016104c2565b61054e610bf4565b005b6003546104ae906001600160a01b031681565b6008545b6040519081526020016104c2565b60005461010090046001600160a01b03166104ae565b61056760095481565b61054e6105a2366004615006565b610d8a565b61054e6105b5366004615115565b610e40565b6005546104ae906001600160a01b031681565b6105366105db366004615006565b601a6020526000908152604090205460ff1681565b61054e6105fe366004615166565b610e8e565b610567610611366004615006565b600e6020526000908152604090205481565b61054e610f89565b600154610567565b610646610641366004615166565b611082565b604080516001600160a01b0394851681529390921660208401521515908201526060016104c2565b61054e61067c366004615115565b611191565b61069461068f36600461517f565b6111da565b6040516104c29392919061527d565b61054e6106b13660046152c0565b6113dd565b6002546104ae906001600160a01b031681565b6105676106d7366004615006565b60216020526000908152604090205481565b6105676106f7366004615166565b60009081526017602052604090205490565b610536610717366004615006565b601b6020526000908152604090205460ff1681565b61054e61073a36600461537a565b611958565b61054e61074d366004615412565b611c9e565b610567610760366004615446565b611d6e565b610501610773366004615006565b601e6020526000908152604090205460ff1681565b61054e6107963660046154f1565b611d98565b610567600c5481565b61054e6107b23660046155a8565b611f02565b61054e6107c5366004615613565b611fc0565b61054e6107d83660046154f1565b612087565b61054e6107eb366004615166565b6121f1565b6105676107fe366004615006565b6001600160a01b031660009081526015602052604090205490565b6000546104ae9061010090046001600160a01b031681565b610839612282565b6040516104c29190615635565b6105676122e4565b61054e61085c366004615006565b612304565b61056761086f366004615006565b60186020526000908152604090205481565b61054e61088f366004615648565b6125a2565b61054e6108a2366004615006565b612624565b61054e6108b5366004615676565b61286e565b61054e6108c83660046156d9565b612879565b6105676108db366004615006565b6128b6565b61054e6108ee366004615006565b6128ed565b610536610901366004615006565b60196020526000908152604090205460ff1681565b6104ae610924366004615166565b6129a3565b6104ae610937366004615006565b6013602052600090815260409020546001600160a01b031681565b6020546104ae906001600160a01b031681565b61054e61097336600461570d565b6129cd565b600454610567565b6007546104ae906001600160a01b031681565b61054e6109a136600461573d565b612a4d565b6104ae6109b4366004615006565b600f602052600090815260409020546001600160a01b031681565b61054e6109dd366004615676565b612a98565b6104ae6109f0366004615446565b612b6e565b61054e610a03366004615412565b612ba6565b61054e610a16366004615676565b612cca565b610567610a2936600461573d565b601460209081526000928352604080842090915290825290205481565b61054e610a5436600461576b565b612da0565b61054e610a67366004615006565b612e08565b61054e613040565b61054e610a8236600461573d565b61307f565b610a9a610a95366004615446565b6130ca565b604080516001600160a01b03948516815292841660208401529216918101919091526060016104c2565b61054e610ad2366004615799565b6130f2565b61054e610ae5366004615006565b6132f6565b610af2613307565b6040516104c29190615839565b610567610b0d366004615006565b60106020526000908152604090205481565b6104ae610b2d366004615006565b6012602052600090815260409020546001600160a01b031681565b61054e610b5636600461586c565b613395565b610536610b69366004615006565b601f6020526000908152604090205460ff1681565b61054e610b8c3660046158d8565b613437565b6105676134d8565b61054e610ba7366004615412565b61354b565b61054e610bba366004615006565b613555565b61083961360b565b610af260405180604001604052806005815260200164352e342e3160d81b81525081565b610567600b5481565b610bfc61366b565b33610c06816136c4565b6001600160a01b038116600090815260156020908152604080832080548251818502810185019093528083529192909190830182828015610c7057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c52575b505050505090506000815190506000816001600160401b03811115610c9757610c9761502a565b604051908082528060200260200182016040528015610cc0578160200160208202803683370190505b50905060005b82811015610d55576001600160a01b03851660009081526014602052604081208551909190869084908110610cfd57610cfd61591e565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054828281518110610d3857610d3861591e565b602090810291909101015280610d4d8161594a565b915050610cc6565b50610d6184848361371b565b5050506001600160a01b03166000908152601860205260409020429055610d886001605455565b565b610d92613bf0565b806001600160a01b03163b600003610dbd57604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b038116610de45760405163d92e233d60e01b815260040160405180910390fd5b6007546040516001600160a01b038084169216907f19fff1a4baaea69caec27e064bdfc0cb61d642370694fffdc0d38a568eb89b7390600090a3600780546001600160a01b0319166001600160a01b0392909216919091179055565b610e48613c7e565b60005b8251811015610e8957610e77838281518110610e6957610e6961591e565b602002602001015183613d2a565b80610e818161594a565b915050610e4b565b505050565b6006546001600160a01b03163314610eb95760405163ea8e4eb560e01b815260040160405180910390fd5b600062093a80610ec76134d8565b610ed19190615963565b6000818152601760205260408120549192508115610f095781610efc85670de0b6b3a764000061597a565b610f069190615999565b90505b8015610f27578060096000828254610f2191906159bb565b90915550505b6002546040518581526001600160a01b039091169033907ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf508269060200160405180910390a3600254610f83906001600160a01b0316333087613df6565b50505050565b610f9161366b565b600660009054906101000a90046001600160a01b03166001600160a01b031663ed29fc116040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061100a91906159d3565b506008546000905b8082101561107657611064600f6000600885815481106110345761103461591e565b60009182526020808320909101546001600160a01b03908116845290830193909352604090910190205416613e61565b8161106e8161594a565b925050611012565b5050610d886001605455565b6020546040516319d5af4160e31b815260048101839052600091829182916001600160a01b03169063cead7a0890602401602060405180830381865afa1580156110d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f491906159ec565b158061110257506001548410155b156111155750600091508190508061118a565b600184815481106111285761112861591e565b600091825260209091200154600480546001600160a01b03909216945090859081106111565761115661591e565b6000918252602090912001546001600160a01b03908116925083161580159061118757506001600160a01b03821615155b90505b9193909250565b611199613c7e565b60005b8251811015610e89576111c88382815181106111ba576111ba61591e565b602002602001015183614076565b806111d28161594a565b91505061119c565b60608060606111e761366b565b835185511461120c576040516001621398b960e31b0319815260040160405180910390fd5b600a8551111561122f57604051630f2b9ca360e31b815260040160405180910390fd5b600085516001600160401b0381111561124a5761124a61502a565b604051908082528060200260200182016040528015611273578160200160208202803683370190505b509050600086516001600160401b038111156112915761129161502a565b6040519080825280602002602001820160405280156112ba578160200160208202803683370190505b509050600087516001600160401b038111156112d8576112d861502a565b604051908082528060200260200182016040528015611301578160200160208202803683370190505b50905060005b88518110156113c45761134c8982815181106113255761132561591e565b602002602001015189838151811061133f5761133f61591e565b6020026020010151614142565b86848151811061135e5761135e61591e565b602002602001018685815181106113775761137761591e565b602002602001018686815181106113905761139061591e565b6001600160a01b039485166020918202929092010152928216909252919091169052806113bc8161594a565b915050611307565b50919450925090506113d66001605455565b9250925092565b6113e5613bf0565b6020546040516319d5af4160e31b8152600481018590526001600160a01b039091169063cead7a0890602401602060405180830381865afa15801561142e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061145291906159ec565b61146f57604051633ec8776b60e01b815260040160405180910390fd5b6001600160a01b0382161580159061148f57506001600160a01b0382163b155b156114ad57604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b038116158015906114cd57506001600160a01b0381163b155b156114eb57604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b03821615801561150a57506001600160a01b03811615155b1561152857604051638b44eb4d60e01b815260040160405180910390fd5b6001600160a01b0382161580159061154757506001600160a01b038116155b1561156557604051638b44eb4d60e01b815260040160405180910390fd5b5b60015483106115e7576001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b03199081169091556004805492830181556000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90910180549091169055611566565b6000600184815481106115fc576115fc61591e565b6000918252602082200154600480546001600160a01b03909216935090869081106116295761162961591e565b60009182526020822001546001600160a01b03908116925083161580159061165957506001600160a01b03821615155b905060006001600160a01b0386161580159061167d57506001600160a01b03851615155b90506001600160a01b038416156116cf576001600160a01b0384166000908152601d60205260408120805460ff16916116b583615a09565b91906101000a81548160ff021916908360ff160217905550505b6001600160a01b03831615611740576001600160a01b0383166000908152601e602052604090205460ff1615611740576001600160a01b0383166000908152601e60205260408120805460ff169161172683615a09565b91906101000a81548160ff021916908360ff160217905550505b85600188815481106117545761175461591e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555084600488815481106117965761179661591e565b600091825260209091200180546001600160a01b0319166001600160a01b03928316179055861615611845576001600160a01b0386166000908152601d60205260408120805460ff16916117e983615a26565b82546101009290920a60ff8181021990931691831602179091556001600160a01b0387166000908152601e602052604081208054909216925061182b83615a26565b91906101000a81548160ff021916908360ff160217905550505b811580156118505750805b1561189b57846001600160a01b0316866001600160a01b0316887fe3fc081d6f052556cd57e83799d0056060f5b8ee94a9f035d6f84310e0afe4cc60405160405180910390a461194f565b8180156118a6575080155b156118f157826001600160a01b0316846001600160a01b0316887fce7a57c8921d9c54bc89feca7fd76c0e0fd5f0e77a1c8333484ac3944b4b871260405160405180910390a461194f565b8180156118fb5750805b1561194f57604080516001600160a01b038881168252878116602083015280861692908716918a917f842bae1770fbb1ce6402c4c0606f3a37381914d8fa33e1c4e2ad0f51a60234a6910160405180910390a45b50505050505050565b605354610100900460ff16158080156119785750605354600160ff909116105b806119925750303b158015611992575060535460ff166001145b6119fa5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6053805460ff191660011790558015611a1d576053805461ff0019166101001790555b611a2561427c565b86600060016101000a8154816001600160a01b0302191690836001600160a01b03160217905550866001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aae9190615a45565b600280546001600160a01b039283166001600160a01b031991821617909155600180548082019091557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601805492891692909116821790556000908152601d60205260408120805460ff1691611b2383615a26565b82546101009290920a60ff81810219909316918316021790915560048054600181019091557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b0319166001600160a01b0389169081179091556000908152601e6020526040812080549092169250611ba483615a26565b825460ff9182166101009390930a928302919092021990911617905550600580546001600160a01b0386166001600160a01b03199182161790915560068054821633908117909155600780549092161790558151600003611c1857604051635e2eba9760e01b815260040160405180910390fd5b8151611c2b906022906020850190614f3e565b506000600b81905562093a80600a819055600c55805460ff19169055611c50836142ab565b801561194f576053805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050505050565b611ca661366b565b600660009054906101000a90046001600160a01b03166001600160a01b031663ed29fc116040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611cfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d1f91906159d3565b5060005b8151811015611d6057611d4e828281518110611d4157611d4161591e565b6020026020010151613e61565b80611d588161594a565b915050611d23565b50611d6b6001605455565b50565b60008181526016602090815260408083206001600160a01b03861684529091529020545b92915050565b600054604051636bec739760e01b81526101009091046001600160a01b031690636bec739790611dce9033908590600401615a62565b602060405180830381865afa158015611deb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0f91906159ec565b611e2c5760405163390cdd9b60e21b815260040160405180910390fd5b8151835114611e51576040516001621398b960e31b0319815260040160405180910390fd5b60005b8351811015610f8357838181518110611e6f57611e6f61591e565b60200260200101516001600160a01b031663a7852afa83858481518110611e9857611e9861591e565b60200260200101516040518363ffffffff1660e01b8152600401611ebd929190615a7b565b600060405180830381600087803b158015611ed757600080fd5b505af1158015611eeb573d6000803e3d6000fd5b505050508080611efa9061594a565b915050611e54565b611f0a61366b565b611f13336136c4565b828114611f36576040516001621398b960e31b0319815260040160405180910390fd5b611fa4338585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080890282810182019093528882529093508892508791829185019084908082843760009201919091525061371b92505050565b336000908152601860205260409020429055610f836001605455565b611fc861366b565b600660009054906101000a90046001600160a01b03166001600160a01b031663ed29fc116040518163ffffffff1660e01b81526004016020604051808303816000875af115801561201d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204191906159d3565b50815b8181101561207857612066600f6000600884815481106110345761103461591e565b806120708161594a565b915050612044565b506120836001605455565b5050565b600054604051636bec739760e01b81526101009091046001600160a01b031690636bec7397906120bd9033908590600401615a62565b602060405180830381865afa1580156120da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120fe91906159ec565b61211b5760405163390cdd9b60e21b815260040160405180910390fd5b8151835114612140576040516001621398b960e31b0319815260040160405180910390fd5b60005b8351811015610f835783818151811061215e5761215e61591e565b60200260200101516001600160a01b031663a7852afa838584815181106121875761218761591e565b60200260200101516040518363ffffffff1660e01b81526004016121ac929190615a7b565b600060405180830381600087803b1580156121c657600080fd5b505af11580156121da573d6000803e3d6000fd5b5050505080806121e99061594a565b915050612143565b6121f9613bf0565b600b54810361221e576040516001626a738d60e11b0319815260040160405180910390fd5b600c54811115612241576040516327df2edd60e11b815260040160405180910390fd5b600b5460408051918252602082018390527f17fb843f25faf2431510bc4a71e4898ccc1a28a3e985450e509670d14be2eaa1910160405180910390a1600b55565b606060048054806020026020016040519081016040528092919081815260200182805480156122da57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116122bc575b5050505050905090565b6000806122ef6134d8565b60009081526017602052604090205492915050565b61230c613c7e565b6001600160a01b0381166000908152601c602052604090205460ff1661234557604051633f88da5160e21b815260040160405180910390fd5b60025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390612378908490600090600401615a62565b6020604051808303816000875af1158015612397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bb91906159ec565b506003546001600160a01b0316156124455760035460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390612400908490600090600401615a62565b6020604051808303816000875af115801561241f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061244391906159ec565b505b600061245082614393565b905080156124b957600654600254612475916001600160a01b03918216911683614443565b816001600160a01b03167f17b30dc851ebb8ff6aaaf985b0588fb1f53a24b553db88d364af4c3f259b2af5826040516124b091815260200190565b60405180910390a25b6001600160a01b0382166000908152601c60205260408120805460ff191690556124e16134d8565b60008181526016602090815260408083206001600160a01b0380891685526011845282852054168452825280832054848452601790925282208054939450909290919061252f908490615963565b909155505060008181526016602090815260408083206001600160a01b038088168086526011855283862054909116855290835281842084905580845260219092528083208490555190917f04a5d3f5d80d22d9345acc80618f4a4e7e663cf9e1aed23b57d975acec002ba791a2505050565b6125aa61366b565b6125b2613bf0565b825b8281101561261957612607600f6000600884815481106125d6576125d661591e565b60009182526020808320909101546001600160a01b0390811684529083019390935260409091019020541683614462565b806126118161594a565b9150506125b4565b50610e896001605455565b61262c613c7e565b6001600160a01b0381166000908152601c602052604090205460ff1615612666576040516368368ceb60e01b815260040160405180910390fd5b6001600160a01b03811660009081526019602052604090205460ff1661269f576040516304639b6160e11b815260040160405180910390fd5b6126a76134d8565b6001600160a01b038216600090815260216020526040902054106126de5760405163760d387760e01b815260040160405180910390fd5b6001600160a01b0381166000908152601c60205260409020805460ff191660011790556127096134d8565b6001600160a01b03808316600090815260106020908152604080832094909455600954600d9091529290209190915560035416156127be5760035460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061277590849060001990600401615a62565b6020604051808303816000875af1158015612794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b891906159ec565b50612837565b60025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b3906127f290849060001990600401615a62565b6020604051808303816000875af1158015612811573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283591906159ec565b505b6040516001600160a01b038216907fed18e9faa3dccfd8aa45f69c4de40546b2ca9cccc4538a2323531656516db1aa90600090a250565b612083828233614617565b612881613bf0565b80516000036128a357604051635e2eba9760e01b815260040160405180910390fd5b8051612083906022906020840190614f3e565b6000806128c16134d8565b60009081526016602090815260408083206001600160a01b039096168352949052929092205492915050565b6128f5613bf0565b806001600160a01b03163b60000361292057604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b0381166129475760405163d92e233d60e01b815260040160405180910390fd5b6005546040516001600160a01b038084169216907f03ab94e51ce1ec8483c05c7425f6ebc27289fbd4a9c8b6ba0c2fe3b8b01765d490600090a3600580546001600160a01b0319166001600160a01b0392909216919091179055565b600881815481106129b357600080fd5b6000918252602090912001546001600160a01b0316905081565b6129d5613bf0565b6001600160a01b03831660009081526019602052604090205460ff16612a0e576040516304639b6160e11b815260040160405180910390fd5b826001600160a01b03163b600003612a3957604051636f7c43f160e01b815260040160405180910390fd5b612a4383836146c8565b610e898382614779565b612a55613bf0565b6001600160a01b03821660009081526019602052604090205460ff16612a8e576040516304639b6160e11b815260040160405180910390fd5b61208382826146c8565b8051825114612abd576040516001621398b960e31b0319815260040160405180910390fd5b60005b8251811015610e8957828181518110612adb57612adb61591e565b60200260200101516001600160a01b031663db0ea98433848481518110612b0457612b0461591e565b60200260200101516040518363ffffffff1660e01b8152600401612b29929190615a94565b600060405180830381600087803b158015612b4357600080fd5b505af1158015612b57573d6000803e3d6000fd5b505050508080612b669061594a565b915050612ac0565b60156020528160005260406000208181548110612b8a57600080fd5b6000918252602090912001546001600160a01b03169150829050565b60005b81518110156120835760196000838381518110612bc857612bc861591e565b6020908102919091018101516001600160a01b031682528101919091526040016000205460ff168015612c345750601c6000838381518110612c0c57612c0c61591e565b6020908102919091018101516001600160a01b031682528101919091526040016000205460ff165b15612cb857818181518110612c4b57612c4b61591e565b60200260200101516001600160a01b031663d294f0936040518163ffffffff1660e01b815260040160408051808303816000875af1158015612c91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cb59190615ab8565b50505b80612cc28161594a565b915050612ba9565b8051825114612cef576040516001621398b960e31b0319815260040160405180910390fd5b60005b8251811015610e8957828181518110612d0d57612d0d61591e565b60200260200101516001600160a01b031663db0ea98433848481518110612d3657612d3661591e565b60200260200101516040518363ffffffff1660e01b8152600401612d5b929190615a94565b600060405180830381600087803b158015612d7557600080fd5b505af1158015612d89573d6000803e3d6000fd5b505050508080612d989061594a565b915050612cf2565b612da8613bf0565b6001600160a01b0382166000818152601f6020908152604091829020805460ff191685151590811790915591519182527fb1252fa67b4c05afbdaadfae34890b205103c0212a9e062b3978e8cd573631a991015b60405180910390a25050565b612e10613bf0565b6001600160a01b03811615801590612e3057506001600160a01b0381163b155b15612e4e57604051630f58058360e11b815260040160405180910390fd5b6003546001600160a01b03168015612ed85760025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390612e93908490600090600401615a62565b6020604051808303816000875af1158015612eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ed691906159ec565b505b6001600160a01b038181166000908152601f6020526040808220805460ff19169055600380546001600160a01b03191693861693841790555182917fa078441d4124bc8d06fb66724c308c14f635a5a0d2bb1f422400e094b5cac40c91a36001600160a01b03821615612fe157600380546001600160a01b039081166000908152601f602052604090819020805460ff191660011790556002549254905163095ea7b360e01b81529282169263095ea7b392612f9c92169060001990600401615a62565b6020604051808303816000875af1158015612fbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fdf91906159ec565b505b600854606481111561302c57506040805160008152606460208201819052917f54cf3663cc24a77872b6e7edff8268ac45778eda977c143530389972902aa0a9910160405180910390a15b60085415610e8957610e89600082846125a2565b61304861366b565b33613052816136c4565b61305b81614827565b6001600160a01b03166000908152601860205260409020429055610d886001605455565b613087613bf0565b6001600160a01b03821660009081526019602052604090205460ff166130c0576040516304639b6160e11b815260040160405180910390fd5b6120838282614779565b60008060006130d761366b565b6130e18585614142565b919450925090506113d66001605455565b6006546001600160a01b0316331480159061317b5750600754604051634448e1eb60e01b81526001600160a01b0390911690634448e1eb90613138903390600401615adc565b602060405180830381865afa158015613155573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061317991906159ec565b155b156131995760405163ea8e4eb560e01b815260040160405180910390fd5b60005460ff16156131bc5760405162dc149f60e41b815260040160405180910390fd5b60005b84518110156131fe576131ec8582815181106131dd576131dd61591e565b60200260200101516001614076565b806131f68161594a565b9150506131bf565b50600680546001600160a01b038085166001600160a01b0319928316179092556007805486841692169190911790558116156132c157600380546001600160a01b0319166001600160a01b0383811691821790925560025460405163095ea7b360e01b815292169163095ea7b39161327c9160001990600401615a62565b6020604051808303816000875af115801561329b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bf91906159ec565b505b50506003546001600160a01b03166000908152601f602052604081208054600160ff1991821681179092558254161790555050565b6132fe613bf0565b611d6b816142ab565b6022805461331490615b12565b80601f016020809104026020016040519081016040528092919081815260200182805461334090615b12565b801561338d5780601f106133625761010080835404028352916020019161338d565b820191906000526020600020905b81548152906001019060200180831161337057829003601f168201915b505050505081565b6000546040516328ba294360e21b81526001600160a01b0383811660048301523360248301526101009092049091169063a2e8a50c90604401602060405180830381865afa1580156133eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061340f91906159ec565b61342c5760405163390cdd9b60e21b815260040160405180910390fd5b610e89838383614617565b6000546040516328ba294360e21b81526001600160a01b0383811660048301523360248301526101009092049091169063a2e8a50c90604401602060405180830381865afa15801561348d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134b191906159ec565b6134ce5760405163390cdd9b60e21b815260040160405180910390fd5b6120838282614b72565b60065460408051631a2732c160e31b815290516000926001600160a01b03169163d13996089160048083019260209291908290030181865afa158015613522573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061354691906159d3565b905090565b611d6b8133614b72565b61355d613bf0565b6001600160a01b0381166135845760405163d92e233d60e01b815260040160405180910390fd5b806001600160a01b03163b6000036135af57604051636f7c43f160e01b815260040160405180910390fd5b6006546040516001600160a01b038084169216907fe490d3138e32f1f66ef3971a3c73c7f7704ba0c1d1000f1e2c3df6fc0376610b90600090a3600680546001600160a01b0319166001600160a01b0392909216919091179055565b606060018054806020026020016040519081016040528092919081815260200182805480156122da576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116122bc575050505050905090565b6002605454036136bd5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016119f1565b6002605455565b600b546001600160a01b0382166000908152601860205260409020546136ea91906159bb565b421180156136fe57506136fb6134d8565b42115b611d6b57604051632add46eb60e01b815260040160405180910390fd5b61372483614827565b815160006137306134d8565b60008054604051630748d63560e31b815292935090916101009091046001600160a01b031690633a46b1a89061376c9089908690600401615a62565b602060405180830381865afa158015613789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137ad91906159d3565b90506000806000805b8681101561385557601c6000600f60008c85815181106137d8576137d861591e565b6020908102919091018101516001600160a01b0390811683528282019390935260409182016000908120549093168452830193909352910190205460ff16156138435787818151811061382d5761382d61591e565b60200260200101518461384091906159bb565b93505b8061384d8161594a565b9150506137b6565b5060005b86811015613b995760008982815181106138755761387561591e565b6020908102919091018101516001600160a01b038082166000908152600f84526040808220549092168082526019909452205490925060ff1680156138d257506001600160a01b0381166000908152601c602052604090205460ff165b15613b8457600086888c86815181106138ed576138ed61591e565b60200260200101516138ff919061597a565b6139099190615999565b6001600160a01b03808f166000908152601460209081526040808320938816835292905220549091501561395057604051631d9fc79760e11b815260040160405180910390fd5b806000036139715760405163cabeb65560e01b815260040160405180910390fd5b6001600160a01b038d8116600090815260156020908152604080832080546001810182559084528284200180546001600160a01b03191694881694851790558c83526016825280832093835292905290812080548392906139d39084906159bb565b90915550506001600160a01b03808e16600090815260146020908152604080832093871683529290529081208054839290613a0f9084906159bb565b90915550613a1f905081866159bb565b9450613a2b81876159bb565b60405182815290965033907f4d99b957a2bc29a30ebd96a7be8e68fe50a3c701db28a91436490b7d53870ca49060200160405180910390a26001600160a01b0380831660009081526012602052604090819020549051636e553f6560e01b8152600481018490528f83166024820152911690636e553f6590604401600060405180830381600087803b158015613ac057600080fd5b505af1158015613ad4573d6000803e3d6000fd5b5050505060136000836001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166001600160a01b0316636e553f65828f6040518363ffffffff1660e01b8152600401613b509291909182526001600160a01b0316602082015260400190565b600060405180830381600087803b158015613b6a57600080fd5b505af1158015613b7e573d6000803e3d6000fd5b50505050505b50508080613b919061594a565b915050613859565b5081841015613bbb5760405163cabeb65560e01b815260040160405180910390fd5b60008581526017602052604081208054849290613bd99084906159bb565b9091555050505050505050505050565b6001605455565b600754604051634448e1eb60e01b81526001600160a01b0390911690634448e1eb90613c20903390600401615adc565b602060405180830381865afa158015613c3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c6191906159ec565b610d885760405163cbe39e7f60e01b815260040160405180910390fd5b60075460408051634448e1eb60e01b81526004810191909152600a604482015269474f5645524e414e434560b01b60648201523360248201526001600160a01b0390911690634448e1eb90608401602060405180830381865afa158015613ce9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d0d91906159ec565b610d8857604051632d5be4cb60e21b815260040160405180910390fd5b816001600160a01b03163b600003613d5557604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b0382166000908152601b60205260409020805460ff19168215801591909117909155613dbc576040516001600160a01b0383169033907f88e52751b2db8161caf8700d6127c81ae036b0c7bd22fdbcfeff5d7f79cdcae590600090a35050565b6040516001600160a01b0383169033907f44ae20f34ded1b62036f5645208c7c8b0c79b5c9c659ae5019c3b12679ed86ed90600090a35050565b6040516001600160a01b0380851660248301528316604482015260648101829052610f839085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614c0a565b6000613e6c82614393565b90508015612083576001600160a01b0382166000908152601c602052604090205460ff161561401e576003546001600160a01b031615613f73576003546040516340c10f1960e01b81526001600160a01b03909116906340c10f1990613ed89030908590600401615a62565b600060405180830381600087803b158015613ef257600080fd5b505af1158015613f06573d6000803e3d6000fd5b505060035460405163b66503cf60e01b81526001600160a01b03808716945063b66503cf9350613f3c9216908590600401615a62565b600060405180830381600087803b158015613f5657600080fd5b505af1158015613f6a573d6000803e3d6000fd5b50505050613fda565b60025460405163b66503cf60e01b81526001600160a01b038481169263b66503cf92613fa792909116908590600401615a62565b600060405180830381600087803b158015613fc157600080fd5b505af1158015613fd5573d6000803e3d6000fd5b505050505b6040518181526001600160a01b0383169033907f4fa9693cae526341d334e2862ca2413b2e503f1266255f9e0869fb36e6d89b179060200160405180910390a35050565b60065460025461403b916001600160a01b03918216911683614443565b816001600160a01b03167f17b30dc851ebb8ff6aaaf985b0588fb1f53a24b553db88d364af4c3f259b2af582604051612dfc91815260200190565b816001600160a01b03163b6000036140a157604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b0382166000908152601a60205260409020805460ff19168215801591909117909155614108576040516001600160a01b0383169033907f6661a7108aecd07864384529117d96c319c1163e3010c01390f6b704726e07de90600090a35050565b6040516001600160a01b0383169033907fd36871fdf6981136f3ac0564927005901eda06f7a9dff1e8b2a1d7846b8ebb5090600090a35050565b60205460405160009182918291829182916001600160a01b031690636e6cf23d60e11b90614176908a908a90602401615a62565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516141b49190615b4c565b600060405180830381855af49150503d80600081146141ef576040519150601f19603f3d011682016040523d82523d6000602084013e6141f4565b606091505b509150915060006142058383614cdf565b90508080602001905181019061421b9190615b68565b604080513381526001600160a01b038085166020830152949a50929850909650828b169280881692908a16917fa4d97e9e7c65249b4cd01acb82add613adea98af32daf092366982f0a0d4e453910160405180910390a45050509250925092565b605354610100900460ff166142a35760405162461bcd60e51b81526004016119f190615baa565b610d88614d87565b6040516301ffc9a760e01b81526344707c4760e01b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa1580156142f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061431a91906159ec565b6143375760405163f233911760e01b815260040160405180910390fd5b6020546040516001600160a01b038084169216907f1bdeec301704e428e5c1929a6f66bfbd6661886a1ee802affbec12e4798b9c0690600090a3602080546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166000908152600d60205260408120805460095491829055908262093a806143c26134d8565b6143cc9190615963565b60008181526016602090815260408083206001600160a01b03808b1685526011845282852054168452909152902054909150801561443a5760006144108585615963565b9050801561443857670de0b6b3a764000061442b828461597a565b6144359190615999565b95505b505b50505050919050565b610e898363a9059cbb60e01b8484604051602401613e2a929190615a62565b6001600160a01b038116156144e55760405163095ea7b360e01b81526001600160a01b0382169063095ea7b3906144a0908590600090600401615a62565b6020604051808303816000875af11580156144bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144e391906159ec565b505b6003546001600160a01b031661456c5760025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061452990859060001990600401615a62565b6020604051808303816000875af1158015614548573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8991906159ec565b60025460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061459f908590600090600401615a62565b6020604051808303816000875af11580156145be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145e291906159ec565b5060035460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061452990859060001990600401615a62565b60005b8351811015610f83578381815181106146355761463561591e565b60200260200101516001600160a01b03166331279d3d8385848151811061465e5761465e61591e565b60200260200101516040518363ffffffff1660e01b8152600401614683929190615a94565b600060405180830381600087803b15801561469d57600080fd5b505af11580156146b1573d6000803e3d6000fd5b5050505080806146c09061594a565b91505061461a565b806001600160a01b03163b6000036146f357604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b03828116600081815260126020908152604091829020549151600181529293858116939216917fddf0ec8cba6dad18edc75774c452fa4201ff17ec1761486329f3321936ce66d0910160405180910390a46001600160a01b03918216600090815260126020526040902080546001600160a01b03191691909216179055565b806001600160a01b03163b6000036147a457604051636f7c43f160e01b815260040160405180910390fd5b6001600160a01b03828116600081815260136020908152604080832054905192835292938581169316917fddf0ec8cba6dad18edc75774c452fa4201ff17ec1761486329f3321936ce66d0910160405180910390a46001600160a01b03918216600090815260136020526040902080546001600160a01b03191691909216179055565b6001600160a01b0381166000908152601560205260408120805490918061484c6134d8565b6001600160a01b038616600090815260186020526040812054919250908210905b84811015614b005760008682815481106148895761488961591e565b60009182526020808320909101546001600160a01b038b81168452601483526040808520919092168085529252909120549091508015614aeb578380156148f757506001600160a01b038083166000908152600f60209081526040808320549093168252601c9052205460ff165b156149345760008581526016602090815260408083206001600160a01b03861684529091528120805483929061492e908490615963565b90915550505b6001600160a01b03808a1660009081526014602090815260408083209386168352929052908120805483929061496b908490615963565b90915550506001600160a01b038083166000908152600f60209081526040808320549093168252601c9052205460ff16156149ad576149aa81876159bb565b95505b7f433fa8b9e8e2cb00bd714503252e8bfd144f0f318459a2ea8b39c1ae2536171789826040516149de929190615a62565b60405180910390a16001600160a01b038281166000908152600f602090815260408083205484168352601290915290819020549051627b8a6760e11b8152600481018490528b8316602482015291169062f714ce90604401600060405180830381600087803b158015614a5057600080fd5b505af1158015614a64573d6000803e3d6000fd5b505050506001600160a01b038281166000908152600f602090815260408083205484168352601390915290819020549051627b8a6760e11b8152600481018490528b8316602482015291169062f714ce90604401600060405180830381600087803b158015614ad257600080fd5b505af1158015614ae6573d6000803e3d6000fd5b505050505b50508080614af89061594a565b91505061486d565b506001600160a01b038616600090815260186020526040902054821115614b2657600092505b60008281526017602052604081208054859290614b44908490615963565b90915550506001600160a01b0386166000908152601560205260408120614b6a91614fc2565b505050505050565b60005b8251811015610e8957828181518110614b9057614b9061591e565b6020908102919091010151604051630c00007b60e41b81526001600160a01b0384811660048301529091169063c00007b090602401600060405180830381600087803b158015614bdf57600080fd5b505af1158015614bf3573d6000803e3d6000fd5b505050508080614c029061594a565b915050614b75565b6000614c5f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614dae9092919063ffffffff16565b9050805160001480614c80575080806020019051810190614c8091906159ec565b610e895760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016119f1565b60608215614cee575080611d92565b604482511015614d545760405162461bcd60e51b815260206004820152602b60248201527f64656c656761746563616c6c206661696c656420776974686f7574206120726560448201526a3b32b93a103932b0b9b7b760a91b60648201526084016119f1565b60048201915081806020019051810190614d6e9190615bf5565b60405162461bcd60e51b81526004016119f19190615839565b605354610100900460ff16613be95760405162461bcd60e51b81526004016119f190615baa565b6060614dbd8484600085614dc5565b949350505050565b606082471015614e265760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016119f1565b600080866001600160a01b03168587604051614e429190615b4c565b60006040518083038185875af1925050503d8060008114614e7f576040519150601f19603f3d011682016040523d82523d6000602084013e614e84565b606091505b5091509150614e9587838387614ea0565b979650505050505050565b60608315614f0f578251600003614f08576001600160a01b0385163b614f085760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016119f1565b5081614dbd565b614dbd8383815115614f245781518083602001fd5b8060405162461bcd60e51b81526004016119f19190615839565b828054614f4a90615b12565b90600052602060002090601f016020900481019282614f6c5760008555614fb2565b82601f10614f8557805160ff1916838001178555614fb2565b82800160010185558215614fb2579182015b82811115614fb2578251825591602001919060010190614f97565b50614fbe929150614fdc565b5090565b5080546000825590600052602060002090810190611d6b91905b5b80821115614fbe5760008155600101614fdd565b6001600160a01b0381168114611d6b57600080fd5b60006020828403121561501857600080fd5b813561502381614ff1565b9392505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156150685761506861502a565b604052919050565b60006001600160401b038211156150895761508961502a565b5060051b60200190565b600082601f8301126150a457600080fd5b813560206150b96150b483615070565b615040565b82815260059290921b840181019181810190868411156150d857600080fd5b8286015b848110156150fc5780356150ef81614ff1565b83529183019183016150dc565b509695505050505050565b8015158114611d6b57600080fd5b6000806040838503121561512857600080fd5b82356001600160401b0381111561513e57600080fd5b61514a85828601615093565b925050602083013561515b81615107565b809150509250929050565b60006020828403121561517857600080fd5b5035919050565b6000806040838503121561519257600080fd5b82356001600160401b03808211156151a957600080fd5b6151b586838701615093565b93506020915081850135818111156151cc57600080fd5b85019050601f810186136151df57600080fd5b80356151ed6150b482615070565b81815260059190911b8201830190838101908883111561520c57600080fd5b928401925b8284101561522a57833582529284019290840190615211565b80955050505050509250929050565b600081518084526020808501945080840160005b838110156152725781516001600160a01b03168752958201959082019060010161524d565b509495945050505050565b6060815260006152906060830186615239565b82810360208401526152a28186615239565b905082810360408401526152b68185615239565b9695505050505050565b6000806000606084860312156152d557600080fd5b8335925060208401356152e781614ff1565b915060408401356152f781614ff1565b809150509250925092565b60006001600160401b0382111561531b5761531b61502a565b50601f01601f191660200190565b600082601f83011261533a57600080fd5b81356153486150b482615302565b81815284602083860101111561535d57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c0878903121561539357600080fd5b863561539e81614ff1565b955060208701356153ae81614ff1565b945060408701356153be81614ff1565b935060608701356153ce81614ff1565b925060808701356153de81614ff1565b915060a08701356001600160401b038111156153f957600080fd5b61540589828a01615329565b9150509295509295509295565b60006020828403121561542457600080fd5b81356001600160401b0381111561543a57600080fd5b614dbd84828501615093565b6000806040838503121561545957600080fd5b823561546481614ff1565b946020939093013593505050565b600082601f83011261548357600080fd5b813560206154936150b483615070565b82815260059290921b840181019181810190868411156154b257600080fd5b8286015b848110156150fc5780356001600160401b038111156154d55760008081fd5b6154e38986838b0101615093565b8452509183019183016154b6565b60008060006060848603121561550657600080fd5b83356001600160401b038082111561551d57600080fd5b61552987838801615093565b9450602086013591508082111561553f57600080fd5b5061554c86828701615472565b925050604084013590509250925092565b60008083601f84011261556f57600080fd5b5081356001600160401b0381111561558657600080fd5b6020830191508360208260051b85010111156155a157600080fd5b9250929050565b600080600080604085870312156155be57600080fd5b84356001600160401b03808211156155d557600080fd5b6155e18883890161555d565b909650945060208701359150808211156155fa57600080fd5b506156078782880161555d565b95989497509550505050565b6000806040838503121561562657600080fd5b50508035926020909101359150565b6020815260006150236020830184615239565b60008060006060848603121561565d57600080fd5b833592506020840135915060408401356152f781614ff1565b6000806040838503121561568957600080fd5b82356001600160401b03808211156156a057600080fd5b6156ac86838701615093565b935060208501359150808211156156c257600080fd5b506156cf85828601615472565b9150509250929050565b6000602082840312156156eb57600080fd5b81356001600160401b0381111561570157600080fd5b614dbd84828501615329565b60008060006060848603121561572257600080fd5b833561572d81614ff1565b925060208401356152e781614ff1565b6000806040838503121561575057600080fd5b823561575b81614ff1565b9150602083013561515b81614ff1565b6000806040838503121561577e57600080fd5b823561578981614ff1565b9150602083013561515b81615107565b600080600080608085870312156157af57600080fd5b84356001600160401b038111156157c557600080fd5b6157d187828801615093565b94505060208501356157e281614ff1565b925060408501356157f281614ff1565b9150606085013561580281614ff1565b939692955090935050565b60005b83811015615828578181015183820152602001615810565b83811115610f835750506000910152565b602081526000825180602084015261585881604085016020870161580d565b601f01601f19169190910160400192915050565b60008060006060848603121561588157600080fd5b83356001600160401b038082111561589857600080fd5b6158a487838801615093565b945060208601359150808211156158ba57600080fd5b506158c786828701615472565b92505060408401356152f781614ff1565b600080604083850312156158eb57600080fd5b82356001600160401b0381111561590157600080fd5b61590d85828601615093565b925050602083013561515b81614ff1565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820161595c5761595c615934565b5060010190565b60008282101561597557615975615934565b500390565b600081600019048311821515161561599457615994615934565b500290565b6000826159b657634e487b7160e01b600052601260045260246000fd5b500490565b600082198211156159ce576159ce615934565b500190565b6000602082840312156159e557600080fd5b5051919050565b6000602082840312156159fe57600080fd5b815161502381615107565b600060ff821680615a1c57615a1c615934565b6000190192915050565b600060ff821660ff8103615a3c57615a3c615934565b60010192915050565b600060208284031215615a5757600080fd5b815161502381614ff1565b6001600160a01b03929092168252602082015260400190565b828152604060208201526000614dbd6040830184615239565b6001600160a01b0383168152604060208201819052600090614dbd90830184615239565b60008060408385031215615acb57600080fd5b505080516020909101519092909150565b6040808252600b908201526a2b27aa22a92fa0a226a4a760a91b60608201526001600160a01b0391909116602082015260800190565b600181811c90821680615b2657607f821691505b602082108103615b4657634e487b7160e01b600052602260045260246000fd5b50919050565b60008251615b5e81846020870161580d565b9190910192915050565b600080600060608486031215615b7d57600080fd5b8351615b8881614ff1565b6020850151909350615b9981614ff1565b60408501519092506152f781614ff1565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600060208284031215615c0757600080fd5b81516001600160401b03811115615c1d57600080fd5b8201601f81018413615c2e57600080fd5b8051615c3c6150b482615302565b818152856020838501011115615c5157600080fd5b615c6282602083016020860161580d565b9594505050505056fea264697066735822122086e1cd468c84e2b446a6800656904d8ee61182a14559fc951003393267d69a6f64736f6c634300080d0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.