creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n"
      },
      "contracts/interfaces/IERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\n\n/**\n* @dev Interface of the ERC165 standard, as defined in the\n* https://eips.ethereum.org/EIPS/eip-165[EIP].\n*\n* Implementers can declare support of contract interfaces, which can then be\n* queried by others ({ERC165Checker}).\n*\n* For an implementation, see {ERC165}.\n*/\ninterface IERC165 {\n    /**\n    * @dev Returns true if this contract implements the interface defined by\n    * `interfaceId`. See the corresponding\n    * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n    * to learn more about how these ids are created.\n    *\n    * This function call must use less than 30 000 gas.\n    */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}"
      },
      "contracts/interfaces/IERC721.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport './IERC165.sol';\n\n/**\n* @dev Required interface of an ERC721 compliant contract.\n*/\ninterface IERC721 is IERC165 {\n    /**\n    * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n    */\n    event Transfer(address indexed from, address indexed to, uint indexed tokenId);\n\n    /**\n    * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n    */\n    event Approval(address indexed owner, address indexed approved, uint indexed tokenId);\n\n    /**\n    * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n    */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n    * @dev Returns the number of tokens in ``owner``'s account.\n    */\n    function balanceOf(address owner) external view returns (uint balance);\n\n    /**\n    * @dev Returns the owner of the `tokenId` token.\n    *\n    * Requirements:\n    *\n    * - `tokenId` must exist.\n    */\n    function ownerOf(uint tokenId) external view returns (address owner);\n\n    /**\n    * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n    * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n    *\n    * Requirements:\n    *\n    * - `from` cannot be the zero address.\n    * - `to` cannot be the zero address.\n    * - `tokenId` token must exist and be owned by `from`.\n    * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n    * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n    *\n    * Emits a {Transfer} event.\n    */\n    function safeTransferFrom(\n        address from,\n        address to,\n        uint tokenId\n    ) external;\n\n    /**\n    * @dev Transfers `tokenId` token from `from` to `to`.\n    *\n    * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n    *\n    * Requirements:\n    *\n    * - `from` cannot be the zero address.\n    * - `to` cannot be the zero address.\n    * - `tokenId` token must be owned by `from`.\n    * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n    *\n    * Emits a {Transfer} event.\n    */\n    function transferFrom(\n        address from,\n        address to,\n        uint tokenId\n    ) external;\n\n    /**\n    * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n    * The approval is cleared when the token is transferred.\n    *\n    * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n    *\n    * Requirements:\n    *\n    * - The caller must own the token or be an approved operator.\n    * - `tokenId` must exist.\n    *\n    * Emits an {Approval} event.\n    */\n    function approve(address to, uint tokenId) external;\n\n    /**\n    * @dev Returns the account approved for `tokenId` token.\n    *\n    * Requirements:\n    *\n    * - `tokenId` must exist.\n    */\n    function getApproved(uint tokenId) external view returns (address operator);\n\n    /**\n    * @dev Approve or remove `operator` as an operator for the caller.\n    * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n    *\n    * Requirements:\n    *\n    * - The `operator` cannot be the caller.\n    *\n    * Emits an {ApprovalForAll} event.\n    */\n    function setApprovalForAll(address operator, bool _approved) external;\n\n    /**\n    * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n    *\n    * See {setApprovalForAll}\n    */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n    /**\n    * @dev Safely transfers `tokenId` token from `from` to `to`.\n    *\n    * Requirements:\n    *\n    * - `from` cannot be the zero address.\n    * - `to` cannot be the zero address.\n    * - `tokenId` token must exist and be owned by `from`.\n    * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n    * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n    *\n    * Emits a {Transfer} event.\n    */\n    function safeTransferFrom(\n        address from,\n        address to,\n        uint tokenId,\n        bytes calldata data\n    ) external;\n}"
      },
      "contracts/interfaces/IERC721Metadata.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport './IERC721.sol';\n\n/**\n* @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n* @dev See https://eips.ethereum.org/EIPS/eip-721\n*/\ninterface IERC721Metadata is IERC721 {\n    /**\n    * @dev Returns the token collection name.\n    */\n    function name() external view returns (string memory);\n\n    /**\n    * @dev Returns the token collection symbol.\n    */\n    function symbol() external view returns (string memory);\n\n    /**\n    * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n    */\n    function tokenURI(uint tokenId) external view returns (string memory);\n}"
      },
      "contracts/interfaces/IERC721Receiver.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\n/**\n* @title ERC721 token receiver interface\n* @dev Interface for any contract that wants to support safeTransfers\n* from ERC721 asset contracts.\n*/\ninterface IERC721Receiver {\n    /**\n    * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n    * by `operator` from `from`, this function is called.\n    *\n    * It must return its Solidity selector to confirm the token transfer.\n    * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n    *\n    * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n    */\n    function onERC721Received(\n        address operator,\n        address from,\n        uint tokenId,\n        bytes calldata data\n    ) external returns (bytes4);\n}"
      },
      "contracts/interfaces/IVeArtProxy.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\ninterface IVeArtProxy {\n    function _tokenURI(uint _tokenId, uint _balanceOf, uint _locked_end, uint _value) external pure returns (string memory output);\n}\n"
      },
      "contracts/veArc.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IERC721} from \"./interfaces/IERC721.sol\";\nimport {IERC721Receiver} from \"./interfaces/IERC721Receiver.sol\";\nimport {IERC721Metadata} from \"./interfaces/IERC721Metadata.sol\";\nimport {IVeArtProxy} from \"./interfaces/IVeArtProxy.sol\";\n\n/**\n@title Voting Escrow\n@author Curve Finance\n@license MIT\n@notice Votes have a weight depending on time, so that users are\ncommitted to the future of (whatever they are voting for)\n@dev Vote weight decays linearly over time. Lock time cannot be\nmore than `MAXTIME` (4 years).\n\n# Voting escrow to have time-weighted votes\n# Votes have a weight depending on time, so that users are committed\n# to the future of (whatever they are voting for).\n# The weight in this implementation is linear, and lock cannot be more than maxtime:\n# w ^\n# 1 +        /\n#   |      /\n#   |    /\n#   |  /\n#   |/\n# 0 +--------+------> time\n#       maxtime (4 years?)\n*/\n\nstruct Point {\n    int128 bias;\n    int128 slope; // # -dweight / dt\n    uint ts;\n    uint blk; // block\n}\n/* We cannot really do block numbers per se b/c slope is per time, not per block\n* and per block could be fairly bad b/c Ethereum changes blocktimes.\n* What we can do is to extrapolate ***At functions */\n\nstruct LockedBalance {\n    int128 amount;\n    uint end;\n}\n\ncontract veArc is IERC721, IERC721Metadata {\n    enum DepositType {\n        DEPOSIT_FOR_TYPE,\n        CREATE_LOCK_TYPE,\n        INCREASE_LOCK_AMOUNT,\n        INCREASE_UNLOCK_TIME,\n        MERGE_TYPE\n    }\n\n    event Deposit(\n        address indexed provider,\n        uint tokenId,\n        uint value,\n        uint indexed locktime,\n        DepositType deposit_type,\n        uint ts\n    );\n    event Withdraw(address indexed provider, uint tokenId, uint value, uint ts);\n    event Supply(uint prevSupply, uint supply);\n    event Burn(address indexed provider, uint tokenId, uint value, uint ts);\n\n    uint internal constant WEEK = 1 weeks;\n    uint internal constant MAXTIME = 4 * 365 * 86400;\n    int128 internal constant iMAXTIME = 4 * 365 * 86400;\n    uint internal constant MULTIPLIER = 1 ether;\n\n    address immutable public token;\n    uint public supply;\n    mapping(uint => LockedBalance) public locked;\n    \n    address public admin;\n    address public pendingAdmin;\n    address public artProxy;\n\n    mapping(uint => uint) public ownership_change;\n\n    uint public epoch;\n    mapping(uint => Point) public point_history; // epoch -> unsigned point\n    mapping(uint => Point[1000000000]) public user_point_history; // user -> Point[user_epoch]\n\n    mapping(uint => uint) public user_point_epoch;\n    mapping(uint => int128) public slope_changes; // time -> signed slope change\n\n    mapping(uint => uint) public attachments;\n    mapping(uint => bool) public voted;\n    address public voter;\n\n    string constant public name = \"veArc\";\n    string constant public symbol = \"veArc\";\n    string constant public version = \"2.0.0\";\n    uint8 constant public decimals = 18;\n\n    /// @dev Current count of token\n    uint internal tokenId;\n\n    /// @dev Mapping from NFT ID to the address that owns it.\n    mapping(uint => address) internal idToOwner;\n\n    /// @dev Mapping from NFT ID to approved address.\n    mapping(uint => address) internal idToApprovals;\n\n    /// @dev Mapping from owner address to count of his tokens.\n    mapping(address => uint) internal ownerToNFTokenCount;\n\n    /// @dev Mapping from owner address to mapping of index to tokenIds\n    mapping(address => mapping(uint => uint)) internal ownerToNFTokenIdList;\n\n    /// @dev Mapping from NFT ID to index of owner\n    mapping(uint => uint) internal tokenToOwnerIndex;\n\n    /// @dev Mapping of vote expiry date per NFT ID\n    mapping(uint => uint) internal idToVoteExpiry;\n\n    /// @dev Mapping from owner address to mapping of operator addresses.\n    mapping(address => mapping(address => bool)) internal ownerToOperators;\n\n    /// @dev Mapping of interface id to bool about whether or not it's supported\n    mapping(bytes4 => bool) internal supportedInterfaces;\n\n    /// @dev ERC165 interface ID of ERC165\n    bytes4 internal constant ERC165_INTERFACE_ID = 0x01ffc9a7;\n\n    /// @dev ERC165 interface ID of ERC721\n    bytes4 internal constant ERC721_INTERFACE_ID = 0x80ac58cd;\n\n    /// @dev ERC165 interface ID of ERC721Metadata\n    bytes4 internal constant ERC721_METADATA_INTERFACE_ID = 0x5b5e139f;\n\n    /// @dev reentrancy guard\n    uint8 internal constant _not_entered = 1;\n    uint8 internal constant _entered = 2;\n    uint8 internal _entered_state = 1;\n    modifier nonreentrant() {\n        require(_entered_state == _not_entered);\n        _entered_state = _entered;\n        _;\n        _entered_state = _not_entered;\n    }\n\n    /// @notice Contract constructor\n    /// @param token_addr `ERC20CRV` token address\n    constructor(\n        address token_addr,\n        address art_proxy\n    ) {\n        require(\n            token_addr != address(0),\n            \"VotingEscrow: zero address provided in constructor\"\n        );\n        token = token_addr;\n        voter = msg.sender;\n        point_history[0].blk = block.number;\n        point_history[0].ts = block.timestamp;\n        \n        admin = msg.sender;\n        artProxy = art_proxy;\n\n        supportedInterfaces[ERC165_INTERFACE_ID] = true;\n        supportedInterfaces[ERC721_INTERFACE_ID] = true;\n        supportedInterfaces[ERC721_METADATA_INTERFACE_ID] = true;\n\n        // mint-ish\n        emit Transfer(address(0), address(this), tokenId);\n        // burn-ish\n        emit Transfer(address(this), address(0), tokenId);\n    }\n\n    /// @dev Interface identification is specified in ERC-165.\n    /// @param _interfaceID Id of the interface\n    function supportsInterface(bytes4 _interfaceID) external view returns (bool) {\n        return supportedInterfaces[_interfaceID];\n    }\n\n    /// @notice Get the most recently recorded rate of voting power decrease for `_tokenId`\n    /// @param _tokenId token of the NFT\n    /// @return Value of the slope\n    function get_last_user_slope(uint _tokenId) external view returns (int128) {\n        uint uepoch = user_point_epoch[_tokenId];\n        return user_point_history[_tokenId][uepoch].slope;\n    }\n\n    /// @notice Get the timestamp for checkpoint `_idx` for `_tokenId`\n    /// @param _tokenId token of the NFT\n    /// @param _idx User epoch number\n    /// @return Epoch time of the checkpoint\n    function user_point_history__ts(uint _tokenId, uint _idx) external view returns (uint) {\n        return user_point_history[_tokenId][_idx].ts;\n    }\n\n    /// @notice Get timestamp when `_tokenId`'s lock finishes\n    /// @param _tokenId User NFT\n    /// @return Epoch time of the lock end\n    function locked__end(uint _tokenId) external view returns (uint) {\n        return locked[_tokenId].end;\n    }\n    \n    /// @notice Get `_tokenId`'s locked amount\n    /// @param _tokenId User NFT\n    function locked__amount(uint _tokenId) external view returns (uint) {\n        return uint(int256(locked[_tokenId].amount));\n    }\n\n    /// @dev Returns the number of NFTs owned by `_owner`.\n    ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.\n    /// @param _owner Address for whom to query the balance.\n    function _balance(address _owner) internal view returns (uint) {\n        return ownerToNFTokenCount[_owner];\n    }\n\n    /// @dev Returns the number of NFTs owned by `_owner`.\n    ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.\n    /// @param _owner Address for whom to query the balance.\n    function balanceOf(address _owner) external view returns (uint) {\n        return _balance(_owner);\n    }\n\n    /// @dev Returns the address of the owner of the NFT.\n    /// @param _tokenId The identifier for an NFT.\n    function ownerOf(uint _tokenId) public view returns (address) {\n        return idToOwner[_tokenId];\n    }\n\n    /// @dev Get the approved address for a single NFT.\n    /// @param _tokenId ID of the NFT to query the approval of.\n    function getApproved(uint _tokenId) external view returns (address) {\n        return idToApprovals[_tokenId];\n    }\n\n    /// @dev Checks if `_operator` is an approved operator for `_owner`.\n    /// @param _owner The address that owns the NFTs.\n    /// @param _operator The address that acts on behalf of the owner.\n    function isApprovedForAll(address _owner, address _operator) external view returns (bool) {\n        return (ownerToOperators[_owner])[_operator];\n    }\n\n    /// @dev  Get token by index\n    function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint) {\n        return ownerToNFTokenIdList[_owner][_tokenIndex];\n    }\n\n    /// @dev Returns whether the given spender can transfer a given token ID\n    /// @param _spender address of the spender to query\n    /// @param _tokenId uint ID of the token to be transferred\n    /// @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\n    function _isApprovedOrOwner(address _spender, uint _tokenId) internal view returns (bool) {\n        address owner = idToOwner[_tokenId];\n        bool spenderIsOwner = owner == _spender;\n        bool spenderIsApproved = _spender == idToApprovals[_tokenId];\n        bool spenderIsApprovedForAll = (ownerToOperators[owner])[_spender];\n        return spenderIsOwner || spenderIsApproved || spenderIsApprovedForAll;\n    }\n\n    function isApprovedOrOwner(address _spender, uint _tokenId) external view returns (bool) {\n        return _isApprovedOrOwner(_spender, _tokenId);\n    }\n\n    /// @dev Returns true if NFT is unlocked, false if locked in vote.\n    /// @param _tokenId The identifier for an NFT.\n    function isVoteExpired(uint _tokenId) external view returns (bool) {\n        return _isVoteExpired(_tokenId);\n    }\n    \n    /// @dev Returns true if NFT is unlocked, false if locked in vote.\n    /// @param _tokenId The identifier for an NFT.\n    function _isVoteExpired(uint _tokenId) internal view returns (bool) {\n        return idToVoteExpiry[_tokenId] < block.timestamp;\n    }\n\n    /// @dev Returns remaining vote lock for an NFT.\n    /// @param _tokenId The identifier for an NFT.\n    function voteExpiry(uint _tokenId) external view returns (uint) {\n        return idToVoteExpiry[_tokenId];\n    }\n\n    /// @dev Add a NFT to an index mapping to a given address\n    /// @param _to address of the receiver\n    /// @param _tokenId uint ID Of the token to be added\n    function _addTokenToOwnerList(address _to, uint _tokenId) internal {\n        uint current_count = _balance(_to);\n\n        ownerToNFTokenIdList[_to][current_count] = _tokenId;\n        tokenToOwnerIndex[_tokenId] = current_count;\n    }\n\n    /// @dev Remove a NFT from an index mapping to a given address\n    /// @param _from address of the sender\n    /// @param _tokenId uint ID Of the token to be removed\n    function _removeTokenFromOwnerList(address _from, uint _tokenId) internal {\n        // Delete\n        uint current_count = _balance(_from)-1;\n        uint current_index = tokenToOwnerIndex[_tokenId];\n\n        if (current_count == current_index) {\n            // update ownerToNFTokenIdList\n            ownerToNFTokenIdList[_from][current_count] = 0;\n            // update tokenToOwnerIndex\n            tokenToOwnerIndex[_tokenId] = 0;\n        } else {\n            uint lastTokenId = ownerToNFTokenIdList[_from][current_count];\n\n            // Add\n            // update ownerToNFTokenIdList\n            ownerToNFTokenIdList[_from][current_index] = lastTokenId;\n            // update tokenToOwnerIndex\n            tokenToOwnerIndex[lastTokenId] = current_index;\n\n            // Delete\n            // update ownerToNFTokenIdList\n            ownerToNFTokenIdList[_from][current_count] = 0;\n            // update tokenToOwnerIndex\n            tokenToOwnerIndex[_tokenId] = 0;\n        }\n    }\n\n    /// @dev Add a NFT to a given address\n    ///      Throws if `_tokenId` is owned by someone.\n    function _addTokenTo(address _to, uint _tokenId) internal {\n        // Throws if `_tokenId` is owned by someone\n        assert(idToOwner[_tokenId] == address(0));\n        // Change the owner\n        idToOwner[_tokenId] = _to;\n        // Update owner token index tracking\n        _addTokenToOwnerList(_to, _tokenId);\n        // Change count tracking\n        ownerToNFTokenCount[_to] += 1;\n    }\n\n    /// @dev Remove a NFT from a given address\n    ///      Throws if `_from` is not the current owner.\n    function _removeTokenFrom(address _from, uint _tokenId) internal {\n        // Throws if `_from` is not the current owner\n        assert(idToOwner[_tokenId] == _from);\n        // Change the owner\n        idToOwner[_tokenId] = address(0);\n        // Update owner token index tracking\n        _removeTokenFromOwnerList(_from, _tokenId);\n        // Change count tracking\n        ownerToNFTokenCount[_from] -= 1;\n    }\n\n    /// @dev Clear an approval of a given address\n    ///      Throws if `_owner` is not the current owner.\n    function _clearApproval(address _owner, uint _tokenId) internal {\n        // Throws if `_owner` is not the current owner\n        assert(idToOwner[_tokenId] == _owner);\n        if (idToApprovals[_tokenId] != address(0)) {\n            // Reset approvals\n            idToApprovals[_tokenId] = address(0);\n        }\n    }\n\n    /// @dev Exeute transfer of a NFT.\n    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the approved\n    ///      address for this NFT. (NOTE: `msg.sender` not allowed in internal function so pass `_sender`.)\n    ///      Throws if `_to` is the zero address.\n    ///      Throws if `_from` is not the current owner.\n    ///      Throws if `_tokenId` is not a valid NFT.\n    function _transferFrom(\n        address _from,\n        address _to,\n        uint _tokenId,\n        address _sender\n    ) internal {\n        require(attachments[_tokenId] == 0 && !voted[_tokenId], \"attached\");\n        // Check requirements\n        require(_isApprovedOrOwner(_sender, _tokenId));\n        // Clear approval. Throws if `_from` is not the current owner\n        _clearApproval(_from, _tokenId);\n        // Remove NFT. Throws if `_tokenId` is not a valid NFT\n        _removeTokenFrom(_from, _tokenId);\n        // Add NFT\n        _addTokenTo(_to, _tokenId);\n        // Set the block of ownership transfer (for Flash NFT protection)\n        ownership_change[_tokenId] = block.number;\n        // Log the transfer\n        emit Transfer(_from, _to, _tokenId);\n    }\n\n    /* TRANSFER FUNCTIONS */\n    /// @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved address for this NFT.\n    ///      Throws if `_from` is not the current owner.\n    ///      Throws if `_to` is the zero address.\n    ///      Throws if `_tokenId` is not a valid NFT.\n    /// @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else\n    ///        they maybe be permanently lost.\n    /// @param _from The current owner of the NFT.\n    /// @param _to The new owner.\n    /// @param _tokenId The NFT to transfer.\n    function transferFrom(\n        address _from,\n        address _to,\n        uint _tokenId\n    ) external {\n        _transferFrom(_from, _to, _tokenId, msg.sender);\n    }\n\n    function _isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize, which returns 0 for contracts in\n        // construction, since the code is only stored at the end of the\n        // constructor execution.\n        uint size;\n        assembly {\n            size := extcodesize(account)\n        }\n        return size > 0;\n    }\n\n    /// @dev Transfers the ownership of an NFT from one address to another address.\n    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the\n    ///      approved address for this NFT.\n    ///      Throws if `_from` is not the current owner.\n    ///      Throws if `_to` is the zero address.\n    ///      Throws if `_tokenId` is not a valid NFT.\n    ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if\n    ///      the return value is not `bytes4(keccak256(\"onERC721Received(address,address,uint,bytes)\"))`.\n    /// @param _from The current owner of the NFT.\n    /// @param _to The new owner.\n    /// @param _tokenId The NFT to transfer.\n    /// @param _data Additional data with no specified format, sent in call to `_to`.\n    function safeTransferFrom(\n        address _from,\n        address _to,\n        uint _tokenId,\n        bytes memory _data\n    ) public {\n        _transferFrom(_from, _to, _tokenId, msg.sender);\n\n        // Check if recipient is contract\n        if (_isContract(_to)) {\n            bytes4 retval = IERC721Receiver(_to).onERC721Received(\n                msg.sender,\n                _from,\n                _tokenId,\n                _data\n            );\n            require(retval == IERC721Receiver.onERC721Received.selector, \"ERC721: transfer rejected\");\n        }\n    }\n\n    /// @dev Transfers the ownership of an NFT from one address to another address.\n    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the\n    ///      approved address for this NFT.\n    ///      Throws if `_from` is not the current owner.\n    ///      Throws if `_to` is the zero address.\n    ///      Throws if `_tokenId` is not a valid NFT.\n    ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if\n    ///      the return value is not `bytes4(keccak256(\"onERC721Received(address,address,uint,bytes)\"))`.\n    /// @param _from The current owner of the NFT.\n    /// @param _to The new owner.\n    /// @param _tokenId The NFT to transfer.\n    function safeTransferFrom(\n        address _from,\n        address _to,\n        uint _tokenId\n    ) external {\n        safeTransferFrom(_from, _to, _tokenId, '');\n    }\n\n    /// @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address.\n    ///      Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.\n    ///      Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP)\n    ///      Throws if `_approved` is the current owner. (NOTE: This is not written the EIP)\n    /// @param _approved Address to be approved for the given NFT ID.\n    /// @param _tokenId ID of the token to be approved.\n    function approve(address _approved, uint _tokenId) public {\n        address owner = idToOwner[_tokenId];\n        // Throws if `_tokenId` is not a valid NFT\n        require(owner != address(0));\n        // Throws if `_approved` is the current owner\n        require(_approved != owner);\n        // Check requirements\n        bool senderIsOwner = (idToOwner[_tokenId] == msg.sender);\n        bool senderIsApprovedForAll = (ownerToOperators[owner])[msg.sender];\n        require(senderIsOwner || senderIsApprovedForAll);\n        // Set the approval\n        idToApprovals[_tokenId] = _approved;\n        emit Approval(owner, _approved, _tokenId);\n    }\n\n    /// @dev Enables or disables approval for a third party (\"operator\") to manage all of\n    ///      `msg.sender`'s assets. It also emits the ApprovalForAll event.\n    ///      Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP)\n    /// @notice This works even if sender doesn't own any tokens at the time.\n    /// @param _operator Address to add to the set of authorized operators.\n    /// @param _approved True if the operators is approved, false to revoke approval.\n    function setApprovalForAll(address _operator, bool _approved) external {\n        // Throws if `_operator` is the `msg.sender`\n        assert(_operator != msg.sender);\n        ownerToOperators[msg.sender][_operator] = _approved;\n        emit ApprovalForAll(msg.sender, _operator, _approved);\n    }\n\n    /// @dev Function to mint tokens\n    ///      Throws if `_to` is zero address.\n    ///      Throws if `_tokenId` is owned by someone.\n    /// @param _to The address that will receive the minted tokens.\n    /// @param _tokenId The token id to mint.\n    /// @return A boolean that indicates if the operation was successful.\n    function _mint(address _to, uint _tokenId) internal returns (bool) {\n        // Throws if `_to` is zero address\n        assert(_to != address(0));\n        // Add NFT. Throws if `_tokenId` is owned by someone\n        _addTokenTo(_to, _tokenId);\n        if (_isContract(_to)) {\n            bytes4 retval = IERC721Receiver(_to).onERC721Received(\n                msg.sender,\n                address(0),\n                _tokenId,\n                ''\n            );\n            require(retval == IERC721Receiver.onERC721Received.selector);\n        }\n\n        emit Transfer(address(0), _to, _tokenId);\n        return true;\n    }\n\n    /// @notice Record global and per-user data to checkpoint\n    /// @param _tokenId NFT token ID. No user checkpoint if 0\n    /// @param old_locked Pevious locked amount / end lock time for the user\n    /// @param new_locked New locked amount / end lock time for the user\n    function _checkpoint(\n        uint _tokenId,\n        LockedBalance memory old_locked,\n        LockedBalance memory new_locked\n    ) internal {\n        Point memory u_old;\n        Point memory u_new;\n        int128 old_dslope = 0;\n        int128 new_dslope = 0;\n        uint _epoch = epoch;\n\n        if (_tokenId != 0) {\n            // Calculate slopes and biases\n            // Kept at zero when they have to\n            if (old_locked.end > block.timestamp && old_locked.amount > 0) {\n                u_old.slope = old_locked.amount / iMAXTIME;\n                u_old.bias = u_old.slope * int128(int256(old_locked.end - block.timestamp));\n            }\n            if (new_locked.end > block.timestamp && new_locked.amount > 0) {\n                u_new.slope = new_locked.amount / iMAXTIME;\n                u_new.bias = u_new.slope * int128(int256(new_locked.end - block.timestamp));\n            }\n\n            // Read values of scheduled changes in the slope\n            // old_locked.end can be in the past and in the future\n            // new_locked.end can ONLY by in the FUTURE unless everything expired: than zeros\n            old_dslope = slope_changes[old_locked.end];\n            if (new_locked.end != 0) {\n                if (new_locked.end == old_locked.end) {\n                    new_dslope = old_dslope;\n                } else {\n                    new_dslope = slope_changes[new_locked.end];\n                }\n            }\n        }\n\n        Point memory last_point = Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number});\n        if (_epoch > 0) {\n            last_point = point_history[_epoch];\n        }\n        uint last_checkpoint = last_point.ts;\n        // initial_last_point is used for extrapolation to calculate block number\n        // (approximately, for *At methods) and save them\n        // as we cannot figure that out exactly from inside the contract\n        Point memory initial_last_point = last_point;\n        uint block_slope = 0; // dblock/dt\n        if (block.timestamp > last_point.ts) {\n            block_slope = (MULTIPLIER * (block.number - last_point.blk)) / (block.timestamp - last_point.ts);\n        }\n        // If last point is already recorded in this block, slope=0\n        // But that's ok b/c we know the block in such case\n\n        // Go over weeks to fill history and calculate what the current point is\n        {\n            uint t_i = (last_checkpoint / WEEK) * WEEK;\n            for (uint i = 0; i < 255; ++i) {\n                // Hopefully it won't happen that this won't get used in 5 years!\n                // If it does, users will be able to withdraw but vote weight will be broken\n                t_i += WEEK;\n                int128 d_slope = 0;\n                if (t_i > block.timestamp) {\n                    t_i = block.timestamp;\n                } else {\n                    d_slope = slope_changes[t_i];\n                }\n                last_point.bias -= last_point.slope * int128(int256(t_i - last_checkpoint));\n                last_point.slope += d_slope;\n                if (last_point.bias < 0) {\n                    // This can happen\n                    last_point.bias = 0;\n                }\n                if (last_point.slope < 0) {\n                    // This cannot happen - just in case\n                    last_point.slope = 0;\n                }\n                last_checkpoint = t_i;\n                last_point.ts = t_i;\n                last_point.blk = initial_last_point.blk + (block_slope * (t_i - initial_last_point.ts)) / MULTIPLIER;\n                _epoch += 1;\n                if (t_i == block.timestamp) {\n                    last_point.blk = block.number;\n                    break;\n                } else {\n                    point_history[_epoch] = last_point;\n                }\n            }\n        }\n\n        epoch = _epoch;\n        // Now point_history is filled until t=now\n\n        if (_tokenId != 0) {\n            // If last point was in this block, the slope change has been applied already\n            // But in such case we have 0 slope(s)\n            last_point.slope += (u_new.slope - u_old.slope);\n            last_point.bias += (u_new.bias - u_old.bias);\n            if (last_point.slope < 0) {\n                last_point.slope = 0;\n            }\n            if (last_point.bias < 0) {\n                last_point.bias = 0;\n            }\n        }\n\n        // Record the changed point into history\n        point_history[_epoch] = last_point;\n\n        if (_tokenId != 0) {\n            // Schedule the slope changes (slope is going down)\n            // We subtract new_user_slope from [new_locked.end]\n            // and add old_user_slope to [old_locked.end]\n            if (old_locked.end > block.timestamp) {\n                // old_dslope was <something> - u_old.slope, so we cancel that\n                old_dslope += u_old.slope;\n                if (new_locked.end == old_locked.end) {\n                    old_dslope -= u_new.slope; // It was a new deposit, not extension\n                }\n                slope_changes[old_locked.end] = old_dslope;\n            }\n\n            if (new_locked.end > block.timestamp) {\n                if (new_locked.end > old_locked.end) {\n                    new_dslope -= u_new.slope; // old slope disappeared at this point\n                    slope_changes[new_locked.end] = new_dslope;\n                }\n                // else: we recorded it already in old_dslope\n            }\n            // Now handle user history\n            uint user_epoch = user_point_epoch[_tokenId] + 1;\n\n            user_point_epoch[_tokenId] = user_epoch;\n            u_new.ts = block.timestamp;\n            u_new.blk = block.number;\n            user_point_history[_tokenId][user_epoch] = u_new;\n        }\n    }\n\n    /// @notice Deposit and lock tokens for a user\n    /// @param _tokenId NFT that holds lock\n    /// @param _value Amount to deposit\n    /// @param unlock_time New time when to unlock the tokens, or 0 if unchanged\n    /// @param locked_balance Previous locked amount / timestamp\n    /// @param deposit_type The type of deposit\n    function _deposit_for(\n        uint _tokenId,\n        uint _value,\n        uint unlock_time,\n        LockedBalance memory locked_balance,\n        DepositType deposit_type\n    ) internal {\n        LockedBalance memory _locked = locked_balance;\n        uint supply_before = supply;\n\n        supply = supply_before + _value;\n        LockedBalance memory old_locked;\n        (old_locked.amount, old_locked.end) = (_locked.amount, _locked.end);\n        // Adding to existing lock, or if a lock is expired - creating a new one\n        _locked.amount += int128(int256(_value));\n        if (unlock_time != 0) {\n            _locked.end = unlock_time;\n        }\n        locked[_tokenId] = _locked;\n\n        // Possibilities:\n        // Both old_locked.end could be current or expired (>/< block.timestamp)\n        // value == 0 (extend lock) or value > 0 (add to lock or extend lock)\n        // _locked.end > block.timestamp (always)\n        _checkpoint(_tokenId, old_locked, _locked);\n\n        address from = msg.sender;\n        if (_value != 0 && deposit_type != DepositType.MERGE_TYPE) {\n            assert(IERC20(token).transferFrom(from, address(this), _value));\n        }\n\n        emit Deposit(from, _tokenId, _value, _locked.end, deposit_type, block.timestamp);\n        emit Supply(supply_before, supply_before + _value);\n    }\n\n    /// @notice Locks tokenID vote for 1 week.\n    /// @param _tokenId The identifier for an NFT.\n    function lockVote(uint _tokenId) external {\n        require(msg.sender == voter);\n        require(_isVoteExpired(_tokenId),\"Vote Locked!\");\n        idToVoteExpiry[_tokenId] = block.timestamp + WEEK;\n    }\n\n    function setVoter(address _voter) external {\n        require(msg.sender == voter);\n        voter = _voter;\n    }\n\n    function voting(uint _tokenId) external {\n        require(msg.sender == voter);\n        voted[_tokenId] = true;\n    }\n\n    function abstain(uint _tokenId) external {\n        require(msg.sender == voter);\n        voted[_tokenId] = false;\n    }\n\n    function attach(uint _tokenId) external {\n        require(msg.sender == voter);\n        attachments[_tokenId] = attachments[_tokenId]+1;\n    }\n\n    function detach(uint _tokenId) external {\n        require(msg.sender == voter);\n        attachments[_tokenId] = attachments[_tokenId]-1;\n    }\n\n    function merge(uint _from, uint _to) external {\n        require(attachments[_from] == 0 && !voted[_from], \"attached\");\n        require(_from != _to);\n        require(_isApprovedOrOwner(msg.sender, _from));\n        require(_isApprovedOrOwner(msg.sender, _to));\n\n        LockedBalance memory _locked0 = locked[_from];\n        LockedBalance memory _locked1 = locked[_to];\n        uint value0 = uint(int256(_locked0.amount));\n        uint end = _locked0.end >= _locked1.end ? _locked0.end : _locked1.end;\n\n        locked[_from] = LockedBalance(0, 0);\n        _checkpoint(_from, _locked0, LockedBalance(0, 0));\n        _burn(_from);\n        _deposit_for(_to, value0, end, _locked1, DepositType.MERGE_TYPE);\n    }\n\n    function block_number() external view returns (uint) {\n        return block.number;\n    }\n\n    /// @notice Record global data to checkpoint\n    function checkpoint() external {\n        _checkpoint(0, LockedBalance(0, 0), LockedBalance(0, 0));\n    }\n\n    /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock\n    /// @dev Anyone (even a smart contract) can deposit for someone else, but\n    ///      cannot extend their locktime and deposit for a brand new user\n    /// @param _tokenId lock NFT\n    /// @param _value Amount to add to user's lock\n    function deposit_for(uint _tokenId, uint _value) external nonreentrant {\n        LockedBalance memory _locked = locked[_tokenId];\n\n        require(_value > 0); // dev: need non-zero value\n        require(_locked.amount > 0, 'No existing lock found');\n        require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');\n        _deposit_for(_tokenId, _value, 0, _locked, DepositType.DEPOSIT_FOR_TYPE);\n    }\n\n    /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`\n    /// @param _value Amount to deposit\n    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)\n    /// @param _to Address to deposit\n    function _create_lock(uint _value, uint _lock_duration, address _to) internal returns (uint) {\n        uint unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // Locktime is rounded down to weeks\n\n        require(_value > 0); // dev: need non-zero value\n        require(unlock_time > block.timestamp, 'Can only lock until time in the future');\n        require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max');\n\n        ++tokenId;\n        uint _tokenId = tokenId;\n        _mint(_to, _tokenId);\n\n        _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.CREATE_LOCK_TYPE);\n        return _tokenId;\n    }\n\n    /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`\n    /// @param _value Amount to deposit\n    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)\n    /// @param _to Address to deposit\n    function create_lock_for(uint _value, uint _lock_duration, address _to) external nonreentrant returns (uint) {\n        return _create_lock(_value, _lock_duration, _to);\n    }\n\n    /// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lock_duration`\n    /// @param _value Amount to deposit\n    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)\n    function create_lock(uint _value, uint _lock_duration) external nonreentrant returns (uint) {\n        return _create_lock(_value, _lock_duration, msg.sender);\n    }\n\n    /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time\n    /// @param _value Amount of tokens to deposit and add to the lock\n    function increase_amount(uint _tokenId, uint _value) external nonreentrant {\n        assert(_isApprovedOrOwner(msg.sender, _tokenId));\n\n        LockedBalance memory _locked = locked[_tokenId];\n\n        assert(_value > 0); // dev: need non-zero value\n        require(_locked.amount > 0, 'No existing lock found');\n        require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');\n\n        _deposit_for(_tokenId, _value, 0, _locked, DepositType.INCREASE_LOCK_AMOUNT);\n    }\n\n    /// @notice Extend the unlock time for `_tokenId`\n    /// @param _lock_duration New number of seconds until tokens unlock\n    function increase_unlock_time(uint _tokenId, uint _lock_duration) external nonreentrant {\n        assert(_isApprovedOrOwner(msg.sender, _tokenId));\n\n        LockedBalance memory _locked = locked[_tokenId];\n        uint unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // Locktime is rounded down to weeks\n\n        require(_locked.end > block.timestamp, 'Lock expired');\n        require(_locked.amount > 0, 'Nothing is locked');\n        require(unlock_time > _locked.end, 'Can only increase lock duration');\n        require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max');\n\n        _deposit_for(_tokenId, 0, unlock_time, _locked, DepositType.INCREASE_UNLOCK_TIME);\n    }\n\n    /// @notice Withdraw all tokens for `_tokenId`\n    /// @dev Only possible if the lock has expired\n    function withdraw(uint _tokenId) external nonreentrant {\n        assert(_isApprovedOrOwner(msg.sender, _tokenId));\n        require(attachments[_tokenId] == 0 && !voted[_tokenId], \"attached\");\n\n        LockedBalance memory _locked = locked[_tokenId];\n        require(block.timestamp >= _locked.end, \"The lock didn't expire\");\n        uint value = uint(int256(_locked.amount));\n\n        locked[_tokenId] = LockedBalance(0,0);\n        uint supply_before = supply;\n        supply = supply_before - value;\n\n        // old_locked can have either expired <= timestamp or zero end\n        // _locked has only 0 end\n        // Both can have >= 0 amount\n        _checkpoint(_tokenId, _locked, LockedBalance(0,0));\n\n        assert(IERC20(token).transfer(msg.sender, value));\n\n        // Burn the NFT\n        _burn(_tokenId);\n\n        emit Withdraw(msg.sender, _tokenId, value, block.timestamp);\n        emit Supply(supply_before, supply_before - value);\n    }\n\n    // The following ERC20/minime-compatible methods are not real balanceOf and supply!\n    // They measure the weights for the purpose of voting, so they don't represent\n    // real coins.\n\n    /// @notice Binary search to estimate timestamp for block number\n    /// @param _block Block to find\n    /// @param max_epoch Don't go beyond this epoch\n    /// @return Approximate timestamp for block\n    function _find_block_epoch(uint _block, uint max_epoch) internal view returns (uint) {\n        // Binary search\n        uint _min = 0;\n        uint _max = max_epoch;\n        for (uint i = 0; i < 128; ++i) {\n            // Will be always enough for 128-bit numbers\n            if (_min >= _max) {\n                break;\n            }\n            uint _mid = (_min + _max + 1) / 2;\n            if (point_history[_mid].blk <= _block) {\n                _min = _mid;\n            } else {\n                _max = _mid - 1;\n            }\n        }\n        return _min;\n    }\n\n    /// @notice Get the current voting power for `_tokenId`\n    /// @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility\n    /// @param _tokenId NFT for lock\n    /// @param _t Epoch time to return voting power at\n    /// @return User voting power\n    function _balanceOfNFT(uint _tokenId, uint _t) internal view returns (uint) {\n        uint _epoch = user_point_epoch[_tokenId];\n        if (_epoch == 0) {\n            return 0;\n        } else {\n            Point memory last_point = user_point_history[_tokenId][_epoch];\n            last_point.bias -= last_point.slope * int128(int256(_t) - int256(last_point.ts));\n            if (last_point.bias < 0) {\n                last_point.bias = 0;\n            }\n            return uint(int256(last_point.bias));\n        }\n    }\n    \n    modifier onlyAdmin() {\n        require(msg.sender == admin, \"veArc: only admin\");\n        _;\n    }\n    \n    function setAdmin(address _admin) external onlyAdmin {\n        pendingAdmin = _admin;\n    }\n\n    function acceptAdmin() external {\n        require(msg.sender == pendingAdmin);\n        admin = pendingAdmin;\n    }\n    \n    function setArtProxy(address _proxy) external onlyAdmin {\n        artProxy = _proxy;\n    }\n\n    /// @dev Returns current token URI metadata\n    /// @param _tokenId Token ID to fetch URI for.\n    function tokenURI(uint _tokenId) external view returns (string memory) {\n        require(idToOwner[_tokenId] != address(0), \"Query for nonexistent token\");\n        LockedBalance memory _locked = locked[_tokenId];\n        return IVeArtProxy(artProxy)._tokenURI(\n            _tokenId,\n            _balanceOfNFT(_tokenId, block.timestamp),\n            _locked.end,\n            uint(int256(_locked.amount))\n        );\n    }\n\n    function balanceOfNFT(uint _tokenId) external view returns (uint) {\n        if (ownership_change[_tokenId] == block.number) return 0;\n        return _balanceOfNFT(_tokenId, block.timestamp);\n    }\n\n    function balanceOfNFTAt(uint _tokenId, uint _t) external view returns (uint) {\n        return _balanceOfNFT(_tokenId, _t);\n    }\n\n    /// @notice Measure voting power of `_tokenId` at block height `_block`\n    /// @dev Adheres to MiniMe `balanceOfAt` interface: https://github.com/Giveth/minime\n    /// @param _tokenId User's wallet NFT\n    /// @param _block Block to calculate the voting power at\n    /// @return Voting power\n    function _balanceOfAtNFT(uint _tokenId, uint _block) internal view returns (uint) {\n        // Copying and pasting totalSupply code because Vyper cannot pass by\n        // reference yet\n        assert(_block <= block.number);\n\n        // Binary search\n        uint _min = 0;\n        uint _max = user_point_epoch[_tokenId];\n        for (uint i = 0; i < 128; ++i) {\n            // Will be always enough for 128-bit numbers\n            if (_min >= _max) {\n                break;\n            }\n            uint _mid = (_min + _max + 1) / 2;\n            if (user_point_history[_tokenId][_mid].blk <= _block) {\n                _min = _mid;\n            } else {\n                _max = _mid - 1;\n            }\n        }\n\n        Point memory upoint = user_point_history[_tokenId][_min];\n\n        uint max_epoch = epoch;\n        uint _epoch = _find_block_epoch(_block, max_epoch);\n        Point memory point_0 = point_history[_epoch];\n        uint d_block = 0;\n        uint d_t = 0;\n        if (_epoch < max_epoch) {\n            Point memory point_1 = point_history[_epoch + 1];\n            d_block = point_1.blk - point_0.blk;\n            d_t = point_1.ts - point_0.ts;\n        } else {\n            d_block = block.number - point_0.blk;\n            d_t = block.timestamp - point_0.ts;\n        }\n        uint block_time = point_0.ts;\n        if (d_block != 0) {\n            block_time += (d_t * (_block - point_0.blk)) / d_block;\n        }\n\n        upoint.bias -= upoint.slope * int128(int256(block_time - upoint.ts));\n        if (upoint.bias >= 0) {\n            return uint(uint128(upoint.bias));\n        } else {\n            return 0;\n        }\n    }\n\n    function balanceOfAtNFT(uint _tokenId, uint _block) external view returns (uint) {\n        return _balanceOfAtNFT(_tokenId, _block);\n    }\n\n    /// @notice Calculate total voting power at some point in the past\n    /// @param point The point (bias/slope) to start search from\n    /// @param t Time to calculate the total voting power at\n    /// @return Total voting power at that time\n    function _supply_at(Point memory point, uint t) internal view returns (uint) {\n        Point memory last_point = point;\n        uint t_i = (last_point.ts / WEEK) * WEEK;\n        for (uint i = 0; i < 255; ++i) {\n            t_i += WEEK;\n            int128 d_slope = 0;\n            if (t_i > t) {\n                t_i = t;\n            } else {\n                d_slope = slope_changes[t_i];\n            }\n            last_point.bias -= last_point.slope * int128(int256(t_i - last_point.ts));\n            if (t_i == t) {\n                break;\n            }\n            last_point.slope += d_slope;\n            last_point.ts = t_i;\n        }\n\n        if (last_point.bias < 0) {\n            last_point.bias = 0;\n        }\n        return uint(uint128(last_point.bias));\n    }\n\n    /// @notice Calculate total voting power\n    /// @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility\n    /// @return Total voting power\n    function totalSupplyAtT(uint t) public view returns (uint) {\n        uint _epoch = epoch;\n        Point memory last_point = point_history[_epoch];\n        return _supply_at(last_point, t);\n    }\n\n    function totalSupply() external view returns (uint) {\n        return totalSupplyAtT(block.timestamp);\n    }\n\n    /// @notice Calculate total voting power at some point in the past\n    /// @param _block Block to calculate the total voting power at\n    /// @return Total voting power at `_block`\n    function totalSupplyAt(uint _block) external view returns (uint) {\n        assert(_block <= block.number);\n        uint _epoch = epoch;\n        uint target_epoch = _find_block_epoch(_block, _epoch);\n\n        Point memory point = point_history[target_epoch];\n        uint dt = 0;\n        if (target_epoch < _epoch) {\n            Point memory point_next = point_history[target_epoch + 1];\n            if (point.blk != point_next.blk) {\n                dt = ((_block - point.blk) * (point_next.ts - point.ts)) / (point_next.blk - point.blk);\n            }\n        } else {\n            if (point.blk != block.number) {\n                dt = ((_block - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk);\n            }\n        }\n        // Now dt contains info on how far are we beyond point\n        return _supply_at(point, point.ts + dt);\n    }\n\n    function _burn(uint _tokenId) internal {\n        require(_isApprovedOrOwner(msg.sender, _tokenId), \"caller is not owner nor approved\");\n\n        address owner = ownerOf(_tokenId);\n\n        // Clear approval\n        approve(address(0), _tokenId);\n        // Remove token\n        _removeTokenFrom(owner, _tokenId);\n        emit Transfer(owner, address(0), _tokenId);\n    }\n\n    function tokensOfOwner(address _owner) external view returns (uint256[] memory) {\n        uint256 tokenCount = _balance(_owner);\n        uint256[] memory tokensId = new uint256[](tokenCount);\n\n        for (uint256 i = 0; i < tokenCount; i++) {\n            tokensId[i] = ownerToNFTokenIdList[_owner][i];\n        }\n        return tokensId;\n    }\n    \n    /// @notice Burn all tokens for `_tokenId`\n    /// @dev Only possible if not vote locked\n    function burn(uint _tokenId) external nonreentrant {\n        assert(_isApprovedOrOwner(msg.sender, _tokenId));\n        require(attachments[_tokenId] == 0 && !voted[_tokenId], \"attached\");\n        require(_isVoteExpired(_tokenId), \"Vote Locked!\");\n\n        LockedBalance memory _locked = locked[_tokenId];\n        uint value = uint(int256(_locked.amount));\n\n        locked[_tokenId] = LockedBalance(0,0);\n        uint supply_before = supply;\n        supply = supply_before - value;\n\n        // old_locked can have either expired <= timestamp or zero end\n        // _locked has only 0 end\n        // Both can have >= 0 amount\n        _checkpoint(_tokenId, _locked, LockedBalance(0,0));\n\n        assert(IERC20(token).transfer(address(0), value));\n\n        // Burn the NFT\n        _burn(_tokenId);\n\n        emit Burn(msg.sender, _tokenId, value, block.timestamp);\n        emit Supply(supply_before, supply_before - value);\n    }\n}"
      }
    },
    "settings": {
      "evmVersion": "paris",
      "optimizer": {
        "enabled": true,
        "runs": 10000
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}