MATIC Price: $0.496205 (-3.48%)
Gas: 40.9 GWei
 

Overview

TokenID

1493

Total Transfers

-

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
VotingEscrow

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 10 : IVotes.sol
// 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;

    /**
     * @dev Delegates votes from signer to `delegatee`.
     */
    function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
}

File 2 of 10 : IERC20.sol
// 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);
}

File 3 of 10 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 4 of 10 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @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);
}

File 5 of 10 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 6 of 10 : IERC165.sol
// 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);
}

File 7 of 10 : Epoch.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

uint256 constant EPOCH_DURATION = 7 days;

File 8 of 10 : IVeArtProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IVeArtProxy {
    function _tokenURI(
        uint256 _tokenId,
        uint256 _balanceOf,
        uint256 _locked_end,
        uint256 _value
    ) external pure returns (string memory output);
}

File 9 of 10 : IVotingEscrow.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

    struct LockedBalance {
        int128 amount;
        uint256 end;
    }

    function create_lock_for(uint256 _value, uint256 _lock_duration, address _to) external returns (uint256);

    function locked(uint256 id) external view returns (LockedBalance memory);

    function tokenOfOwnerByIndex(address _owner, uint256 _tokenIndex) external view returns (uint256);

    function token() external view returns (address);

    function team() external returns (address);

    function epoch() external view returns (uint256);

    function point_history(uint256 loc) external view returns (Point memory);

    function user_point_history(uint256 tokenId, uint256 loc) external view returns (Point memory);

    function user_point_epoch(uint256 tokenId) external view returns (uint256);

    function ownerOf(uint256) external view returns (address);

    function isApprovedOrOwner(address, uint256) external view returns (bool);

    function transferFrom(address, address, uint256) external;

    function voted(uint256) external view returns (bool);

    function attachments(uint256) external view returns (uint256);

    function voting(uint256 tokenId) external;

    function abstain(uint256 tokenId) external;

    function attach(uint256 tokenId) external;

    function detach(uint256 tokenId) external;

    function checkpoint() external;

    function deposit_for(uint256 tokenId, uint256 value) external;

    function balanceOfNFT(uint256 _id) external view returns (uint256);

    function balanceOf(address _owner) external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function supply() external view returns (uint256);

    function decimals() external view returns (uint8);
}

File 10 of 10 : VotingEscrow.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721, IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

import {IVeArtProxy} from "./interfaces/IVeArtProxy.sol";
import {IVotingEscrow} from "./interfaces/IVotingEscrow.sol";
import {EPOCH_DURATION} from "./Epoch.sol";

/// @title Voting Escrow
/// @notice veNFT implementation that escrows ERC-20 tokens in the form of an ERC-721 NFT
/// @notice Votes have a weight depending on time, so that users are committed to the future of (whatever they are voting for)
/// @author Modified from Solidly (https://github.com/solidlyexchange/solidly/blob/master/contracts/ve.sol)
/// @author Modified from Curve (https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy)
/// @author Modified from Nouns DAO (https://github.com/withtally/my-nft-dao-project/blob/main/contracts/ERC721Checkpointable.sol)
/// @dev Vote weight decays linearly over time. Lock time cannot be more than `MAXTIME` (2 years).
contract VotingEscrow is IERC721, IERC721Metadata, IVotes {
    enum DepositType {
        DEPOSIT_FOR_TYPE,
        CREATE_LOCK_TYPE,
        INCREASE_LOCK_AMOUNT,
        INCREASE_UNLOCK_TIME,
        MERGE_TYPE,
        SPLIT_TYPE
    }

    struct LockedBalance {
        int128 amount;
        uint256 end;
    }

    struct Point {
        int128 bias;
        int128 slope; // # -dweight / dt
        uint256 ts;
        uint256 blk; // block
    }
    /* We cannot really do block numbers per se b/c slope is per time, not per block
     * and per block could be fairly bad b/c Ethereum changes blocktimes.
     * What we can do is to extrapolate ***At functions */

    /// @notice A checkpoint for marking delegated tokenIds from a given timestamp
    struct Checkpoint {
        uint256 timestamp;
        uint256[] tokenIds;
    }

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Deposit(address indexed provider, uint256 tokenId, uint256 value, uint256 indexed locktime, DepositType deposit_type, uint256 ts);
    event Withdraw(address indexed provider, uint256 tokenId, uint256 value, uint256 ts);
    event Supply(uint256 prevSupply, uint256 supply);

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    address public immutable token;
    address public voter;
    address public team;
    address public artProxy;

    mapping(uint256 => Point) public point_history; // epoch -> unsigned point

    /// @dev Mapping of interface id to bool about whether or not it's supported
    mapping(bytes4 => bool) internal supportedInterfaces;

    /// @dev ERC165 interface ID of ERC165
    bytes4 internal constant ERC165_INTERFACE_ID = 0x01ffc9a7;

    /// @dev ERC165 interface ID of ERC721
    bytes4 internal constant ERC721_INTERFACE_ID = 0x80ac58cd;

    /// @dev ERC165 interface ID of ERC721Metadata
    bytes4 internal constant ERC721_METADATA_INTERFACE_ID = 0x5b5e139f;

    /// @dev Current count of token
    uint256 internal tokenId;

    /// @notice Contract constructor
    /// @param token_addr `PEARL` token address
    constructor(address token_addr, address art_proxy) {
        token = token_addr;
        team = msg.sender;
        artProxy = art_proxy;

        point_history[0].blk = block.number;
        point_history[0].ts = block.timestamp;

        supportedInterfaces[ERC165_INTERFACE_ID] = true;
        supportedInterfaces[ERC721_INTERFACE_ID] = true;
        supportedInterfaces[ERC721_METADATA_INTERFACE_ID] = true;

        // mint-ish
        emit Transfer(address(0), address(this), tokenId);
        // burn-ish
        emit Transfer(address(this), address(0), tokenId);
    }

    /*//////////////////////////////////////////////////////////////
                                MODIFIERS
    //////////////////////////////////////////////////////////////*/

    /// @dev reentrancy guard
    uint8 internal constant _not_entered = 1;
    uint8 internal constant _entered = 2;
    uint8 internal _entered_state = 1;
    modifier nonreentrant() {
        require(_entered_state == _not_entered);
        _entered_state = _entered;
        _;
        _entered_state = _not_entered;
    }

    /*///////////////////////////////////////////////////////////////
                             METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public constant name = "vePearl";
    string public constant symbol = "vePEARL";
    string public constant version = "1.0.0";
    uint8 public constant decimals = 18;

    function setTeam(address _team) external {
        require(msg.sender == team);
        team = _team;
    }

    function setArtProxy(address _proxy) external {
        require(msg.sender == team);
        artProxy = _proxy;
    }

    /// @dev Returns current token URI metadata
    /// @param _tokenId Token ID to fetch URI for.
    function tokenURI(uint256 _tokenId) external view returns (string memory) {
        require(idToOwner[_tokenId] != address(0), "Query for nonexistent token");
        LockedBalance memory _locked = locked[_tokenId];
        return
            IVeArtProxy(artProxy)._tokenURI(
                _tokenId,
                _balanceOfNFT(_tokenId, block.timestamp),
                _locked.end,
                uint256(int256(_locked.amount))
            );
    }

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @dev Mapping from NFT ID to the address that owns it.
    mapping(uint256 => address) internal idToOwner;

    /// @dev Mapping from owner address to count of his tokens.
    mapping(address => uint256) internal ownerToNFTokenCount;

    /// @dev Returns the address of the owner of the NFT.
    /// @param _tokenId The identifier for an NFT.
    function ownerOf(uint256 _tokenId) public view returns (address) {
        return idToOwner[_tokenId];
    }

    /// @dev Returns the number of NFTs owned by `_owner`.
    ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.
    /// @param _owner Address for whom to query the balance.
    function _balance(address _owner) internal view returns (uint256) {
        return ownerToNFTokenCount[_owner];
    }

    /// @dev Returns the number of NFTs owned by `_owner`.
    ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.
    /// @param _owner Address for whom to query the balance.
    function balanceOf(address _owner) external view returns (uint256) {
        return _balance(_owner);
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @dev Mapping from NFT ID to approved address.
    mapping(uint256 => address) internal idToApprovals;

    /// @dev Mapping from owner address to mapping of operator addresses.
    mapping(address => mapping(address => bool)) internal ownerToOperators;

    mapping(uint256 => uint256) public ownership_change;

    /// @dev Get the approved address for a single NFT.
    /// @param _tokenId ID of the NFT to query the approval of.
    function getApproved(uint256 _tokenId) external view returns (address) {
        return idToApprovals[_tokenId];
    }

    /// @dev Checks if `_operator` is an approved operator for `_owner`.
    /// @param _owner The address that owns the NFTs.
    /// @param _operator The address that acts on behalf of the owner.
    function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
        return (ownerToOperators[_owner])[_operator];
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address.
    ///      Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.
    ///      Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP)
    ///      Throws if `_approved` is the current owner. (NOTE: This is not written the EIP)
    /// @param _approved Address to be approved for the given NFT ID.
    /// @param _tokenId ID of the token to be approved.
    function approve(address _approved, uint256 _tokenId) public {
        address owner = idToOwner[_tokenId];
        // Throws if `_tokenId` is not a valid NFT
        require(owner != address(0));
        // Throws if `_approved` is the current owner
        require(_approved != owner);
        // Check requirements
        bool senderIsOwner = (idToOwner[_tokenId] == msg.sender);
        bool senderIsApprovedForAll = (ownerToOperators[owner])[msg.sender];
        require(senderIsOwner || senderIsApprovedForAll);
        // Set the approval
        idToApprovals[_tokenId] = _approved;
        emit Approval(owner, _approved, _tokenId);
    }

    /// @dev Enables or disables approval for a third party ("operator") to manage all of
    ///      `msg.sender`'s assets. It also emits the ApprovalForAll event.
    ///      Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP)
    /// @notice This works even if sender doesn't own any tokens at the time.
    /// @param _operator Address to add to the set of authorized operators.
    /// @param _approved True if the operators is approved, false to revoke approval.
    function setApprovalForAll(address _operator, bool _approved) external {
        // Throws if `_operator` is the `msg.sender`
        assert(_operator != msg.sender);
        ownerToOperators[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    /* TRANSFER FUNCTIONS */
    /// @dev Clear an approval of a given address
    ///      Throws if `_owner` is not the current owner.
    function _clearApproval(address _owner, uint256 _tokenId) internal {
        // Throws if `_owner` is not the current owner
        assert(idToOwner[_tokenId] == _owner);
        if (idToApprovals[_tokenId] != address(0)) {
            // Reset approvals
            idToApprovals[_tokenId] = address(0);
        }
    }

    /// @dev Returns whether the given spender can transfer a given token ID
    /// @param _spender address of the spender to query
    /// @param _tokenId uint ID of the token to be transferred
    /// @return bool whether the msg.sender is approved for the given token ID, is an operator of the owner, or is the owner of the token
    function _isApprovedOrOwner(address _spender, uint256 _tokenId) internal view returns (bool) {
        address owner = idToOwner[_tokenId];
        bool spenderIsOwner = owner == _spender;
        bool spenderIsApproved = _spender == idToApprovals[_tokenId];
        bool spenderIsApprovedForAll = (ownerToOperators[owner])[_spender];
        return spenderIsOwner || spenderIsApproved || spenderIsApprovedForAll;
    }

    function isApprovedOrOwner(address _spender, uint256 _tokenId) external view returns (bool) {
        return _isApprovedOrOwner(_spender, _tokenId);
    }

    /// @dev Exeute transfer of a NFT.
    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
    ///      address for this NFT. (NOTE: `msg.sender` not allowed in internal function so pass `_sender`.)
    ///      Throws if `_to` is the zero address.
    ///      Throws if `_from` is not the current owner.
    ///      Throws if `_tokenId` is not a valid NFT.
    function _transferFrom(address _from, address _to, uint256 _tokenId, address _sender) internal {
        require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");
        // Check requirements
        require(_isApprovedOrOwner(_sender, _tokenId));
        // Clear approval. Throws if `_from` is not the current owner
        _clearApproval(_from, _tokenId);
        // Remove NFT. Throws if `_tokenId` is not a valid NFT
        _removeTokenFrom(_from, _tokenId);
        // auto re-delegate
        _moveTokenDelegates(delegates(_from), delegates(_to), _tokenId);
        // Add NFT
        _addTokenTo(_to, _tokenId);
        // Set the block of ownership transfer (for Flash NFT protection)
        ownership_change[_tokenId] = block.number;
        // Log the transfer
        emit Transfer(_from, _to, _tokenId);
    }

    /// @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved address for this NFT.
    ///      Throws if `_from` is not the current owner.
    ///      Throws if `_to` is the zero address.
    ///      Throws if `_tokenId` is not a valid NFT.
    /// @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else
    ///        they maybe be permanently lost.
    /// @param _from The current owner of the NFT.
    /// @param _to The new owner.
    /// @param _tokenId The NFT to transfer.
    function transferFrom(address _from, address _to, uint256 _tokenId) external {
        _transferFrom(_from, _to, _tokenId, msg.sender);
    }

    /// @dev Transfers the ownership of an NFT from one address to another address.
    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the
    ///      approved address for this NFT.
    ///      Throws if `_from` is not the current owner.
    ///      Throws if `_to` is the zero address.
    ///      Throws if `_tokenId` is not a valid NFT.
    ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if
    ///      the return value is not `bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))`.
    /// @param _from The current owner of the NFT.
    /// @param _to The new owner.
    /// @param _tokenId The NFT to transfer.
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
        safeTransferFrom(_from, _to, _tokenId, "");
    }

    function _isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /// @dev Transfers the ownership of an NFT from one address to another address.
    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the
    ///      approved address for this NFT.
    ///      Throws if `_from` is not the current owner.
    ///      Throws if `_to` is the zero address.
    ///      Throws if `_tokenId` is not a valid NFT.
    ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if
    ///      the return value is not `bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))`.
    /// @param _from The current owner of the NFT.
    /// @param _to The new owner.
    /// @param _tokenId The NFT to transfer.
    /// @param _data Additional data with no specified format, sent in call to `_to`.
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public {
        _transferFrom(_from, _to, _tokenId, msg.sender);

        if (_isContract(_to)) {
            // Throws if transfer destination is a contract which does not implement 'onERC721Received'
            try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4 response) {
                if (response != IERC721Receiver(_to).onERC721Received.selector) {
                    revert("ERC721: ERC721Receiver rejected tokens");
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @dev Interface identification is specified in ERC-165.
    /// @param _interfaceID Id of the interface
    function supportsInterface(bytes4 _interfaceID) external view returns (bool) {
        return supportedInterfaces[_interfaceID];
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @dev Mapping from owner address to mapping of index to tokenIds
    mapping(address => mapping(uint256 => uint256)) internal ownerToNFTokenIdList;

    /// @dev Mapping from NFT ID to index of owner
    mapping(uint256 => uint256) internal tokenToOwnerIndex;

    /// @dev  Get token by index
    function tokenOfOwnerByIndex(address _owner, uint256 _tokenIndex) external view returns (uint256) {
        return ownerToNFTokenIdList[_owner][_tokenIndex];
    }

    /// @dev Add a NFT to an index mapping to a given address
    /// @param _to address of the receiver
    /// @param _tokenId uint ID Of the token to be added
    function _addTokenToOwnerList(address _to, uint256 _tokenId) internal {
        uint256 current_count = _balance(_to);

        ownerToNFTokenIdList[_to][current_count] = _tokenId;
        tokenToOwnerIndex[_tokenId] = current_count;
    }

    /// @dev Add a NFT to a given address
    ///      Throws if `_tokenId` is owned by someone.
    function _addTokenTo(address _to, uint256 _tokenId) internal {
        // Throws if `_tokenId` is owned by someone
        assert(idToOwner[_tokenId] == address(0));
        // Change the owner
        idToOwner[_tokenId] = _to;
        // Update owner token index tracking
        _addTokenToOwnerList(_to, _tokenId);
        // Change count tracking
        ownerToNFTokenCount[_to] += 1;
    }

    /// @dev Function to mint tokens
    ///      Throws if `_to` is zero address.
    ///      Throws if `_tokenId` is owned by someone.
    /// @param _to The address that will receive the minted tokens.
    /// @param _tokenId The token id to mint.
    /// @return A boolean that indicates if the operation was successful.
    function _mint(address _to, uint256 _tokenId) internal returns (bool) {
        // Throws if `_to` is zero address
        assert(_to != address(0));
        // checkpoint for gov
        _moveTokenDelegates(address(0), delegates(_to), _tokenId);
        // Add NFT. Throws if `_tokenId` is owned by someone
        _addTokenTo(_to, _tokenId);
        emit Transfer(address(0), _to, _tokenId);
        return true;
    }

    /// @dev Remove a NFT from an index mapping to a given address
    /// @param _from address of the sender
    /// @param _tokenId uint ID Of the token to be removed
    function _removeTokenFromOwnerList(address _from, uint256 _tokenId) internal {
        // Delete
        uint256 current_count = _balance(_from) - 1;
        uint256 current_index = tokenToOwnerIndex[_tokenId];

        if (current_count == current_index) {
            // update ownerToNFTokenIdList
            ownerToNFTokenIdList[_from][current_count] = 0;
            // update tokenToOwnerIndex
            tokenToOwnerIndex[_tokenId] = 0;
        } else {
            uint256 lastTokenId = ownerToNFTokenIdList[_from][current_count];

            // Add
            // update ownerToNFTokenIdList
            ownerToNFTokenIdList[_from][current_index] = lastTokenId;
            // update tokenToOwnerIndex
            tokenToOwnerIndex[lastTokenId] = current_index;

            // Delete
            // update ownerToNFTokenIdList
            ownerToNFTokenIdList[_from][current_count] = 0;
            // update tokenToOwnerIndex
            tokenToOwnerIndex[_tokenId] = 0;
        }
    }

    /// @dev Remove a NFT from a given address
    ///      Throws if `_from` is not the current owner.
    function _removeTokenFrom(address _from, uint256 _tokenId) internal {
        // Throws if `_from` is not the current owner
        assert(idToOwner[_tokenId] == _from);
        // Change the owner
        idToOwner[_tokenId] = address(0);
        // Update owner token index tracking
        _removeTokenFromOwnerList(_from, _tokenId);
        // Change count tracking
        ownerToNFTokenCount[_from] -= 1;
    }

    function _burn(uint256 _tokenId) internal {
        require(_isApprovedOrOwner(msg.sender, _tokenId), "caller is not owner nor approved");

        address owner = ownerOf(_tokenId);

        // Clear approval
        approve(address(0), _tokenId);
        // checkpoint for gov
        _moveTokenDelegates(delegates(owner), address(0), _tokenId);
        // Remove token
        //_removeTokenFrom(msg.sender, _tokenId);
        _removeTokenFrom(owner, _tokenId);

        emit Transfer(owner, address(0), _tokenId);
    }

    /*//////////////////////////////////////////////////////////////
                             ESCROW STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => uint256) public user_point_epoch;
    mapping(uint256 => Point[1000000000]) public user_point_history; // user -> Point[user_epoch]
    mapping(uint256 => LockedBalance) public locked;
    uint256 public epoch;
    mapping(uint256 => int128) public slope_changes; // time -> signed slope change
    uint256 public supply;

    uint256 internal constant MAXTIME = 2 * 365 * 86400;
    int128 internal constant iMAXTIME = 2 * 365 * 86400;
    uint256 internal constant MULTIPLIER = 1 ether;

    /*//////////////////////////////////////////////////////////////
                              ESCROW LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Get the most recently recorded rate of voting power decrease for `_tokenId`
    /// @param _tokenId token of the NFT
    /// @return Value of the slope
    function get_last_user_slope(uint256 _tokenId) external view returns (int128) {
        uint256 uepoch = user_point_epoch[_tokenId];
        return user_point_history[_tokenId][uepoch].slope;
    }

    /// @notice Get the timestamp for checkpoint `_idx` for `_tokenId`
    /// @param _tokenId token of the NFT
    /// @param _idx User epoch number
    /// @return Epoch time of the checkpoint
    function user_point_history__ts(uint256 _tokenId, uint256 _idx) external view returns (uint256) {
        return user_point_history[_tokenId][_idx].ts;
    }

    /// @notice Get timestamp when `_tokenId`'s lock finishes
    /// @param _tokenId User NFT
    /// @return Epoch time of the lock end
    function locked__end(uint256 _tokenId) external view returns (uint256) {
        return locked[_tokenId].end;
    }

    /// @notice Record global and per-user data to checkpoint
    /// @param _tokenId NFT token ID. No user checkpoint if 0
    /// @param old_locked Pevious locked amount / end lock time for the user
    /// @param new_locked New locked amount / end lock time for the user
    function _checkpoint(uint256 _tokenId, LockedBalance memory old_locked, LockedBalance memory new_locked) internal {
        Point memory u_old;
        Point memory u_new;
        int128 old_dslope = 0;
        int128 new_dslope = 0;
        uint256 _epoch = epoch;

        if (_tokenId != 0) {
            // Calculate slopes and biases
            // Kept at zero when they have to
            if (old_locked.end > block.timestamp && old_locked.amount > 0) {
                u_old.slope = old_locked.amount / iMAXTIME;
                u_old.bias = u_old.slope * int128(int256(old_locked.end - block.timestamp));
            }
            if (new_locked.end > block.timestamp && new_locked.amount > 0) {
                u_new.slope = new_locked.amount / iMAXTIME;
                u_new.bias = u_new.slope * int128(int256(new_locked.end - block.timestamp));
            }

            // Read values of scheduled changes in the slope
            // old_locked.end can be in the past and in the future
            // new_locked.end can ONLY by in the FUTURE unless everything expired: than zeros
            old_dslope = slope_changes[old_locked.end];
            if (new_locked.end != 0) {
                if (new_locked.end == old_locked.end) {
                    new_dslope = old_dslope;
                } else {
                    new_dslope = slope_changes[new_locked.end];
                }
            }
        }

        Point memory last_point = Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number});
        if (_epoch > 0) {
            last_point = point_history[_epoch];
        }
        uint256 last_checkpoint = last_point.ts;
        // initial_last_point is used for extrapolation to calculate block number
        // (approximately, for *At methods) and save them
        // as we cannot figure that out exactly from inside the contract
        Point memory initial_last_point = last_point;
        uint256 block_slope = 0; // dblock/dt
        if (block.timestamp > last_point.ts) {
            block_slope = (MULTIPLIER * (block.number - last_point.blk)) / (block.timestamp - last_point.ts);
        }
        // If last point is already recorded in this block, slope=0
        // But that's ok b/c we know the block in such case

        // Go over weeks to fill history and calculate what the current point is
        {
            uint256 t_i = (last_checkpoint / EPOCH_DURATION) * EPOCH_DURATION;
            for (uint256 i = 0; i < 255; ++i) {
                // Hopefully it won't happen that this won't get used in 5 years!
                // If it does, users will be able to withdraw but vote weight will be broken
                t_i += EPOCH_DURATION;
                int128 d_slope = 0;
                if (t_i > block.timestamp) {
                    t_i = block.timestamp;
                } else {
                    d_slope = slope_changes[t_i];
                }
                last_point.bias -= last_point.slope * int128(int256(t_i - last_checkpoint));
                last_point.slope += d_slope;
                if (last_point.bias < 0) {
                    // This can happen
                    last_point.bias = 0;
                }
                if (last_point.slope < 0) {
                    // This cannot happen - just in case
                    last_point.slope = 0;
                }
                last_checkpoint = t_i;
                last_point.ts = t_i;
                last_point.blk = initial_last_point.blk + (block_slope * (t_i - initial_last_point.ts)) / MULTIPLIER;
                _epoch += 1;
                if (t_i == block.timestamp) {
                    last_point.blk = block.number;
                    break;
                } else {
                    point_history[_epoch] = last_point;
                }
            }
        }

        epoch = _epoch;
        // Now point_history is filled until t=now

        if (_tokenId != 0) {
            // If last point was in this block, the slope change has been applied already
            // But in such case we have 0 slope(s)
            last_point.slope += (u_new.slope - u_old.slope);
            last_point.bias += (u_new.bias - u_old.bias);
            if (last_point.slope < 0) {
                last_point.slope = 0;
            }
            if (last_point.bias < 0) {
                last_point.bias = 0;
            }
        }

        // Record the changed point into history
        point_history[_epoch] = last_point;

        if (_tokenId != 0) {
            // Schedule the slope changes (slope is going down)
            // We subtract new_user_slope from [new_locked.end]
            // and add old_user_slope to [old_locked.end]
            if (old_locked.end > block.timestamp) {
                // old_dslope was <something> - u_old.slope, so we cancel that
                old_dslope += u_old.slope;
                if (new_locked.end == old_locked.end) {
                    old_dslope -= u_new.slope; // It was a new deposit, not extension
                }
                slope_changes[old_locked.end] = old_dslope;
            }

            if (new_locked.end > block.timestamp) {
                if (new_locked.end > old_locked.end) {
                    new_dslope -= u_new.slope; // old slope disappeared at this point
                    slope_changes[new_locked.end] = new_dslope;
                }
                // else: we recorded it already in old_dslope
            }
            // Now handle user history
            uint256 user_epoch = user_point_epoch[_tokenId] + 1;

            user_point_epoch[_tokenId] = user_epoch;
            u_new.ts = block.timestamp;
            u_new.blk = block.number;
            user_point_history[_tokenId][user_epoch] = u_new;
        }
    }

    /// @notice Deposit and lock tokens for a user
    /// @param _tokenId NFT that holds lock
    /// @param _value Amount to deposit
    /// @param unlock_time New time when to unlock the tokens, or 0 if unchanged
    /// @param locked_balance Previous locked amount / timestamp
    /// @param deposit_type The type of deposit
    function _deposit_for(
        uint256 _tokenId,
        uint256 _value,
        uint256 unlock_time,
        LockedBalance memory locked_balance,
        DepositType deposit_type
    ) internal {
        LockedBalance memory _locked = locked_balance;
        uint256 supply_before = supply;

        supply = supply_before + _value;
        LockedBalance memory old_locked;
        (old_locked.amount, old_locked.end) = (_locked.amount, _locked.end);
        // Adding to existing lock, or if a lock is expired - creating a new one
        _locked.amount += int128(int256(_value));
        if (unlock_time != 0) {
            _locked.end = unlock_time;
        }
        locked[_tokenId] = _locked;

        // Possibilities:
        // Both old_locked.end could be current or expired (>/< block.timestamp)
        // value == 0 (extend lock) or value > 0 (add to lock or extend lock)
        // _locked.end > block.timestamp (always)
        _checkpoint(_tokenId, old_locked, _locked);

        address from = msg.sender;
        if (_value != 0 && deposit_type != DepositType.MERGE_TYPE && deposit_type != DepositType.SPLIT_TYPE) {
            assert(IERC20(token).transferFrom(from, address(this), _value));
        }

        emit Deposit(from, _tokenId, _value, _locked.end, deposit_type, block.timestamp);
        emit Supply(supply_before, supply_before + _value);
    }

    function block_number() external view returns (uint256) {
        return block.number;
    }

    /// @notice Record global data to checkpoint
    function checkpoint() external {
        _checkpoint(0, LockedBalance(0, 0), LockedBalance(0, 0));
    }

    /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock
    /// @dev Anyone (even a smart contract) can deposit for someone else, but
    ///      cannot extend their locktime and deposit for a brand new user
    /// @param _tokenId lock NFT
    /// @param _value Amount to add to user's lock
    function deposit_for(uint256 _tokenId, uint256 _value) external nonreentrant {
        LockedBalance memory _locked = locked[_tokenId];

        require(_value > 0); // dev: need non-zero value
        require(_locked.amount > 0, "No existing lock found");
        require(_locked.end > block.timestamp, "Cannot add to expired lock. Withdraw");
        _deposit_for(_tokenId, _value, 0, _locked, DepositType.DEPOSIT_FOR_TYPE);
    }

    /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`
    /// @param _value Amount to deposit
    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
    /// @param _to Address to deposit
    function _create_lock(uint256 _value, uint256 _lock_duration, address _to) internal returns (uint256) {
        uint256 unlock_time = ((block.timestamp + _lock_duration) / EPOCH_DURATION) * EPOCH_DURATION; // Locktime is rounded down to weeks

        require(_value > 0); // dev: need non-zero value
        require(unlock_time > block.timestamp, "Can only lock until time in the future");
        require(unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 2 years max");

        ++tokenId;
        uint256 _tokenId = tokenId;
        _mint(_to, _tokenId);

        _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.CREATE_LOCK_TYPE);
        return _tokenId;
    }

    /// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lock_duration`
    /// @param _value Amount to deposit
    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
    function create_lock(uint256 _value, uint256 _lock_duration) external nonreentrant returns (uint256) {
        return _create_lock(_value, _lock_duration, msg.sender);
    }

    /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`
    /// @param _value Amount to deposit
    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
    /// @param _to Address to deposit
    function create_lock_for(uint256 _value, uint256 _lock_duration, address _to) external nonreentrant returns (uint256) {
        return _create_lock(_value, _lock_duration, _to);
    }

    /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time
    /// @param _value Amount of tokens to deposit and add to the lock
    function increase_amount(uint256 _tokenId, uint256 _value) external nonreentrant {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));

        LockedBalance memory _locked = locked[_tokenId];

        assert(_value > 0); // dev: need non-zero value
        require(_locked.amount > 0, "No existing lock found");
        require(_locked.end > block.timestamp, "Cannot add to expired lock. Withdraw");

        _deposit_for(_tokenId, _value, 0, _locked, DepositType.INCREASE_LOCK_AMOUNT);
    }

    /// @notice Extend the unlock time for `_tokenId`
    /// @param _lock_duration New number of seconds until tokens unlock
    function increase_unlock_time(uint256 _tokenId, uint256 _lock_duration) external nonreentrant {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));

        LockedBalance memory _locked = locked[_tokenId];
        uint256 unlock_time = ((block.timestamp + _lock_duration) / EPOCH_DURATION) * EPOCH_DURATION; // Locktime is rounded down to weeks

        require(_locked.end > block.timestamp, "Lock expired");
        require(_locked.amount > 0, "Nothing is locked");
        require(unlock_time > _locked.end, "Can only increase lock duration");
        require(unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 2 years max");

        _deposit_for(_tokenId, 0, unlock_time, _locked, DepositType.INCREASE_UNLOCK_TIME);
    }

    /// @notice Withdraw all tokens for `_tokenId`
    /// @dev Only possible if the lock has expired
    function withdraw(uint256 _tokenId) external nonreentrant {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));
        require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");

        LockedBalance memory _locked = locked[_tokenId];
        require(block.timestamp >= _locked.end, "The lock didn't expire");
        uint256 value = uint256(int256(_locked.amount));

        locked[_tokenId] = LockedBalance(0, 0);
        uint256 supply_before = supply;
        supply = supply_before - value;

        // old_locked can have either expired <= timestamp or zero end
        // _locked has only 0 end
        // Both can have >= 0 amount
        _checkpoint(_tokenId, _locked, LockedBalance(0, 0));

        assert(IERC20(token).transfer(msg.sender, value));

        // Burn the NFT
        _burn(_tokenId);

        emit Withdraw(msg.sender, _tokenId, value, block.timestamp);
        emit Supply(supply_before, supply_before - value);
    }

    /*///////////////////////////////////////////////////////////////
                           GAUGE VOTING STORAGE
    //////////////////////////////////////////////////////////////*/

    // The following ERC20/minime-compatible methods are not real balanceOf and supply!
    // They measure the weights for the purpose of voting, so they don't represent
    // real coins.

    /// @notice Binary search to estimate timestamp for block number
    /// @param _block Block to find
    /// @param max_epoch Don't go beyond this epoch
    /// @return Approximate timestamp for block
    function _find_block_epoch(uint256 _block, uint256 max_epoch) internal view returns (uint256) {
        // Binary search
        uint256 _min = 0;
        uint256 _max = max_epoch;
        for (uint256 i = 0; i < 128; ++i) {
            // Will be always enough for 128-bit numbers
            if (_min >= _max) {
                break;
            }
            uint256 _mid = (_min + _max + 1) / 2;
            if (point_history[_mid].blk <= _block) {
                _min = _mid;
            } else {
                _max = _mid - 1;
            }
        }
        return _min;
    }

    /// @notice Get the current voting power for `_tokenId`
    /// @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
    /// @param _tokenId NFT for lock
    /// @param _t Epoch time to return voting power at
    /// @return User voting power
    function _balanceOfNFT(uint256 _tokenId, uint256 _t) internal view returns (uint256) {
        uint256 _epoch = user_point_epoch[_tokenId];
        if (_epoch == 0) {
            return 0;
        } else {
            Point memory last_point = user_point_history[_tokenId][_epoch];
            last_point.bias -= last_point.slope * int128(int256(_t) - int256(last_point.ts));
            if (last_point.bias < 0) {
                last_point.bias = 0;
            }
            return uint256(int256(last_point.bias));
        }
    }

    function balanceOfNFT(uint256 _tokenId) external view returns (uint256) {
        if (ownership_change[_tokenId] == block.number) return 0;
        return _balanceOfNFT(_tokenId, block.timestamp);
    }

    function balanceOfNFTAt(uint256 _tokenId, uint256 _t) external view returns (uint256) {
        return _balanceOfNFT(_tokenId, _t);
    }

    /// @notice Measure voting power of `_tokenId` at block height `_block`
    /// @dev Adheres to MiniMe `balanceOfAt` interface: https://github.com/Giveth/minime
    /// @param _tokenId User's wallet NFT
    /// @param _block Block to calculate the voting power at
    /// @return Voting power
    function _balanceOfAtNFT(uint256 _tokenId, uint256 _block) internal view returns (uint256) {
        // Copying and pasting totalSupply code because Vyper cannot pass by
        // reference yet
        assert(_block <= block.number);

        // Binary search
        uint256 _min = 0;
        uint256 _max = user_point_epoch[_tokenId];
        for (uint256 i = 0; i < 128; ++i) {
            // Will be always enough for 128-bit numbers
            if (_min >= _max) {
                break;
            }
            uint256 _mid = (_min + _max + 1) / 2;
            if (user_point_history[_tokenId][_mid].blk <= _block) {
                _min = _mid;
            } else {
                _max = _mid - 1;
            }
        }

        Point memory upoint = user_point_history[_tokenId][_min];

        uint256 max_epoch = epoch;
        uint256 _epoch = _find_block_epoch(_block, max_epoch);
        Point memory point_0 = point_history[_epoch];
        uint256 d_block = 0;
        uint256 d_t = 0;
        if (_epoch < max_epoch) {
            Point memory point_1 = point_history[_epoch + 1];
            d_block = point_1.blk - point_0.blk;
            d_t = point_1.ts - point_0.ts;
        } else {
            d_block = block.number - point_0.blk;
            d_t = block.timestamp - point_0.ts;
        }
        uint256 block_time = point_0.ts;
        if (d_block != 0) {
            block_time += (d_t * (_block - point_0.blk)) / d_block;
        }

        upoint.bias -= upoint.slope * int128(int256(block_time - upoint.ts));
        if (upoint.bias >= 0) {
            return uint256(uint128(upoint.bias));
        } else {
            return 0;
        }
    }

    function balanceOfAtNFT(uint256 _tokenId, uint256 _block) external view returns (uint256) {
        return _balanceOfAtNFT(_tokenId, _block);
    }

    /// @notice Calculate total voting power at some point in the past
    /// @param _block Block to calculate the total voting power at
    /// @return Total voting power at `_block`
    function totalSupplyAt(uint256 _block) external view returns (uint256) {
        assert(_block <= block.number);
        uint256 _epoch = epoch;
        uint256 target_epoch = _find_block_epoch(_block, _epoch);

        Point memory point = point_history[target_epoch];
        uint256 dt = 0;
        if (target_epoch < _epoch) {
            Point memory point_next = point_history[target_epoch + 1];
            if (point.blk != point_next.blk) {
                dt = ((_block - point.blk) * (point_next.ts - point.ts)) / (point_next.blk - point.blk);
            }
        } else {
            if (point.blk != block.number) {
                dt = ((_block - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk);
            }
        }
        // Now dt contains info on how far are we beyond point
        return _supply_at(point, point.ts + dt);
    }

    /// @notice Calculate total voting power at some point in the past
    /// @param point The point (bias/slope) to start search from
    /// @param t Time to calculate the total voting power at
    /// @return Total voting power at that time
    function _supply_at(Point memory point, uint256 t) internal view returns (uint256) {
        Point memory last_point = point;
        uint256 t_i = (last_point.ts / EPOCH_DURATION) * EPOCH_DURATION;
        for (uint256 i = 0; i < 255; ++i) {
            t_i += EPOCH_DURATION;
            int128 d_slope = 0;
            if (t_i > t) {
                t_i = t;
            } else {
                d_slope = slope_changes[t_i];
            }
            last_point.bias -= last_point.slope * int128(int256(t_i - last_point.ts));
            if (t_i == t) {
                break;
            }
            last_point.slope += d_slope;
            last_point.ts = t_i;
        }

        if (last_point.bias < 0) {
            last_point.bias = 0;
        }
        return uint256(uint128(last_point.bias));
    }

    function totalSupply() external view returns (uint256) {
        return totalSupplyAtT(block.timestamp);
    }

    /// @notice Calculate total voting power
    /// @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility
    /// @return Total voting power
    function totalSupplyAtT(uint256 t) public view returns (uint256) {
        uint256 _epoch = epoch;
        Point memory last_point = point_history[_epoch];
        return _supply_at(last_point, t);
    }

    /*///////////////////////////////////////////////////////////////
                            GAUGE VOTING LOGIC
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => uint256) public attachments;
    mapping(uint256 => bool) public voted;

    function setVoter(address _voter) external {
        require(msg.sender == team);
        voter = _voter;
    }

    function voting(uint256 _tokenId) external {
        require(msg.sender == voter);
        voted[_tokenId] = true;
    }

    function abstain(uint256 _tokenId) external {
        require(msg.sender == voter);
        voted[_tokenId] = false;
    }

    function attach(uint256 _tokenId) external {
        require(msg.sender == voter);
        attachments[_tokenId] = attachments[_tokenId] + 1;
    }

    function detach(uint256 _tokenId) external {
        require(msg.sender == voter);
        attachments[_tokenId] = attachments[_tokenId] - 1;
    }

    function merge(uint256 _from, uint256 _to) external {
        require(attachments[_from] == 0 && !voted[_from], "attached");
        require(_from != _to);
        require(_isApprovedOrOwner(msg.sender, _from));
        require(_isApprovedOrOwner(msg.sender, _to));

        LockedBalance memory _locked0 = locked[_from];
        LockedBalance memory _locked1 = locked[_to];
        uint256 value0 = uint256(int256(_locked0.amount));
        uint256 end = _locked0.end >= _locked1.end ? _locked0.end : _locked1.end;

        locked[_from] = LockedBalance(0, 0);
        _checkpoint(_from, _locked0, LockedBalance(0, 0));
        _burn(_from);
        _deposit_for(_to, value0, end, _locked1, DepositType.MERGE_TYPE);
    }

    /**
     * @notice split NFT into multiple
     * @param amounts   % of split
     * @param _tokenId  NFTs ID
     */
    function split(uint256[] memory amounts, uint256 _tokenId) external {
        // check permission and vote
        require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");
        require(_isApprovedOrOwner(msg.sender, _tokenId));

        // save old data and totalWeight
        address _to = idToOwner[_tokenId];
        LockedBalance memory _locked = locked[_tokenId];
        uint256 end = _locked.end;
        uint256 value = uint256(int256(_locked.amount));
        require(value > 0); // dev: need non-zero value

        // reset supply, _deposit_for increase it
        supply = supply - value;

        uint256 i;
        uint256 totalWeight = 0;
        for (i = 0; i < amounts.length; i++) {
            totalWeight += amounts[i];
        }

        // remove old data
        locked[_tokenId] = LockedBalance(0, 0);
        _checkpoint(_tokenId, _locked, LockedBalance(0, 0));
        _burn(_tokenId);

        // save end
        uint256 unlock_time = end;
        require(unlock_time > block.timestamp, "Can only lock until time in the future");
        require(unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 2 years max");

        // mint
        uint256 _value = 0;
        for (i = 0; i < amounts.length; i++) {
            ++tokenId;
            _tokenId = tokenId;
            _mint(_to, _tokenId);
            _value = (value * amounts[i]) / totalWeight;
            _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.SPLIT_TYPE);
        }
    }

    /*///////////////////////////////////////////////////////////////
                            DAO VOTING STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice The EIP-712 typehash for the contract's domain
    bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

    /// @notice The EIP-712 typehash for the delegation struct used by the contract
    bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");

    /// @notice A record of each accounts delegate
    mapping(address => address) private _delegates;
    uint256 public constant MAX_DELEGATES = 1024; // avoid too much gas

    /// @notice A record of delegated token checkpoints for each account, by index
    mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;

    /// @notice The number of checkpoints for each account
    mapping(address => uint32) public numCheckpoints;

    /// @notice A record of states for signing / validating signatures
    mapping(address => uint256) public nonces;

    /**
     * @notice Overrides the standard `Comp.sol` delegates mapping to return
     * the delegator's own address if they haven't delegated.
     * This avoids having to delegate to oneself.
     */
    function delegates(address delegator) public view returns (address) {
        address current = _delegates[delegator];
        return current == address(0) ? delegator : current;
    }

    /**
     * @notice Gets the current votes balance for `account`
     * @param account The address to get votes balance
     * @return The number of current votes for `account`
     */
    function getVotes(address account) external view returns (uint256) {
        uint32 nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }
        uint256[] storage _tokenIds = checkpoints[account][nCheckpoints - 1].tokenIds;
        uint256 votes = 0;
        for (uint256 i = 0; i < _tokenIds.length; i++) {
            uint256 tId = _tokenIds[i];
            votes = votes + _balanceOfNFT(tId, block.timestamp);
        }
        return votes;
    }

    function getPastVotesIndex(address account, uint256 timestamp) public view returns (uint32) {
        uint32 nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }
        // First check most recent balance
        if (checkpoints[account][nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }

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

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

    function getPastVotes(address account, uint256 timestamp) public view returns (uint256) {
        uint32 _checkIndex = getPastVotesIndex(account, timestamp);
        // Sum votes
        uint256[] storage _tokenIds = checkpoints[account][_checkIndex].tokenIds;
        uint256 votes = 0;
        for (uint256 i = 0; i < _tokenIds.length; i++) {
            uint256 tId = _tokenIds[i];
            // Use the provided input timestamp here to get the right decay
            votes = votes + _balanceOfNFT(tId, timestamp);
        }
        return votes;
    }

    function getPastTotalSupply(uint256 timestamp) external view returns (uint256) {
        return totalSupplyAtT(timestamp);
    }

    /*///////////////////////////////////////////////////////////////
                             DAO VOTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function _moveTokenDelegates(address srcRep, address dstRep, uint256 _tokenId) internal {
        if (srcRep != dstRep && _tokenId > 0) {
            if (srcRep != address(0)) {
                uint32 srcRepNum = numCheckpoints[srcRep];
                uint256[] storage srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].tokenIds : checkpoints[srcRep][0].tokenIds;
                uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);
                uint256[] storage srcRepNew = checkpoints[srcRep][nextSrcRepNum].tokenIds;
                // All the same except _tokenId
                for (uint256 i = 0; i < srcRepOld.length; i++) {
                    uint256 tId = srcRepOld[i];
                    if (tId != _tokenId) {
                        srcRepNew.push(tId);
                    }
                }

                numCheckpoints[srcRep] = srcRepNum + 1;
            }

            if (dstRep != address(0)) {
                uint32 dstRepNum = numCheckpoints[dstRep];
                uint256[] storage dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].tokenIds : checkpoints[dstRep][0].tokenIds;
                uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);
                uint256[] storage dstRepNew = checkpoints[dstRep][nextDstRepNum].tokenIds;
                // All the same plus _tokenId
                require(dstRepOld.length + 1 <= MAX_DELEGATES, "dstRep would have too many tokenIds");
                for (uint256 i = 0; i < dstRepOld.length; i++) {
                    uint256 tId = dstRepOld[i];
                    dstRepNew.push(tId);
                }
                dstRepNew.push(_tokenId);

                numCheckpoints[dstRep] = dstRepNum + 1;
            }
        }
    }

    function _findWhatCheckpointToWrite(address account) internal view returns (uint32) {
        uint256 _timestamp = block.timestamp;
        uint32 _nCheckPoints = numCheckpoints[account];

        if (_nCheckPoints > 0 && checkpoints[account][_nCheckPoints - 1].timestamp == _timestamp) {
            return _nCheckPoints - 1;
        } else {
            return _nCheckPoints;
        }
    }

    function _moveAllDelegates(address owner, address srcRep, address dstRep) internal {
        // You can only redelegate what you own
        if (srcRep != dstRep) {
            if (srcRep != address(0)) {
                uint32 srcRepNum = numCheckpoints[srcRep];
                uint256[] storage srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].tokenIds : checkpoints[srcRep][0].tokenIds;
                uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);
                uint256[] storage srcRepNew = checkpoints[srcRep][nextSrcRepNum].tokenIds;
                // All the same except what owner owns
                for (uint256 i = 0; i < srcRepOld.length; i++) {
                    uint256 tId = srcRepOld[i];
                    if (idToOwner[tId] != owner) {
                        srcRepNew.push(tId);
                    }
                }

                numCheckpoints[srcRep] = srcRepNum + 1;
            }

            if (dstRep != address(0)) {
                uint32 dstRepNum = numCheckpoints[dstRep];
                uint256[] storage dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].tokenIds : checkpoints[dstRep][0].tokenIds;
                uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);
                uint256[] storage dstRepNew = checkpoints[dstRep][nextDstRepNum].tokenIds;
                uint256 ownerTokenCount = ownerToNFTokenCount[owner];
                require(dstRepOld.length + ownerTokenCount <= MAX_DELEGATES, "dstRep would have too many tokenIds");
                // All the same
                for (uint256 i = 0; i < dstRepOld.length; i++) {
                    uint256 tId = dstRepOld[i];
                    dstRepNew.push(tId);
                }
                // Plus all that's owned
                for (uint256 i = 0; i < ownerTokenCount; i++) {
                    uint256 tId = ownerToNFTokenIdList[owner][i];
                    dstRepNew.push(tId);
                }

                numCheckpoints[dstRep] = dstRepNum + 1;
            }
        }
    }

    function _delegate(address delegator, address delegatee) internal {
        /// @notice differs from `_delegate()` in `Comp.sol` to use `delegates` override method to simulate auto-delegation
        address currentDelegate = delegates(delegator);

        _delegates[delegator] = delegatee;

        emit DelegateChanged(delegator, currentDelegate, delegatee);
        _moveAllDelegates(delegator, currentDelegate, delegatee);
    }

    /**
     * @notice Delegate votes from `msg.sender` to `delegatee`
     * @param delegatee The address to delegate votes to
     */
    function delegate(address delegatee) public {
        if (delegatee == address(0)) delegatee = msg.sender;
        return _delegate(msg.sender, delegatee);
    }

    function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) public {
        require(delegatee != msg.sender);
        require(delegatee != address(0));

        bytes32 domainSeparator = keccak256(
            abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), keccak256(bytes(version)), block.chainid, address(this))
        );
        bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), "VotingEscrow::delegateBySig: invalid signature");
        require(nonce == nonces[signatory]++, "VotingEscrow::delegateBySig: invalid nonce");
        require(block.timestamp <= expiry, "VotingEscrow::delegateBySig: signature expired");
        return _delegate(signatory, delegatee);
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"token_addr","type":"address"},{"internalType":"address","name":"art_proxy","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"locktime","type":"uint256"},{"indexed":false,"internalType":"enum VotingEscrow.DepositType","name":"deposit_type","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"prevSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supply","type":"uint256"}],"name":"Supply","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DELEGATES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"abstain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"artProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"attach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"attachments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"balanceOfAtNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_t","type":"uint256"}],"name":"balanceOfNFTAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"block_number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"checkpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"create_lock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"create_lock_for","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"deposit_for","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"detach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastVotesIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"get_last_user_slope","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increase_amount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"increase_unlock_time","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"locked","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"locked__end","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"merge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ownership_change","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"point_history","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_proxy","type":"address"}],"name":"setArtProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_team","type":"address"}],"name":"setTeam","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_voter","type":"address"}],"name":"setVoter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slope_changes","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"split","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"team","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_tokenIndex","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"totalSupplyAtT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"user_point_epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"user_point_history","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"user_point_history__ts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"voted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"voting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040526006805460ff191660011790553480156200001e57600080fd5b5060405162004f8738038062004f878339810160408190526200004191620001af565b6001600160a01b0382811660805260018054336001600160a01b03199182161782556002805490911692841692909217909155437f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92f0155427f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92f005560046020527f9fe05126d2d9ecf60592e254dead906a4b2e492f36cca727682c38e9008c6ac1805460ff1990811683179091557f4267c0a6fd96b7a87f183ee8744f24d011423cd0e0142b3f563f183d8d9a456b8054821683179055635b5e139f60e01b60009081527e24030bcf4927897dffe721c2d8dda4bfd8910861687c42b03a463b43b04147805490921690921790556005546040519091309160008051602062004f67833981519152908290a4600554604051600090309060008051602062004f67833981519152908390a45050620001e7565b80516001600160a01b0381168114620001aa57600080fd5b919050565b60008060408385031215620001c357600080fd5b620001ce8362000192565b9150620001de6020840162000192565b90509250929050565b608051614d566200021160003960008181610bde0152818161111401526133ce0152614d566000f3fe608060405234801561001057600080fd5b50600436106104725760003560e01c80637116c60c11610250578063c1f0fb9f11610150578063e7a324dc116100c8578063f1127ed811610097578063fbd3a29d1161007c578063fbd3a29d14610bc6578063fc0c546a14610bd9578063fd4a77f114610c0057600080fd5b8063f1127ed814610b78578063f8a0576314610ba357600080fd5b8063e7a324dc14610aef578063e7e242d414610b16578063e985e9c514610b29578063ee99fe2814610b6557600080fd5b8063d1c2babb1161011f578063d4e54c3b11610104578063d4e54c3b14610aa9578063e0514aba14610abc578063e441135c14610acf57600080fd5b8063d1c2babb14610a58578063d1febfb914610a6b57600080fd5b8063c1f0fb9f14610a17578063c2c4c5c114610a2a578063c3cda52014610a32578063c87b56dd14610a4557600080fd5b806395d89b41116101e3578063a183af52116101b2578063a4d855df11610197578063a4d855df146109a9578063b45a3c0e146109bc578063b88d4fde14610a0457600080fd5b8063a183af5214610983578063a22cb4651461099657600080fd5b806395d89b411461090e578063981b24d01461094a578063986b7d8a1461095d5780639ab24eb01461097057600080fd5b80638c2c9baf1161021f5780638c2c9baf146108bc5780638e539e8c146108cf5780638fbb38ff146108e2578063900cf0cf1461090557600080fd5b80637116c60c1461085357806371197484146108665780637ecebe001461088957806385f2aef2146108a957600080fd5b8063313ce5671161037657806356afe744116102ee5780636352211e116102bd5780636f548837116102a25780636f548837146107fa5780636fcfff451461081a57806370a082311461084057600080fd5b80636352211e146107be57806365fc3873146107e757600080fd5b806356afe7441461077c578063587cde1e1461078f5780635c19a95c146107a25780635f5b0c32146107b557600080fd5b8063461f711c116103455780634bc2a6571161032a5780634bc2a6571461073257806354fd4d50146107455780635594a0451461076957600080fd5b8063461f711c146106f957806346c96aac1461071f57600080fd5b8063313ce567146106a65780633a46b1a8146106c057806342842e0e146106d3578063430c2081146106e657600080fd5b80631376f3da1161040957806323b872dd116103d85780632e1a7d4d116103bd5780632e1a7d4d1461064a5780632e720f7d1461065d5780632f745c591461067057600080fd5b806323b872dd1461063157806325a58b561461064457600080fd5b80631376f3da146105b457806318160ddd146105ef5780631c984bc3146105f757806320606b701461060a57600080fd5b8063081812fc11610445578063081812fc1461052b578063095cf5c61461056c578063095ea7b3146105815780630d6a20331461059457600080fd5b806301ffc9a714610477578063047fc9aa146104b957806306fdde03146104d05780630758c7d814610503575b600080fd5b6104a461048536600461456e565b6001600160e01b03191660009081526004602052604090205460ff1690565b60405190151581526020015b60405180910390f35b6104c260135481565b6040519081526020016104b0565b6104f6604051806040016040528060078152602001661d995419585c9b60ca1b81525081565b6040516104b091906145db565b61051661051136600461460a565b610c13565b60405163ffffffff90911681526020016104b0565b610554610539366004614634565b6000908152600960205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020016104b0565b61057f61057a36600461464d565b610d86565b005b61057f61058f36600461460a565b610dbf565b6104c26105a2366004614634565b60146020526000908152604090205481565b6105c76105c2366004614668565b610ea7565b60408051600f95860b81529390940b60208401529282015260608101919091526080016104b0565b6104c2610eee565b6104c2610605366004614668565b610efe565b6104c27f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61057f61063f36600461468a565b610f30565b436104c2565b61057f610658366004614634565b610f41565b61057f61066b36600461464d565b611237565b6104c261067e36600461460a565b6001600160a01b03919091166000908152600c60209081526040808320938352929052205490565b6106ae601281565b60405160ff90911681526020016104b0565b6104c26106ce36600461460a565b611270565b61057f6106e136600461468a565b61130f565b6104a46106f436600461460a565b61132a565b61070c610707366004614634565b61133d565b604051600f9190910b81526020016104b0565b600054610554906001600160a01b031681565b61057f61074036600461464d565b611380565b6104f6604051806040016040528060058152602001640312e302e360dc1b81525081565b600254610554906001600160a01b031681565b61057f61078a36600461470d565b6113b9565b61055461079d36600461464d565b6116c1565b61057f6107b036600461464d565b6116f1565b6104c261040081565b6105546107cc366004614634565b6000908152600760205260409020546001600160a01b031690565b6104c26107f5366004614668565b61170f565b6104c2610808366004614634565b600b6020526000908152604090205481565b61051661082836600461464d565b60186020526000908152604090205463ffffffff1681565b6104c261084e36600461464d565b611751565b6104c2610861366004614634565b61176f565b61070c610874366004614634565b601260205260009081526040902054600f0b81565b6104c261089736600461464d565b60196020526000908152604090205481565b600154610554906001600160a01b031681565b6104c26108ca366004614668565b6117cf565b6104c26108dd366004614634565b6117db565b6104a46108f0366004614634565b60156020526000908152604090205460ff1681565b6104c260115481565b6104f66040518060400160405280600781526020017f7665504541524c0000000000000000000000000000000000000000000000000081525081565b6104c2610958366004614634565b6117e6565b61057f61096b366004614634565b611988565b6104c261097e36600461464d565b6119cc565b61057f610991366004614668565b611a9f565b61057f6109a43660046147c7565b611be1565b61057f6109b7366004614668565b611c65565b6109ea6109ca366004614634565b60106020526000908152604090208054600190910154600f9190910b9082565b60408051600f9390930b83526020830191909152016104b0565b61057f610a12366004614826565b611e67565b61057f610a25366004614634565b612028565b61057f612057565b61057f610a403660046148d1565b612097565b6104f6610a53366004614634565b612448565b61057f610a66366004614668565b612574565b6105c7610a79366004614634565b600360205260009081526040902080546001820154600290920154600f82810b93600160801b909304900b919084565b6104c2610ab7366004614931565b6126fd565b6104c2610aca366004614668565b612740565b6104c2610add366004614634565b600e6020526000908152604090205481565b6104c27fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b6104c2610b24366004614634565b61274c565b6104a4610b37366004614966565b6001600160a01b039182166000908152600a6020908152604080832093909416825291909152205460ff1690565b61057f610b73366004614668565b612774565b6104c2610b86366004614999565b601760209081526000928352604080842090915290825290205481565b6104c2610bb1366004614634565b60009081526010602052604090206001015490565b61057f610bd4366004614634565b61288b565b6105547f000000000000000000000000000000000000000000000000000000000000000081565b61057f610c0e366004614634565b6128bc565b6001600160a01b03821660009081526018602052604081205463ffffffff16808203610c43576000915050610d80565b6001600160a01b03841660009081526017602052604081208491610c686001856149e4565b63ffffffff16815260208101919091526040016000205411610c9757610c8f6001826149e4565b915050610d80565b6001600160a01b0384166000908152601760209081526040808320838052909152902054831015610ccc576000915050610d80565b600080610cda6001846149e4565b90505b8163ffffffff168163ffffffff161115610d7b5760006002610cff84846149e4565b610d099190614a17565b610d1390836149e4565b6001600160a01b038816600090815260176020908152604080832063ffffffff851684529091529020805491925090879003610d5557509350610d8092505050565b8054871115610d6657819350610d74565b610d716001836149e4565b92505b5050610cdd565b509150505b92915050565b6001546001600160a01b03163314610d9d57600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000818152600760205260409020546001600160a01b031680610de157600080fd5b806001600160a01b0316836001600160a01b031603610dff57600080fd5b6000828152600760209081526040808320546001600160a01b038581168552600a845282852033808752945291909320549216149060ff168180610e405750805b610e4957600080fd5b60008481526009602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b600f60205281600052604060002081633b9aca008110610ec657600080fd5b6003020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b6000610ef94261176f565b905090565b6000828152600f6020526040812082633b9aca008110610f2057610f20614a3a565b6003020160010154905092915050565b610f3c838383336128ee565b505050565b60065460ff16600114610f5357600080fd5b6006805460ff19166002179055610f6a33826129ea565b610f7657610f76614a50565b600081815260146020526040902054158015610fa1575060008181526015602052604090205460ff16155b610fdd5760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b60448201526064015b60405180910390fd5b60008181526010602090815260409182902082518084019093528054600f0b8352600101549082018190524210156110575760405162461bcd60e51b815260206004820152601660248201527f546865206c6f636b206469646e277420657870697265000000000000000000006044820152606401610fd4565b8051604080518082018252600080825260208083018281528783526010909152929020905181546fffffffffffffffffffffffffffffffff19166001600160801b039091161781559051600190910155601354600f9190910b906110bb8282614a66565b60135560408051808201909152600080825260208201526110df9085908590612a50565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015611165573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111899190614a79565b61119557611195614a50565b61119e8461307e565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c8161120c8482614a66565b6040805192835260208301919091520160405180910390a150506006805460ff191660011790555050565b6001546001600160a01b0316331461124e57600080fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60008061127d8484610c13565b6001600160a01b038516600090815260176020908152604080832063ffffffff851684529091528120919250600190910190805b82548110156113055760008382815481106112ce576112ce614a3a565b906000526020600020015490506112e58188613151565b6112ef9084614a96565b92505080806112fd90614aa9565b9150506112b1565b5095945050505050565b610f3c83838360405180602001604052806000815250611e67565b600061133683836129ea565b9392505050565b6000818152600e6020908152604080832054600f909252822081633b9aca00811061136a5761136a614a3a565b6003020154600160801b9004600f0b9392505050565b6001546001600160a01b0316331461139757600080fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000818152601460205260409020541580156113e4575060008181526015602052604090205460ff16155b61141b5760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610fd4565b61142533826129ea565b61142e57600080fd5b600081815260076020908152604080832054601083529281902081518083019092528054600f0b8083526001909101549282018390526001600160a01b03909316929091908061147d57600080fd5b8060135461148b9190614a66565b6013556000805b87518210156114d4578782815181106114ad576114ad614a3a565b6020026020010151816114c09190614a96565b9050816114cc81614aa9565b925050611492565b604080518082018252600080825260208083018281528b835260108252848320935184546fffffffffffffffffffffffffffffffff19166001600160801b039091161784555160019093019290925582518084019093528083529082015261153f9088908790612a50565b6115488761307e565b834281116115a75760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201526566757475726560d01b6064820152608401610fd4565b6115b56303c2670042614a96565b8111156116045760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652032207965617273206d617800006044820152606401610fd4565b60008093505b89518410156116b55760056000815461162290614aa9565b909155506005549850611635888a61322c565b50828a858151811061164957611649614a3a565b60200260200101518661165c9190614ac2565b6116669190614ad9565b60008a81526010602090815260409182902082518084019093528054600f0b835260010154908201529091506116a3908a9083908590600561329d565b836116ad81614aa9565b94505061160a565b50505050505050505050565b6001600160a01b0380821660009081526016602052604081205490911680156116ea5780611336565b5090919050565b6001600160a01b0381166117025750335b61170c33826134e2565b50565b60065460009060ff1660011461172457600080fd5b6006805460ff1916600217905561173c838333613555565b90506006805460ff1916600117905592915050565b6001600160a01b038116600090815260086020526040812054610d80565b601154600081815260036020908152604080832081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608201529091906117c781856136ac565b949350505050565b600061133683836137ad565b6000610d808261176f565b6000438211156117f8576117f8614a50565b60115460006118078483613a86565b600081815260036020908152604080832081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608201529192508383101561191657600060038161186a866001614a96565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608083018290528501519192501461191057826060015181606001516118d69190614a66565b836040015182604001516118ea9190614a66565b60608501516118f9908a614a66565b6119039190614ac2565b61190d9190614ad9565b91505b50611965565b438260600151146119655760608201516119309043614a66565b604083015161193f9042614a66565b606084015161194e9089614a66565b6119589190614ac2565b6119629190614ad9565b90505b61197e828284604001516119799190614a96565b6136ac565b9695505050505050565b6000546001600160a01b0316331461199f57600080fd5b6000818152601460205260409020546119ba90600190614a66565b60009182526014602052604090912055565b6001600160a01b03811660009081526018602052604081205463ffffffff168082036119fb5750600092915050565b6001600160a01b038316600090815260176020526040812081611a1f6001856149e4565b63ffffffff1663ffffffff16815260200190815260200160002060010190506000805b8254811015611a96576000838281548110611a5f57611a5f614a3a565b90600052602060002001549050611a768142613151565b611a809084614a96565b9250508080611a8e90614aa9565b915050611a42565b50949350505050565b60065460ff16600114611ab157600080fd5b6006805460ff19166002179055611ac833836129ea565b611ad457611ad4614a50565b60008281526010602090815260409182902082518084019093528054600f0b8352600101549082015281611b0a57611b0a614a50565b60008160000151600f0b13611b615760405162461bcd60e51b815260206004820152601660248201527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006044820152606401610fd4565b42816020015111611bc05760405162461bcd60e51b8152602060048201526024808201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686044820152636472617760e01b6064820152608401610fd4565b611bcf8383600084600261329d565b50506006805460ff1916600117905550565b336001600160a01b03831603611bf957611bf9614a50565b336000818152600a602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b60065460ff16600114611c7757600080fd5b6006805460ff19166002179055611c8e33836129ea565b611c9a57611c9a614a50565b600082815260106020908152604080832081518083019092528054600f0b825260010154918101919091529062093a8080611cd58542614a96565b611cdf9190614ad9565b611ce99190614ac2565b905042826020015111611d3e5760405162461bcd60e51b815260206004820152600c60248201527f4c6f636b206578706972656400000000000000000000000000000000000000006044820152606401610fd4565b60008260000151600f0b13611d955760405162461bcd60e51b815260206004820152601160248201527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006044820152606401610fd4565b81602001518111611de85760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006044820152606401610fd4565b611df66303c2670042614a96565b811115611e455760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652032207965617273206d617800006044820152606401610fd4565b611e548460008385600361329d565b50506006805460ff191660011790555050565b611e73848484336128ee565b823b1561202257604051630a85bd0160e11b81526001600160a01b0384169063150b7a0290611eac903390889087908790600401614aed565b6020604051808303816000875af1925050508015611ee7575060408051601f3d908101601f19168201909252611ee491810190614b1f565b60015b611f9a573d808015611f15576040519150601f19603f3d011682016040523d82523d6000602084013e611f1a565b606091505b508051600003611f925760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610fd4565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146120205760405162461bcd60e51b815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a65637465642060448201527f746f6b656e7300000000000000000000000000000000000000000000000000006064820152608401610fd4565b505b50505050565b6000546001600160a01b0316331461203f57600080fd5b6000908152601560205260409020805460ff19169055565b612095600060405180604001604052806000600f0b8152602001600081525060405180604001604052806000600f0b81526020016000815250612a50565b565b336001600160a01b038716036120ac57600080fd5b6001600160a01b0386166120bf57600080fd5b60408051808201825260078152661d995419585c9b60ca1b6020918201528151808301835260058152640312e302e360dc1b9082015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f2345aff9dfc8e79edd2931b80e3b4343b5eb41635e88104cb74e0c7a90fa379f818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608201524660808201523060a0808301919091528351808303909101815260c0820184528051908301207fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60e08301526001600160a01b038a166101008301526101208201899052610140808301899052845180840390910181526101608301909452835193909201929092207f190100000000000000000000000000000000000000000000000000000000000061018084015261018283018290526101a2830181905290916000906101c20160408051601f198184030181528282528051602091820120600080855291840180845281905260ff8a169284019290925260608301889052608083018790529092509060019060a0016020604051602081039080840390855afa15801561229b573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166123245760405162461bcd60e51b815260206004820152602e60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a20696e7660448201527f616c6964207369676e61747572650000000000000000000000000000000000006064820152608401610fd4565b6001600160a01b038116600090815260196020526040812080549161234883614aa9565b9190505589146123c05760405162461bcd60e51b815260206004820152602a60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a20696e7660448201527f616c6964206e6f6e6365000000000000000000000000000000000000000000006064820152608401610fd4565b874211156124365760405162461bcd60e51b815260206004820152602e60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a2073696760448201527f6e617475726520657870697265640000000000000000000000000000000000006064820152608401610fd4565b6116b5818b6134e2565b505050505050565b6000818152600760205260409020546060906001600160a01b03166124af5760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e00000000006044820152606401610fd4565b60008281526010602090815260409182902082518084019093528054600f0b835260010154908201526002546001600160a01b031663dd9ec149846124f48142613151565b6020850151855160405160e086901b6001600160e01b0319168152600481019490945260248401929092526044830152600f0b6064820152608401600060405180830381865afa15801561254c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113369190810190614b3c565b60008281526014602052604090205415801561259f575060008281526015602052604090205460ff16155b6125d65760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610fd4565b8082036125e257600080fd5b6125ec33836129ea565b6125f557600080fd5b6125ff33826129ea565b61260857600080fd5b6000828152601060208181526040808420815180830183528154600f90810b825260019283015482860190815288885295855283872084518086019095528054820b855290920154938301849052805194519095929490910b921115612672578260200151612678565b83602001515b604080518082018252600080825260208083018281528b835260108252848320935184546fffffffffffffffffffffffffffffffff19166001600160801b03909116178455516001909301929092558251808401909352808352908201529091506126e69087908690612a50565b6126ef8661307e565b61244085838386600461329d565b60065460009060ff1660011461271257600080fd5b6006805460ff1916600217905561272a848484613555565b90506006805460ff191660011790559392505050565b60006113368383613151565b6000818152600b602052604081205443900361276a57506000919050565b610d808242613151565b60065460ff1660011461278657600080fd5b6006805460ff1916600217905560008281526010602090815260409182902082518084019093528054600f0b83526001015490820152816127c657600080fd5b60008160000151600f0b1361281d5760405162461bcd60e51b815260206004820152601660248201527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006044820152606401610fd4565b4281602001511161287c5760405162461bcd60e51b8152602060048201526024808201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686044820152636472617760e01b6064820152608401610fd4565b611bcf8383600084600061329d565b6000546001600160a01b031633146128a257600080fd5b6000818152601460205260409020546119ba906001614a96565b6000546001600160a01b031633146128d357600080fd5b6000908152601560205260409020805460ff19166001179055565b600082815260146020526040902054158015612919575060008281526015602052604090205460ff16155b6129505760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610fd4565b61295a81836129ea565b61296357600080fd5b61296d8483613b0c565b6129778483613b73565b612992612983856116c1565b61298c856116c1565b84613bf4565b61299c8383613f92565b6000828152600b60205260408082204390555183916001600160a01b0380871692908816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450505050565b60008181526007602090815260408083205460098352818420546001600160a01b03918216808652600a855283862088841680885295529285205492938085149392909116149060ff168280612a3d5750815b80612a455750805b979650505050505050565b60408051608081018252600080825260208201819052918101829052606081019190915260408051608081018252600080825260208201819052918101829052606081019190915260115460009081908715612bbb57428760200151118015612ac0575060008760000151600f0b135b15612b05578651612ad6906303c2670090614baa565b600f0b602080870191909152870151612af0904290614a66565b8560200151612aff9190614bf1565b600f0b85525b428660200151118015612b1f575060008660000151600f0b135b15612b64578551612b35906303c2670090614baa565b600f0b602080860191909152860151612b4f904290614a66565b8460200151612b5e9190614bf1565b600f0b84525b602080880151600090815260128252604090205490870151600f9190910b935015612bbb578660200151866020015103612ba057829150612bbb565b602080870151600090815260129091526040902054600f0b91505b604080516080810182526000808252602082015242918101919091524360608201528115612c30575060008181526003602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b604081015181600042831015612c7d576040840151612c4f9042614a66565b6060850151612c5e9043614a66565b612c7090670de0b6b3a7640000614ac2565b612c7a9190614ad9565b90505b600062093a80612c8d8186614ad9565b612c979190614ac2565b905060005b60ff811015612e1157612cb262093a8083614a96565b9150600042831115612cc657429250612cda565b50600082815260126020526040902054600f0b5b612ce48684614a66565b8760200151612cf39190614bf1565b87518890612d02908390614c11565b600f0b905250602087018051829190612d1c908390614c50565b600f90810b90915288516000910b12159050612d3757600087525b60008760200151600f0b1215612d4f57600060208801525b60408088018490528501519295508592670de0b6b3a764000090612d739085614a66565b612d7d9086614ac2565b612d879190614ad9565b8560600151612d969190614a96565b6060880152612da6600189614a96565b9750428303612dbb5750436060870152612e11565b6000888152600360209081526040918290208951918a01516001600160801b03908116600160801b029216919091178155908801516001820155606088015160029091015550612e0a81614aa9565b9050612c9c565b505060118590558b15612e9c5788602001518860200151612e329190614c11565b84602001818151612e439190614c50565b600f0b90525088518851612e579190614c11565b84518590612e66908390614c50565b600f90810b90915260208601516000910b12159050612e8757600060208501525b60008460000151600f0b1215612e9c57600084525b6000858152600360209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b1561307057428b602001511115612f5c576020890151612f029088614c50565b96508a602001518a6020015103612f25576020880151612f229088614c11565b96505b60208b810151600090815260129091526040902080546fffffffffffffffffffffffffffffffff19166001600160801b0389161790555b428a602001511115612fc0578a602001518a602001511115612fc0576020880151612f879087614c11565b60208b810151600090815260129091526040902080546fffffffffffffffffffffffffffffffff19166001600160801b03831617905595505b60008c8152600e6020526040812054612fda906001614a96565b905080600e60008f815260200190815260200160002081905550428960400181815250504389606001818152505088600f60008f815260200190815260200160002082633b9aca00811061303057613030614a3a565b825160208401516001600160801b03908116600160801b029116176003919091029190910190815560408201516001820155606090910151600290910155505b505050505050505050505050565b61308833826129ea565b6130d45760405162461bcd60e51b815260206004820181905260248201527f63616c6c6572206973206e6f74206f776e6572206e6f7220617070726f7665646044820152606401610fd4565b6000818152600760205260408120546001600160a01b0316906130f79083610dbf565b61310b613103826116c1565b600084613bf4565b6131158183613b73565b60405182906000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000828152600e6020526040812054808203613171576000915050610d80565b6000848152600f6020526040812082633b9aca00811061319357613193614a3a565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b6020830152600181015492820183905260020154606082015291506131df9085614c8f565b81602001516131ee9190614bf1565b815182906131fd908390614c11565b600f90810b90915282516000910b1215905061321857600081525b51600f0b9150610d809050565b5092915050565b60006001600160a01b03831661324457613244614a50565b613252600061298c856116c1565b61325c8383613f92565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600192915050565b60135482906132ac8682614a96565b6013556040805180820190915260008082526020820152825160208085015190830152600f0b81528251879084906132e5908390614c50565b600f0b90525085156132f957602083018690525b6000888152601060209081526040909120845181546fffffffffffffffffffffffffffffffff19166001600160801b0390911617815590840151600190910155613344888285612a50565b3387158015906133665750600485600581111561336357613363614caf565b14155b80156133845750600585600581111561338157613381614caf565b14155b15613447576040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152306024830152604482018a90527f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303816000875af1158015613417573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343b9190614a79565b61344757613447614a50565b8360200151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b894260405161348b9493929190614cc5565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c836134bf8a82614a96565b6040805192835260208301919091520160405180910390a1505050505050505050565b60006134ed836116c1565b6001600160a01b0384811660008181526016602052604080822080546001600160a01b031916888616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610f3c838284614028565b60008062093a80806135678642614a96565b6135719190614ad9565b61357b9190614ac2565b90506000851161358a57600080fd5b4281116135e85760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201526566757475726560d01b6064820152608401610fd4565b6135f66303c2670042614a96565b8111156136455760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652032207965617273206d617800006044820152606401610fd4565b60056000815461365490614aa9565b90915550600554613665848261322c565b5060008181526010602090815260409182902082518084019093528054600f0b8352600190810154918301919091526136a39183918991869161329d565b95945050505050565b600080839050600062093a808083604001516136c89190614ad9565b6136d29190614ac2565b905060005b60ff811015613785576136ed62093a8083614a96565b915060008583111561370157859250613715565b50600082815260126020526040902054600f0b5b60408401516137249084614a66565b84602001516137339190614bf1565b84518590613742908390614c11565b600f0b9052508583036137555750613785565b80846020018181516137679190614c50565b600f0b905250506040830182905261377e81614aa9565b90506136d7565b5060008260000151600f0b121561379b57600082525b50516001600160801b03169392505050565b6000438211156137bf576137bf614a50565b6000838152600e6020526040812054815b608081101561385f578183101561385f57600060026137ef8486614a96565b6137fa906001614a96565b6138049190614ad9565b6000888152600f60205260409020909150869082633b9aca00811061382b5761382b614a3a565b6003020160020154116138405780935061384e565b61384b600182614a66565b92505b5061385881614aa9565b90506137d0565b506000858152600f6020526040812083633b9aca00811061388257613882614a3a565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b6020830152600181015492820192909252600290910154606082015260115490915060006138d78783613a86565b600081815260036020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925080848410156139b657600060038161393b876001614a96565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608083018290528601519192506139989190614a66565b9250836040015181604001516139ae9190614a66565b9150506139da565b60608301516139c59043614a66565b91508260400151426139d79190614a66565b90505b60408301518215613a17578284606001518c6139f69190614a66565b613a009084614ac2565b613a0a9190614ad9565b613a149082614a96565b90505b6040870151613a269082614a66565b8760200151613a359190614bf1565b87518890613a44908390614c11565b600f90810b90915288516000910b129050613a7457505093516001600160801b03169650610d8095505050505050565b60009950505050505050505050610d80565b60008082815b6080811015613b025781831015613b025760006002613aab8486614a96565b613ab6906001614a96565b613ac09190614ad9565b6000818152600360205260409020600201549091508710613ae357809350613af1565b613aee600182614a66565b92505b50613afb81614aa9565b9050613a8c565b5090949350505050565b6000818152600760205260409020546001600160a01b03838116911614613b3557613b35614a50565b6000818152600960205260409020546001600160a01b031615613b6f57600081815260096020526040902080546001600160a01b03191690555b5050565b6000818152600760205260409020546001600160a01b03838116911614613b9c57613b9c614a50565b600081815260076020526040902080546001600160a01b0319169055613bc28282614420565b6001600160a01b0382166000908152600860205260408120805460019290613beb908490614a66565b90915550505050565b816001600160a01b0316836001600160a01b031614158015613c165750600081115b15610f3c576001600160a01b03831615613d97576001600160a01b03831660009081526018602052604081205463ffffffff169081613c7a576001600160a01b03851660009081526017602090815260408083208380529091529020600101613cbc565b6001600160a01b038516600090815260176020526040812090613c9e6001856149e4565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000613cc9866144df565b6001600160a01b038716600090815260176020908152604080832063ffffffff8516845290915281209192506001909101905b8354811015613d56576000848281548110613d1957613d19614a3a565b90600052602060002001549050868114613d43578254600181018455600084815260209020018190555b5080613d4e81614aa9565b915050613cfc565b50613d62846001614d03565b6001600160a01b0388166000908152601860205260409020805463ffffffff191663ffffffff92909216919091179055505050505b6001600160a01b03821615610f3c576001600160a01b03821660009081526018602052604081205463ffffffff169081613df6576001600160a01b03841660009081526017602090815260408083208380529091529020600101613e38565b6001600160a01b038416600090815260176020526040812090613e1a6001856149e4565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000613e45856144df565b6001600160a01b038616600090815260176020908152604080832063ffffffff851684529091529020835491925060019081019161040091613e879190614a96565b1115613ee15760405162461bcd60e51b815260206004820152602360248201527f64737452657020776f756c64206861766520746f6f206d616e7920746f6b656e60448201526249647360e81b6064820152608401610fd4565b60005b8354811015613f33576000848281548110613f0157613f01614a3a565b600091825260208083209091015485546001810187558684529190922001555080613f2b81614aa9565b915050613ee4565b50805460018181018355600083815260209020909101869055613f57908590614d03565b6001600160a01b0387166000908152601860205260409020805463ffffffff9290921663ffffffff1990921691909117905550505050505050565b6000818152600760205260409020546001600160a01b031615613fb757613fb7614a50565b600081815260076020908152604080832080546001600160a01b0319166001600160a01b03871690811790915580845260088084528285208054600c86528487208188528652848720889055878752600d865293862093909355908452909152805460019290613beb908490614a96565b806001600160a01b0316826001600160a01b031614610f3c576001600160a01b038216156141db576001600160a01b03821660009081526018602052604081205463ffffffff1690816140a0576001600160a01b038416600090815260176020908152604080832083805290915290206001016140e2565b6001600160a01b0384166000908152601760205260408120906140c46001856149e4565b63ffffffff1663ffffffff1681526020019081526020016000206001015b905060006140ef856144df565b6001600160a01b038616600090815260176020908152604080832063ffffffff8516845290915281209192506001909101905b835481101561419a57600084828154811061413f5761413f614a3a565b600091825260208083209091015480835260079091526040909120549091506001600160a01b03908116908a1614614187578254600181018455600084815260209020018190555b508061419281614aa9565b915050614122565b506141a6846001614d03565b6001600160a01b0387166000908152601860205260409020805463ffffffff191663ffffffff92909216919091179055505050505b6001600160a01b03811615610f3c576001600160a01b03811660009081526018602052604081205463ffffffff16908161423a576001600160a01b0383166000908152601760209081526040808320838052909152902060010161427c565b6001600160a01b03831660009081526017602052604081209061425e6001856149e4565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000614289846144df565b6001600160a01b03808616600090815260176020908152604080832063ffffffff861684528252808320938b16835260089091529020548454929350600190910191610400906142da908390614a96565b11156143345760405162461bcd60e51b815260206004820152602360248201527f64737452657020776f756c64206861766520746f6f206d616e7920746f6b656e60448201526249647360e81b6064820152608401610fd4565b60005b845481101561438657600085828154811061435457614354614a3a565b60009182526020808320909101548654600181018855878452919092200155508061437e81614aa9565b915050614337565b5060005b818110156143d8576001600160a01b0389166000908152600c6020908152604080832084845282528220548554600181018755868452919092200155806143d081614aa9565b91505061438a565b506143e4856001614d03565b6001600160a01b0387166000908152601860205260409020805463ffffffff9290921663ffffffff199092169190911790555050505050505050565b6001600160a01b03821660009081526008602052604081205461444590600190614a66565b6000838152600d6020526040902054909150808203614494576001600160a01b0384166000908152600c602090815260408083208584528252808320839055858352600d909152812055612022565b6001600160a01b03939093166000908152600c6020908152604080832093835292815282822080548684528484208190558352600d9091528282209490945592839055908252812055565b6001600160a01b038116600090815260186020526040812054429063ffffffff16801580159061454857506001600160a01b0384166000908152601760205260408120839161452f6001856149e4565b63ffffffff168152602081019190915260400160002054145b15611336576117c76001826149e4565b6001600160e01b03198116811461170c57600080fd5b60006020828403121561458057600080fd5b813561133681614558565b60005b838110156145a657818101518382015260200161458e565b50506000910152565b600081518084526145c781602086016020860161458b565b601f01601f19169290920160200192915050565b60208152600061133660208301846145af565b80356001600160a01b038116811461460557600080fd5b919050565b6000806040838503121561461d57600080fd5b614626836145ee565b946020939093013593505050565b60006020828403121561464657600080fd5b5035919050565b60006020828403121561465f57600080fd5b611336826145ee565b6000806040838503121561467b57600080fd5b50508035926020909101359150565b60008060006060848603121561469f57600080fd5b6146a8846145ee565b92506146b6602085016145ee565b9150604084013590509250925092565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715614705576147056146c6565b604052919050565b6000806040838503121561472057600080fd5b823567ffffffffffffffff8082111561473857600080fd5b818501915085601f83011261474c57600080fd5b8135602082821115614760576147606146c6565b8160051b92506147718184016146dc565b828152928401810192818101908985111561478b57600080fd5b948201945b848610156147a957853582529482019490820190614790565b9997909101359750505050505050565b801515811461170c57600080fd5b600080604083850312156147da57600080fd5b6147e3836145ee565b915060208301356147f3816147b9565b809150509250929050565b600067ffffffffffffffff821115614818576148186146c6565b50601f01601f191660200190565b6000806000806080858703121561483c57600080fd5b614845856145ee565b9350614853602086016145ee565b925060408501359150606085013567ffffffffffffffff81111561487657600080fd5b8501601f8101871361488757600080fd5b803561489a614895826147fe565b6146dc565b8181528860208385010111156148af57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b60008060008060008060c087890312156148ea57600080fd5b6148f3876145ee565b95506020870135945060408701359350606087013560ff8116811461491757600080fd5b9598949750929560808101359460a0909101359350915050565b60008060006060848603121561494657600080fd5b833592506020840135915061495d604085016145ee565b90509250925092565b6000806040838503121561497957600080fd5b614982836145ee565b9150614990602084016145ee565b90509250929050565b600080604083850312156149ac57600080fd5b6149b5836145ee565b9150602083013563ffffffff811681146147f357600080fd5b634e487b7160e01b600052601160045260246000fd5b63ffffffff828116828216039080821115613225576132256149ce565b634e487b7160e01b600052601260045260246000fd5b600063ffffffff80841680614a2e57614a2e614a01565b92169190910492915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b81810381811115610d8057610d806149ce565b600060208284031215614a8b57600080fd5b8151611336816147b9565b80820180821115610d8057610d806149ce565b600060018201614abb57614abb6149ce565b5060010190565b8082028115828204841417610d8057610d806149ce565b600082614ae857614ae8614a01565b500490565b60006001600160a01b0380871683528086166020840152508360408301526080606083015261197e60808301846145af565b600060208284031215614b3157600080fd5b815161133681614558565b600060208284031215614b4e57600080fd5b815167ffffffffffffffff811115614b6557600080fd5b8201601f81018413614b7657600080fd5b8051614b84614895826147fe565b818152856020838501011115614b9957600080fd5b6136a382602083016020860161458b565b600081600f0b83600f0b80614bc157614bc1614a01565b6f7fffffffffffffffffffffffffffffff19821460001982141615614be857614be86149ce565b90059392505050565b600082600f0b82600f0b0280600f0b9150808214613225576132256149ce565b600f82810b9082900b036f7fffffffffffffffffffffffffffffff1981126f7fffffffffffffffffffffffffffffff82131715610d8057610d806149ce565b600f81810b9083900b016f7fffffffffffffffffffffffffffffff81136f7fffffffffffffffffffffffffffffff1982121715610d8057610d806149ce565b8181036000831280158383131683831282161715613225576132256149ce565b634e487b7160e01b600052602160045260246000fd5b848152602081018490526080810160068410614cf157634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b63ffffffff818116838216019080821115613225576132256149ce56fea2646970667358221220fc1f617f4ba88836ff35c946fb26f96fcdb8000c48de45406923073ef6b6bbbe64736f6c63430008120033ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef0000000000000000000000007238390d5f6f64e67c3211c343a410e2a3dec142000000000000000000000000a599c88b6360e5ece09fd2e724157b3a599d5ab1

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104725760003560e01c80637116c60c11610250578063c1f0fb9f11610150578063e7a324dc116100c8578063f1127ed811610097578063fbd3a29d1161007c578063fbd3a29d14610bc6578063fc0c546a14610bd9578063fd4a77f114610c0057600080fd5b8063f1127ed814610b78578063f8a0576314610ba357600080fd5b8063e7a324dc14610aef578063e7e242d414610b16578063e985e9c514610b29578063ee99fe2814610b6557600080fd5b8063d1c2babb1161011f578063d4e54c3b11610104578063d4e54c3b14610aa9578063e0514aba14610abc578063e441135c14610acf57600080fd5b8063d1c2babb14610a58578063d1febfb914610a6b57600080fd5b8063c1f0fb9f14610a17578063c2c4c5c114610a2a578063c3cda52014610a32578063c87b56dd14610a4557600080fd5b806395d89b41116101e3578063a183af52116101b2578063a4d855df11610197578063a4d855df146109a9578063b45a3c0e146109bc578063b88d4fde14610a0457600080fd5b8063a183af5214610983578063a22cb4651461099657600080fd5b806395d89b411461090e578063981b24d01461094a578063986b7d8a1461095d5780639ab24eb01461097057600080fd5b80638c2c9baf1161021f5780638c2c9baf146108bc5780638e539e8c146108cf5780638fbb38ff146108e2578063900cf0cf1461090557600080fd5b80637116c60c1461085357806371197484146108665780637ecebe001461088957806385f2aef2146108a957600080fd5b8063313ce5671161037657806356afe744116102ee5780636352211e116102bd5780636f548837116102a25780636f548837146107fa5780636fcfff451461081a57806370a082311461084057600080fd5b80636352211e146107be57806365fc3873146107e757600080fd5b806356afe7441461077c578063587cde1e1461078f5780635c19a95c146107a25780635f5b0c32146107b557600080fd5b8063461f711c116103455780634bc2a6571161032a5780634bc2a6571461073257806354fd4d50146107455780635594a0451461076957600080fd5b8063461f711c146106f957806346c96aac1461071f57600080fd5b8063313ce567146106a65780633a46b1a8146106c057806342842e0e146106d3578063430c2081146106e657600080fd5b80631376f3da1161040957806323b872dd116103d85780632e1a7d4d116103bd5780632e1a7d4d1461064a5780632e720f7d1461065d5780632f745c591461067057600080fd5b806323b872dd1461063157806325a58b561461064457600080fd5b80631376f3da146105b457806318160ddd146105ef5780631c984bc3146105f757806320606b701461060a57600080fd5b8063081812fc11610445578063081812fc1461052b578063095cf5c61461056c578063095ea7b3146105815780630d6a20331461059457600080fd5b806301ffc9a714610477578063047fc9aa146104b957806306fdde03146104d05780630758c7d814610503575b600080fd5b6104a461048536600461456e565b6001600160e01b03191660009081526004602052604090205460ff1690565b60405190151581526020015b60405180910390f35b6104c260135481565b6040519081526020016104b0565b6104f6604051806040016040528060078152602001661d995419585c9b60ca1b81525081565b6040516104b091906145db565b61051661051136600461460a565b610c13565b60405163ffffffff90911681526020016104b0565b610554610539366004614634565b6000908152600960205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020016104b0565b61057f61057a36600461464d565b610d86565b005b61057f61058f36600461460a565b610dbf565b6104c26105a2366004614634565b60146020526000908152604090205481565b6105c76105c2366004614668565b610ea7565b60408051600f95860b81529390940b60208401529282015260608101919091526080016104b0565b6104c2610eee565b6104c2610605366004614668565b610efe565b6104c27f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61057f61063f36600461468a565b610f30565b436104c2565b61057f610658366004614634565b610f41565b61057f61066b36600461464d565b611237565b6104c261067e36600461460a565b6001600160a01b03919091166000908152600c60209081526040808320938352929052205490565b6106ae601281565b60405160ff90911681526020016104b0565b6104c26106ce36600461460a565b611270565b61057f6106e136600461468a565b61130f565b6104a46106f436600461460a565b61132a565b61070c610707366004614634565b61133d565b604051600f9190910b81526020016104b0565b600054610554906001600160a01b031681565b61057f61074036600461464d565b611380565b6104f6604051806040016040528060058152602001640312e302e360dc1b81525081565b600254610554906001600160a01b031681565b61057f61078a36600461470d565b6113b9565b61055461079d36600461464d565b6116c1565b61057f6107b036600461464d565b6116f1565b6104c261040081565b6105546107cc366004614634565b6000908152600760205260409020546001600160a01b031690565b6104c26107f5366004614668565b61170f565b6104c2610808366004614634565b600b6020526000908152604090205481565b61051661082836600461464d565b60186020526000908152604090205463ffffffff1681565b6104c261084e36600461464d565b611751565b6104c2610861366004614634565b61176f565b61070c610874366004614634565b601260205260009081526040902054600f0b81565b6104c261089736600461464d565b60196020526000908152604090205481565b600154610554906001600160a01b031681565b6104c26108ca366004614668565b6117cf565b6104c26108dd366004614634565b6117db565b6104a46108f0366004614634565b60156020526000908152604090205460ff1681565b6104c260115481565b6104f66040518060400160405280600781526020017f7665504541524c0000000000000000000000000000000000000000000000000081525081565b6104c2610958366004614634565b6117e6565b61057f61096b366004614634565b611988565b6104c261097e36600461464d565b6119cc565b61057f610991366004614668565b611a9f565b61057f6109a43660046147c7565b611be1565b61057f6109b7366004614668565b611c65565b6109ea6109ca366004614634565b60106020526000908152604090208054600190910154600f9190910b9082565b60408051600f9390930b83526020830191909152016104b0565b61057f610a12366004614826565b611e67565b61057f610a25366004614634565b612028565b61057f612057565b61057f610a403660046148d1565b612097565b6104f6610a53366004614634565b612448565b61057f610a66366004614668565b612574565b6105c7610a79366004614634565b600360205260009081526040902080546001820154600290920154600f82810b93600160801b909304900b919084565b6104c2610ab7366004614931565b6126fd565b6104c2610aca366004614668565b612740565b6104c2610add366004614634565b600e6020526000908152604090205481565b6104c27fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b6104c2610b24366004614634565b61274c565b6104a4610b37366004614966565b6001600160a01b039182166000908152600a6020908152604080832093909416825291909152205460ff1690565b61057f610b73366004614668565b612774565b6104c2610b86366004614999565b601760209081526000928352604080842090915290825290205481565b6104c2610bb1366004614634565b60009081526010602052604090206001015490565b61057f610bd4366004614634565b61288b565b6105547f0000000000000000000000007238390d5f6f64e67c3211c343a410e2a3dec14281565b61057f610c0e366004614634565b6128bc565b6001600160a01b03821660009081526018602052604081205463ffffffff16808203610c43576000915050610d80565b6001600160a01b03841660009081526017602052604081208491610c686001856149e4565b63ffffffff16815260208101919091526040016000205411610c9757610c8f6001826149e4565b915050610d80565b6001600160a01b0384166000908152601760209081526040808320838052909152902054831015610ccc576000915050610d80565b600080610cda6001846149e4565b90505b8163ffffffff168163ffffffff161115610d7b5760006002610cff84846149e4565b610d099190614a17565b610d1390836149e4565b6001600160a01b038816600090815260176020908152604080832063ffffffff851684529091529020805491925090879003610d5557509350610d8092505050565b8054871115610d6657819350610d74565b610d716001836149e4565b92505b5050610cdd565b509150505b92915050565b6001546001600160a01b03163314610d9d57600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000818152600760205260409020546001600160a01b031680610de157600080fd5b806001600160a01b0316836001600160a01b031603610dff57600080fd5b6000828152600760209081526040808320546001600160a01b038581168552600a845282852033808752945291909320549216149060ff168180610e405750805b610e4957600080fd5b60008481526009602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b600f60205281600052604060002081633b9aca008110610ec657600080fd5b6003020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b6000610ef94261176f565b905090565b6000828152600f6020526040812082633b9aca008110610f2057610f20614a3a565b6003020160010154905092915050565b610f3c838383336128ee565b505050565b60065460ff16600114610f5357600080fd5b6006805460ff19166002179055610f6a33826129ea565b610f7657610f76614a50565b600081815260146020526040902054158015610fa1575060008181526015602052604090205460ff16155b610fdd5760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b60448201526064015b60405180910390fd5b60008181526010602090815260409182902082518084019093528054600f0b8352600101549082018190524210156110575760405162461bcd60e51b815260206004820152601660248201527f546865206c6f636b206469646e277420657870697265000000000000000000006044820152606401610fd4565b8051604080518082018252600080825260208083018281528783526010909152929020905181546fffffffffffffffffffffffffffffffff19166001600160801b039091161781559051600190910155601354600f9190910b906110bb8282614a66565b60135560408051808201909152600080825260208201526110df9085908590612a50565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018390527f0000000000000000000000007238390d5f6f64e67c3211c343a410e2a3dec1426001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015611165573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111899190614a79565b61119557611195614a50565b61119e8461307e565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c8161120c8482614a66565b6040805192835260208301919091520160405180910390a150506006805460ff191660011790555050565b6001546001600160a01b0316331461124e57600080fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60008061127d8484610c13565b6001600160a01b038516600090815260176020908152604080832063ffffffff851684529091528120919250600190910190805b82548110156113055760008382815481106112ce576112ce614a3a565b906000526020600020015490506112e58188613151565b6112ef9084614a96565b92505080806112fd90614aa9565b9150506112b1565b5095945050505050565b610f3c83838360405180602001604052806000815250611e67565b600061133683836129ea565b9392505050565b6000818152600e6020908152604080832054600f909252822081633b9aca00811061136a5761136a614a3a565b6003020154600160801b9004600f0b9392505050565b6001546001600160a01b0316331461139757600080fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000818152601460205260409020541580156113e4575060008181526015602052604090205460ff16155b61141b5760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610fd4565b61142533826129ea565b61142e57600080fd5b600081815260076020908152604080832054601083529281902081518083019092528054600f0b8083526001909101549282018390526001600160a01b03909316929091908061147d57600080fd5b8060135461148b9190614a66565b6013556000805b87518210156114d4578782815181106114ad576114ad614a3a565b6020026020010151816114c09190614a96565b9050816114cc81614aa9565b925050611492565b604080518082018252600080825260208083018281528b835260108252848320935184546fffffffffffffffffffffffffffffffff19166001600160801b039091161784555160019093019290925582518084019093528083529082015261153f9088908790612a50565b6115488761307e565b834281116115a75760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201526566757475726560d01b6064820152608401610fd4565b6115b56303c2670042614a96565b8111156116045760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652032207965617273206d617800006044820152606401610fd4565b60008093505b89518410156116b55760056000815461162290614aa9565b909155506005549850611635888a61322c565b50828a858151811061164957611649614a3a565b60200260200101518661165c9190614ac2565b6116669190614ad9565b60008a81526010602090815260409182902082518084019093528054600f0b835260010154908201529091506116a3908a9083908590600561329d565b836116ad81614aa9565b94505061160a565b50505050505050505050565b6001600160a01b0380821660009081526016602052604081205490911680156116ea5780611336565b5090919050565b6001600160a01b0381166117025750335b61170c33826134e2565b50565b60065460009060ff1660011461172457600080fd5b6006805460ff1916600217905561173c838333613555565b90506006805460ff1916600117905592915050565b6001600160a01b038116600090815260086020526040812054610d80565b601154600081815260036020908152604080832081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608201529091906117c781856136ac565b949350505050565b600061133683836137ad565b6000610d808261176f565b6000438211156117f8576117f8614a50565b60115460006118078483613a86565b600081815260036020908152604080832081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608201529192508383101561191657600060038161186a866001614a96565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608083018290528501519192501461191057826060015181606001516118d69190614a66565b836040015182604001516118ea9190614a66565b60608501516118f9908a614a66565b6119039190614ac2565b61190d9190614ad9565b91505b50611965565b438260600151146119655760608201516119309043614a66565b604083015161193f9042614a66565b606084015161194e9089614a66565b6119589190614ac2565b6119629190614ad9565b90505b61197e828284604001516119799190614a96565b6136ac565b9695505050505050565b6000546001600160a01b0316331461199f57600080fd5b6000818152601460205260409020546119ba90600190614a66565b60009182526014602052604090912055565b6001600160a01b03811660009081526018602052604081205463ffffffff168082036119fb5750600092915050565b6001600160a01b038316600090815260176020526040812081611a1f6001856149e4565b63ffffffff1663ffffffff16815260200190815260200160002060010190506000805b8254811015611a96576000838281548110611a5f57611a5f614a3a565b90600052602060002001549050611a768142613151565b611a809084614a96565b9250508080611a8e90614aa9565b915050611a42565b50949350505050565b60065460ff16600114611ab157600080fd5b6006805460ff19166002179055611ac833836129ea565b611ad457611ad4614a50565b60008281526010602090815260409182902082518084019093528054600f0b8352600101549082015281611b0a57611b0a614a50565b60008160000151600f0b13611b615760405162461bcd60e51b815260206004820152601660248201527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006044820152606401610fd4565b42816020015111611bc05760405162461bcd60e51b8152602060048201526024808201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686044820152636472617760e01b6064820152608401610fd4565b611bcf8383600084600261329d565b50506006805460ff1916600117905550565b336001600160a01b03831603611bf957611bf9614a50565b336000818152600a602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b60065460ff16600114611c7757600080fd5b6006805460ff19166002179055611c8e33836129ea565b611c9a57611c9a614a50565b600082815260106020908152604080832081518083019092528054600f0b825260010154918101919091529062093a8080611cd58542614a96565b611cdf9190614ad9565b611ce99190614ac2565b905042826020015111611d3e5760405162461bcd60e51b815260206004820152600c60248201527f4c6f636b206578706972656400000000000000000000000000000000000000006044820152606401610fd4565b60008260000151600f0b13611d955760405162461bcd60e51b815260206004820152601160248201527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006044820152606401610fd4565b81602001518111611de85760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006044820152606401610fd4565b611df66303c2670042614a96565b811115611e455760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652032207965617273206d617800006044820152606401610fd4565b611e548460008385600361329d565b50506006805460ff191660011790555050565b611e73848484336128ee565b823b1561202257604051630a85bd0160e11b81526001600160a01b0384169063150b7a0290611eac903390889087908790600401614aed565b6020604051808303816000875af1925050508015611ee7575060408051601f3d908101601f19168201909252611ee491810190614b1f565b60015b611f9a573d808015611f15576040519150601f19603f3d011682016040523d82523d6000602084013e611f1a565b606091505b508051600003611f925760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610fd4565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146120205760405162461bcd60e51b815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a65637465642060448201527f746f6b656e7300000000000000000000000000000000000000000000000000006064820152608401610fd4565b505b50505050565b6000546001600160a01b0316331461203f57600080fd5b6000908152601560205260409020805460ff19169055565b612095600060405180604001604052806000600f0b8152602001600081525060405180604001604052806000600f0b81526020016000815250612a50565b565b336001600160a01b038716036120ac57600080fd5b6001600160a01b0386166120bf57600080fd5b60408051808201825260078152661d995419585c9b60ca1b6020918201528151808301835260058152640312e302e360dc1b9082015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f2345aff9dfc8e79edd2931b80e3b4343b5eb41635e88104cb74e0c7a90fa379f818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608201524660808201523060a0808301919091528351808303909101815260c0820184528051908301207fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60e08301526001600160a01b038a166101008301526101208201899052610140808301899052845180840390910181526101608301909452835193909201929092207f190100000000000000000000000000000000000000000000000000000000000061018084015261018283018290526101a2830181905290916000906101c20160408051601f198184030181528282528051602091820120600080855291840180845281905260ff8a169284019290925260608301889052608083018790529092509060019060a0016020604051602081039080840390855afa15801561229b573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166123245760405162461bcd60e51b815260206004820152602e60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a20696e7660448201527f616c6964207369676e61747572650000000000000000000000000000000000006064820152608401610fd4565b6001600160a01b038116600090815260196020526040812080549161234883614aa9565b9190505589146123c05760405162461bcd60e51b815260206004820152602a60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a20696e7660448201527f616c6964206e6f6e6365000000000000000000000000000000000000000000006064820152608401610fd4565b874211156124365760405162461bcd60e51b815260206004820152602e60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a2073696760448201527f6e617475726520657870697265640000000000000000000000000000000000006064820152608401610fd4565b6116b5818b6134e2565b505050505050565b6000818152600760205260409020546060906001600160a01b03166124af5760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e00000000006044820152606401610fd4565b60008281526010602090815260409182902082518084019093528054600f0b835260010154908201526002546001600160a01b031663dd9ec149846124f48142613151565b6020850151855160405160e086901b6001600160e01b0319168152600481019490945260248401929092526044830152600f0b6064820152608401600060405180830381865afa15801561254c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113369190810190614b3c565b60008281526014602052604090205415801561259f575060008281526015602052604090205460ff16155b6125d65760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610fd4565b8082036125e257600080fd5b6125ec33836129ea565b6125f557600080fd5b6125ff33826129ea565b61260857600080fd5b6000828152601060208181526040808420815180830183528154600f90810b825260019283015482860190815288885295855283872084518086019095528054820b855290920154938301849052805194519095929490910b921115612672578260200151612678565b83602001515b604080518082018252600080825260208083018281528b835260108252848320935184546fffffffffffffffffffffffffffffffff19166001600160801b03909116178455516001909301929092558251808401909352808352908201529091506126e69087908690612a50565b6126ef8661307e565b61244085838386600461329d565b60065460009060ff1660011461271257600080fd5b6006805460ff1916600217905561272a848484613555565b90506006805460ff191660011790559392505050565b60006113368383613151565b6000818152600b602052604081205443900361276a57506000919050565b610d808242613151565b60065460ff1660011461278657600080fd5b6006805460ff1916600217905560008281526010602090815260409182902082518084019093528054600f0b83526001015490820152816127c657600080fd5b60008160000151600f0b1361281d5760405162461bcd60e51b815260206004820152601660248201527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006044820152606401610fd4565b4281602001511161287c5760405162461bcd60e51b8152602060048201526024808201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686044820152636472617760e01b6064820152608401610fd4565b611bcf8383600084600061329d565b6000546001600160a01b031633146128a257600080fd5b6000818152601460205260409020546119ba906001614a96565b6000546001600160a01b031633146128d357600080fd5b6000908152601560205260409020805460ff19166001179055565b600082815260146020526040902054158015612919575060008281526015602052604090205460ff16155b6129505760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610fd4565b61295a81836129ea565b61296357600080fd5b61296d8483613b0c565b6129778483613b73565b612992612983856116c1565b61298c856116c1565b84613bf4565b61299c8383613f92565b6000828152600b60205260408082204390555183916001600160a01b0380871692908816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450505050565b60008181526007602090815260408083205460098352818420546001600160a01b03918216808652600a855283862088841680885295529285205492938085149392909116149060ff168280612a3d5750815b80612a455750805b979650505050505050565b60408051608081018252600080825260208201819052918101829052606081019190915260408051608081018252600080825260208201819052918101829052606081019190915260115460009081908715612bbb57428760200151118015612ac0575060008760000151600f0b135b15612b05578651612ad6906303c2670090614baa565b600f0b602080870191909152870151612af0904290614a66565b8560200151612aff9190614bf1565b600f0b85525b428660200151118015612b1f575060008660000151600f0b135b15612b64578551612b35906303c2670090614baa565b600f0b602080860191909152860151612b4f904290614a66565b8460200151612b5e9190614bf1565b600f0b84525b602080880151600090815260128252604090205490870151600f9190910b935015612bbb578660200151866020015103612ba057829150612bbb565b602080870151600090815260129091526040902054600f0b91505b604080516080810182526000808252602082015242918101919091524360608201528115612c30575060008181526003602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b604081015181600042831015612c7d576040840151612c4f9042614a66565b6060850151612c5e9043614a66565b612c7090670de0b6b3a7640000614ac2565b612c7a9190614ad9565b90505b600062093a80612c8d8186614ad9565b612c979190614ac2565b905060005b60ff811015612e1157612cb262093a8083614a96565b9150600042831115612cc657429250612cda565b50600082815260126020526040902054600f0b5b612ce48684614a66565b8760200151612cf39190614bf1565b87518890612d02908390614c11565b600f0b905250602087018051829190612d1c908390614c50565b600f90810b90915288516000910b12159050612d3757600087525b60008760200151600f0b1215612d4f57600060208801525b60408088018490528501519295508592670de0b6b3a764000090612d739085614a66565b612d7d9086614ac2565b612d879190614ad9565b8560600151612d969190614a96565b6060880152612da6600189614a96565b9750428303612dbb5750436060870152612e11565b6000888152600360209081526040918290208951918a01516001600160801b03908116600160801b029216919091178155908801516001820155606088015160029091015550612e0a81614aa9565b9050612c9c565b505060118590558b15612e9c5788602001518860200151612e329190614c11565b84602001818151612e439190614c50565b600f0b90525088518851612e579190614c11565b84518590612e66908390614c50565b600f90810b90915260208601516000910b12159050612e8757600060208501525b60008460000151600f0b1215612e9c57600084525b6000858152600360209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b1561307057428b602001511115612f5c576020890151612f029088614c50565b96508a602001518a6020015103612f25576020880151612f229088614c11565b96505b60208b810151600090815260129091526040902080546fffffffffffffffffffffffffffffffff19166001600160801b0389161790555b428a602001511115612fc0578a602001518a602001511115612fc0576020880151612f879087614c11565b60208b810151600090815260129091526040902080546fffffffffffffffffffffffffffffffff19166001600160801b03831617905595505b60008c8152600e6020526040812054612fda906001614a96565b905080600e60008f815260200190815260200160002081905550428960400181815250504389606001818152505088600f60008f815260200190815260200160002082633b9aca00811061303057613030614a3a565b825160208401516001600160801b03908116600160801b029116176003919091029190910190815560408201516001820155606090910151600290910155505b505050505050505050505050565b61308833826129ea565b6130d45760405162461bcd60e51b815260206004820181905260248201527f63616c6c6572206973206e6f74206f776e6572206e6f7220617070726f7665646044820152606401610fd4565b6000818152600760205260408120546001600160a01b0316906130f79083610dbf565b61310b613103826116c1565b600084613bf4565b6131158183613b73565b60405182906000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000828152600e6020526040812054808203613171576000915050610d80565b6000848152600f6020526040812082633b9aca00811061319357613193614a3a565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b6020830152600181015492820183905260020154606082015291506131df9085614c8f565b81602001516131ee9190614bf1565b815182906131fd908390614c11565b600f90810b90915282516000910b1215905061321857600081525b51600f0b9150610d809050565b5092915050565b60006001600160a01b03831661324457613244614a50565b613252600061298c856116c1565b61325c8383613f92565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600192915050565b60135482906132ac8682614a96565b6013556040805180820190915260008082526020820152825160208085015190830152600f0b81528251879084906132e5908390614c50565b600f0b90525085156132f957602083018690525b6000888152601060209081526040909120845181546fffffffffffffffffffffffffffffffff19166001600160801b0390911617815590840151600190910155613344888285612a50565b3387158015906133665750600485600581111561336357613363614caf565b14155b80156133845750600585600581111561338157613381614caf565b14155b15613447576040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152306024830152604482018a90527f0000000000000000000000007238390d5f6f64e67c3211c343a410e2a3dec14216906323b872dd906064016020604051808303816000875af1158015613417573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343b9190614a79565b61344757613447614a50565b8360200151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b894260405161348b9493929190614cc5565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c836134bf8a82614a96565b6040805192835260208301919091520160405180910390a1505050505050505050565b60006134ed836116c1565b6001600160a01b0384811660008181526016602052604080822080546001600160a01b031916888616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610f3c838284614028565b60008062093a80806135678642614a96565b6135719190614ad9565b61357b9190614ac2565b90506000851161358a57600080fd5b4281116135e85760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201526566757475726560d01b6064820152608401610fd4565b6135f66303c2670042614a96565b8111156136455760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652032207965617273206d617800006044820152606401610fd4565b60056000815461365490614aa9565b90915550600554613665848261322c565b5060008181526010602090815260409182902082518084019093528054600f0b8352600190810154918301919091526136a39183918991869161329d565b95945050505050565b600080839050600062093a808083604001516136c89190614ad9565b6136d29190614ac2565b905060005b60ff811015613785576136ed62093a8083614a96565b915060008583111561370157859250613715565b50600082815260126020526040902054600f0b5b60408401516137249084614a66565b84602001516137339190614bf1565b84518590613742908390614c11565b600f0b9052508583036137555750613785565b80846020018181516137679190614c50565b600f0b905250506040830182905261377e81614aa9565b90506136d7565b5060008260000151600f0b121561379b57600082525b50516001600160801b03169392505050565b6000438211156137bf576137bf614a50565b6000838152600e6020526040812054815b608081101561385f578183101561385f57600060026137ef8486614a96565b6137fa906001614a96565b6138049190614ad9565b6000888152600f60205260409020909150869082633b9aca00811061382b5761382b614a3a565b6003020160020154116138405780935061384e565b61384b600182614a66565b92505b5061385881614aa9565b90506137d0565b506000858152600f6020526040812083633b9aca00811061388257613882614a3a565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b6020830152600181015492820192909252600290910154606082015260115490915060006138d78783613a86565b600081815260036020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925080848410156139b657600060038161393b876001614a96565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608083018290528601519192506139989190614a66565b9250836040015181604001516139ae9190614a66565b9150506139da565b60608301516139c59043614a66565b91508260400151426139d79190614a66565b90505b60408301518215613a17578284606001518c6139f69190614a66565b613a009084614ac2565b613a0a9190614ad9565b613a149082614a96565b90505b6040870151613a269082614a66565b8760200151613a359190614bf1565b87518890613a44908390614c11565b600f90810b90915288516000910b129050613a7457505093516001600160801b03169650610d8095505050505050565b60009950505050505050505050610d80565b60008082815b6080811015613b025781831015613b025760006002613aab8486614a96565b613ab6906001614a96565b613ac09190614ad9565b6000818152600360205260409020600201549091508710613ae357809350613af1565b613aee600182614a66565b92505b50613afb81614aa9565b9050613a8c565b5090949350505050565b6000818152600760205260409020546001600160a01b03838116911614613b3557613b35614a50565b6000818152600960205260409020546001600160a01b031615613b6f57600081815260096020526040902080546001600160a01b03191690555b5050565b6000818152600760205260409020546001600160a01b03838116911614613b9c57613b9c614a50565b600081815260076020526040902080546001600160a01b0319169055613bc28282614420565b6001600160a01b0382166000908152600860205260408120805460019290613beb908490614a66565b90915550505050565b816001600160a01b0316836001600160a01b031614158015613c165750600081115b15610f3c576001600160a01b03831615613d97576001600160a01b03831660009081526018602052604081205463ffffffff169081613c7a576001600160a01b03851660009081526017602090815260408083208380529091529020600101613cbc565b6001600160a01b038516600090815260176020526040812090613c9e6001856149e4565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000613cc9866144df565b6001600160a01b038716600090815260176020908152604080832063ffffffff8516845290915281209192506001909101905b8354811015613d56576000848281548110613d1957613d19614a3a565b90600052602060002001549050868114613d43578254600181018455600084815260209020018190555b5080613d4e81614aa9565b915050613cfc565b50613d62846001614d03565b6001600160a01b0388166000908152601860205260409020805463ffffffff191663ffffffff92909216919091179055505050505b6001600160a01b03821615610f3c576001600160a01b03821660009081526018602052604081205463ffffffff169081613df6576001600160a01b03841660009081526017602090815260408083208380529091529020600101613e38565b6001600160a01b038416600090815260176020526040812090613e1a6001856149e4565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000613e45856144df565b6001600160a01b038616600090815260176020908152604080832063ffffffff851684529091529020835491925060019081019161040091613e879190614a96565b1115613ee15760405162461bcd60e51b815260206004820152602360248201527f64737452657020776f756c64206861766520746f6f206d616e7920746f6b656e60448201526249647360e81b6064820152608401610fd4565b60005b8354811015613f33576000848281548110613f0157613f01614a3a565b600091825260208083209091015485546001810187558684529190922001555080613f2b81614aa9565b915050613ee4565b50805460018181018355600083815260209020909101869055613f57908590614d03565b6001600160a01b0387166000908152601860205260409020805463ffffffff9290921663ffffffff1990921691909117905550505050505050565b6000818152600760205260409020546001600160a01b031615613fb757613fb7614a50565b600081815260076020908152604080832080546001600160a01b0319166001600160a01b03871690811790915580845260088084528285208054600c86528487208188528652848720889055878752600d865293862093909355908452909152805460019290613beb908490614a96565b806001600160a01b0316826001600160a01b031614610f3c576001600160a01b038216156141db576001600160a01b03821660009081526018602052604081205463ffffffff1690816140a0576001600160a01b038416600090815260176020908152604080832083805290915290206001016140e2565b6001600160a01b0384166000908152601760205260408120906140c46001856149e4565b63ffffffff1663ffffffff1681526020019081526020016000206001015b905060006140ef856144df565b6001600160a01b038616600090815260176020908152604080832063ffffffff8516845290915281209192506001909101905b835481101561419a57600084828154811061413f5761413f614a3a565b600091825260208083209091015480835260079091526040909120549091506001600160a01b03908116908a1614614187578254600181018455600084815260209020018190555b508061419281614aa9565b915050614122565b506141a6846001614d03565b6001600160a01b0387166000908152601860205260409020805463ffffffff191663ffffffff92909216919091179055505050505b6001600160a01b03811615610f3c576001600160a01b03811660009081526018602052604081205463ffffffff16908161423a576001600160a01b0383166000908152601760209081526040808320838052909152902060010161427c565b6001600160a01b03831660009081526017602052604081209061425e6001856149e4565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000614289846144df565b6001600160a01b03808616600090815260176020908152604080832063ffffffff861684528252808320938b16835260089091529020548454929350600190910191610400906142da908390614a96565b11156143345760405162461bcd60e51b815260206004820152602360248201527f64737452657020776f756c64206861766520746f6f206d616e7920746f6b656e60448201526249647360e81b6064820152608401610fd4565b60005b845481101561438657600085828154811061435457614354614a3a565b60009182526020808320909101548654600181018855878452919092200155508061437e81614aa9565b915050614337565b5060005b818110156143d8576001600160a01b0389166000908152600c6020908152604080832084845282528220548554600181018755868452919092200155806143d081614aa9565b91505061438a565b506143e4856001614d03565b6001600160a01b0387166000908152601860205260409020805463ffffffff9290921663ffffffff199092169190911790555050505050505050565b6001600160a01b03821660009081526008602052604081205461444590600190614a66565b6000838152600d6020526040902054909150808203614494576001600160a01b0384166000908152600c602090815260408083208584528252808320839055858352600d909152812055612022565b6001600160a01b03939093166000908152600c6020908152604080832093835292815282822080548684528484208190558352600d9091528282209490945592839055908252812055565b6001600160a01b038116600090815260186020526040812054429063ffffffff16801580159061454857506001600160a01b0384166000908152601760205260408120839161452f6001856149e4565b63ffffffff168152602081019190915260400160002054145b15611336576117c76001826149e4565b6001600160e01b03198116811461170c57600080fd5b60006020828403121561458057600080fd5b813561133681614558565b60005b838110156145a657818101518382015260200161458e565b50506000910152565b600081518084526145c781602086016020860161458b565b601f01601f19169290920160200192915050565b60208152600061133660208301846145af565b80356001600160a01b038116811461460557600080fd5b919050565b6000806040838503121561461d57600080fd5b614626836145ee565b946020939093013593505050565b60006020828403121561464657600080fd5b5035919050565b60006020828403121561465f57600080fd5b611336826145ee565b6000806040838503121561467b57600080fd5b50508035926020909101359150565b60008060006060848603121561469f57600080fd5b6146a8846145ee565b92506146b6602085016145ee565b9150604084013590509250925092565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715614705576147056146c6565b604052919050565b6000806040838503121561472057600080fd5b823567ffffffffffffffff8082111561473857600080fd5b818501915085601f83011261474c57600080fd5b8135602082821115614760576147606146c6565b8160051b92506147718184016146dc565b828152928401810192818101908985111561478b57600080fd5b948201945b848610156147a957853582529482019490820190614790565b9997909101359750505050505050565b801515811461170c57600080fd5b600080604083850312156147da57600080fd5b6147e3836145ee565b915060208301356147f3816147b9565b809150509250929050565b600067ffffffffffffffff821115614818576148186146c6565b50601f01601f191660200190565b6000806000806080858703121561483c57600080fd5b614845856145ee565b9350614853602086016145ee565b925060408501359150606085013567ffffffffffffffff81111561487657600080fd5b8501601f8101871361488757600080fd5b803561489a614895826147fe565b6146dc565b8181528860208385010111156148af57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b60008060008060008060c087890312156148ea57600080fd5b6148f3876145ee565b95506020870135945060408701359350606087013560ff8116811461491757600080fd5b9598949750929560808101359460a0909101359350915050565b60008060006060848603121561494657600080fd5b833592506020840135915061495d604085016145ee565b90509250925092565b6000806040838503121561497957600080fd5b614982836145ee565b9150614990602084016145ee565b90509250929050565b600080604083850312156149ac57600080fd5b6149b5836145ee565b9150602083013563ffffffff811681146147f357600080fd5b634e487b7160e01b600052601160045260246000fd5b63ffffffff828116828216039080821115613225576132256149ce565b634e487b7160e01b600052601260045260246000fd5b600063ffffffff80841680614a2e57614a2e614a01565b92169190910492915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b81810381811115610d8057610d806149ce565b600060208284031215614a8b57600080fd5b8151611336816147b9565b80820180821115610d8057610d806149ce565b600060018201614abb57614abb6149ce565b5060010190565b8082028115828204841417610d8057610d806149ce565b600082614ae857614ae8614a01565b500490565b60006001600160a01b0380871683528086166020840152508360408301526080606083015261197e60808301846145af565b600060208284031215614b3157600080fd5b815161133681614558565b600060208284031215614b4e57600080fd5b815167ffffffffffffffff811115614b6557600080fd5b8201601f81018413614b7657600080fd5b8051614b84614895826147fe565b818152856020838501011115614b9957600080fd5b6136a382602083016020860161458b565b600081600f0b83600f0b80614bc157614bc1614a01565b6f7fffffffffffffffffffffffffffffff19821460001982141615614be857614be86149ce565b90059392505050565b600082600f0b82600f0b0280600f0b9150808214613225576132256149ce565b600f82810b9082900b036f7fffffffffffffffffffffffffffffff1981126f7fffffffffffffffffffffffffffffff82131715610d8057610d806149ce565b600f81810b9083900b016f7fffffffffffffffffffffffffffffff81136f7fffffffffffffffffffffffffffffff1982121715610d8057610d806149ce565b8181036000831280158383131683831282161715613225576132256149ce565b634e487b7160e01b600052602160045260246000fd5b848152602081018490526080810160068410614cf157634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b63ffffffff818116838216019080821115613225576132256149ce56fea2646970667358221220fc1f617f4ba88836ff35c946fb26f96fcdb8000c48de45406923073ef6b6bbbe64736f6c63430008120033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000007238390d5f6f64e67c3211c343a410e2a3dec142000000000000000000000000a599c88b6360e5ece09fd2e724157b3a599d5ab1

-----Decoded View---------------
Arg [0] : token_addr (address): 0x7238390d5f6F64e67c3211C343A410E2A3DEc142
Arg [1] : art_proxy (address): 0xA599C88B6360E5ece09Fd2E724157b3a599d5ab1

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000007238390d5f6f64e67c3211c343a410e2a3dec142
Arg [1] : 000000000000000000000000a599c88b6360e5ece09fd2e724157b3a599d5ab1


Loading...
Loading
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.