{"sources":{"contracts/interfaces/IVeArtProxy.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.13;\r\n\r\ninterface IVeArtProxy {\r\n    function _tokenURI(uint _tokenId, uint _balanceOf, uint _locked_end, uint _value) external pure returns (string memory output);\r\n}\r\n"},"contracts/interfaces/IVotingEscrow.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.13;\r\n\r\ninterface IVotingEscrow {\r\n\r\n    struct Point {\r\n        int128 bias;\r\n        int128 slope; // # -dweight / dt\r\n        uint256 ts;\r\n        uint256 blk; // block\r\n    }\r\n\r\n    struct LockedBalance {\r\n        int128 amount;\r\n        uint end;\r\n    }\r\n\r\n    function create_lock_for(uint _value, uint _lock_duration, address _to) external returns (uint);\r\n\r\n    function locked(uint id) external view returns(LockedBalance memory);\r\n    function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint);\r\n\r\n    function token() external view returns (address);\r\n    function team() external returns (address);\r\n    function epoch() external view returns (uint);\r\n    function point_history(uint loc) external view returns (Point memory);\r\n    function user_point_history(uint tokenId, uint loc) external view returns (Point memory);\r\n    function user_point_epoch(uint tokenId) external view returns (uint);\r\n\r\n    function ownerOf(uint) external view returns (address);\r\n    function isApprovedOrOwner(address, uint) external view returns (bool);\r\n    function transferFrom(address, address, uint) external;\r\n\r\n    function voted(uint) external view returns (bool);\r\n    function attachments(uint) external view returns (uint);\r\n    function voting(uint tokenId) external;\r\n    function abstain(uint tokenId) external;\r\n    function attach(uint tokenId) external;\r\n    function detach(uint tokenId) external;\r\n\r\n    function checkpoint() external;\r\n    function deposit_for(uint tokenId, uint value) external;\r\n\r\n    function balanceOfNFT(uint _id) external view returns (uint);\r\n    function balanceOf(address _owner) external view returns (uint);\r\n    function totalSupply() external view returns (uint);\r\n    function supply() external view returns (uint);\r\n\r\n\r\n    function decimals() external view returns(uint8);\r\n}\r\n"},"@openzeppelin/contracts/governance/utils/IVotes.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)\npragma solidity ^0.8.0;\n\n/**\n * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.\n *\n * _Available since v4.5._\n */\ninterface IVotes {\n    /**\n     * @dev Emitted when an account changes their delegate.\n     */\n    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);\n\n    /**\n     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.\n     */\n    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);\n\n    /**\n     * @dev Returns the current amount of votes that `account` has.\n     */\n    function getVotes(address account) external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).\n     */\n    function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);\n\n    /**\n     * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).\n     *\n     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.\n     * Votes that have not been delegated are still part of total supply, even though they would not participate in a\n     * vote.\n     */\n    function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);\n\n    /**\n     * @dev Returns the delegate that `account` has chosen.\n     */\n    function delegates(address account) external view returns (address);\n\n    /**\n     * @dev Delegates votes from the sender to `delegatee`.\n     */\n    function delegate(address delegatee) external;\n\n    /**\n     * @dev Delegates votes from signer to `delegatee`.\n     */\n    function delegateBySig(\n        address delegatee,\n        uint256 nonce,\n        uint256 expiry,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n}\n"},"@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)\n\npragma solidity ^0.8.0;\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(uint256 tokenId) external view returns (string memory);\n}\n"},"@openzeppelin/contracts/token/ERC721/IERC721.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/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, uint256 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, uint256 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 (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\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        uint256 tokenId,\n        bytes calldata data\n    ) external;\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 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        uint256 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        uint256 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, uint256 tokenId) external;\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 the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\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"},"@openzeppelin/contracts/utils/introspection/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\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}\n"},"contracts/VotingEscrow.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.13;\r\n\r\nimport {IERC721, IERC721Metadata} from \"@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol\";\r\nimport {IVotes} from \"@openzeppelin/contracts/governance/utils/IVotes.sol\";\r\nimport {IERC721Receiver} from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\r\nimport {IERC20} from \"./interfaces/IERC20.sol\";\r\nimport {IVeArtProxy} from \"./interfaces/IVeArtProxy.sol\";\r\nimport {IVotingEscrow} from \"./interfaces/IVotingEscrow.sol\";\r\n\r\n/// @title Voting Escrow\r\n/// @notice veNFT implementation that escrows ERC-20 tokens in the form of an ERC-721 NFT\r\n/// @notice Votes have a weight depending on time, so that users are committed to the future of (whatever they are voting for)\r\n/// @author Modified from Solidly (https://github.com/solidlyexchange/solidly/blob/master/contracts/ve.sol)\r\n/// @author Modified from Curve (https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy)\r\n/// @author Modified from Nouns DAO (https://github.com/withtally/my-nft-dao-project/blob/main/contracts/ERC721Checkpointable.sol)\r\n/// @dev Vote weight decays linearly over time. Lock time cannot be more than `MAXTIME` (2 years).\r\ncontract VotingEscrow is IERC721, IERC721Metadata, IVotes {\r\n    enum DepositType {\r\n        DEPOSIT_FOR_TYPE,\r\n        CREATE_LOCK_TYPE,\r\n        INCREASE_LOCK_AMOUNT,\r\n        INCREASE_UNLOCK_TIME,\r\n        MERGE_TYPE,\r\n        SPLIT_TYPE\r\n    }\r\n\r\n    struct LockedBalance {\r\n        int128 amount;\r\n        uint end;\r\n    }\r\n\r\n    struct Point {\r\n        int128 bias;\r\n        int128 slope; // # -dweight / dt\r\n        uint ts;\r\n        uint blk; // block\r\n    }\r\n    /* We cannot really do block numbers per se b/c slope is per time, not per block\r\n     * and per block could be fairly bad b/c Ethereum changes blocktimes.\r\n     * What we can do is to extrapolate ***At functions */\r\n\r\n    /// @notice A checkpoint for marking delegated tokenIds from a given timestamp\r\n    struct Checkpoint {\r\n        uint timestamp;\r\n        uint[] tokenIds;\r\n    }\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                                 EVENTS\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    event Deposit(\r\n        address indexed provider,\r\n        uint tokenId,\r\n        uint value,\r\n        uint indexed locktime,\r\n        DepositType deposit_type,\r\n        uint ts\r\n    );\r\n    event Withdraw(address indexed provider, uint tokenId, uint value, uint ts);\r\n    event Supply(uint prevSupply, uint supply);\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                               CONSTRUCTOR\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    address public immutable token;\r\n    address public voter;\r\n    address public team;\r\n    address public artProxy;\r\n\r\n    mapping(uint => Point) public point_history; // epoch -> unsigned point\r\n\r\n    /// @dev Mapping of interface id to bool about whether or not it's supported\r\n    mapping(bytes4 => bool) internal supportedInterfaces;\r\n\r\n    /// @dev ERC165 interface ID of ERC165\r\n    bytes4 internal constant ERC165_INTERFACE_ID = 0x01ffc9a7;\r\n\r\n    /// @dev ERC165 interface ID of ERC721\r\n    bytes4 internal constant ERC721_INTERFACE_ID = 0x80ac58cd;\r\n\r\n    /// @dev ERC165 interface ID of ERC721Metadata\r\n    bytes4 internal constant ERC721_METADATA_INTERFACE_ID = 0x5b5e139f;\r\n\r\n    /// @dev Current count of token\r\n    uint internal tokenId;\r\n\r\n    /// @notice Contract constructor\r\n    /// @param token_addr `THENA` token address\r\n    constructor(address token_addr, address art_proxy) {\r\n        token = token_addr;\r\n        voter = msg.sender;\r\n        team = msg.sender;\r\n        artProxy = art_proxy;\r\n\r\n        point_history[0].blk = block.number;\r\n        point_history[0].ts = block.timestamp;\r\n\r\n        supportedInterfaces[ERC165_INTERFACE_ID] = true;\r\n        supportedInterfaces[ERC721_INTERFACE_ID] = true;\r\n        supportedInterfaces[ERC721_METADATA_INTERFACE_ID] = true;\r\n\r\n        // mint-ish\r\n        emit Transfer(address(0), address(this), tokenId);\r\n        // burn-ish\r\n        emit Transfer(address(this), address(0), tokenId);\r\n    }\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                                MODIFIERS\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    /// @dev reentrancy guard\r\n    uint8 internal constant _not_entered = 1;\r\n    uint8 internal constant _entered = 2;\r\n    uint8 internal _entered_state = 1;\r\n    modifier nonreentrant() {\r\n        require(_entered_state == _not_entered);\r\n        _entered_state = _entered;\r\n        _;\r\n        _entered_state = _not_entered;\r\n    }\r\n\r\n    /*///////////////////////////////////////////////////////////////\r\n                             METADATA STORAGE\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    string constant public name = \"veThena\";\r\n    string constant public symbol = \"veTHE\";\r\n    string constant public version = \"1.0.0\";\r\n    uint8 constant public decimals = 18;\r\n\r\n    function setTeam(address _team) external {\r\n        require(msg.sender == team);\r\n        team = _team;\r\n    }\r\n\r\n    function setArtProxy(address _proxy) external {\r\n        require(msg.sender == team);\r\n        artProxy = _proxy;\r\n    }\r\n\r\n    /// @dev Returns current token URI metadata\r\n    /// @param _tokenId Token ID to fetch URI for.\r\n    function tokenURI(uint _tokenId) external view returns (string memory) {\r\n        require(idToOwner[_tokenId] != address(0), \"Query for nonexistent token\");\r\n        LockedBalance memory _locked = locked[_tokenId];\r\n        return IVeArtProxy(artProxy)._tokenURI(_tokenId,_balanceOfNFT(_tokenId, block.timestamp),_locked.end,uint(int256(_locked.amount)));\r\n    }\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                      ERC721 BALANCE/OWNER STORAGE\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    /// @dev Mapping from NFT ID to the address that owns it.\r\n    mapping(uint => address) internal idToOwner;\r\n\r\n    /// @dev Mapping from owner address to count of his tokens.\r\n    mapping(address => uint) internal ownerToNFTokenCount;\r\n\r\n    /// @dev Returns the address of the owner of the NFT.\r\n    /// @param _tokenId The identifier for an NFT.\r\n    function ownerOf(uint _tokenId) public view returns (address) {\r\n        return idToOwner[_tokenId];\r\n    }\r\n\r\n    /// @dev Returns the number of NFTs owned by `_owner`.\r\n    ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.\r\n    /// @param _owner Address for whom to query the balance.\r\n    function _balance(address _owner) internal view returns (uint) {\r\n        return ownerToNFTokenCount[_owner];\r\n    }\r\n\r\n    /// @dev Returns the number of NFTs owned by `_owner`.\r\n    ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.\r\n    /// @param _owner Address for whom to query the balance.\r\n    function balanceOf(address _owner) external view returns (uint) {\r\n        return _balance(_owner);\r\n    }\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                         ERC721 APPROVAL STORAGE\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    /// @dev Mapping from NFT ID to approved address.\r\n    mapping(uint => address) internal idToApprovals;\r\n\r\n    /// @dev Mapping from owner address to mapping of operator addresses.\r\n    mapping(address => mapping(address => bool)) internal ownerToOperators;\r\n\r\n    mapping(uint => uint) public ownership_change;\r\n\r\n    /// @dev Get the approved address for a single NFT.\r\n    /// @param _tokenId ID of the NFT to query the approval of.\r\n    function getApproved(uint _tokenId) external view returns (address) {\r\n        return idToApprovals[_tokenId];\r\n    }\r\n\r\n    /// @dev Checks if `_operator` is an approved operator for `_owner`.\r\n    /// @param _owner The address that owns the NFTs.\r\n    /// @param _operator The address that acts on behalf of the owner.\r\n    function isApprovedForAll(address _owner, address _operator) external view returns (bool) {\r\n        return (ownerToOperators[_owner])[_operator];\r\n    }\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                              ERC721 LOGIC\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    /// @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address.\r\n    ///      Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.\r\n    ///      Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP)\r\n    ///      Throws if `_approved` is the current owner. (NOTE: This is not written the EIP)\r\n    /// @param _approved Address to be approved for the given NFT ID.\r\n    /// @param _tokenId ID of the token to be approved.\r\n    function approve(address _approved, uint _tokenId) public {\r\n        address owner = idToOwner[_tokenId];\r\n        // Throws if `_tokenId` is not a valid NFT\r\n        require(owner != address(0));\r\n        // Throws if `_approved` is the current owner\r\n        require(_approved != owner);\r\n        // Check requirements\r\n        bool senderIsOwner = (idToOwner[_tokenId] == msg.sender);\r\n        bool senderIsApprovedForAll = (ownerToOperators[owner])[msg.sender];\r\n        require(senderIsOwner || senderIsApprovedForAll);\r\n        // Set the approval\r\n        idToApprovals[_tokenId] = _approved;\r\n        emit Approval(owner, _approved, _tokenId);\r\n    }\r\n\r\n    /// @dev Enables or disables approval for a third party (\"operator\") to manage all of\r\n    ///      `msg.sender`'s assets. It also emits the ApprovalForAll event.\r\n    ///      Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP)\r\n    /// @notice This works even if sender doesn't own any tokens at the time.\r\n    /// @param _operator Address to add to the set of authorized operators.\r\n    /// @param _approved True if the operators is approved, false to revoke approval.\r\n    function setApprovalForAll(address _operator, bool _approved) external {\r\n        // Throws if `_operator` is the `msg.sender`\r\n        assert(_operator != msg.sender);\r\n        ownerToOperators[msg.sender][_operator] = _approved;\r\n        emit ApprovalForAll(msg.sender, _operator, _approved);\r\n    }\r\n\r\n    /* TRANSFER FUNCTIONS */\r\n    /// @dev Clear an approval of a given address\r\n    ///      Throws if `_owner` is not the current owner.\r\n    function _clearApproval(address _owner, uint _tokenId) internal {\r\n        // Throws if `_owner` is not the current owner\r\n        assert(idToOwner[_tokenId] == _owner);\r\n        if (idToApprovals[_tokenId] != address(0)) {\r\n            // Reset approvals\r\n            idToApprovals[_tokenId] = address(0);\r\n        }\r\n    }\r\n\r\n    /// @dev Returns whether the given spender can transfer a given token ID\r\n    /// @param _spender address of the spender to query\r\n    /// @param _tokenId uint ID of the token to be transferred\r\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\r\n    function _isApprovedOrOwner(address _spender, uint _tokenId) internal view returns (bool) {\r\n        address owner = idToOwner[_tokenId];\r\n        bool spenderIsOwner = owner == _spender;\r\n        bool spenderIsApproved = _spender == idToApprovals[_tokenId];\r\n        bool spenderIsApprovedForAll = (ownerToOperators[owner])[_spender];\r\n        return spenderIsOwner || spenderIsApproved || spenderIsApprovedForAll;\r\n    }\r\n\r\n    function isApprovedOrOwner(address _spender, uint _tokenId) external view returns (bool) {\r\n        return _isApprovedOrOwner(_spender, _tokenId);\r\n    }\r\n\r\n    /// @dev Exeute transfer of a NFT.\r\n    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the approved\r\n    ///      address for this NFT. (NOTE: `msg.sender` not allowed in internal function so pass `_sender`.)\r\n    ///      Throws if `_to` is the zero address.\r\n    ///      Throws if `_from` is not the current owner.\r\n    ///      Throws if `_tokenId` is not a valid NFT.\r\n    function _transferFrom(\r\n        address _from,\r\n        address _to,\r\n        uint _tokenId,\r\n        address _sender\r\n    ) internal {\r\n        require(attachments[_tokenId] == 0 && !voted[_tokenId], \"attached\");\r\n        // Check requirements\r\n        require(_isApprovedOrOwner(_sender, _tokenId));\r\n        // Clear approval. Throws if `_from` is not the current owner\r\n        _clearApproval(_from, _tokenId);\r\n        // Remove NFT. Throws if `_tokenId` is not a valid NFT\r\n        _removeTokenFrom(_from, _tokenId);\r\n        // auto re-delegate\r\n        _moveTokenDelegates(delegates(_from), delegates(_to), _tokenId);\r\n        // Add NFT\r\n        _addTokenTo(_to, _tokenId);\r\n        // Set the block of ownership transfer (for Flash NFT protection)\r\n        ownership_change[_tokenId] = block.number;\r\n        // Log the transfer\r\n        emit Transfer(_from, _to, _tokenId);\r\n    }\r\n\r\n    /// @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved address for this NFT.\r\n    ///      Throws if `_from` is not the current owner.\r\n    ///      Throws if `_to` is the zero address.\r\n    ///      Throws if `_tokenId` is not a valid NFT.\r\n    /// @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else\r\n    ///        they maybe be permanently lost.\r\n    /// @param _from The current owner of the NFT.\r\n    /// @param _to The new owner.\r\n    /// @param _tokenId The NFT to transfer.\r\n    function transferFrom(\r\n        address _from,\r\n        address _to,\r\n        uint _tokenId\r\n    ) external {\r\n        _transferFrom(_from, _to, _tokenId, msg.sender);\r\n    }\r\n\r\n    /// @dev Transfers the ownership of an NFT from one address to another address.\r\n    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the\r\n    ///      approved address for this NFT.\r\n    ///      Throws if `_from` is not the current owner.\r\n    ///      Throws if `_to` is the zero address.\r\n    ///      Throws if `_tokenId` is not a valid NFT.\r\n    ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if\r\n    ///      the return value is not `bytes4(keccak256(\"onERC721Received(address,address,uint,bytes)\"))`.\r\n    /// @param _from The current owner of the NFT.\r\n    /// @param _to The new owner.\r\n    /// @param _tokenId The NFT to transfer.\r\n    function safeTransferFrom(\r\n        address _from,\r\n        address _to,\r\n        uint _tokenId\r\n    ) external {\r\n        safeTransferFrom(_from, _to, _tokenId, \"\");\r\n    }\r\n\r\n    function _isContract(address account) internal view returns (bool) {\r\n        // This method relies on extcodesize, which returns 0 for contracts in\r\n        // construction, since the code is only stored at the end of the\r\n        // constructor execution.\r\n        uint size;\r\n        assembly {\r\n            size := extcodesize(account)\r\n        }\r\n        return size > 0;\r\n    }\r\n\r\n    /// @dev Transfers the ownership of an NFT from one address to another address.\r\n    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the\r\n    ///      approved address for this NFT.\r\n    ///      Throws if `_from` is not the current owner.\r\n    ///      Throws if `_to` is the zero address.\r\n    ///      Throws if `_tokenId` is not a valid NFT.\r\n    ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if\r\n    ///      the return value is not `bytes4(keccak256(\"onERC721Received(address,address,uint,bytes)\"))`.\r\n    /// @param _from The current owner of the NFT.\r\n    /// @param _to The new owner.\r\n    /// @param _tokenId The NFT to transfer.\r\n    /// @param _data Additional data with no specified format, sent in call to `_to`.\r\n    function safeTransferFrom(\r\n        address _from,\r\n        address _to,\r\n        uint _tokenId,\r\n        bytes memory _data\r\n    ) public {\r\n        _transferFrom(_from, _to, _tokenId, msg.sender);\r\n\r\n        if (_isContract(_to)) {\r\n            // Throws if transfer destination is a contract which does not implement 'onERC721Received'\r\n            try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4 response) {\r\n                if (response != IERC721Receiver(_to).onERC721Received.selector) {\r\n                    revert(\"ERC721: ERC721Receiver rejected tokens\");\r\n                }\r\n            } catch (bytes memory reason) {\r\n                if (reason.length == 0) {\r\n                    revert('ERC721: transfer to non ERC721Receiver implementer');\r\n                } else {\r\n                    assembly {\r\n                        revert(add(32, reason), mload(reason))\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                              ERC165 LOGIC\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    /// @dev Interface identification is specified in ERC-165.\r\n    /// @param _interfaceID Id of the interface\r\n    function supportsInterface(bytes4 _interfaceID) external view returns (bool) {\r\n        return supportedInterfaces[_interfaceID];\r\n    }\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                        INTERNAL MINT/BURN LOGIC\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    /// @dev Mapping from owner address to mapping of index to tokenIds\r\n    mapping(address => mapping(uint => uint)) internal ownerToNFTokenIdList;\r\n\r\n    /// @dev Mapping from NFT ID to index of owner\r\n    mapping(uint => uint) internal tokenToOwnerIndex;\r\n\r\n    /// @dev  Get token by index\r\n    function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint) {\r\n        return ownerToNFTokenIdList[_owner][_tokenIndex];\r\n    }\r\n\r\n    /// @dev Add a NFT to an index mapping to a given address\r\n    /// @param _to address of the receiver\r\n    /// @param _tokenId uint ID Of the token to be added\r\n    function _addTokenToOwnerList(address _to, uint _tokenId) internal {\r\n        uint current_count = _balance(_to);\r\n\r\n        ownerToNFTokenIdList[_to][current_count] = _tokenId;\r\n        tokenToOwnerIndex[_tokenId] = current_count;\r\n    }\r\n\r\n    /// @dev Add a NFT to a given address\r\n    ///      Throws if `_tokenId` is owned by someone.\r\n    function _addTokenTo(address _to, uint _tokenId) internal {\r\n        // Throws if `_tokenId` is owned by someone\r\n        assert(idToOwner[_tokenId] == address(0));\r\n        // Change the owner\r\n        idToOwner[_tokenId] = _to;\r\n        // Update owner token index tracking\r\n        _addTokenToOwnerList(_to, _tokenId);\r\n        // Change count tracking\r\n        ownerToNFTokenCount[_to] += 1;\r\n    }\r\n\r\n    /// @dev Function to mint tokens\r\n    ///      Throws if `_to` is zero address.\r\n    ///      Throws if `_tokenId` is owned by someone.\r\n    /// @param _to The address that will receive the minted tokens.\r\n    /// @param _tokenId The token id to mint.\r\n    /// @return A boolean that indicates if the operation was successful.\r\n    function _mint(address _to, uint _tokenId) internal returns (bool) {\r\n        // Throws if `_to` is zero address\r\n        assert(_to != address(0));\r\n        // checkpoint for gov\r\n        _moveTokenDelegates(address(0), delegates(_to), _tokenId);\r\n        // Add NFT. Throws if `_tokenId` is owned by someone\r\n        _addTokenTo(_to, _tokenId);\r\n        emit Transfer(address(0), _to, _tokenId);\r\n        return true;\r\n    }\r\n\r\n    /// @dev Remove a NFT from an index mapping to a given address\r\n    /// @param _from address of the sender\r\n    /// @param _tokenId uint ID Of the token to be removed\r\n    function _removeTokenFromOwnerList(address _from, uint _tokenId) internal {\r\n        // Delete\r\n        uint current_count = _balance(_from) - 1;\r\n        uint current_index = tokenToOwnerIndex[_tokenId];\r\n\r\n        if (current_count == current_index) {\r\n            // update ownerToNFTokenIdList\r\n            ownerToNFTokenIdList[_from][current_count] = 0;\r\n            // update tokenToOwnerIndex\r\n            tokenToOwnerIndex[_tokenId] = 0;\r\n        } else {\r\n            uint lastTokenId = ownerToNFTokenIdList[_from][current_count];\r\n\r\n            // Add\r\n            // update ownerToNFTokenIdList\r\n            ownerToNFTokenIdList[_from][current_index] = lastTokenId;\r\n            // update tokenToOwnerIndex\r\n            tokenToOwnerIndex[lastTokenId] = current_index;\r\n\r\n            // Delete\r\n            // update ownerToNFTokenIdList\r\n            ownerToNFTokenIdList[_from][current_count] = 0;\r\n            // update tokenToOwnerIndex\r\n            tokenToOwnerIndex[_tokenId] = 0;\r\n        }\r\n    }\r\n\r\n    /// @dev Remove a NFT from a given address\r\n    ///      Throws if `_from` is not the current owner.\r\n    function _removeTokenFrom(address _from, uint _tokenId) internal {\r\n        // Throws if `_from` is not the current owner\r\n        assert(idToOwner[_tokenId] == _from);\r\n        // Change the owner\r\n        idToOwner[_tokenId] = address(0);\r\n        // Update owner token index tracking\r\n        _removeTokenFromOwnerList(_from, _tokenId);\r\n        // Change count tracking\r\n        ownerToNFTokenCount[_from] -= 1;\r\n    }\r\n\r\n    function _burn(uint _tokenId) internal {\r\n        require(_isApprovedOrOwner(msg.sender, _tokenId), \"caller is not owner nor approved\");\r\n\r\n        address owner = ownerOf(_tokenId);\r\n\r\n        // Clear approval\r\n        approve(address(0), _tokenId);\r\n        // checkpoint for gov\r\n        _moveTokenDelegates(delegates(owner), address(0), _tokenId);\r\n        // Remove token\r\n        //_removeTokenFrom(msg.sender, _tokenId);\r\n        _removeTokenFrom(owner, _tokenId);\r\n        \r\n        emit Transfer(owner, address(0), _tokenId);\r\n    }\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                             ESCROW STORAGE\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    mapping(uint => uint) public user_point_epoch;\r\n    mapping(uint => Point[1000000000]) public user_point_history; // user -> Point[user_epoch]\r\n    mapping(uint => LockedBalance) public locked;\r\n    uint public epoch;\r\n    mapping(uint => int128) public slope_changes; // time -> signed slope change\r\n    uint public supply;\r\n\r\n    uint internal constant WEEK = 1 weeks;\r\n    uint internal constant MAXTIME = 2 * 365 * 86400;\r\n    int128 internal constant iMAXTIME = 2 * 365 * 86400;\r\n    uint internal constant MULTIPLIER = 1 ether;\r\n\r\n    /*//////////////////////////////////////////////////////////////\r\n                              ESCROW LOGIC\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    /// @notice Get the most recently recorded rate of voting power decrease for `_tokenId`\r\n    /// @param _tokenId token of the NFT\r\n    /// @return Value of the slope\r\n    function get_last_user_slope(uint _tokenId) external view returns (int128) {\r\n        uint uepoch = user_point_epoch[_tokenId];\r\n        return user_point_history[_tokenId][uepoch].slope;\r\n    }\r\n\r\n    /// @notice Get the timestamp for checkpoint `_idx` for `_tokenId`\r\n    /// @param _tokenId token of the NFT\r\n    /// @param _idx User epoch number\r\n    /// @return Epoch time of the checkpoint\r\n    function user_point_history__ts(uint _tokenId, uint _idx) external view returns (uint) {\r\n        return user_point_history[_tokenId][_idx].ts;\r\n    }\r\n\r\n    /// @notice Get timestamp when `_tokenId`'s lock finishes\r\n    /// @param _tokenId User NFT\r\n    /// @return Epoch time of the lock end\r\n    function locked__end(uint _tokenId) external view returns (uint) {\r\n        return locked[_tokenId].end;\r\n    }\r\n\r\n    /// @notice Record global and per-user data to checkpoint\r\n    /// @param _tokenId NFT token ID. No user checkpoint if 0\r\n    /// @param old_locked Pevious locked amount / end lock time for the user\r\n    /// @param new_locked New locked amount / end lock time for the user\r\n    function _checkpoint(\r\n        uint _tokenId,\r\n        LockedBalance memory old_locked,\r\n        LockedBalance memory new_locked\r\n    ) internal {\r\n        Point memory u_old;\r\n        Point memory u_new;\r\n        int128 old_dslope = 0;\r\n        int128 new_dslope = 0;\r\n        uint _epoch = epoch;\r\n\r\n        if (_tokenId != 0) {\r\n            // Calculate slopes and biases\r\n            // Kept at zero when they have to\r\n            if (old_locked.end > block.timestamp && old_locked.amount > 0) {\r\n                u_old.slope = old_locked.amount / iMAXTIME;\r\n                u_old.bias = u_old.slope * int128(int256(old_locked.end - block.timestamp));\r\n            }\r\n            if (new_locked.end > block.timestamp && new_locked.amount > 0) {\r\n                u_new.slope = new_locked.amount / iMAXTIME;\r\n                u_new.bias = u_new.slope * int128(int256(new_locked.end - block.timestamp));\r\n            }\r\n\r\n            // Read values of scheduled changes in the slope\r\n            // old_locked.end can be in the past and in the future\r\n            // new_locked.end can ONLY by in the FUTURE unless everything expired: than zeros\r\n            old_dslope = slope_changes[old_locked.end];\r\n            if (new_locked.end != 0) {\r\n                if (new_locked.end == old_locked.end) {\r\n                    new_dslope = old_dslope;\r\n                } else {\r\n                    new_dslope = slope_changes[new_locked.end];\r\n                }\r\n            }\r\n        }\r\n\r\n        Point memory last_point = Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number});\r\n        if (_epoch > 0) {\r\n            last_point = point_history[_epoch];\r\n        }\r\n        uint last_checkpoint = last_point.ts;\r\n        // initial_last_point is used for extrapolation to calculate block number\r\n        // (approximately, for *At methods) and save them\r\n        // as we cannot figure that out exactly from inside the contract\r\n        Point memory initial_last_point = last_point;\r\n        uint block_slope = 0; // dblock/dt\r\n        if (block.timestamp > last_point.ts) {\r\n            block_slope = (MULTIPLIER * (block.number - last_point.blk)) / (block.timestamp - last_point.ts);\r\n        }\r\n        // If last point is already recorded in this block, slope=0\r\n        // But that's ok b/c we know the block in such case\r\n\r\n        // Go over weeks to fill history and calculate what the current point is\r\n        {\r\n            uint t_i = (last_checkpoint / WEEK) * WEEK;\r\n            for (uint i = 0; i < 255; ++i) {\r\n                // Hopefully it won't happen that this won't get used in 5 years!\r\n                // If it does, users will be able to withdraw but vote weight will be broken\r\n                t_i += WEEK;\r\n                int128 d_slope = 0;\r\n                if (t_i > block.timestamp) {\r\n                    t_i = block.timestamp;\r\n                } else {\r\n                    d_slope = slope_changes[t_i];\r\n                }\r\n                last_point.bias -= last_point.slope * int128(int256(t_i - last_checkpoint));\r\n                last_point.slope += d_slope;\r\n                if (last_point.bias < 0) {\r\n                    // This can happen\r\n                    last_point.bias = 0;\r\n                }\r\n                if (last_point.slope < 0) {\r\n                    // This cannot happen - just in case\r\n                    last_point.slope = 0;\r\n                }\r\n                last_checkpoint = t_i;\r\n                last_point.ts = t_i;\r\n                last_point.blk = initial_last_point.blk + (block_slope * (t_i - initial_last_point.ts)) / MULTIPLIER;\r\n                _epoch += 1;\r\n                if (t_i == block.timestamp) {\r\n                    last_point.blk = block.number;\r\n                    break;\r\n                } else {\r\n                    point_history[_epoch] = last_point;\r\n                }\r\n            }\r\n        }\r\n\r\n        epoch = _epoch;\r\n        // Now point_history is filled until t=now\r\n\r\n        if (_tokenId != 0) {\r\n            // If last point was in this block, the slope change has been applied already\r\n            // But in such case we have 0 slope(s)\r\n            last_point.slope += (u_new.slope - u_old.slope);\r\n            last_point.bias += (u_new.bias - u_old.bias);\r\n            if (last_point.slope < 0) {\r\n                last_point.slope = 0;\r\n            }\r\n            if (last_point.bias < 0) {\r\n                last_point.bias = 0;\r\n            }\r\n        }\r\n\r\n        // Record the changed point into history\r\n        point_history[_epoch] = last_point;\r\n\r\n        if (_tokenId != 0) {\r\n            // Schedule the slope changes (slope is going down)\r\n            // We subtract new_user_slope from [new_locked.end]\r\n            // and add old_user_slope to [old_locked.end]\r\n            if (old_locked.end > block.timestamp) {\r\n                // old_dslope was <something> - u_old.slope, so we cancel that\r\n                old_dslope += u_old.slope;\r\n                if (new_locked.end == old_locked.end) {\r\n                    old_dslope -= u_new.slope; // It was a new deposit, not extension\r\n                }\r\n                slope_changes[old_locked.end] = old_dslope;\r\n            }\r\n\r\n            if (new_locked.end > block.timestamp) {\r\n                if (new_locked.end > old_locked.end) {\r\n                    new_dslope -= u_new.slope; // old slope disappeared at this point\r\n                    slope_changes[new_locked.end] = new_dslope;\r\n                }\r\n                // else: we recorded it already in old_dslope\r\n            }\r\n            // Now handle user history\r\n            uint user_epoch = user_point_epoch[_tokenId] + 1;\r\n\r\n            user_point_epoch[_tokenId] = user_epoch;\r\n            u_new.ts = block.timestamp;\r\n            u_new.blk = block.number;\r\n            user_point_history[_tokenId][user_epoch] = u_new;\r\n        }\r\n    }\r\n\r\n    /// @notice Deposit and lock tokens for a user\r\n    /// @param _tokenId NFT that holds lock\r\n    /// @param _value Amount to deposit\r\n    /// @param unlock_time New time when to unlock the tokens, or 0 if unchanged\r\n    /// @param locked_balance Previous locked amount / timestamp\r\n    /// @param deposit_type The type of deposit\r\n    function _deposit_for(\r\n        uint _tokenId,\r\n        uint _value,\r\n        uint unlock_time,\r\n        LockedBalance memory locked_balance,\r\n        DepositType deposit_type\r\n    ) internal {\r\n        LockedBalance memory _locked = locked_balance;\r\n        uint supply_before = supply;\r\n\r\n        supply = supply_before + _value;\r\n        LockedBalance memory old_locked;\r\n        (old_locked.amount, old_locked.end) = (_locked.amount, _locked.end);\r\n        // Adding to existing lock, or if a lock is expired - creating a new one\r\n        _locked.amount += int128(int256(_value));\r\n        if (unlock_time != 0) {\r\n            _locked.end = unlock_time;\r\n        }\r\n        locked[_tokenId] = _locked;\r\n\r\n        // Possibilities:\r\n        // Both old_locked.end could be current or expired (>/< block.timestamp)\r\n        // value == 0 (extend lock) or value > 0 (add to lock or extend lock)\r\n        // _locked.end > block.timestamp (always)\r\n        _checkpoint(_tokenId, old_locked, _locked);\r\n\r\n        address from = msg.sender;\r\n        if (_value != 0 && deposit_type != DepositType.MERGE_TYPE && deposit_type != DepositType.SPLIT_TYPE ) {\r\n            assert(IERC20(token).transferFrom(from, address(this), _value));\r\n        }\r\n\r\n        emit Deposit(from, _tokenId, _value, _locked.end, deposit_type, block.timestamp);\r\n        emit Supply(supply_before, supply_before + _value);\r\n    }\r\n\r\n    function block_number() external view returns (uint) {\r\n        return block.number;\r\n    }\r\n\r\n    /// @notice Record global data to checkpoint\r\n    function checkpoint() external {\r\n        _checkpoint(0, LockedBalance(0, 0), LockedBalance(0, 0));\r\n    }\r\n\r\n    /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock\r\n    /// @dev Anyone (even a smart contract) can deposit for someone else, but\r\n    ///      cannot extend their locktime and deposit for a brand new user\r\n    /// @param _tokenId lock NFT\r\n    /// @param _value Amount to add to user's lock\r\n    function deposit_for(uint _tokenId, uint _value) external nonreentrant {\r\n        LockedBalance memory _locked = locked[_tokenId];\r\n\r\n        require(_value > 0); // dev: need non-zero value\r\n        require(_locked.amount > 0, 'No existing lock found');\r\n        require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');\r\n        _deposit_for(_tokenId, _value, 0, _locked, DepositType.DEPOSIT_FOR_TYPE);\r\n    }\r\n\r\n    /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`\r\n    /// @param _value Amount to deposit\r\n    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)\r\n    /// @param _to Address to deposit\r\n    function _create_lock(uint _value, uint _lock_duration, address _to) internal returns (uint) {\r\n        uint unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // Locktime is rounded down to weeks\r\n\r\n        require(_value > 0); // dev: need non-zero value\r\n        require(unlock_time > block.timestamp, 'Can only lock until time in the future');\r\n        require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 2 years max');\r\n\r\n        ++tokenId;\r\n        uint _tokenId = tokenId;\r\n        _mint(_to, _tokenId);\r\n\r\n        _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.CREATE_LOCK_TYPE);\r\n        return _tokenId;\r\n    }\r\n\r\n    /// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lock_duration`\r\n    /// @param _value Amount to deposit\r\n    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)\r\n    function create_lock(uint _value, uint _lock_duration) external nonreentrant returns (uint) {\r\n        return _create_lock(_value, _lock_duration, msg.sender);\r\n    }\r\n\r\n    /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`\r\n    /// @param _value Amount to deposit\r\n    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)\r\n    /// @param _to Address to deposit\r\n    function create_lock_for(uint _value, uint _lock_duration, address _to) external nonreentrant returns (uint) {\r\n        return _create_lock(_value, _lock_duration, _to);\r\n    }\r\n\r\n    /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time\r\n    /// @param _value Amount of tokens to deposit and add to the lock\r\n    function increase_amount(uint _tokenId, uint _value) external nonreentrant {\r\n        assert(_isApprovedOrOwner(msg.sender, _tokenId));\r\n\r\n        LockedBalance memory _locked = locked[_tokenId];\r\n\r\n        assert(_value > 0); // dev: need non-zero value\r\n        require(_locked.amount > 0, 'No existing lock found');\r\n        require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');\r\n\r\n        _deposit_for(_tokenId, _value, 0, _locked, DepositType.INCREASE_LOCK_AMOUNT);\r\n    }\r\n\r\n    /// @notice Extend the unlock time for `_tokenId`\r\n    /// @param _lock_duration New number of seconds until tokens unlock\r\n    function increase_unlock_time(uint _tokenId, uint _lock_duration) external nonreentrant {\r\n        assert(_isApprovedOrOwner(msg.sender, _tokenId));\r\n\r\n        LockedBalance memory _locked = locked[_tokenId];\r\n        uint unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // Locktime is rounded down to weeks\r\n\r\n        require(_locked.end > block.timestamp, 'Lock expired');\r\n        require(_locked.amount > 0, 'Nothing is locked');\r\n        require(unlock_time > _locked.end, 'Can only increase lock duration');\r\n        require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 2 years max');\r\n\r\n        _deposit_for(_tokenId, 0, unlock_time, _locked, DepositType.INCREASE_UNLOCK_TIME);\r\n    }\r\n\r\n    /// @notice Withdraw all tokens for `_tokenId`\r\n    /// @dev Only possible if the lock has expired\r\n    function withdraw(uint _tokenId) external nonreentrant {\r\n        assert(_isApprovedOrOwner(msg.sender, _tokenId));\r\n        require(attachments[_tokenId] == 0 && !voted[_tokenId], \"attached\");\r\n\r\n        LockedBalance memory _locked = locked[_tokenId];\r\n        require(block.timestamp >= _locked.end, \"The lock didn't expire\");\r\n        uint value = uint(int256(_locked.amount));\r\n\r\n        locked[_tokenId] = LockedBalance(0,0);\r\n        uint supply_before = supply;\r\n        supply = supply_before - value;\r\n\r\n        // old_locked can have either expired <= timestamp or zero end\r\n        // _locked has only 0 end\r\n        // Both can have >= 0 amount\r\n        _checkpoint(_tokenId, _locked, LockedBalance(0,0));\r\n\r\n        assert(IERC20(token).transfer(msg.sender, value));\r\n\r\n        // Burn the NFT\r\n        _burn(_tokenId);\r\n\r\n        emit Withdraw(msg.sender, _tokenId, value, block.timestamp);\r\n        emit Supply(supply_before, supply_before - value);\r\n    }\r\n\r\n    /*///////////////////////////////////////////////////////////////\r\n                           GAUGE VOTING STORAGE\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    // The following ERC20/minime-compatible methods are not real balanceOf and supply!\r\n    // They measure the weights for the purpose of voting, so they don't represent\r\n    // real coins.\r\n\r\n    /// @notice Binary search to estimate timestamp for block number\r\n    /// @param _block Block to find\r\n    /// @param max_epoch Don't go beyond this epoch\r\n    /// @return Approximate timestamp for block\r\n    function _find_block_epoch(uint _block, uint max_epoch) internal view returns (uint) {\r\n        // Binary search\r\n        uint _min = 0;\r\n        uint _max = max_epoch;\r\n        for (uint i = 0; i < 128; ++i) {\r\n            // Will be always enough for 128-bit numbers\r\n            if (_min >= _max) {\r\n                break;\r\n            }\r\n            uint _mid = (_min + _max + 1) / 2;\r\n            if (point_history[_mid].blk <= _block) {\r\n                _min = _mid;\r\n            } else {\r\n                _max = _mid - 1;\r\n            }\r\n        }\r\n        return _min;\r\n    }\r\n\r\n    /// @notice Get the current voting power for `_tokenId`\r\n    /// @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility\r\n    /// @param _tokenId NFT for lock\r\n    /// @param _t Epoch time to return voting power at\r\n    /// @return User voting power\r\n    function _balanceOfNFT(uint _tokenId, uint _t) internal view returns (uint) {\r\n        uint _epoch = user_point_epoch[_tokenId];\r\n        if (_epoch == 0) {\r\n            return 0;\r\n        } else {\r\n            Point memory last_point = user_point_history[_tokenId][_epoch];\r\n            last_point.bias -= last_point.slope * int128(int256(_t) - int256(last_point.ts));\r\n            if (last_point.bias < 0) {\r\n                last_point.bias = 0;\r\n            }\r\n            return uint(int256(last_point.bias));\r\n        }\r\n    }\r\n\r\n    function balanceOfNFT(uint _tokenId) external view returns (uint) {\r\n        if (ownership_change[_tokenId] == block.number) return 0;\r\n        return _balanceOfNFT(_tokenId, block.timestamp);\r\n    }\r\n\r\n    function balanceOfNFTAt(uint _tokenId, uint _t) external view returns (uint) {\r\n        return _balanceOfNFT(_tokenId, _t);\r\n    }\r\n\r\n    /// @notice Measure voting power of `_tokenId` at block height `_block`\r\n    /// @dev Adheres to MiniMe `balanceOfAt` interface: https://github.com/Giveth/minime\r\n    /// @param _tokenId User's wallet NFT\r\n    /// @param _block Block to calculate the voting power at\r\n    /// @return Voting power\r\n    function _balanceOfAtNFT(uint _tokenId, uint _block) internal view returns (uint) {\r\n        // Copying and pasting totalSupply code because Vyper cannot pass by\r\n        // reference yet\r\n        assert(_block <= block.number);\r\n\r\n        // Binary search\r\n        uint _min = 0;\r\n        uint _max = user_point_epoch[_tokenId];\r\n        for (uint i = 0; i < 128; ++i) {\r\n            // Will be always enough for 128-bit numbers\r\n            if (_min >= _max) {\r\n                break;\r\n            }\r\n            uint _mid = (_min + _max + 1) / 2;\r\n            if (user_point_history[_tokenId][_mid].blk <= _block) {\r\n                _min = _mid;\r\n            } else {\r\n                _max = _mid - 1;\r\n            }\r\n        }\r\n\r\n        Point memory upoint = user_point_history[_tokenId][_min];\r\n\r\n        uint max_epoch = epoch;\r\n        uint _epoch = _find_block_epoch(_block, max_epoch);\r\n        Point memory point_0 = point_history[_epoch];\r\n        uint d_block = 0;\r\n        uint d_t = 0;\r\n        if (_epoch < max_epoch) {\r\n            Point memory point_1 = point_history[_epoch + 1];\r\n            d_block = point_1.blk - point_0.blk;\r\n            d_t = point_1.ts - point_0.ts;\r\n        } else {\r\n            d_block = block.number - point_0.blk;\r\n            d_t = block.timestamp - point_0.ts;\r\n        }\r\n        uint block_time = point_0.ts;\r\n        if (d_block != 0) {\r\n            block_time += (d_t * (_block - point_0.blk)) / d_block;\r\n        }\r\n\r\n        upoint.bias -= upoint.slope * int128(int256(block_time - upoint.ts));\r\n        if (upoint.bias >= 0) {\r\n            return uint(uint128(upoint.bias));\r\n        } else {\r\n            return 0;\r\n        }\r\n    }\r\n\r\n    function balanceOfAtNFT(uint _tokenId, uint _block) external view returns (uint) {\r\n        return _balanceOfAtNFT(_tokenId, _block);\r\n    }\r\n\r\n    /// @notice Calculate total voting power at some point in the past\r\n    /// @param _block Block to calculate the total voting power at\r\n    /// @return Total voting power at `_block`\r\n    function totalSupplyAt(uint _block) external view returns (uint) {\r\n        assert(_block <= block.number);\r\n        uint _epoch = epoch;\r\n        uint target_epoch = _find_block_epoch(_block, _epoch);\r\n\r\n        Point memory point = point_history[target_epoch];\r\n        uint dt = 0;\r\n        if (target_epoch < _epoch) {\r\n            Point memory point_next = point_history[target_epoch + 1];\r\n            if (point.blk != point_next.blk) {\r\n                dt = ((_block - point.blk) * (point_next.ts - point.ts)) / (point_next.blk - point.blk);\r\n            }\r\n        } else {\r\n            if (point.blk != block.number) {\r\n                dt = ((_block - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk);\r\n            }\r\n        }\r\n        // Now dt contains info on how far are we beyond point\r\n        return _supply_at(point, point.ts + dt);\r\n    }\r\n    /// @notice Calculate total voting power at some point in the past\r\n    /// @param point The point (bias/slope) to start search from\r\n    /// @param t Time to calculate the total voting power at\r\n    /// @return Total voting power at that time\r\n    function _supply_at(Point memory point, uint t) internal view returns (uint) {\r\n        Point memory last_point = point;\r\n        uint t_i = (last_point.ts / WEEK) * WEEK;\r\n        for (uint i = 0; i < 255; ++i) {\r\n            t_i += WEEK;\r\n            int128 d_slope = 0;\r\n            if (t_i > t) {\r\n                t_i = t;\r\n            } else {\r\n                d_slope = slope_changes[t_i];\r\n            }\r\n            last_point.bias -= last_point.slope * int128(int256(t_i - last_point.ts));\r\n            if (t_i == t) {\r\n                break;\r\n            }\r\n            last_point.slope += d_slope;\r\n            last_point.ts = t_i;\r\n        }\r\n\r\n        if (last_point.bias < 0) {\r\n            last_point.bias = 0;\r\n        }\r\n        return uint(uint128(last_point.bias));\r\n    }\r\n\r\n    function totalSupply() external view returns (uint) {\r\n        return totalSupplyAtT(block.timestamp);\r\n    }\r\n\r\n    /// @notice Calculate total voting power\r\n    /// @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility\r\n    /// @return Total voting power\r\n    function totalSupplyAtT(uint t) public view returns (uint) {\r\n        uint _epoch = epoch;\r\n        Point memory last_point = point_history[_epoch];\r\n        return _supply_at(last_point, t);\r\n    }\r\n\r\n    /*///////////////////////////////////////////////////////////////\r\n                            GAUGE VOTING LOGIC\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    mapping(uint => uint) public attachments;\r\n    mapping(uint => bool) public voted;\r\n\r\n    function setVoter(address _voter) external {\r\n        require(msg.sender == team);\r\n        voter = _voter;\r\n    }\r\n\r\n    function voting(uint _tokenId) external {\r\n        require(msg.sender == voter);\r\n        voted[_tokenId] = true;\r\n    }\r\n\r\n    function abstain(uint _tokenId) external {\r\n        require(msg.sender == voter);\r\n        voted[_tokenId] = false;\r\n    }\r\n\r\n    function attach(uint _tokenId) external {\r\n        require(msg.sender == voter);\r\n        attachments[_tokenId] = attachments[_tokenId] + 1;\r\n    }\r\n\r\n    function detach(uint _tokenId) external {\r\n        require(msg.sender == voter);\r\n        attachments[_tokenId] = attachments[_tokenId] - 1;\r\n    }\r\n\r\n    function merge(uint _from, uint _to) external {\r\n        require(attachments[_from] == 0 && !voted[_from], \"attached\");\r\n        require(_from != _to);\r\n        require(_isApprovedOrOwner(msg.sender, _from));\r\n        require(_isApprovedOrOwner(msg.sender, _to));\r\n\r\n        LockedBalance memory _locked0 = locked[_from];\r\n        LockedBalance memory _locked1 = locked[_to];\r\n        uint value0 = uint(int256(_locked0.amount));\r\n        uint end = _locked0.end >= _locked1.end ? _locked0.end : _locked1.end;\r\n\r\n        locked[_from] = LockedBalance(0, 0);\r\n        _checkpoint(_from, _locked0, LockedBalance(0, 0));\r\n        _burn(_from);\r\n        _deposit_for(_to, value0, end, _locked1, DepositType.MERGE_TYPE);\r\n    }\r\n\r\n\r\n    /**\r\n     * @notice split NFT into multiple\r\n     * @param amounts   % of split\r\n     * @param _tokenId  NFTs ID\r\n     */\r\n    function split(uint[] memory amounts, uint _tokenId) external {\r\n        \r\n        // check permission and vote\r\n        require(attachments[_tokenId] == 0 && !voted[_tokenId], \"attached\");\r\n        require(_isApprovedOrOwner(msg.sender, _tokenId));\r\n\r\n        // save old data and totalWeight\r\n        address _to = idToOwner[_tokenId];\r\n        LockedBalance memory _locked = locked[_tokenId];\r\n        uint end = _locked.end;\r\n        uint value = uint(int256(_locked.amount));\r\n        require(value > 0); // dev: need non-zero value\r\n        \r\n        // reset supply, _deposit_for increase it\r\n        supply = supply - value;\r\n\r\n        uint i;\r\n        uint totalWeight = 0;\r\n        for(i = 0; i < amounts.length; i++){\r\n            totalWeight += amounts[i];\r\n        }\r\n\r\n        // remove old data\r\n        locked[_tokenId] = LockedBalance(0, 0);\r\n        _checkpoint(_tokenId, _locked, LockedBalance(0, 0));\r\n        _burn(_tokenId);\r\n\r\n        // save end\r\n        uint unlock_time = end;\r\n        require(unlock_time > block.timestamp, 'Can only lock until time in the future');\r\n        require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 2 years max');\r\n        \r\n        // mint \r\n        uint _value = 0;\r\n        for(i = 0; i < amounts.length; i++){   \r\n            ++tokenId;\r\n            _tokenId = tokenId;\r\n            _mint(_to, _tokenId);\r\n            _value = value * amounts[i] / totalWeight;\r\n            _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.SPLIT_TYPE);\r\n        }     \r\n\r\n    }\r\n\r\n    /*///////////////////////////////////////////////////////////////\r\n                            DAO VOTING STORAGE\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    /// @notice The EIP-712 typehash for the contract's domain\r\n    bytes32 public constant DOMAIN_TYPEHASH = keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\r\n\r\n    /// @notice The EIP-712 typehash for the delegation struct used by the contract\r\n    bytes32 public constant DELEGATION_TYPEHASH = keccak256(\"Delegation(address delegatee,uint256 nonce,uint256 expiry)\");\r\n\r\n    /// @notice A record of each accounts delegate\r\n    mapping(address => address) private _delegates;\r\n    uint public constant MAX_DELEGATES = 1024; // avoid too much gas\r\n\r\n    /// @notice A record of delegated token checkpoints for each account, by index\r\n    mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;\r\n\r\n    /// @notice The number of checkpoints for each account\r\n    mapping(address => uint32) public numCheckpoints;\r\n\r\n    /// @notice A record of states for signing / validating signatures\r\n    mapping(address => uint) public nonces;\r\n\r\n    /**\r\n     * @notice Overrides the standard `Comp.sol` delegates mapping to return\r\n     * the delegator's own address if they haven't delegated.\r\n     * This avoids having to delegate to oneself.\r\n     */\r\n    function delegates(address delegator) public view returns (address) {\r\n        address current = _delegates[delegator];\r\n        return current == address(0) ? delegator : current;\r\n    }\r\n\r\n    /**\r\n     * @notice Gets the current votes balance for `account`\r\n     * @param account The address to get votes balance\r\n     * @return The number of current votes for `account`\r\n     */\r\n    function getVotes(address account) external view returns (uint) {\r\n        uint32 nCheckpoints = numCheckpoints[account];\r\n        if (nCheckpoints == 0) {\r\n            return 0;\r\n        }\r\n        uint[] storage _tokenIds = checkpoints[account][nCheckpoints - 1].tokenIds;\r\n        uint votes = 0;\r\n        for (uint i = 0; i < _tokenIds.length; i++) {\r\n            uint tId = _tokenIds[i];\r\n            votes = votes + _balanceOfNFT(tId, block.timestamp);\r\n        }\r\n        return votes;\r\n    }\r\n\r\n    function getPastVotesIndex(address account, uint timestamp) public view returns (uint32) {\r\n        uint32 nCheckpoints = numCheckpoints[account];\r\n        if (nCheckpoints == 0) {\r\n            return 0;\r\n        }\r\n        // First check most recent balance\r\n        if (checkpoints[account][nCheckpoints - 1].timestamp <= timestamp) {\r\n            return (nCheckpoints - 1);\r\n        }\r\n\r\n        // Next check implicit zero balance\r\n        if (checkpoints[account][0].timestamp > timestamp) {\r\n            return 0;\r\n        }\r\n\r\n        uint32 lower = 0;\r\n        uint32 upper = nCheckpoints - 1;\r\n        while (upper > lower) {\r\n            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow\r\n            Checkpoint storage cp = checkpoints[account][center];\r\n            if (cp.timestamp == timestamp) {\r\n                return center;\r\n            } else if (cp.timestamp < timestamp) {\r\n                lower = center;\r\n            } else {\r\n                upper = center - 1;\r\n            }\r\n        }\r\n        return lower;\r\n    }\r\n\r\n    function getPastVotes(address account, uint timestamp)\r\n        public\r\n        view\r\n        returns (uint)\r\n    {\r\n        uint32 _checkIndex = getPastVotesIndex(account, timestamp);\r\n        // Sum votes\r\n        uint[] storage _tokenIds = checkpoints[account][_checkIndex].tokenIds;\r\n        uint votes = 0;\r\n        for (uint i = 0; i < _tokenIds.length; i++) {\r\n            uint tId = _tokenIds[i];\r\n            // Use the provided input timestamp here to get the right decay\r\n            votes = votes + _balanceOfNFT(tId, timestamp);\r\n        }\r\n        return votes;\r\n    }\r\n\r\n    function getPastTotalSupply(uint256 timestamp) external view returns (uint) {\r\n        return totalSupplyAtT(timestamp);\r\n    }\r\n\r\n    /*///////////////////////////////////////////////////////////////\r\n                             DAO VOTING LOGIC\r\n    //////////////////////////////////////////////////////////////*/\r\n\r\n    function _moveTokenDelegates(\r\n        address srcRep,\r\n        address dstRep,\r\n        uint _tokenId\r\n    ) internal {\r\n        if (srcRep != dstRep && _tokenId > 0) {\r\n            if (srcRep != address(0)) {\r\n                uint32 srcRepNum = numCheckpoints[srcRep];\r\n                uint[] storage srcRepOld = srcRepNum > 0\r\n                    ? checkpoints[srcRep][srcRepNum - 1].tokenIds\r\n                    : checkpoints[srcRep][0].tokenIds;\r\n                uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);\r\n                uint[] storage srcRepNew = checkpoints[srcRep][\r\n                    nextSrcRepNum\r\n                ].tokenIds;\r\n                // All the same except _tokenId\r\n                for (uint i = 0; i < srcRepOld.length; i++) {\r\n                    uint tId = srcRepOld[i];\r\n                    if (tId != _tokenId) {\r\n                        srcRepNew.push(tId);\r\n                    }\r\n                }\r\n\r\n                numCheckpoints[srcRep] = srcRepNum + 1;\r\n            }\r\n\r\n            if (dstRep != address(0)) {\r\n                uint32 dstRepNum = numCheckpoints[dstRep];\r\n                uint[] storage dstRepOld = dstRepNum > 0\r\n                    ? checkpoints[dstRep][dstRepNum - 1].tokenIds\r\n                    : checkpoints[dstRep][0].tokenIds;\r\n                uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);\r\n                uint[] storage dstRepNew = checkpoints[dstRep][\r\n                    nextDstRepNum\r\n                ].tokenIds;\r\n                // All the same plus _tokenId\r\n                require(\r\n                    dstRepOld.length + 1 <= MAX_DELEGATES,\r\n                    \"dstRep would have too many tokenIds\"\r\n                );\r\n                for (uint i = 0; i < dstRepOld.length; i++) {\r\n                    uint tId = dstRepOld[i];\r\n                    dstRepNew.push(tId);\r\n                }\r\n                dstRepNew.push(_tokenId);\r\n\r\n                numCheckpoints[dstRep] = dstRepNum + 1;\r\n            }\r\n        }\r\n    }\r\n\r\n    function _findWhatCheckpointToWrite(address account)\r\n        internal\r\n        view\r\n        returns (uint32)\r\n    {\r\n        uint _timestamp = block.timestamp;\r\n        uint32 _nCheckPoints = numCheckpoints[account];\r\n\r\n        if (\r\n            _nCheckPoints > 0 &&\r\n            checkpoints[account][_nCheckPoints - 1].timestamp == _timestamp\r\n        ) {\r\n            return _nCheckPoints - 1;\r\n        } else {\r\n            return _nCheckPoints;\r\n        }\r\n    }\r\n\r\n    function _moveAllDelegates(\r\n        address owner,\r\n        address srcRep,\r\n        address dstRep\r\n    ) internal {\r\n        // You can only redelegate what you own\r\n        if (srcRep != dstRep) {\r\n            if (srcRep != address(0)) {\r\n                uint32 srcRepNum = numCheckpoints[srcRep];\r\n                uint[] storage srcRepOld = srcRepNum > 0\r\n                    ? checkpoints[srcRep][srcRepNum - 1].tokenIds\r\n                    : checkpoints[srcRep][0].tokenIds;\r\n                uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);\r\n                uint[] storage srcRepNew = checkpoints[srcRep][\r\n                    nextSrcRepNum\r\n                ].tokenIds;\r\n                // All the same except what owner owns\r\n                for (uint i = 0; i < srcRepOld.length; i++) {\r\n                    uint tId = srcRepOld[i];\r\n                    if (idToOwner[tId] != owner) {\r\n                        srcRepNew.push(tId);\r\n                    }\r\n                }\r\n\r\n                numCheckpoints[srcRep] = srcRepNum + 1;\r\n            }\r\n\r\n            if (dstRep != address(0)) {\r\n                uint32 dstRepNum = numCheckpoints[dstRep];\r\n                uint[] storage dstRepOld = dstRepNum > 0\r\n                    ? checkpoints[dstRep][dstRepNum - 1].tokenIds\r\n                    : checkpoints[dstRep][0].tokenIds;\r\n                uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);\r\n                uint[] storage dstRepNew = checkpoints[dstRep][\r\n                    nextDstRepNum\r\n                ].tokenIds;\r\n                uint ownerTokenCount = ownerToNFTokenCount[owner];\r\n                require(\r\n                    dstRepOld.length + ownerTokenCount <= MAX_DELEGATES,\r\n                    \"dstRep would have too many tokenIds\"\r\n                );\r\n                // All the same\r\n                for (uint i = 0; i < dstRepOld.length; i++) {\r\n                    uint tId = dstRepOld[i];\r\n                    dstRepNew.push(tId);\r\n                }\r\n                // Plus all that's owned\r\n                for (uint i = 0; i < ownerTokenCount; i++) {\r\n                    uint tId = ownerToNFTokenIdList[owner][i];\r\n                    dstRepNew.push(tId);\r\n                }\r\n\r\n                numCheckpoints[dstRep] = dstRepNum + 1;\r\n            }\r\n        }\r\n    }\r\n\r\n    function _delegate(address delegator, address delegatee) internal {\r\n        /// @notice differs from `_delegate()` in `Comp.sol` to use `delegates` override method to simulate auto-delegation\r\n        address currentDelegate = delegates(delegator);\r\n\r\n        _delegates[delegator] = delegatee;\r\n\r\n        emit DelegateChanged(delegator, currentDelegate, delegatee);\r\n        _moveAllDelegates(delegator, currentDelegate, delegatee);\r\n    }\r\n\r\n    /**\r\n     * @notice Delegate votes from `msg.sender` to `delegatee`\r\n     * @param delegatee The address to delegate votes to\r\n     */\r\n    function delegate(address delegatee) public {\r\n        if (delegatee == address(0)) delegatee = msg.sender;\r\n        return _delegate(msg.sender, delegatee);\r\n    }\r\n\r\n    function delegateBySig(\r\n        address delegatee,\r\n        uint nonce,\r\n        uint expiry,\r\n        uint8 v,\r\n        bytes32 r,\r\n        bytes32 s\r\n    ) public {\r\n        require(delegatee != msg.sender);\r\n        require(delegatee != address(0));\r\n        \r\n        bytes32 domainSeparator = keccak256(\r\n            abi.encode(\r\n                DOMAIN_TYPEHASH,\r\n                keccak256(bytes(name)),\r\n                keccak256(bytes(version)),\r\n                block.chainid,\r\n                address(this)\r\n            )\r\n        );\r\n        bytes32 structHash = keccak256(\r\n            abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)\r\n        );\r\n        bytes32 digest = keccak256(\r\n            abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash)\r\n        );\r\n        address signatory = ecrecover(digest, v, r, s);\r\n        require(\r\n            signatory != address(0),\r\n            \"VotingEscrow::delegateBySig: invalid signature\"\r\n        );\r\n        require(\r\n            nonce == nonces[signatory]++,\r\n            \"VotingEscrow::delegateBySig: invalid nonce\"\r\n        );\r\n        require(\r\n            block.timestamp <= expiry,\r\n            \"VotingEscrow::delegateBySig: signature expired\"\r\n        );\r\n        return _delegate(signatory, delegatee);\r\n    }\r\n}\r\n"},"contracts/interfaces/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.13;\r\n\r\ninterface IERC20 {\r\n    function totalSupply() external view returns (uint256);\r\n    function transfer(address recipient, uint amount) external returns (bool);\r\n    function decimals() external view returns (uint8);\r\n    function symbol() external view returns (string memory);\r\n    function balanceOf(address) external view returns (uint);\r\n    function transferFrom(address sender, address recipient, uint amount) external returns (bool);\r\n    function allowance(address owner, address spender) external view returns (uint);\r\n    function approve(address spender, uint value) external returns (bool);\r\n\r\n    event Transfer(address indexed from, address indexed to, uint value);\r\n    event Approval(address indexed owner, address indexed spender, uint value);\r\n}\r\n"},"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\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 `IERC721Receiver.onERC721Received.selector`.\n     */\n    function onERC721Received(\n        address operator,\n        address from,\n        uint256 tokenId,\n        bytes calldata data\n    ) external returns (bytes4);\n}\n"}},"abi":[{"type":"constructor","inputs":[{"name":"token_addr","type":"address","internalType":"address"},{"name":"art_proxy","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"name":"Approval","type":"event","inputs":[{"name":"owner","type":"address","indexed":true,"internalType":"address"},{"name":"approved","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"name":"ApprovalForAll","type":"event","inputs":[{"name":"owner","type":"address","indexed":true,"internalType":"address"},{"name":"operator","type":"address","indexed":true,"internalType":"address"},{"name":"approved","type":"bool","indexed":false,"internalType":"bool"}],"anonymous":false},{"name":"DelegateChanged","type":"event","inputs":[{"name":"delegator","type":"address","indexed":true,"internalType":"address"},{"name":"fromDelegate","type":"address","indexed":true,"internalType":"address"},{"name":"toDelegate","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"name":"DelegateVotesChanged","type":"event","inputs":[{"name":"delegate","type":"address","indexed":true,"internalType":"address"},{"name":"previousBalance","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"newBalance","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"Deposit","type":"event","inputs":[{"name":"provider","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"locktime","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"deposit_type","type":"uint8","indexed":false,"internalType":"enum VotingEscrow.DepositType"},{"name":"ts","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"Supply","type":"event","inputs":[{"name":"prevSupply","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"supply","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"Transfer","type":"event","inputs":[{"name":"from","type":"address","indexed":true,"internalType":"address"},{"name":"to","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"name":"Withdraw","type":"event","inputs":[{"name":"provider","type":"address","indexed":true,"internalType":"address"},{"name":"tokenId","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"ts","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"DELEGATION_TYPEHASH","type":"function","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"name":"DOMAIN_TYPEHASH","type":"function","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"name":"MAX_DELEGATES","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"abstain","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"approve","type":"function","inputs":[{"name":"_approved","type":"address","internalType":"address"},{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"artProxy","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"attach","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"attachments","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"balanceOf","type":"function","inputs":[{"name":"_owner","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"balanceOfAtNFT","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"},{"name":"_block","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"balanceOfNFT","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"balanceOfNFTAt","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"},{"name":"_t","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"block_number","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"checkpoint","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"name":"checkpoints","type":"function","inputs":[{"name":"","type":"address","internalType":"address"},{"name":"","type":"uint32","internalType":"uint32"}],"outputs":[{"name":"timestamp","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"create_lock","type":"function","inputs":[{"name":"_value","type":"uint256","internalType":"uint256"},{"name":"_lock_duration","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"name":"create_lock_for","type":"function","inputs":[{"name":"_value","type":"uint256","internalType":"uint256"},{"name":"_lock_duration","type":"uint256","internalType":"uint256"},{"name":"_to","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"name":"decimals","type":"function","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"delegate","type":"function","inputs":[{"name":"delegatee","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"delegateBySig","type":"function","inputs":[{"name":"delegatee","type":"address","internalType":"address"},{"name":"nonce","type":"uint256","internalType":"uint256"},{"name":"expiry","type":"uint256","internalType":"uint256"},{"name":"v","type":"uint8","internalType":"uint8"},{"name":"r","type":"bytes32","internalType":"bytes32"},{"name":"s","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"delegates","type":"function","inputs":[{"name":"delegator","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"deposit_for","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"},{"name":"_value","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"detach","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"epoch","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"getApproved","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"getPastTotalSupply","type":"function","inputs":[{"name":"timestamp","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"getPastVotes","type":"function","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"timestamp","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"getPastVotesIndex","type":"function","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"timestamp","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint32","internalType":"uint32"}],"stateMutability":"view"},{"name":"getVotes","type":"function","inputs":[{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"get_last_user_slope","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"int128","internalType":"int128"}],"stateMutability":"view"},{"name":"increase_amount","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"},{"name":"_value","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"increase_unlock_time","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"},{"name":"_lock_duration","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"isApprovedForAll","type":"function","inputs":[{"name":"_owner","type":"address","internalType":"address"},{"name":"_operator","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"isApprovedOrOwner","type":"function","inputs":[{"name":"_spender","type":"address","internalType":"address"},{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"locked","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"amount","type":"int128","internalType":"int128"},{"name":"end","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"locked__end","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"merge","type":"function","inputs":[{"name":"_from","type":"uint256","internalType":"uint256"},{"name":"_to","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"name","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"nonces","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"numCheckpoints","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint32","internalType":"uint32"}],"stateMutability":"view"},{"name":"ownerOf","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"ownership_change","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"point_history","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"bias","type":"int128","internalType":"int128"},{"name":"slope","type":"int128","internalType":"int128"},{"name":"ts","type":"uint256","internalType":"uint256"},{"name":"blk","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"safeTransferFrom","type":"function","inputs":[{"name":"_from","type":"address","internalType":"address"},{"name":"_to","type":"address","internalType":"address"},{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"safeTransferFrom","type":"function","inputs":[{"name":"_from","type":"address","internalType":"address"},{"name":"_to","type":"address","internalType":"address"},{"name":"_tokenId","type":"uint256","internalType":"uint256"},{"name":"_data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setApprovalForAll","type":"function","inputs":[{"name":"_operator","type":"address","internalType":"address"},{"name":"_approved","type":"bool","internalType":"bool"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setArtProxy","type":"function","inputs":[{"name":"_proxy","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setTeam","type":"function","inputs":[{"name":"_team","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"setVoter","type":"function","inputs":[{"name":"_voter","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"slope_changes","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"int128","internalType":"int128"}],"stateMutability":"view"},{"name":"split","type":"function","inputs":[{"name":"amounts","type":"uint256[]","internalType":"uint256[]"},{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"supply","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"supportsInterface","type":"function","inputs":[{"name":"_interfaceID","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"symbol","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"team","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"token","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"tokenOfOwnerByIndex","type":"function","inputs":[{"name":"_owner","type":"address","internalType":"address"},{"name":"_tokenIndex","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"tokenURI","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"totalSupply","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"totalSupplyAt","type":"function","inputs":[{"name":"_block","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"totalSupplyAtT","type":"function","inputs":[{"name":"t","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"transferFrom","type":"function","inputs":[{"name":"_from","type":"address","internalType":"address"},{"name":"_to","type":"address","internalType":"address"},{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"user_point_epoch","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"user_point_history","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"},{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"bias","type":"int128","internalType":"int128"},{"name":"slope","type":"int128","internalType":"int128"},{"name":"ts","type":"uint256","internalType":"uint256"},{"name":"blk","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"user_point_history__ts","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"},{"name":"_idx","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"version","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"voted","type":"function","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"voter","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"voting","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"withdraw","type":"function","inputs":[{"name":"_tokenId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"}],"matchId":"28983888","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-05-15T17:03:15Z","match":"exact_match","chainId":"56","address":"0xfBBF371C9B0B994EebFcC977CEf603F7f31c070D"}