// File: src/PSYCHOLimited.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

import "./IPSYCHOLimited.sol";
import "./PSYCHOSetup.sol";

/// @title See {IPSYCHOLimited}
/// @notice See {IPSYCHOLimited}
contract PSYCHOLimited is IPSYCHOLimited, PSYCHOSetup {
    uint256 private _countMaster = 0;
    uint256 private _count = 0;

    /// @dev See {IPSYCHOLimited-extension}
    function extension(uint256 _avatarId, string memory _extension)
        public
        payable
        override(IPSYCHOLimited)
    {
        if (!_isApprovedOrOwner(msg.sender, _avatarId)) {
            revert NonApprovedNonOwner(
                isApprovedForAll(ownerOf(_avatarId), msg.sender),
                getApproved(_avatarId),
                ownerOf(_avatarId),
                msg.sender
            );
        }
        if (!_isApprovedOwnerOrOwnership(msg.sender)) {
            if (msg.value < _fee(1)) {
                revert FundAccountWith(_fee(1) - msg.value);
            }
        }
        _setCustomExtension(_extension, _avatarId);
    }

    /// @dev See {IPSYCHOLimited-fee}
    function fee(uint256 _multiplier)
        public
        view
        override(IPSYCHOLimited)
        returns (uint256)
    {
        return _fee(_multiplier);
    }

    /// @dev See {IPSYCHOLimited-stock}
    function stock() public view override(IPSYCHOLimited) returns (uint256) {
        if (_generative()) {
            return 1001 - _count;
        } else {
            return 0;
        }
    }

    /// @dev See {IPSYCHOLimited-mint}
    function mint(uint256 _quantity)
        public
        payable
        override(IPSYCHOLimited)
    {
        if (!_isApprovedOwnerOrOwnership(msg.sender)) {
            if (stock() == 0) {
                revert StockRemaining(stock());
            }
            if (msg.value < _fee(_quantity)) {
                revert FundAccountWith(_fee(_quantity) - msg.value);
            }
            if (_count + _quantity > 1001) {
                revert ExceedsGenerationLimitBy((_count + _quantity) - 1001);
            }
            if (_quantity > 20) {
                revert ExceedsGenerationLimitBy(_quantity - 20);
            }
            _count += _quantity;
        } else {
            if (_countMaster + _quantity > 99) {
                revert ExceedsGenerationLimitBy(
                    (_countMaster + _quantity) - 99
                );
            }
            _countMaster += _quantity;
        }
        _eoaMint(msg.sender, _quantity);
    }

    /// @dev See {IPSYCHOLimited-burn}
    function burn(uint256 _avatarId) public override(IPSYCHOLimited) {
        if (tx.origin != msg.sender) {
            revert TxOriginNonSender(tx.origin, msg.sender);
        }
        _burn(msg.sender, _avatarId);
        if (!_isApprovedOwnerOrOwnership(msg.sender)) {
            _count -= 1;
        } else {
            if (_count > 0) {
                _count -= 1;
            } else {
                _countMaster -= 1;
            }
        }
    }
}

// File: src/IPSYCHOLimited.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

/// @title PSYCHO Limited
/// @notice A network of fashionable (limited) avatars
interface IPSYCHOLimited {
    /// @notice Set custom json extension for avatar
    /// @param _avatarId designated json extension
    /// @param _extension should not have {} in json
    /// @dev Requires minimum `fee(1)`
    function extension(uint256 _avatarId, string memory _extension)
        external
        payable;

    /// @notice Mint and extension fee
    /// @param _quantity multiplied by mint quantity or 1 for extension
    /// @return Wei fee
    function fee(uint256 _quantity) external view returns (uint256);

    /// @notice Available avatars to mint
    /// @return Avatar stock
    function stock() external view returns (uint256);

    /// @notice Mints up to 1001 avatars
    /// @param _quantity max 20 per transaction
    /// @dev Requires minimum `fee(_quantity)` and `stock() != 0`
    function mint(uint256 _quantity) external payable;

    /// @notice Burn an avatar
    /// @param _avatarId to burn
    /// @dev Subtracts the avatar mint counter by 1
    function burn(uint256 _avatarId) external;
}

// File: src/PSYCHOSetup.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

import "@0xver/solver/supports/ERC721Supports.sol";
import "./IPSYCHOLimitedErrors.sol";

contract PSYCHOSetup is IPSCYHOLimitedErrors, ERC721Supports {
    error InitiateStatusIs(bool _status);

    bool private _initiated = false;
    bool private _locked = false;

    uint256 private _weiFee = 200000000000000000;

    mapping(uint256 => uint256) private _block;

    event Withdraw(address operator, address receiver, uint256 value);

    constructor() ERC721Metadata("PSYCHO Limited", "PSYCHO") Owner(msg.sender) {
        _setDefaultExtension(
            '"image":"ipfs://bafybeidob7iaynjg6h6c3igqnac2qnprlzsfatybuqkxhcizcgpfowwgm4","animation_url":"ipfs://bafybeihmygiurvygn7oaruaz66njkvlicbfg7lnsc64ttxbc3o3x4fezfi"'
        );
        _mint(msg.sender, 1);
    }

    receive() external payable {}

    fallback() external payable {}

    function withdraw(address _to) public ownership {
        _withdraw(_to);
    }

    function initialize() public ownership {
        if (_initiated == false) {
            _initiated = true;
        } else {
            revert InitiateStatusIs(_initiated);
        }
    }

    function setFee(uint256 _wei) public ownership {
        _weiFee = _wei;
    }

    function renounce(bool _bool) public ownership {
        require(_bool == true);
        _weiFee = 0;
        _withdraw(msg.sender);
        _transferOwnership(address(0));
        _locked = true;
    }

    function renounced() public view returns (bool) {
        return _locked;
    }

    function _generative() internal view returns (bool) {
        if (totalSupply() != 1101) {
            return _initiated;
        } else {
            return false;
        }
    }

    function _fee(uint256 _multiplier) internal view returns (uint256) {
        return _weiFee * _multiplier;
    }

    function _mintHook(uint256 _avatarId) internal override(ERC721) {
        _block[_avatarId] = block.number;
    }

    function _defaultExtensionTokenURI(uint256 _avatarId)
        internal
        view
        override(ERC721Metadata)
        returns (bytes memory)
    {
        return
            abi.encodePacked(
                _coreTokenURI(_avatarId),
                ",",
                _description(_avatarId),
                ",",
                _defaultExtensionString(),
                ",",
                _attributes(_avatarId)
            );
    }

    function _customExtensionTokenURI(uint256 _avatarId)
        internal
        view
        override(ERC721Metadata)
        returns (bytes memory)
    {
        return
            abi.encodePacked(
                _description(_avatarId),
                ",",
                _customExtensionString(_avatarId),
                ",",
                _attributes(_avatarId)
            );
    }

    function _description(uint256 _avatarId)
        internal
        view
        returns (bytes memory)
    {
        return
            abi.encodePacked(
                '"description":"',
                Encode.toString(_block[_avatarId]),
                '"'
            );
    }

    function _attributes(uint256 _avatarId)
        internal
        view
        returns (bytes memory)
    {
        return
            abi.encodePacked(
                '"attributes":[{"trait_type":"Block","value":"',
                Encode.toHexString(_block[_avatarId]),
                '"}]'
            );
    }

    function _withdraw(address _to) private {
        uint256 balance = address(this).balance;
        (bool success, ) = payable(_to).call{value: address(this).balance}("");
        require(success, "ETH_TRANSFER_FAILED");
        emit Withdraw(msg.sender, _to, balance);
    }
}

// File: src/IPSYCHOLimitedErrors.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

interface IPSCYHOLimitedErrors {
    error ExceedsGenerationLimitBy(uint256 _exceeds);

    error FundAccountWith(uint256 _amount);

    error StockRemaining(uint256 _stock);
}

// File: @0xver/solver/supports/ERC721Supports.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

import "../interface/IERC165.sol";
import "../auth/Owner.sol";
import "../token/metadata/ERC721Metadata.sol";

abstract contract ERC721Supports is IERC165, Owner, ERC721Metadata {
    function supportsInterface(bytes4 interfaceId)
        public
        pure
        virtual
        override(IERC165)
        returns (bool)
    {
        return
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IERC173).interfaceId ||
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC721Receiver).interfaceId;
    }
}

// File: @0xver/solver/interface/IERC165.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: @0xver/solver/auth/Owner.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

import "../interface/IERC173.sol";
import "../interface/errors/IERC173Errors.sol";

contract Owner is IERC173, IERC173Errors {
    address private _owner;
    address private _getApprovedOwner;

    constructor(address owner_) {
        _transferOwnership(owner_);
    }

    modifier ownership() {
        if (owner() != msg.sender) {
            revert NonOwnership(owner(), msg.sender);
        }
        _;
    }

    function owner() public view virtual override(IERC173) returns (address) {
        return _owner;
    }

    function transferOwnership(address _to)
        public
        virtual
        override(IERC173)
        ownership
    {
        if (_to == address(0)) {
            revert TransferOwnershipToZeroAddress(owner(), _to);
        }
        _transferOwnership(_to);
    }

    function approveOwnership(address _approved) public virtual ownership {
        _getApprovedOwner = _approved;
    }

    function getApprovedOwner() public view virtual returns (address) {
        return _getApprovedOwner;
    }

    function _isApprovedOwnerOrOwnership(address _address)
        internal
        view
        virtual
        returns (bool)
    {
        return _getApprovedOwner == _address || _owner == _address;
    }

    function _transferOwnership(address _to) internal virtual {
        address _from = _owner;
        _owner = _to;
        emit OwnershipTransferred(_from, _to);
    }
}

// File: @0xver/solver/token/metadata/ERC721Metadata.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

import "../ERC721.sol";
import "../../interface/metadata/IERC721Metadata.sol";
import "../../library/Encode.sol";

contract ERC721Metadata is ERC721, IERC721Metadata {
    string private _name;
    string private _symbol;
    string private _defaultExtension;
    mapping(uint256 => string) private _customExtension;

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _defaultExtension = "";
    }

    function name()
        public
        view
        virtual
        override(IERC721Metadata)
        returns (string memory)
    {
        return _name;
    }

    function symbol()
        public
        view
        virtual
        override(IERC721Metadata)
        returns (string memory)
    {
        return _symbol;
    }

    function tokenURI(uint256 _tokenId)
        public
        view
        virtual
        override(IERC721Metadata)
        returns (string memory)
    {
        bytes memory core = _coreTokenURI(_tokenId);
        bytes memory extension;
        if (
            Encode.toBytes32(_customExtension[_tokenId]) == Encode.toBytes32("")
        ) {
            if (Encode.toBytes32(_defaultExtension) == Encode.toBytes32("")) {
                delete extension;
            } else {
                delete extension;
                core = _defaultExtensionTokenURI(_tokenId);
            }
        } else {
            extension = abi.encodePacked(
                ",",
                _customExtensionTokenURI(_tokenId)
            );
        }
        bytes memory data = abi.encodePacked("{", core, extension, "}");
        if (ownerOf(_tokenId) == address(0)) {
            return "INVALID_ID";
        } else {
            return
                string(
                    abi.encodePacked(
                        "data:application/json;base64,",
                        Encode.toBase64(data)
                    )
                );
        }
    }

    function _coreTokenURI(uint256 _tokenId)
        internal
        view
        virtual
        returns (bytes memory)
    {
        return
            abi.encodePacked(
                '"name":"',
                name(),
                " #",
                Encode.toString(_tokenId),
                '"'
            );
    }

    function _defaultExtensionTokenURI(uint256 _tokenId)
        internal
        view
        virtual
        returns (bytes memory)
    {
        return
            abi.encodePacked(
                _coreTokenURI(_tokenId),
                ",",
                _defaultExtensionString()
            );
    }

    function _customExtensionTokenURI(uint256 _tokenId)
        internal
        view
        virtual
        returns (bytes memory)
    {
        return abi.encodePacked(_customExtensionString(_tokenId));
    }

    function _defaultExtensionString()
        internal
        view
        virtual
        returns (string memory)
    {
        return _defaultExtension;
    }

    function _customExtensionString(uint256 _tokenId)
        internal
        view
        virtual
        returns (string memory)
    {
        return _customExtension[_tokenId];
    }

    function _setDefaultExtension(string memory _extension) internal virtual {
        _defaultExtension = _extension;
    }

    function _setCustomExtension(string memory _extension, uint256 _tokenId)
        internal
        virtual
    {
        _customExtension[_tokenId] = _extension;
    }
}

// File: @0xver/solver/interface/IERC173.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

interface IERC173 {
    event OwnershipTransferred(address indexed _from, address indexed _to);

    function owner() external view returns (address);

    function transferOwnership(address _to) external;
}

// File: @0xver/solver/interface/errors/IERC173Errors.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

interface IERC173Errors {
    error NonOwnership(address _owner, address _sender);

    error TransferOwnershipToZeroAddress(address _from, address _to);
}

// File: @0xver/solver/token/ERC721.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

import "../interface/IERC721.sol";
import "../interface/errors/IERC721Errors.sol";
import "../interface/receiver/IERC721Receiver.sol";

contract ERC721 is IERC721, IERC721Errors {
    uint256 private _currentId;
    uint256 private _subtractId;
    mapping(address => uint256) private _balanceOf;
    mapping(uint256 => address) private _ownerOf;
    mapping(uint256 => address) private _getApproved;
    mapping(address => mapping(address => bool)) private _isApprovedForAll;

    function balanceOf(address _owner)
        public
        view
        virtual
        override(IERC721)
        returns (uint256)
    {
        return _balanceOf[_owner];
    }

    function ownerOf(uint256 _tokenId)
        public
        view
        virtual
        override(IERC721)
        returns (address)
    {
        return _ownerOf[_tokenId];
    }

    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    ) public virtual override(IERC721) {
        safeTransferFrom(_from, _to, _tokenId, "");
    }

    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes memory _data
    ) public virtual override(IERC721) {
        if (!_isApprovedOrOwner(msg.sender, _tokenId)) {
            revert NonApprovedNonOwner(
                _isApprovedForAll[_ownerOf[_tokenId]][msg.sender],
                _getApproved[_tokenId],
                _ownerOf[_tokenId],
                msg.sender
            );
        }
        _transfer(_from, _to, _tokenId);
        _onERC721Received(_from, _to, _tokenId, _data);
    }

    function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    ) public virtual override(IERC721) {
        _transfer(_from, _to, _tokenId);
    }

    function approve(address _approved, uint256 _tokenId)
        public
        virtual
        override(IERC721)
    {
        if (_ownerOf[_tokenId] != msg.sender) {
            revert NonOwnerApproval(_ownerOf[_tokenId], msg.sender);
        }
        _getApproved[_tokenId] = _approved;
        emit Approval(msg.sender, _approved, _tokenId);
    }

    function setApprovalForAll(address _operator, bool _approved)
        public
        virtual
        override(IERC721)
    {
        _isApprovedForAll[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    function getApproved(uint256 _tokenId)
        public
        view
        virtual
        override(IERC721)
        returns (address)
    {
        return _getApproved[_tokenId];
    }

    function isApprovedForAll(address _owner, address _operator)
        public
        view
        virtual
        override(IERC721)
        returns (bool)
    {
        return _isApprovedForAll[_owner][_operator];
    }

    function totalSupply() public view virtual returns (uint256) {
        return _currentId - _subtractId;
    }

    function _mintHook(uint256 _tokenId) internal virtual {}

    function _burnHook(uint256 _tokenId) internal virtual {}

    function _transferHook(
        address _from,
        address _to,
        uint256 _tokenId
    ) internal virtual {}

    function _isApprovedOrOwner(address _address, uint256 _tokenId)
        internal
        view
        virtual
        returns (bool)
    {
        return
            _ownerOf[_tokenId] == _address ||
            _isApprovedForAll[_ownerOf[_tokenId]][_address] ||
            _getApproved[_tokenId] == _address;
    }

    function _transfer(
        address _from,
        address _to,
        uint256 _tokenId
    ) internal virtual {
        if (!_isApprovedOrOwner(msg.sender, _tokenId)) {
            revert NonApprovedNonOwner(
                _isApprovedForAll[_ownerOf[_tokenId]][msg.sender],
                _getApproved[_tokenId],
                _ownerOf[_tokenId],
                msg.sender
            );
        }
        if (_to == address(0)) {
            revert TransferTokenToZeroAddress(_from, _to, _tokenId);
        }
        _transferHook(_from, _to, _tokenId);
        delete _getApproved[_tokenId];
        unchecked {
            _balanceOf[_from] -= 1;
            _balanceOf[_to] += 1;
        }
        _ownerOf[_tokenId] = _to;
        emit Transfer(_from, _to, _tokenId);
    }

    function _totalMinted() internal view virtual returns (uint256) {
        return _currentId;
    }

    function _totalBurned() internal view virtual returns (uint256) {
        return _subtractId;
    }

    function _safeMint(address _to) internal virtual {
        _safeMint(_to, "");
    }

    function _safeMint(address _to, bytes memory _data) internal virtual {
        _mint(_to, 1);
        if (!_onERC721Received(address(0), _to, _currentId, _data)) {
            revert TransferToNonERC721Receiver(_to);
        }
    }

    function _eoaMint(address _to, uint256 _quantity) internal virtual {
        if (tx.origin != msg.sender) {
            revert TxOriginNonSender(tx.origin, msg.sender);
        }
        _mint(_to, _quantity);
    }

    function _mint(address _to, uint256 _quantity) internal virtual {
        unchecked {
            for (uint256 i = 0; i < _quantity; i++) {
                uint256 _tokenId = _currentId + i + 1;
                _mintHook(_tokenId);
                _ownerOf[_tokenId] = _to;
                emit Transfer(address(0), _to, _tokenId);
            }
            _balanceOf[_to] += _quantity;
            _currentId += _quantity;
        }
    }

    function _burn(address _from, uint256 _tokenId) internal virtual {
        if (!_isApprovedOrOwner(msg.sender, _tokenId)) {
            revert NonApprovedNonOwner(
                _isApprovedForAll[_ownerOf[_tokenId]][msg.sender],
                _getApproved[_tokenId],
                _ownerOf[_tokenId],
                msg.sender
            );
        }
        delete _getApproved[_tokenId];
        _ownerOf[_tokenId] = address(0);
        unchecked {
            _balanceOf[_from] -= 1;
            _subtractId += 1;
        }
        _burnHook(_tokenId);
        emit Transfer(_from, address(0), _tokenId);
    }

    function _onERC721Received(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (_to.code.length > 0) {
            try
                IERC721Receiver(_to).onERC721Received(
                    msg.sender,
                    _from,
                    _tokenId,
                    _data
                )
            returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert TransferToNonERC721Receiver(_to);
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }
}

// File: @0xver/solver/interface/metadata/IERC721Metadata.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

interface IERC721Metadata {
    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function tokenURI(uint256 _tokenId) external view returns (string memory);
}

// File: @0xver/solver/library/Encode.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

import "./Log.sol";

library Encode {
    string internal constant _TABLE =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    function toBase64(bytes memory _data)
        internal
        pure
        returns (string memory)
    {
        if (_data.length == 0) return "";
        string memory table = _TABLE;
        string memory result = new string(4 * ((_data.length + 2) / 3));
        assembly {
            let tablePtr := add(table, 1)
            let resultPtr := add(result, 32)
            for {
                let dataPtr := _data
                let endPtr := add(_data, mload(_data))
            } lt(dataPtr, endPtr) {

            } {
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)
                mstore8(
                    resultPtr,
                    mload(add(tablePtr, and(shr(18, input), 0x3F)))
                )
                resultPtr := add(resultPtr, 1)
                mstore8(
                    resultPtr,
                    mload(add(tablePtr, and(shr(12, input), 0x3F)))
                )
                resultPtr := add(resultPtr, 1)
                mstore8(
                    resultPtr,
                    mload(add(tablePtr, and(shr(6, input), 0x3F)))
                )
                resultPtr := add(resultPtr, 1)
                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1)
            }
            switch mod(mload(_data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }
        return result;
    }

    function toBytes32(string memory _string) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(_string));
    }

    function toString(uint256 _value) internal pure returns (string memory) {
        unchecked {
            uint256 _ptr;
            uint256 _length = Log.log10(_value) + 1;
            string memory buffer = new string(_length);
            assembly {
                _ptr := add(buffer, add(32, _length))
            }
            while (true) {
                _ptr--;
                assembly {
                    mstore8(_ptr, byte(mod(_value, 10), _SYMBOLS))
                }
                _value /= 10;
                if (_value == 0) break;
            }
            return buffer;
        }
    }

    function toHexString(uint256 _value) internal pure returns (string memory) {
        unchecked {
            return toHexString(_value, Log.log256(_value) + 1);
        }
    }

    function toHexString(address _addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(_addr)), _ADDRESS_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] = _SYMBOLS[_value & 0xf];
            _value >>= 4;
        }
        require(_value == 0);
        return string(buffer);
    }
}

// File: @0xver/solver/interface/IERC721.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

interface IERC721 {
    event Transfer(
        address indexed _from,
        address indexed _to,
        uint256 indexed _tokenId
    );

    event Approval(
        address indexed _owner,
        address indexed _approved,
        uint256 indexed _tokenId
    );

    event ApprovalForAll(
        address indexed _owner,
        address indexed _operator,
        bool _approved
    );

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

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

    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes calldata _data
    ) external;

    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    ) external;

    function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    ) external;

    function approve(address _approved, uint256 _tokenId) external;

    function setApprovalForAll(address _operator, bool _approved) external;

    function getApproved(uint256 _tokenId) external view returns (address);

    function isApprovedForAll(address _owner, address _operator)
        external
        view
        returns (bool);
}

// File: @0xver/solver/interface/errors/IERC721Errors.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

interface IERC721Errors {
    error NonApprovedNonOwner(
        bool _isApprovedForAll,
        address _getApproved,
        address _ownerOf,
        address _sender
    );

    error NonOwnerApproval(address _ownerOf, address _sender);

    error TransferTokenToZeroAddress(
        address _from,
        address _to,
        uint256 _tokenId
    );

    error TransferToNonERC721Receiver(address _contract);

    error TxOriginNonSender(address _origin, address _sender);
}

// File: @0xver/solver/interface/receiver/IERC721Receiver.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

interface IERC721Receiver {
    function onERC721Received(
        address _operator,
        address _from,
        uint256 _tokenId,
        bytes calldata _data
    ) external returns (bytes4);
}

// File: @0xver/solver/library/Log.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity ^0.8.4;

library Log {
    function log2(uint256 _value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (_value >> 128 > 0) {
                _value >>= 128;
                result += 128;
            }
            if (_value >> 64 > 0) {
                _value >>= 64;
                result += 64;
            }
            if (_value >> 32 > 0) {
                _value >>= 32;
                result += 32;
            }
            if (_value >> 16 > 0) {
                _value >>= 16;
                result += 16;
            }
            if (_value >> 8 > 0) {
                _value >>= 8;
                result += 8;
            }
            if (_value >> 4 > 0) {
                _value >>= 4;
                result += 4;
            }
            if (_value >> 2 > 0) {
                _value >>= 2;
                result += 2;
            }
            if (_value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    function log10(uint256 _value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (_value >= 10**64) {
                _value /= 10**64;
                result += 64;
            }
            if (_value >= 10**32) {
                _value /= 10**32;
                result += 32;
            }
            if (_value >= 10**16) {
                _value /= 10**16;
                result += 16;
            }
            if (_value >= 10**8) {
                _value /= 10**8;
                result += 8;
            }
            if (_value >= 10**4) {
                _value /= 10**4;
                result += 4;
            }
            if (_value >= 10**2) {
                _value /= 10**2;
                result += 2;
            }
            if (_value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    function log256(uint256 _value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (_value >> 128 > 0) {
                _value >>= 128;
                result += 16;
            }
            if (_value >> 64 > 0) {
                _value >>= 64;
                result += 8;
            }
            if (_value >> 32 > 0) {
                _value >>= 32;
                result += 4;
            }
            if (_value >> 16 > 0) {
                _value >>= 16;
                result += 2;
            }
            if (_value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }
}