ETH Price: $2,092.64 (-1.52%)
Gas: 0.04 Gwei

Transaction Decoder

Block:
16762876 at Mar-05-2023 02:45:35 PM +UTC
Transaction Fee:
0.0058972870240284 ETH $12.34
Gas Used:
239,694 Gas / 24.6033986 Gwei

Emitted Events:

304 LOOTaDOG.Transfer( _from=0x00000000...000000000, _to=[Sender] 0x1c06809fd3ced2c8fdf9c08d385a5d2aed988c53, _tokenId=454 )
305 LOOTaDOG.SlotChanged( _tokenId=454, _oldSlot=0, _newSlot=1 )
306 LOOTaDOG.TransferValue( _fromTokenId=0, _toTokenId=454, _value=1 )

Account State Difference:

  Address   Before After State Difference Code
0x1c06809F...Aed988c53
0.013431007055862261 Eth
Nonce: 43
0.007533720031833861 Eth
Nonce: 44
0.0058972870240284
0x68b9c676...39Bbb3382
(Flashbots: Builder)
2.179201545238264264 Eth2.179321392238264264 Eth0.000119847

Execution Trace

LOOTaDOG.mintToken( id=1489, amount=1, expirationTime=1678113924, owner=0x1c06809FD3ceD2C8Fdf9C08D385A5d2Aed988c53, v=28, r=C6B09CB93C4616D9973D95450D8C3DC818BF4026A286628411931050E680907A, s=1CF563AFF4E5DDF3C29347A14FE4991869243A0E7BA44C94F45F46C605375D10 )
  • Null: 0x000...001.c890821e( )
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        bool private _paused;
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            _requireNotPaused();
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            _requirePaused();
            _;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Throws if the contract is paused.
         */
        function _requireNotPaused() internal view virtual {
            require(!paused(), "Pausable: paused");
        }
        /**
         * @dev Throws if the contract is not paused.
         */
        function _requirePaused() internal view virtual {
            require(paused(), "Pausable: not paused");
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    /// @solidity memory-safe-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
    pragma solidity ^0.8.0;
    /**
     * @title Counters
     * @author Matt Condon (@shrugs)
     * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
     *
     * Include with `using Counters for Counters.Counter;`
     */
    library Counters {
        struct Counter {
            // This variable should never be directly accessed by users of the library: interactions must be restricted to
            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
            // this feature: see https://github.com/ethereum/solidity/issues/4637
            uint256 _value; // default: 0
        }
        function current(Counter storage counter) internal view returns (uint256) {
            return counter._value;
        }
        function increment(Counter storage counter) internal {
            unchecked {
                counter._value += 1;
            }
        }
        function decrement(Counter storage counter) internal {
            uint256 value = counter._value;
            require(value > 0, "Counter: decrement overflow");
            unchecked {
                counter._value = value - 1;
            }
        }
        function reset(Counter storage counter) internal {
            counter._value = 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        uint8 private constant _ADDRESS_LENGTH = 20;
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/utils/Context.sol";
    import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    import "@openzeppelin/contracts/utils/Address.sol";
    import "@openzeppelin/contracts/utils/Counters.sol";
    import "./IERC721.sol";
    import "./IERC3525.sol";
    import "./IERC721Receiver.sol";
    import "./IERC3525Receiver.sol";
    import "./extensions/IERC721Enumerable.sol";
    import "./extensions/IERC721Metadata.sol";
    import "./extensions/IERC3525Metadata.sol";
    import "./periphery/interface/IERC3525MetadataDescriptor.sol";
    contract ERC3525 is Context, IERC3525Metadata, IERC721Enumerable {
        using Strings for address;
        using Strings for uint256;
        using Address for address;
        using Counters for Counters.Counter;
        event SetMetadataDescriptor(address indexed metadataDescriptor);
        struct TokenData {
            uint256 id;
            uint256 slot;
            uint256 balance;
            address owner;
            address approved;
            address[] valueApprovals;
        }
        struct AddressData {
            uint256[] ownedTokens;
            mapping(uint256 => uint256) ownedTokensIndex;
            mapping(address => bool) approvals;
        }
        string private _name;
        string private _symbol;
        uint8 private _decimals;
        Counters.Counter private _tokenIdGenerator;
        // id => (approval => allowance)
        // @dev _approvedValues cannot be defined within TokenData, cause struct containing mappings cannot be constructed.
        mapping(uint256 => mapping(address => uint256)) private _approvedValues;
        TokenData[] private _allTokens;
        // key: id
        mapping(uint256 => uint256) private _allTokensIndex;
        mapping(address => AddressData) private _addressData;
        IERC3525MetadataDescriptor public metadataDescriptor;
        constructor(string memory name_, string memory symbol_, uint8 decimals_) {
             _name = name_;
            _symbol = symbol_;
            _decimals = decimals_;
        }
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return
                interfaceId == type(IERC165).interfaceId ||
                interfaceId == type(IERC3525).interfaceId ||
                interfaceId == type(IERC721).interfaceId ||
                interfaceId == type(IERC3525Metadata).interfaceId ||
                interfaceId == type(IERC721Enumerable).interfaceId || 
                interfaceId == type(IERC721Metadata).interfaceId;
        }
        /**
         * @dev Returns the token collection name.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals the token uses for value.
         */
        function valueDecimals() public view virtual override returns (uint8) {
            return _decimals;
        }
        function balanceOf(uint256 tokenId_) public view virtual override returns (uint256) {
            _requireMinted(tokenId_);
            return _allTokens[_allTokensIndex[tokenId_]].balance;
        }
        function ownerOf(uint256 tokenId_) public view virtual override returns (address owner_) {
            _requireMinted(tokenId_);
            owner_ = _allTokens[_allTokensIndex[tokenId_]].owner;
            require(owner_ != address(0), "ERC3525: invalid token ID");
        }
        function slotOf(uint256 tokenId_) public view virtual override returns (uint256) {
            _requireMinted(tokenId_);
            return _allTokens[_allTokensIndex[tokenId_]].slot;
        }
        function _baseURI() internal view virtual returns (string memory) {
            return "";
        }
        function contractURI() public view virtual override returns (string memory) {
            string memory baseURI = _baseURI();
            return 
                address(metadataDescriptor) != address(0) ? 
                    metadataDescriptor.constructContractURI() :
                    bytes(baseURI).length > 0 ? 
                        string(abi.encodePacked(baseURI, "contract/", Strings.toHexString(address(this)))) : 
                        "";
        }
        function slotURI(uint256 slot_) public view virtual override returns (string memory) {
            string memory baseURI = _baseURI();
            return 
                address(metadataDescriptor) != address(0) ? 
                    metadataDescriptor.constructSlotURI(slot_) : 
                    bytes(baseURI).length > 0 ? 
                        string(abi.encodePacked(baseURI, "slot/", slot_.toString())) : 
                        "";
        }
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId_) public view virtual override returns (string memory) {
            _requireMinted(tokenId_);
            string memory baseURI = _baseURI();
            return 
                address(metadataDescriptor) != address(0) ? 
                    metadataDescriptor.constructTokenURI(tokenId_) : 
                    bytes(baseURI).length > 0 ? 
                        string(abi.encodePacked(baseURI, tokenId_.toString())) : 
                        "";
        }
        function approve(uint256 tokenId_, address to_, uint256 value_) public payable virtual override {
            address owner = ERC3525.ownerOf(tokenId_);
            require(to_ != owner, "ERC3525: approval to current owner");
            require(
                _msgSender() == owner || ERC3525.isApprovedForAll(owner, _msgSender()),
                "ERC3525: approve caller is not owner nor approved for all"
            );
            _approveValue(tokenId_, to_, value_);
        }
        function allowance(uint256 tokenId_, address operator_) public view virtual override returns (uint256) {
            _requireMinted(tokenId_);
            return _approvedValues[tokenId_][operator_];
        }
        function transferFrom(
            uint256 fromTokenId_,
            address to_,
            uint256 value_
        ) public payable virtual override returns (uint256) {
            _spendAllowance(_msgSender(), fromTokenId_, value_);
            uint256 newTokenId = _createDerivedTokenId(fromTokenId_);
            _mint(to_, newTokenId, ERC3525.slotOf(fromTokenId_), 0);
            _transferValue(fromTokenId_, newTokenId, value_);
            return newTokenId;
        }
        function transferFrom(
            uint256 fromTokenId_,
            uint256 toTokenId_,
            uint256 value_
        ) public payable virtual override {
            _spendAllowance(_msgSender(), fromTokenId_, value_);
            _transferValue(fromTokenId_, toTokenId_, value_);
        }
        function balanceOf(address owner_) public view virtual override returns (uint256 balance) {
            require(owner_ != address(0), "ERC3525: balance query for the zero address");
            return _addressData[owner_].ownedTokens.length;
        }
        function transferFrom(
            address from_,
            address to_,
            uint256 tokenId_
        ) public payable virtual override {
            require(_isApprovedOrOwner(_msgSender(), tokenId_), "ERC3525: transfer caller is not owner nor approved");
            _transferTokenId(from_, to_, tokenId_);
        }
        function safeTransferFrom(
            address from_,
            address to_,
            uint256 tokenId_,
            bytes memory data_
        ) public payable virtual override {
            require(_isApprovedOrOwner(_msgSender(), tokenId_), "ERC3525: transfer caller is not owner nor approved");
            _safeTransferTokenId(from_, to_, tokenId_, data_);
        }
        function safeTransferFrom(
            address from_,
            address to_,
            uint256 tokenId_
        ) public payable virtual override {
            safeTransferFrom(from_, to_, tokenId_, "");
        }
        function approve(address to_, uint256 tokenId_) public payable virtual override {
            address owner = ERC3525.ownerOf(tokenId_);
            require(to_ != owner, "ERC3525: approval to current owner");
            require(
                _msgSender() == owner || ERC3525.isApprovedForAll(owner, _msgSender()),
                "ERC3525: approve caller is not owner nor approved for all"
            );
            _approve(to_, tokenId_);
        }
        function getApproved(uint256 tokenId_) public view virtual override returns (address) {
            _requireMinted(tokenId_);
            return _allTokens[_allTokensIndex[tokenId_]].approved;
        }
        function setApprovalForAll(address operator_, bool approved_) public virtual override {
            _setApprovalForAll(_msgSender(), operator_, approved_);
        }
        function isApprovedForAll(address owner_, address operator_) public view virtual override returns (bool) {
            return _addressData[owner_].approvals[operator_];
        }
        function totalSupply() public view virtual override returns (uint256) {
            return _allTokens.length;
        }
        function tokenByIndex(uint256 index_) public view virtual override returns (uint256) {
            require(index_ < ERC3525.totalSupply(), "ERC3525: global index out of bounds");
            return _allTokens[index_].id;
        }
        function tokenOfOwnerByIndex(address owner_, uint256 index_) public view virtual override returns (uint256) {
            require(index_ < ERC3525.balanceOf(owner_), "ERC3525: owner index out of bounds");
            return _addressData[owner_].ownedTokens[index_];
        }
        function _setApprovalForAll(
            address owner_,
            address operator_,
            bool approved_
        ) internal virtual {
            require(owner_ != operator_, "ERC3525: approve to caller");
            _addressData[owner_].approvals[operator_] = approved_;
            emit ApprovalForAll(owner_, operator_, approved_);
        }
        function _isApprovedOrOwner(address operator_, uint256 tokenId_) internal view virtual returns (bool) {
            _requireMinted(tokenId_);
            address owner = ERC3525.ownerOf(tokenId_);
            return (
                operator_ == owner ||
                ERC3525.isApprovedForAll(owner, operator_) ||
                ERC3525.getApproved(tokenId_) == operator_
            );
        }
        function _spendAllowance(address operator_, uint256 tokenId_, uint256 value_) internal virtual {
            uint256 currentAllowance = ERC3525.allowance(tokenId_, operator_);
            if (!_isApprovedOrOwner(operator_, tokenId_) && currentAllowance != type(uint256).max) {
                require(currentAllowance >= value_, "ERC3525: insufficient allowance");
                _approveValue(tokenId_, operator_, currentAllowance - value_);
            }
        }
        function _exists(uint256 tokenId_) internal view virtual returns (bool) {
            return _allTokens.length != 0 && _allTokens[_allTokensIndex[tokenId_]].id == tokenId_;
        }
        function _requireMinted(uint256 tokenId_) internal view virtual {
            require(_exists(tokenId_), "ERC3525: invalid token ID");
        }
        function _mint(address to_, uint256 slot_, uint256 value_) internal virtual returns (uint256) {
            uint256 tokenId = _createOriginalTokenId();
            _mint(to_, tokenId, slot_, value_);  
            return tokenId;
        }
        function _mint(address to_, uint256 tokenId_, uint256 slot_, uint256 value_) internal virtual {
            require(to_ != address(0), "ERC3525: mint to the zero address");
            require(tokenId_ != 0, "ERC3525: cannot mint zero tokenId");
            require(!_exists(tokenId_), "ERC3525: token already minted");
            _beforeValueTransfer(address(0), to_, 0, tokenId_, slot_, value_);
            __mintToken(to_, tokenId_, slot_);
            __mintValue(tokenId_, value_);
            _afterValueTransfer(address(0), to_, 0, tokenId_, slot_, value_);
        }
        function _mintValue(uint256 tokenId_, uint256 value_) internal virtual {
            _requireMinted(tokenId_);
            address owner = ERC3525.ownerOf(tokenId_);
            uint256 slot = ERC3525.slotOf(tokenId_);
            _beforeValueTransfer(address(0), owner, 0, tokenId_, slot, value_);
            __mintValue(tokenId_, value_);
            _afterValueTransfer(address(0), owner, 0, tokenId_, slot, value_);
        }
        function __mintValue(uint256 tokenId_, uint256 value_) private {
            _allTokens[_allTokensIndex[tokenId_]].balance += value_;
            emit TransferValue(0, tokenId_, value_);
        }
        function __mintToken(address to_, uint256 tokenId_, uint256 slot_) private {
            TokenData memory tokenData = TokenData({
                id: tokenId_,
                slot: slot_,
                balance: 0,
                owner: to_,
                approved: address(0),
                valueApprovals: new address[](0)
            });
            _addTokenToAllTokensEnumeration(tokenData);
            _addTokenToOwnerEnumeration(to_, tokenId_);
            emit Transfer(address(0), to_, tokenId_);
            emit SlotChanged(tokenId_, 0, slot_);
        }
        function _burn(uint256 tokenId_) internal virtual {
            _requireMinted(tokenId_);
            TokenData storage tokenData = _allTokens[_allTokensIndex[tokenId_]];
            address owner = tokenData.owner;
            uint256 slot = tokenData.slot;
            uint256 value = tokenData.balance;
            _beforeValueTransfer(owner, address(0), tokenId_, 0, slot, value);
            _clearApprovedValues(tokenId_);
            _removeTokenFromOwnerEnumeration(owner, tokenId_);
            _removeTokenFromAllTokensEnumeration(tokenId_);
            emit TransferValue(tokenId_, 0, value);
            emit SlotChanged(tokenId_, slot, 0);
            emit Transfer(owner, address(0), tokenId_);
            _afterValueTransfer(owner, address(0), tokenId_, 0, slot, value);
        }
        function _burnValue(uint256 tokenId_, uint256 burnValue_) internal virtual {
            _requireMinted(tokenId_);
            TokenData storage tokenData = _allTokens[_allTokensIndex[tokenId_]];
            address owner = tokenData.owner;
            uint256 slot = tokenData.slot;
            uint256 value = tokenData.balance;
            require(value >= burnValue_, "ERC3525: burn value exceeds balance");
            _beforeValueTransfer(owner, address(0), tokenId_, 0, slot, burnValue_);
            
            tokenData.balance -= burnValue_;
            emit TransferValue(tokenId_, 0, burnValue_);
            
            _afterValueTransfer(owner, address(0), tokenId_, 0, slot, burnValue_);
        }
        function _addTokenToOwnerEnumeration(address to_, uint256 tokenId_) private {
            _allTokens[_allTokensIndex[tokenId_]].owner = to_;
            _addressData[to_].ownedTokensIndex[tokenId_] = _addressData[to_].ownedTokens.length;
            _addressData[to_].ownedTokens.push(tokenId_);
        }
        function _removeTokenFromOwnerEnumeration(address from_, uint256 tokenId_) private {
            _allTokens[_allTokensIndex[tokenId_]].owner = address(0);
            AddressData storage ownerData = _addressData[from_];
            uint256 lastTokenIndex = ownerData.ownedTokens.length - 1;
            uint256 lastTokenId = ownerData.ownedTokens[lastTokenIndex];
            uint256 tokenIndex = ownerData.ownedTokensIndex[tokenId_];
            ownerData.ownedTokens[tokenIndex] = lastTokenId;
            ownerData.ownedTokensIndex[lastTokenId] = tokenIndex;
            delete ownerData.ownedTokensIndex[tokenId_];
            ownerData.ownedTokens.pop();
        }
        function _addTokenToAllTokensEnumeration(TokenData memory tokenData_) private {
            _allTokensIndex[tokenData_.id] = _allTokens.length;
            _allTokens.push(tokenData_);
        }
        function _removeTokenFromAllTokensEnumeration(uint256 tokenId_) private {
            // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
            // then delete the last slot (swap and pop).
            uint256 lastTokenIndex = _allTokens.length - 1;
            uint256 tokenIndex = _allTokensIndex[tokenId_];
            // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
            // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
            // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
            TokenData memory lastTokenData = _allTokens[lastTokenIndex];
            _allTokens[tokenIndex] = lastTokenData; // Move the last token to the slot of the to-delete token
            _allTokensIndex[lastTokenData.id] = tokenIndex; // Update the moved token's index
            // This also deletes the contents at the last position of the array
            delete _allTokensIndex[tokenId_];
            _allTokens.pop();
        }
        function _approve(address to_, uint256 tokenId_) internal virtual {
            _allTokens[_allTokensIndex[tokenId_]].approved = to_;
            emit Approval(ERC3525.ownerOf(tokenId_), to_, tokenId_);
        }
        function _approveValue(
            uint256 tokenId_,
            address to_,
            uint256 value_
        ) internal virtual {
            require(to_ != address(0), "ERC3525: approve value to the zero address");
            if (!_existApproveValue(to_, tokenId_)) {
                _allTokens[_allTokensIndex[tokenId_]].valueApprovals.push(to_);
            }
            _approvedValues[tokenId_][to_] = value_;
            emit ApprovalValue(tokenId_, to_, value_);
        }
        function _clearApprovedValues(uint256 tokenId_) internal virtual {
            TokenData storage tokenData = _allTokens[_allTokensIndex[tokenId_]];
            uint256 length = tokenData.valueApprovals.length;
            for (uint256 i = 0; i < length; i++) {
                address approval = tokenData.valueApprovals[i];
                delete _approvedValues[tokenId_][approval];
            }
        }
        function _existApproveValue(address to_, uint256 tokenId_) internal view virtual returns (bool) {
            uint256 length = _allTokens[_allTokensIndex[tokenId_]].valueApprovals.length;
            for (uint256 i = 0; i < length; i++) {
                if (_allTokens[_allTokensIndex[tokenId_]].valueApprovals[i] == to_) {
                    return true;
                }
            }
            return false;
        }
        function _transferValue(
            uint256 fromTokenId_,
            uint256 toTokenId_,
            uint256 value_
        ) internal virtual {
            require(_exists(fromTokenId_), "ERC3525: transfer from invalid token ID");
            require(_exists(toTokenId_), "ERC3525: transfer to invalid token ID");
            TokenData storage fromTokenData = _allTokens[_allTokensIndex[fromTokenId_]];
            TokenData storage toTokenData = _allTokens[_allTokensIndex[toTokenId_]];
            require(fromTokenData.balance >= value_, "ERC3525: insufficient balance for transfer");
            require(fromTokenData.slot == toTokenData.slot, "ERC3525: transfer to token with different slot");
            _beforeValueTransfer(
                fromTokenData.owner,
                toTokenData.owner,
                fromTokenId_,
                toTokenId_,
                fromTokenData.slot,
                value_
            );
            fromTokenData.balance -= value_;
            toTokenData.balance += value_;
            emit TransferValue(fromTokenId_, toTokenId_, value_);
            _afterValueTransfer(
                fromTokenData.owner,
                toTokenData.owner,
                fromTokenId_,
                toTokenId_,
                fromTokenData.slot,
                value_
            );
            require(
                _checkOnERC3525Received(fromTokenId_, toTokenId_, value_, ""),
                "ERC3525: transfer to non ERC3525Receiver"
            );
        }
        function _transferTokenId(
            address from_,
            address to_,
            uint256 tokenId_
        ) internal virtual {
            require(ERC3525.ownerOf(tokenId_) == from_, "ERC3525: transfer from invalid owner");
            require(to_ != address(0), "ERC3525: transfer to the zero address");
            uint256 slot = ERC3525.slotOf(tokenId_);
            uint256 value = ERC3525.balanceOf(tokenId_);
            _beforeValueTransfer(from_, to_, tokenId_, tokenId_, slot, value);
            _approve(address(0), tokenId_);
            _clearApprovedValues(tokenId_);
            _removeTokenFromOwnerEnumeration(from_, tokenId_);
            _addTokenToOwnerEnumeration(to_, tokenId_);
            emit Transfer(from_, to_, tokenId_);
            _afterValueTransfer(from_, to_, tokenId_, tokenId_, slot, value);
        }
        function _safeTransferTokenId(
            address from_,
            address to_,
            uint256 tokenId_,
            bytes memory data_
        ) internal virtual {
            _transferTokenId(from_, to_, tokenId_);
            require(
                _checkOnERC721Received(from_, to_, tokenId_, data_),
                "ERC3525: transfer to non ERC721Receiver"
            );
        }
        function _checkOnERC3525Received( 
            uint256 fromTokenId_, 
            uint256 toTokenId_, 
            uint256 value_, 
            bytes memory data_
        ) private returns (bool) {
            address to = ERC3525.ownerOf(toTokenId_);
            if (to.isContract() && IERC165(to).supportsInterface(type(IERC3525Receiver).interfaceId)) {
                try
                    IERC3525Receiver(to).onERC3525Received(_msgSender(), fromTokenId_, toTokenId_, value_, data_) returns (bytes4 retval) {
                    return retval == IERC3525Receiver.onERC3525Received.selector;
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        revert( "ERC3525: transfer to non ERC3525Receiver");
                    } else {
                        // solhint-disable-next-line
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            } else {
                return true;
            }
        }
        /**
         * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
         * The call is not executed if the target address is not a contract.
         *
         * @param from_ address representing the previous owner of the given token ID
         * @param to_ target address that will receive the tokens
         * @param tokenId_ uint256 ID of the token to be transferred
         * @param data_ bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(
            address from_,
            address to_,
            uint256 tokenId_,
            bytes memory data_
        ) private returns (bool) {
            if (to_.isContract() && IERC165(to_).supportsInterface(type(IERC721Receiver).interfaceId)) {
                try 
                    IERC721Receiver(to_).onERC721Received(_msgSender(), from_, tokenId_, data_) returns (bytes4 retval) {
                    return retval == IERC721Receiver.onERC721Received.selector;
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        revert("ERC721: transfer to non ERC721Receiver");
                    } else {
                        // solhint-disable-next-line
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            } else {
                return true;
            }
        }
        /* solhint-disable */
        function _beforeValueTransfer(
            address from_,
            address to_,
            uint256 fromTokenId_,
            uint256 toTokenId_,
            uint256 slot_,
            uint256 value_
        ) internal virtual {}
        function _afterValueTransfer(
            address from_,
            address to_,
            uint256 fromTokenId_,
            uint256 toTokenId_,
            uint256 slot_,
            uint256 value_
        ) internal virtual {}
        /* solhint-enable */
        function _setMetadataDescriptor(address metadataDescriptor_) internal virtual {
            metadataDescriptor = IERC3525MetadataDescriptor(metadataDescriptor_);
            emit SetMetadataDescriptor(metadataDescriptor_);
        }
        function _createOriginalTokenId() internal virtual returns (uint256) {
             _tokenIdGenerator.increment();
            return _tokenIdGenerator.current();
        }
        function _createDerivedTokenId(uint256 fromTokenId_) internal virtual returns (uint256) {
            fromTokenId_;
            return _createOriginalTokenId();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC3525.sol";
    import "./IERC721Metadata.sol";
    /**
     * @title ERC-3525 Semi-Fungible Token Standard, optional extension for metadata
     * @dev Interfaces for any contract that wants to support query of the Uniform Resource Identifier
     *  (URI) for the ERC3525 contract as well as a specified slot.
     *  Because of the higher reliability of data stored in smart contracts compared to data stored in
     *  centralized systems, it is recommended that metadata, including `contractURI`, `slotURI` and
     *  `tokenURI`, be directly returned in JSON format, instead of being returned with a url pointing
     *  to any resource stored in a centralized system.
     *  See https://eips.ethereum.org/EIPS/eip-3525
     * Note: the ERC-165 identifier for this interface is 0xe1600902.
     */
    interface IERC3525Metadata is IERC3525, IERC721Metadata {
        /**
         * @notice Returns the Uniform Resource Identifier (URI) for the current ERC3525 contract.
         * @dev This function SHOULD return the URI for this contract in JSON format, starting with
         *  header `data:application/json;`.
         *  See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for contract URI.
         * @return The JSON formatted URI of the current ERC3525 contract
         */
        function contractURI() external view returns (string memory);
        /**
         * @notice Returns the Uniform Resource Identifier (URI) for the specified slot.
         * @dev This function SHOULD return the URI for `_slot` in JSON format, starting with header
         *  `data:application/json;`.
         *  See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for slot URI.
         * @return The JSON formatted URI of `_slot`
         */
        function slotURI(uint256 _slot) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     *  Note: the ERC-165 identifier for this interface is 0x780e9d63.
     */
    interface IERC721Enumerable is IERC721 {
        /** 
         * @notice Count NFTs tracked by this contract
         * @return A count of valid NFTs tracked by this contract, where each one of
         *  them has an assigned and queryable owner not equal to the zero address
         */
        function totalSupply() external view returns (uint256);
        /** 
         * @notice Enumerate valid NFTs
         * @dev Throws if `_index` >= `totalSupply()`.
         * @param _index A counter less than `totalSupply()`
         * @return The token identifier for the `_index`th NFT,
         *  (sort order not specified)
         */
        function tokenByIndex(uint256 _index) external view returns (uint256);
        /** 
         * @notice Enumerate NFTs assigned to an owner
         * @dev Throws if `_index` >= `balanceOf(_owner)` or if
         *  `_owner` is the zero address, representing invalid NFTs.
         * @param _owner An address where we are interested in NFTs owned by them
         * @param _index A counter less than `balanceOf(_owner)`
         * @return The token identifier for the `_index`th NFT assigned to `_owner`,
         *  (sort order not specified)
         */
        function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     *  Note: the ERC-165 identifier for this interface is 0x5b5e139f.
     */
    interface IERC721Metadata is IERC721 {
        /**
         * @notice A descriptive name for a collection of NFTs in this contract
         */
        function name() external view returns (string memory);
        /**
         * @notice An abbreviated name for NFTs in this contract
         */
        function symbol() external view returns (string memory);
        /**
         * @notice A distinct Uniform Resource Identifier (URI) for a given asset.
         * @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
         *  3986. The URI may point to a JSON file that conforms to the "ERC721
         *  Metadata JSON Schema".
         */
        function tokenURI(uint256 _tokenId) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    import "./IERC721.sol";
    /**
     * @title ERC-3525 Semi-Fungible Token Standard
     * @dev See https://eips.ethereum.org/EIPS/eip-3525
     * Note: the ERC-165 identifier for this interface is 0xc97ae3d5.
     */
    interface IERC3525 is IERC165, IERC721 {
        /**
         * @dev MUST emit when value of a token is transferred to another token with the same slot,
         *  including zero value transfers (_value == 0) as well as transfers when tokens are created
         *  (`_fromTokenId` == 0) or destroyed (`_toTokenId` == 0).
         * @param _fromTokenId The token id to transfer value from
         * @param _toTokenId The token id to transfer value to
         * @param _value The transferred value
         */
        event TransferValue(uint256 indexed _fromTokenId, uint256 indexed _toTokenId, uint256 _value);
        /**
         * @dev MUST emits when the approval value of a token is set or changed.
         * @param _tokenId The token to approve
         * @param _operator The operator to approve for
         * @param _value The maximum value that `_operator` is allowed to manage
         */
        event ApprovalValue(uint256 indexed _tokenId, address indexed _operator, uint256 _value);
        /**
         * @dev MUST emit when the slot of a token is set or changed.
         * @param _tokenId The token of which slot is set or changed
         * @param _oldSlot The previous slot of the token
         * @param _newSlot The updated slot of the token
         */ 
        event SlotChanged(uint256 indexed _tokenId, uint256 indexed _oldSlot, uint256 indexed _newSlot);
        /**
         * @notice Get the number of decimals the token uses for value - e.g. 6, means the user
         *  representation of the value of a token can be calculated by dividing it by 1,000,000.
         *  Considering the compatibility with third-party wallets, this function is defined as
         *  `valueDecimals()` instead of `decimals()` to avoid conflict with ERC20 tokens.
         * @return The number of decimals for value
         */
        function valueDecimals() external view returns (uint8);
        /**
         * @notice Get the value of a token.
         * @param _tokenId The token for which to query the balance
         * @return The value of `_tokenId`
         */
        function balanceOf(uint256 _tokenId) external view returns (uint256);
        /**
         * @notice Get the slot of a token.
         * @param _tokenId The identifier for a token
         * @return The slot of the token
         */
        function slotOf(uint256 _tokenId) external view returns (uint256);
        /**
         * @notice Allow an operator to manage the value of a token, up to the `_value` amount.
         * @dev MUST revert unless caller is the current owner, an authorized operator, or the approved
         *  address for `_tokenId`.
         *  MUST emit ApprovalValue event.
         * @param _tokenId The token to approve
         * @param _operator The operator to be approved
         * @param _value The maximum value of `_toTokenId` that `_operator` is allowed to manage
         */
        function approve(
            uint256 _tokenId,
            address _operator,
            uint256 _value
        ) external payable;
        /**
         * @notice Get the maximum value of a token that an operator is allowed to manage.
         * @param _tokenId The token for which to query the allowance
         * @param _operator The address of an operator
         * @return The current approval value of `_tokenId` that `_operator` is allowed to manage
         */
        function allowance(uint256 _tokenId, address _operator) external view returns (uint256);
        /**
         * @notice Transfer value from a specified token to another specified token with the same slot.
         * @dev Caller MUST be the current owner, an authorized operator or an operator who has been
         *  approved the whole `_fromTokenId` or part of it.
         *  MUST revert if `_fromTokenId` or `_toTokenId` is zero token id or does not exist.
         *  MUST revert if slots of `_fromTokenId` and `_toTokenId` do not match.
         *  MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the
         *  operator.
         *  MUST emit `TransferValue` event.
         * @param _fromTokenId The token to transfer value from
         * @param _toTokenId The token to transfer value to
         * @param _value The transferred value
         */
        function transferFrom(
            uint256 _fromTokenId,
            uint256 _toTokenId,
            uint256 _value
        ) external payable;
        /**
         * @notice Transfer value from a specified token to an address. The caller should confirm that
         *  `_to` is capable of receiving ERC3525 tokens.
         * @dev This function MUST create a new ERC3525 token with the same slot for `_to` to receive
         *  the transferred value.
         *  MUST revert if `_fromTokenId` is zero token id or does not exist.
         *  MUST revert if `_to` is zero address.
         *  MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the
         *  operator.
         *  MUST emit `Transfer` and `TransferValue` events.
         * @param _fromTokenId The token to transfer value from
         * @param _to The address to transfer value to
         * @param _value The transferred value
         * @return ID of the new token created for `_to` which receives the transferred value
         */
        function transferFrom(
            uint256 _fromTokenId,
            address _to,
            uint256 _value
        ) external payable returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @title EIP-3525 token receiver interface
     * @dev Interface for a smart contract that wants to be informed by EIP-3525 contracts when 
     *  receiving values from ANY addresses or EIP-3525 tokens.
     * Note: the EIP-165 identifier for this interface is 0x009ce20b.
     */
    interface IERC3525Receiver {
        /**
         * @notice Handle the receipt of an EIP-3525 token value.
         * @dev An EIP-3525 smart contract MUST check whether this function is implemented by the 
         *  recipient contract, if the recipient contract implements this function, the EIP-3525 
         *  contract MUST call this function after a value transfer (i.e. `transferFrom(uint256,
         *  uint256,uint256,bytes)`).
         *  MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256,
         *  uint256,bytes)'))`) if the transfer is accepted.
         *  MUST revert or return any value other than 0x009ce20b if the transfer is rejected.
         * @param _operator The address which triggered the transfer
         * @param _fromTokenId The token id to transfer value from
         * @param _toTokenId The token id to transfer value to
         * @param _value The transferred value
         * @param _data Additional data with no specified format
         * @return `bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)'))` 
         *  unless the transfer is rejected.
         */
        function onERC3525Received(address _operator, uint256 _fromTokenId, uint256 _toTokenId, uint256 _value, bytes calldata _data) external returns (bytes4);
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    /** 
     * @title ERC-721 Non-Fungible Token Standard
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     *  Note: the ERC-165 identifier for this interface is 0x80ac58cd.
     */
    interface IERC721 is IERC165 {
        /** 
         * @dev This emits when ownership of any NFT changes by any mechanism.
         *  This event emits when NFTs are created (`from` == 0) and destroyed
         *  (`to` == 0). Exception: during contract creation, any number of NFTs
         *  may be created and assigned without emitting Transfer. At the time of
         *  any transfer, the approved address for that NFT (if any) is reset to none.
         */
        event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
        /**
         * @dev This emits when the approved address for an NFT is changed or
         *  reaffirmed. The zero address indicates there is no approved address.
         *  When a Transfer event emits, this also indicates that the approved
         *  address for that NFT (if any) is reset to none.
         */
        event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
        /**
         * @dev This emits when an operator is enabled or disabled for an owner.
         *  The operator can manage all NFTs of the owner.
         */
        event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
        /**
         * @notice Count all NFTs assigned to an owner
         * @dev NFTs assigned to the zero address are considered invalid, and this
         *  function throws for queries about the zero address.
         * @param _owner An address for whom to query the balance
         * @return The number of NFTs owned by `_owner`, possibly zero
         */
        function balanceOf(address _owner) external view returns (uint256);
        /**
         * @notice Find the owner of an NFT
         * @dev NFTs assigned to zero address are considered invalid, and queries
         *  about them do throw.
         * @param _tokenId The identifier for an NFT
         * @return The address of the owner of the NFT
         */
        function ownerOf(uint256 _tokenId) external view returns (address);
        /**
         * @notice Transfers the ownership of an NFT from one address to another address
         * @dev Throws unless `msg.sender` is the current owner, an authorized
         *  operator, or the approved address for this NFT. Throws if `_from` is
         *  not the current owner. Throws if `_to` is the zero address. Throws if
         *  `_tokenId` is not a valid NFT. When transfer is complete, this function
         *  checks if `_to` is a smart contract (code size > 0). If so, it calls
         *  `onERC721Received` on `_to` and throws if the return value is not
         *  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
         * @param _from The current owner of the NFT
         * @param _to The new owner
         * @param _tokenId The NFT to transfer
         * @param data Additional data with no specified format, sent in call to `_to`
         */
        function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;
        /**
         * @notice Transfers the ownership of an NFT from one address to another address
         * @dev This works identically to the other function with an extra data parameter,
         *  except this function just sets data to "".
         * @param _from The current owner of the NFT
         * @param _to The new owner
         * @param _tokenId The NFT to transfer
         */
        function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
        /**
         * @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
         *  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
         *  THEY MAY BE PERMANENTLY LOST
         * @dev Throws unless `msg.sender` is the current owner, an authorized
         *  operator, or the approved address for this NFT. Throws if `_from` is
         *  not the current owner. Throws if `_to` is the zero address. Throws if
         *  `_tokenId` is not a valid NFT.
         * @param _from The current owner of the NFT
         * @param _to The new owner
         * @param _tokenId The NFT to transfer
         */
        function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
        /**
         * @notice Change or reaffirm the approved address for an NFT
         * @dev The zero address indicates there is no approved address.
         *  Throws unless `msg.sender` is the current NFT owner, or an authorized
         *  operator of the current owner.
         * @param _approved The new approved NFT controller
         * @param _tokenId The NFT to approve
         */
        function approve(address _approved, uint256 _tokenId) external payable;
        /**
         * @notice Enable or disable approval for a third party ("operator") to manage
         *  all of `msg.sender`'s assets
         * @dev Emits the ApprovalForAll event. The contract MUST allow
         *  multiple operators per owner.
         * @param _operator Address to add to the set of authorized operators
         * @param _approved True if the operator is approved, false to revoke approval
         */
        function setApprovalForAll(address _operator, bool _approved) external;
        /**
         * @notice Get the approved address for a single NFT
         * @dev Throws if `_tokenId` is not a valid NFT.
         * @param _tokenId The NFT to find the approved address for
         * @return The approved address for this NFT, or the zero address if there is none
         */
        function getApproved(uint256 _tokenId) external view returns (address);
        /**
         * @notice Query if an address is an authorized operator for another address
         * @param _owner The address that owns the NFTs
         * @param _operator The address that acts on behalf of the owner
         * @return True if `_operator` is an approved operator for `_owner`, false otherwise
         */
        function isApprovedForAll(address _owner, address _operator) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @title ERC721 token receiver interface
     * @dev Interface for any contract that wants to support safeTransfers from ERC721 asset contracts.
     *  Note: the ERC-165 identifier for this interface is 0x150b7a02.
     */
    interface IERC721Receiver {
        /** 
         * @notice Handle the receipt of an NFT
         * @dev The ERC721 smart contract calls this function on the recipient
         *  after a `transfer`. This function MAY throw to revert and reject the
         *  transfer. Return of other than the magic value MUST result in the
         *  transaction being reverted.
         *  Note: the contract address is always the message sender.
         * @param _operator The address which called `safeTransferFrom` function
         * @param _from The address which previously owned the token
         * @param _tokenId The NFT identifier which is being transferred
         * @param _data Additional data with no specified format
         * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
         *  unless throwing
         */
        function onERC721Received(
            address _operator, 
            address _from, 
            uint256 _tokenId, 
            bytes calldata _data
        ) external returns(bytes4);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./ERC3525.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/security/Pausable.sol";
    contract LOOTaDOG is ERC3525, Ownable, Pausable {
        bool private _allow_transfer;
        bool private _allow_transfer_value;
        address private _signer;
        mapping(uint256 => bool) private _order_ids;
        string private _baseTokenURI;
        uint256 private _token_count_limit;
        /* Represents an un-minted NFT, which has not yet been recorded into the blockchain. A signed voucher can be redeemed for a real NFT using the redeem function. */
        struct Voucher {
            uint256 id;
            /* The tokenId  */
            uint256 tokenId;
            /* The amount  */
            uint256 amount;
            /*expiration Time*/
            uint256 expirationTime;
            /*owner*/
            address owner;
        }
        constructor() ERC3525("LOOTaDOG Pass Card", "LADT", 2) {
            _allow_transfer = false;
            _allow_transfer_value = false;
            _signer = msg.sender;
            _token_count_limit = 1000;
        }
        function setBaseURI(string calldata baseURI) external onlyOwner {
            _baseTokenURI = baseURI;
        }
        function _baseURI() internal view virtual override returns (string memory) {
            return _baseTokenURI;
        }
        function queryId(uint256 id) public view returns (bool) {
            return _order_ids[id];
        }
        function blockId(uint256 id) public {
            _order_ids[id] = true;
        }
        function pause() public onlyOwner {
            _pause();
        }
        function unpause() public onlyOwner {
            _unpause();
        }
        function mintToken(
            uint256 id,
            uint256 amount,
            uint256 expirationTime,
            address owner,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public payable whenNotPaused {
            require(block.timestamp < expirationTime, "Expired voucher");
            require(totalSupply() < _token_count_limit, "Sold out");
            require(balanceOf(msg.sender) == 0, "Already minted");
            require(_order_ids[id] == false, "Duplicate id");
            require(owner == msg.sender, "Invalid owner");
            _verifySign(Voucher(id, 0, amount, expirationTime, owner), v, r, s);
            _mint(msg.sender, 1, amount);
            _order_ids[id] = true;
        }
        function mintValue(
            uint256 id,
            uint256 tokenId,
            uint256 amount,
            uint256 expirationTime,
            address owner,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public payable whenNotPaused {
            require(block.timestamp < expirationTime, "Expired voucher");
            require(_order_ids[id] == false, "Duplicate id");
            require(owner == msg.sender, "Invalid owner");
            require(amount > 0, "Invalid amount");
            _verifySign(Voucher(id, tokenId, amount, expirationTime, owner), v, r, s);
            _mintValue(tokenId, amount);
            _order_ids[id] = true;
        }
        function transferFrom(
            uint256 fromTokenId_,
            address to_,
            uint256 value_
        ) public payable virtual override whenNotPaused returns (uint256) {
            require(_allow_transfer_value, "Value does not allow transfer");
            return super.transferFrom(fromTokenId_, to_, value_);
        }
        function transferFrom(
            uint256 fromTokenId_,
            uint256 toTokenId_,
            uint256 value_
        ) public payable virtual override whenNotPaused {
            super.transferFrom(fromTokenId_, toTokenId_, value_);
        }
        function transferFrom(
            address from_,
            address to_,
            uint256 tokenId_
        ) public payable virtual override whenNotPaused {
            require(_allow_transfer, "Token does not allow transfer");
            super.transferFrom(from_, to_, tokenId_);
        }
        function safeTransferFrom(
            address from_,
            address to_,
            uint256 tokenId_,
            bytes memory data_
        ) public payable virtual override whenNotPaused {
            require(_allow_transfer, "Token does not allow transfer");
            super.safeTransferFrom(from_, to_, tokenId_, data_);
        }
        function safeTransferFrom(
            address from_,
            address to_,
            uint256 tokenId_
        ) public payable virtual override whenNotPaused {
            require(_allow_transfer, "Token does not allow transfer");
            super.safeTransferFrom(from_, to_, tokenId_);
        }
        function setTokenCountLimit(uint256 count) public onlyOwner {
            _token_count_limit = count;
        }
        function getTokenCountLimit() public view returns (uint256) {
            return _token_count_limit;
        }
        function setSigner(address addr) public onlyOwner {
            _signer = addr;
        }
        function getSigner() public view returns (address) {
            return _signer;
        }
        bytes32 private constant salt = 0xf2d857f4a3edcb9b78b4d503bfe733db1e3f6cdc2b7971ee739626c97e86a558;
        //string private constant EIP712_DOMAIN ="EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)";
        bytes32 private constant EIP712_DOMAIN_TYPEHASH =
            0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472; //keccak256(bytes(EIP712_DOMAIN));
        //string private constant VOUCHER_TYPE =
        //"Voucher(uint256 id,uint256 tokenId,uint256 amount,uint256 expirationTime,address owner)";
        bytes32 private constant VOUCHER_TYPEHASH = 0x4bd317336ea30fdfa31168cda381dacdf9ed1dd92eda94108d28629b6cf9b8c7; //keccak256(bytes(VOUCHER_TYPEHASH));
        function _verifySign(
            Voucher memory voucher,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal view {
            address signer = ecrecover(_hashVoucher(voucher), v, r, s);
            require(_signer == signer, "Invalid signer");
        }
        /// @notice Returns a hash of the given NFTVoucher, prepared using EIP712 typed data hashing rules.
        /// @param voucher An NFTVoucher describing an unminted NFT.
        function _hashVoucher(Voucher memory voucher) internal view returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x01", _hashDomainSeparator(), _encodeVoucher(voucher)));
        }
        function _encodeVoucher(Voucher memory voucher) internal pure returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        VOUCHER_TYPEHASH,
                        voucher.id,
                        voucher.tokenId,
                        voucher.amount,
                        voucher.expirationTime,
                        voucher.owner
                    )
                );
        }
        function _hashDomainSeparator() internal view returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        EIP712_DOMAIN_TYPEHASH,
                        keccak256(bytes("LOOTaDOG Dapp")),
                        keccak256(bytes("1")),
                        _getChainID(),
                        address(this),
                        salt
                    )
                );
        }
        /*Since v0.5.12, Solidity provides a CHAINID OPCODE in assembly */
        function _getChainID() internal view returns (uint256) {
            return block.chainid;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface IERC3525MetadataDescriptor {
        function constructContractURI() external view returns (string memory);
        function constructSlotURI(uint256 slot) external view returns (string memory);
        
        function constructTokenURI(uint256 tokenId) external view returns (string memory);
    }