creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/example/UnderworldMirror.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {DN404} from \"../DN404.sol\";\nimport {DN404Mirror} from \"../DN404Mirror.sol\";\nimport {UUPSUpgradeable} from \"solady/utils/UUPSUpgradeable.sol\";\n\n/**\n * @title SimpleDN404\n * @notice Sample DN404 contract that demonstrates the owner selling fungile tokens.\n * When a user has at least one base unit (10^18) amount of tokens, they will automatically receive an NFT.\n * NFTs are minted as an address accumulates each base unit amount of tokens.\n */\ncontract UnderworldMirror is DN404Mirror, UUPSUpgradeable {\n    // init with `address(1)` to prevent double-initializing\n    constructor() payable DN404Mirror(address(1)) {}\n\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    function _checkOwner() internal view {\n        if (msg.sender != owner()) revert Unauthorized();\n    }\n\n    function _authorizeUpgrade(address) internal override onlyOwner {}\n\n    function initialize() public payable {\n        // if deployer was set, can not initialize again\n        if (_getDN404NFTStorage().deployer != address(0)) revert Unauthorized();\n\n        _initializeDN404Mirror(msg.sender);\n    }\n}\n"
      },
      "src/DN404.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {LibBitmap} from \"solady/utils/LibBitmap.sol\";\n\n/// @title DN404\n/// @notice DN404 is a hybrid ERC20 and ERC721 implementation that mints\n/// and burns NFTs based on an account's ERC20 token balance.\n///\n/// @author vectorized.eth (@optimizoor)\n/// @author Quit (@0xQuit)\n/// @author Michael Amadi (@AmadiMichaels)\n/// @author cygaar (@0xCygaar)\n/// @author Thomas (@0xjustadev)\n/// @author Harrison (@PopPunkOnChain)\n///\n/// @dev Note:\n/// - The ERC721 data is stored in this base DN404 contract, however a\n///   DN404Mirror contract ***MUST*** be deployed and linked during\n///   initialization.\nabstract contract DN404 {\n    using LibBitmap for LibBitmap.Bitmap;\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                           EVENTS                           */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.\n    event Transfer(address indexed from, address indexed to, uint256 amount);\n\n    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.\n    event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n    /// @dev Emitted when `target` sets their skipNFT flag to `status`.\n    event SkipNFTSet(address indexed target, bool status);\n\n    /// @dev `keccak256(bytes(\"Transfer(address,address,uint256)\"))`.\n    uint256 private constant _TRANSFER_EVENT_SIGNATURE =\n        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;\n\n    /// @dev `keccak256(bytes(\"Approval(address,address,uint256)\"))`.\n    uint256 private constant _APPROVAL_EVENT_SIGNATURE =\n        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;\n\n    /// @dev `keccak256(bytes(\"SkipNFTSet(address,bool)\"))`.\n    uint256 private constant _SKIP_NFT_SET_EVENT_SIGNATURE =\n        0xb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d6420393;\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                        CUSTOM ERRORS                       */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Thrown when attempting to double-initialize the contract.\n    error DNAlreadyInitialized();\n\n    /// @dev Thrown when attempting to transfer or burn more tokens than sender's balance.\n    error InsufficientBalance();\n\n    /// @dev Thrown when a spender attempts to transfer tokens with an insufficient allowance.\n    error InsufficientAllowance();\n\n    /// @dev Thrown when minting an amount of tokens that would overflow the max tokens.\n    error TotalSupplyOverflow();\n\n    /// @dev The unit cannot be zero.\n    error InvalidUnit();\n\n    /// @dev Thrown when the caller for a fallback NFT function is not the mirror contract.\n    error SenderNotMirror();\n\n    /// @dev Thrown when attempting to transfer tokens to the zero address.\n    error TransferToZeroAddress();\n\n    /// @dev Thrown when the mirror address provided for initialization is the zero address.\n    error MirrorAddressIsZero();\n\n    /// @dev Thrown when the link call to the mirror contract reverts.\n    error LinkMirrorContractFailed();\n\n    /// @dev Thrown when setting an NFT token approval\n    /// and the caller is not the owner or an approved operator.\n    error ApprovalCallerNotOwnerNorApproved();\n\n    /// @dev Thrown when transferring an NFT\n    /// and the caller is not the owner or an approved operator.\n    error TransferCallerNotOwnerNorApproved();\n\n    /// @dev Thrown when transferring an NFT and the from address is not the current owner.\n    error TransferFromIncorrectOwner();\n\n    /// @dev Thrown when checking the owner or approved address for a non-existent NFT.\n    error TokenDoesNotExist();\n\n    /// @dev Thrown when exchanging the NFTs that locked.\n    error ExchangeTokenLocked();\n\n    /// @dev Thrown when exchanging the same NFTs\n    error ExchangeSameToken();\n\n    /// @dev Thrown when attempting to lock the NFTs that locked,\n    ///      or to unlock the NFTs that unlocked.\n    error TokenLockStatusNoChange();\n\n    /// @dev Thrown when transferring tokens but the balance is insufficient to to maintain locked NFTs.\n    error InsufficientBalanceToMaintainLockedTokens();\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                         CONSTANTS                          */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev The flag to denote that the address data is initialized.\n    uint8 internal constant _ADDRESS_DATA_INITIALIZED_FLAG = 1 << 0;\n\n    /// @dev The flag to denote that the address should skip NFTs.\n    uint8 internal constant _ADDRESS_DATA_SKIP_NFT_FLAG = 1 << 1;\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                          STORAGE                           */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Struct containing an address's token data and settings.\n    struct AddressData {\n        // Auxiliary data.\n        uint56 aux;\n        // Flags for `initialized` and `skipNFT`.\n        uint8 flags;\n        // The alias for the address. Zero means absence of an alias.\n        uint32 addressAlias;\n        // The number of NFT tokens locked.\n        uint32 lockedLength;\n        // The number of NFT tokens owned.\n        uint32 ownedLength;\n        // The token balance in wei.\n        uint96 balance;\n        // snapshot of `accFeePerNFT` when the account fee accrued\n        uint96 feePerNFTSnap;\n    }\n\n    /// @dev A uint32 map in storage.\n    struct Uint32Map {\n        mapping(uint256 => uint256) map;\n    }\n\n    /// @dev A struct to wrap a uint256 in storage.\n    struct Uint256Ref {\n        uint256 value;\n    }\n\n    /// @dev Struct containing the base token contract storage.\n    struct DN404Storage {\n        // Current number of address aliases assigned.\n        uint32 numAliases;\n        // Next NFT ID to assign for a mint.\n        uint32 nextTokenId;\n        // Total number of NFT IDs in the burned pool.\n        uint32 burnedPoolSize;\n        // Total supply of minted NFTs.\n        uint32 totalNFTSupply;\n        // Total supply of tokens.\n        uint96 totalSupply;\n        // Address of the NFT mirror contract.\n        address mirrorERC721;\n        // Mapping of a user alias number to their address.\n        mapping(uint32 => address) aliasToAddress;\n        // Mapping of user operator approvals for NFTs.\n        mapping(address => mapping(address => Uint256Ref)) operatorApprovals;\n        // Mapping of NFT approvals to approved operators.\n        mapping(uint256 => address) nftApprovals;\n        // Bitmap of whether an non-zero NFT approval may exist.\n        LibBitmap.Bitmap mayHaveNFTApproval;\n        // Mapping of user allowances for ERC20 spenders.\n        mapping(address => mapping(address => Uint256Ref)) allowance;\n        // Mapping of NFT IDs owned by an address.\n        mapping(address => Uint32Map) owned;\n        // Mapping of NFT token IDs locked by an address.\n        mapping(address => Uint32Map) locked;\n        // The pool of burned NFT IDs.\n        Uint32Map burnedPool;\n        // Even indices: owner aliases. Odd indices: owned indices.\n        // if NFT token was locked, owned indices are ref to `locked`, otherwise `owned`\n        Uint32Map oo;\n        // Mapping of user account AddressData.\n        mapping(address => AddressData) addressData;\n        // Mapping of NFT token to locked flag\n        LibBitmap.Bitmap tokenLocks;\n        // The number of NFT tokens locked globally.\n        uint32 numLockedNFT;\n        // The number of NFT tokens approved to `this` globally.\n        uint32 numExchangableNFT;\n        // Fee rate to charged per NFT when exchange unlocking NFTs\n        uint16 exchangeNFTFeeBips;\n        // accumulated fee per unlocked NFT should receive\n        uint96 accFeePerNFT;\n    }\n\n    /// @dev Returns a storage pointer for DN404Storage.\n    function _getDN404Storage() internal pure virtual returns (DN404Storage storage $) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // `uint72(bytes9(keccak256(\"DN404_STORAGE\")))`.\n            $.slot := 0xa20d6e21d0e5255308 // Truncate to 9 bytes to reduce bytecode size.\n        }\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                         INITIALIZER                        */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Initializes the DN404 contract with an\n    /// `initialTokenSupply`, `initialTokenOwner` and `mirror` NFT contract address.\n    function _initializeDN404(\n        uint256 initialTokenSupply,\n        address initialSupplyOwner,\n        address mirror\n    ) internal virtual {\n        DN404Storage storage $ = _getDN404Storage();\n\n        if ($.mirrorERC721 != address(0)) revert DNAlreadyInitialized();\n\n        if (mirror == address(0)) revert MirrorAddressIsZero();\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Make the call to link the mirror contract.\n            mstore(0x00, 0x0f4599e5) // `linkMirrorContract(address)`.\n            mstore(0x20, caller())\n            if iszero(and(eq(mload(0x00), 1), call(gas(), mirror, 0, 0x1c, 0x24, 0x00, 0x20))) {\n                mstore(0x00, 0xd125259c) // `LinkMirrorContractFailed()`.\n                revert(0x1c, 0x04)\n            }\n        }\n\n        $.mirrorERC721 = mirror;\n\n        if (_unit() < 10 ** decimals() || _unit() > 10 ** 24) revert InvalidUnit();\n\n        if (initialTokenSupply != 0) {\n            if (initialSupplyOwner == address(0)) {\n                revert TransferToZeroAddress();\n            }\n            if (_totalSupplyOverflows(initialTokenSupply)) {\n                revert TotalSupplyOverflow();\n            }\n\n            $.totalSupply = uint96(initialTokenSupply);\n            AddressData storage initialOwnerAddressData = _addressData(initialSupplyOwner);\n            initialOwnerAddressData.balance = uint96(initialTokenSupply);\n\n            /// @solidity memory-safe-assembly\n            assembly {\n                // Emit the {Transfer} event.\n                mstore(0x00, initialTokenSupply)\n                log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, initialSupplyOwner)))\n            }\n\n            _setSkipNFT(initialSupplyOwner, true);\n        }\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*               BASE UNIT FUNCTION TO OVERRIDE               */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Amount of token balance that is equal to one NFT.\n    function _unit() internal view virtual returns (uint256) {\n        return 10 ** 18;\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*               METADATA FUNCTIONS TO OVERRIDE               */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns the name of the token.\n    function name() public view virtual returns (string memory);\n\n    /// @dev Returns the symbol of the token.\n    function symbol() public view virtual returns (string memory);\n\n    /// @dev Returns the Uniform Resource Identifier (URI) for token `id`.\n    function tokenURI(uint256 id) public view virtual returns (string memory);\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                      ERC20 OPERATIONS                      */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns the decimals places of the token. Always 18.\n    function decimals() public pure returns (uint8) {\n        return 18;\n    }\n\n    /// @dev Returns the amount of tokens in existence.\n    function totalSupply() public view virtual returns (uint256) {\n        return uint256(_getDN404Storage().totalSupply);\n    }\n\n    /// @dev Returns the amount of tokens owned by `owner`.\n    function balanceOf(address owner) public view virtual returns (uint256) {\n        return _getDN404Storage().addressData[owner].balance;\n    }\n\n    /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.\n    function allowance(address owner, address spender) public view returns (uint256) {\n        return _getDN404Storage().allowance[owner][spender].value;\n    }\n\n    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n    ///\n    /// Emits a {Approval} event.\n    function approve(address spender, uint256 amount) public virtual returns (bool) {\n        _approve(msg.sender, spender, amount);\n        return true;\n    }\n\n    /// @dev Transfer `amount` tokens from the caller to `to`.\n    ///\n    /// Will burn sender NFTs if balance after transfer is less than\n    /// the amount required to support the current NFT balance.\n    ///\n    /// Will mint NFTs to `to` if the recipient's new balance supports\n    /// additional NFTs ***AND*** the `to` address's skipNFT flag is\n    /// set to false.\n    ///\n    /// Requirements:\n    /// - `from` must at least have `amount`.\n    ///\n    /// Emits a {Transfer} event.\n    function transfer(address to, uint256 amount) public virtual returns (bool) {\n        DN404Storage storage $ = _getDN404Storage();\n        _pullFeeForTwo($, msg.sender, to);\n        _transfer(msg.sender, to, amount);\n        return true;\n    }\n\n    /// @dev Transfers `amount` tokens from `from` to `to`.\n    ///\n    /// Note: Does not update the allowance if it is the maximum uint256 value.\n    ///\n    /// Will burn sender NFTs if balance after transfer is less than\n    /// the amount required to support the current NFT balance.\n    ///\n    /// Will mint NFTs to `to` if the recipient's new balance supports\n    /// additional NFTs ***AND*** the `to` address's skipNFT flag is\n    /// set to false.\n    ///\n    /// Requirements:\n    /// - `from` must at least have `amount`.\n    /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.\n    ///\n    /// Emits a {Transfer} event.\n    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {\n        DN404Storage storage $ = _getDN404Storage();\n        Uint256Ref storage a = $.allowance[from][msg.sender];\n\n        uint256 allowed = a.value;\n        if (allowed != type(uint256).max) {\n            if (amount > allowed) revert InsufficientAllowance();\n            unchecked {\n                a.value = allowed - amount;\n            }\n        }\n        _pullFeeForTwo($, from, to);\n        _transfer(from, to, amount);\n        return true;\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                INTERNAL TRANSFER FUNCTIONS                 */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Moves `amount` of tokens from `from` to `to`.\n    ///\n    /// Will burn sender NFTs if balance after transfer is less than\n    /// the amount required to support the current NFT balance.\n    ///\n    /// Will mint NFTs to `to` if the recipient's new balance supports\n    /// additional NFTs ***AND*** the `to` address's skipNFT flag is\n    /// set to false.\n    ///\n    /// Emits a {Transfer} event.\n    function _transfer(address from, address to, uint256 amount) internal virtual {\n        if (to == address(0)) revert TransferToZeroAddress();\n\n        DN404Storage storage $ = _getDN404Storage();\n\n        AddressData storage fromAddressData = _addressData(from);\n        AddressData storage toAddressData = _addressData(to);\n\n        _TransferTemps memory t;\n        t.fromOwnedLength = fromAddressData.ownedLength;\n        t.toOwnedLength = toAddressData.ownedLength;\n        t.totalSupply = $.totalSupply;\n\n        if (amount > (t.fromBalance = fromAddressData.balance)) {\n            revert InsufficientBalance();\n        }\n\n        unchecked {\n            t.fromBalance -= amount;\n\n            t.fromLockedLength = fromAddressData.lockedLength;\n            // need enough token to maintain locked NFTs\n            if (t.fromBalance < t.fromLockedLength * _unit()) {\n                revert InsufficientBalanceToMaintainLockedTokens();\n            }\n\n            fromAddressData.balance = uint96(t.fromBalance);\n            toAddressData.balance = uint96(t.toBalance = toAddressData.balance + amount);\n\n            t.numNFTBurns =\n                _zeroFloorSub(t.fromOwnedLength + t.fromLockedLength, t.fromBalance / _unit());\n\n            if (toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG == 0) {\n                if (from == to) t.toOwnedLength = t.fromOwnedLength - t.numNFTBurns;\n                t.numNFTMints = _zeroFloorSub(\n                    t.toBalance / _unit(),\n                    t.toOwnedLength + toAddressData.lockedLength // balance needed for locked and owned\n                );\n            }\n\n            {\n                // cache `address(this)` approvals\n                mapping(address => Uint256Ref) storage thisOperatorApprovals =\n                    $.operatorApprovals[address(this)];\n                // `from` burns NFTs\n                if (thisOperatorApprovals[from].value != 0) {\n                    $.numExchangableNFT -= uint32(t.numNFTBurns);\n                }\n                // `to`mints NFTs\n                if (thisOperatorApprovals[to].value != 0) {\n                    $.numExchangableNFT += uint32(t.numNFTMints);\n                }\n            }\n\n            $.totalNFTSupply = uint32(uint256($.totalNFTSupply) + t.numNFTMints - t.numNFTBurns);\n            Uint32Map storage oo = $.oo;\n            {\n                uint256 n = _min(t.numNFTBurns, t.numNFTMints);\n                if (n != 0) {\n                    t.numNFTBurns -= n;\n                    t.numNFTMints -= n;\n\n                    if (from == to) {\n                        t.toOwnedLength += n;\n                    } else {\n                        _DNDirectLogs memory directLogs = _directLogsMalloc(n, from, to);\n                        Uint32Map storage fromOwned = $.owned[from];\n                        Uint32Map storage toOwned = $.owned[to];\n                        uint32 toAlias = _registerAndResolveAlias(toAddressData, to);\n                        // Direct transfer loop.\n                        do {\n                            uint256 id = _get(fromOwned, --t.fromOwnedLength);\n                            _set(toOwned, t.toOwnedLength, uint32(id));\n                            _setOwnerAliasAndOwnedIndex(oo, id, toAlias, uint32(t.toOwnedLength++));\n                            _removeNFTApproval($, id);\n                            _directLogsAppend(directLogs, id);\n                        } while (--n != 0);\n\n                        _directLogsSend(directLogs, $.mirrorERC721);\n                        fromAddressData.ownedLength = uint32(t.fromOwnedLength);\n                        toAddressData.ownedLength = uint32(t.toOwnedLength);\n                    }\n                }\n            }\n\n            _PackedLogs memory packedLogs = _packedLogsMalloc(t.numNFTBurns + t.numNFTMints);\n            uint256 burnedPoolSize = $.burnedPoolSize;\n            if (t.numNFTBurns != 0) {\n                _packedLogsSet(packedLogs, from, 1);\n                Uint32Map storage fromOwned = $.owned[from];\n                uint256 fromIndex = t.fromOwnedLength;\n                uint256 fromEnd = fromIndex - t.numNFTBurns;\n                fromAddressData.ownedLength = uint32(fromEnd);\n                // Burn loop.\n                do {\n                    uint256 id = _get(fromOwned, --fromIndex);\n                    _setOwnerAliasAndOwnedIndex(oo, id, 0, 0);\n                    _set($.burnedPool, burnedPoolSize++, uint32(id));\n                    _removeNFTApproval($, id);\n                    _packedLogsAppend(packedLogs, id);\n                } while (fromIndex != fromEnd);\n            }\n\n            if (t.numNFTMints != 0) {\n                _packedLogsSet(packedLogs, to, 0);\n                uint256 nextTokenId = $.nextTokenId;\n                Uint32Map storage toOwned = $.owned[to];\n                uint256 toIndex = t.toOwnedLength;\n                uint256 toEnd = toIndex + t.numNFTMints;\n                uint32 toAlias = _registerAndResolveAlias(toAddressData, to);\n                toAddressData.ownedLength = uint32(toEnd);\n                // Mint loop.\n                do {\n                    uint256 id;\n                    if (burnedPoolSize != 0) {\n                        id = _get($.burnedPool, --burnedPoolSize);\n                    } else {\n                        id = nextTokenId;\n                        nextTokenId = id + 1;\n                    }\n                    _set(toOwned, toIndex, uint32(id));\n                    _setOwnerAliasAndOwnedIndex(oo, id, toAlias, uint32(toIndex++));\n                    _packedLogsAppend(packedLogs, id);\n                } while (toIndex != toEnd);\n\n                $.nextTokenId = uint32(nextTokenId);\n            }\n\n            if (packedLogs.logs.length != 0) {\n                $.burnedPoolSize = uint32(burnedPoolSize);\n                _packedLogsSend(packedLogs, $.mirrorERC721);\n            }\n            /// @solidity memory-safe-assembly\n            assembly {\n                // Emit the {Transfer} event.\n                mstore(0x00, amount)\n                // forgefmt: disable-next-item\n                log3(\n                    0x00,\n                    0x20,\n                    _TRANSFER_EVENT_SIGNATURE,\n                    shr(96, shl(96, from)),\n                    shr(96, shl(96, to))\n                )\n            }\n        }\n    }\n\n    /// @dev Transfers token `id` from `from` to `to`.\n    ///\n    /// Requirements:\n    ///\n    /// - Call must originate from the mirror contract.\n    /// - Token `id` must exist.\n    /// - `from` must be the owner of the token.\n    /// - `to` cannot be the zero address.\n    ///   `msgSender` must be the owner of the token, or be approved to manage the token.\n    ///\n    /// Emits a {Transfer} event.\n    function _transferFromNFT(address from, address to, uint256 id, address msgSender)\n        internal\n        virtual\n    {\n        if (to == address(0)) revert TransferToZeroAddress();\n\n        DN404Storage storage $ = _getDN404Storage();\n        Uint32Map storage oo = $.oo;\n\n        if (from != $.aliasToAddress[_get(oo, _ownershipIndex(id))]) {\n            revert TransferFromIncorrectOwner();\n        }\n\n        if (msgSender != from) {\n            if ($.operatorApprovals[msgSender][from].value == 0) {\n                if (msgSender != $.nftApprovals[id]) {\n                    revert TransferCallerNotOwnerNorApproved();\n                }\n            }\n        }\n\n        AddressData storage fromAddressData = _addressData(from);\n        AddressData storage toAddressData = _addressData(to);\n\n        uint256 unit = _unit();\n\n        fromAddressData.balance -= uint96(unit);\n\n        unchecked {\n            toAddressData.balance += uint96(unit);\n\n            _removeNFTApproval($, id);\n\n            uint32 toTransferIdx = _get(oo, _ownedIndex(id));\n            if (LibBitmap.get($.tokenLocks, id)) {\n                // operate `locked` map\n                // delete transferred NFT\n                _delNFTAt($.locked[from], oo, toTransferIdx, --fromAddressData.lockedLength);\n            } else {\n                if ($.operatorApprovals[address(this)][from].value != 0) {\n                    // The unlocked NFTs amount of account `from` will decrease, collecting fees first\n                    _pullFeeForTwo($, from, from);\n                    // `from` lock 1 NFT\n                    --$.numExchangableNFT;\n                }\n\n                // operate `owned` map\n                // delete transferred NFT\n                _delNFTAt($.owned[from], oo, toTransferIdx, --fromAddressData.ownedLength);\n\n                // lock\n                LibBitmap.setTo($.tokenLocks, id, true);\n                ++$.numLockedNFT;\n            }\n\n            // transfer ownership\n            // lock the NFT by default for ERC721 transfer\n            uint256 n = toAddressData.lockedLength++;\n            _set($.locked[to], n, uint32(id));\n            _setOwnerAliasAndOwnedIndex(\n                oo, id, _registerAndResolveAlias(toAddressData, to), uint32(n)\n            );\n        }\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Emit the {Transfer} event.\n            mstore(0x00, unit)\n            // forgefmt: disable-next-item\n            log3(\n                0x00,\n                0x20,\n                _TRANSFER_EVENT_SIGNATURE,\n                shr(96, shl(96, from)),\n                shr(96, shl(96, to))\n            )\n        }\n    }\n\n    function _exchangeNFT(uint256 idX, uint256 idY, address msgSender)\n        internal\n        virtual\n        returns (address x, address y, uint256 exchangeFee)\n    {\n        if (idX == idY) revert ExchangeSameToken();\n\n        DN404Storage storage $ = _getDN404Storage();\n\n        LibBitmap.Bitmap storage tokenLocks = $.tokenLocks;\n        if (_toUint(LibBitmap.get(tokenLocks, idX)) | _toUint(LibBitmap.get(tokenLocks, idY)) != 0)\n        {\n            revert ExchangeTokenLocked();\n        }\n\n        x = _ownerOf(idX);\n        y = _ownerOf(idY);\n        // Only owner or spender can operate the token `idX`\n        if (msgSender != x) {\n            if ($.operatorApprovals[msgSender][x].value == 0) {\n                if (msgSender != $.nftApprovals[idX]) {\n                    revert TransferCallerNotOwnerNorApproved();\n                }\n            }\n        }\n\n        mapping(address => Uint256Ref) storage thisOperatorApprovals =\n            $.operatorApprovals[address(this)];\n        if (thisOperatorApprovals[y].value == 0) {\n            revert ApprovalCallerNotOwnerNorApproved();\n        }\n\n        _removeNFTApproval($, idX);\n        _removeNFTApproval($, idY);\n\n        // collecting fees for account `x` and `y` first\n        _pullFeeForTwo($, x, y);\n\n        Uint32Map storage oo = $.oo;\n\n        // idY to account x, then lock\n        // must transfer `idY` firstly, otherwise the ownedIndex of `idX` is wrong\n        AddressData storage xAddressData = _addressData(x);\n        {\n            uint256 xIndex = _get(oo, _ownedIndex(idX));\n            // remove NFT `idX` from account `x`\n            _delNFTAt($.owned[x], oo, xIndex, --xAddressData.ownedLength);\n        }\n\n        // snapshot owned index of `idY`\n        uint256 yIndex = _get(oo, _ownedIndex(idY));\n\n        {\n            // append `idY` to `locked`\n            uint256 n = xAddressData.lockedLength++;\n            _set($.locked[x], n, uint32(idY));\n            _setOwnerAliasAndOwnedIndex(oo, idY, xAddressData.addressAlias, uint32(n));\n\n            // lock `idY`\n            LibBitmap.setTo(tokenLocks, idY, true);\n            ++$.numLockedNFT;\n        }\n\n        // idX to account y\n        AddressData storage yAddressData = _addressData(y);\n        _setOwnerAliasAndOwnedIndex(oo, idX, yAddressData.addressAlias, uint32(yIndex));\n        _set($.owned[y], yIndex, uint32(idX));\n\n        // transfer nft first, then token, otherwise specified NFT transfer may not success\n        // fee charges in percentage of the unit\n        exchangeFee = $.exchangeNFTFeeBips;\n        if (exchangeFee > 0) {\n            // Only refresh when the balance of `msgSender` will be changed.\n            if (msgSender != x) _pullFeeForTwo($, msgSender, msgSender);\n            unchecked {\n                exchangeFee *= _unit() / 10000;\n                _transfer(msgSender, address(this), exchangeFee);\n                $.accFeePerNFT += uint96(exchangeFee / $.numExchangableNFT);\n            }\n        }\n\n        // If `msgSender` exchanged on behalf of `x`, `msgSender` receive the NFT.\n        if (msgSender != x) _transferFromNFT(x, msgSender, idY, x);\n        // x lock 1 NFT\n        if (thisOperatorApprovals[x].value != 0) {\n            --$.numExchangableNFT;\n        }\n    }\n\n    function _pullFeeForTwo(DN404Storage storage $, address account1, address account2)\n        internal\n        virtual\n    {\n        // Cannot receive fee if `address(this)` has no operator approvals\n        mapping(address => Uint256Ref) storage thisOperatorApprovals =\n            $.operatorApprovals[address(this)];\n        uint256 accFeePerNFT;\n        uint256 accruedFee1;\n        if (thisOperatorApprovals[account1].value > 0) {\n            accFeePerNFT = $.accFeePerNFT;\n            AddressData storage addressData = $.addressData[account1];\n            // only unlocked NFTs receive fee\n            accruedFee1 = (accFeePerNFT - addressData.feePerNFTSnap) * (addressData.ownedLength);\n            addressData.feePerNFTSnap = uint96(accFeePerNFT);\n        }\n        if (account2 != account1) {\n            if (thisOperatorApprovals[account2].value > 0) {\n                if (accFeePerNFT == 0) {\n                    accFeePerNFT = $.accFeePerNFT;\n                }\n                AddressData storage addressData = $.addressData[account2];\n                // only unlocked NFTs receive fee\n                uint256 accrued =\n                    (accFeePerNFT - addressData.feePerNFTSnap) * (addressData.ownedLength);\n                addressData.feePerNFTSnap = uint96(accFeePerNFT);\n                if (accrued > 0) {\n                    _transfer(address(this), account2, accrued);\n                }\n            }\n        }\n        if (accruedFee1 > 0) {\n            _transfer(address(this), account1, accruedFee1);\n        }\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                 INTERNAL APPROVE FUNCTIONS                 */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.\n    ///\n    /// Emits a {Approval} event.\n    function _approve(address owner, address spender, uint256 amount) internal virtual {\n        Uint256Ref storage ref = _getDN404Storage().allowance[owner][spender];\n        if (amount > 0 && ref.value > 0) revert();\n\n        ref.value = amount;\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Emit the {Approval} event.\n            mstore(0x00, amount)\n            // forgefmt: disable-next-item\n            log3(\n                0x00,\n                0x20,\n                _APPROVAL_EVENT_SIGNATURE,\n                shr(96, shl(96, owner)),\n                shr(96, shl(96, spender))\n            )\n        }\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                 DATA HITCHHIKING FUNCTIONS                 */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns the auxiliary data for `owner`.\n    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.\n    /// Auxiliary data can be set for any address, even if it does not have any tokens.\n    function _getAux(address owner) internal view virtual returns (uint56) {\n        return _getDN404Storage().addressData[owner].aux;\n    }\n\n    /// @dev Set the auxiliary data for `owner` to `value`.\n    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.\n    /// Auxiliary data can be set for any address, even if it does not have any tokens.\n    function _setAux(address owner, uint56 value) internal virtual {\n        _getDN404Storage().addressData[owner].aux = value;\n    }\n\n    function _setExchangeNFTFeeRate(uint256 feeBips) internal virtual {\n        if (feeBips > 10000) revert();\n        _getDN404Storage().exchangeNFTFeeBips = uint16(feeBips);\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                     SKIP NFT FUNCTIONS                     */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns true if minting and transferring ERC20s to `owner` will skip minting NFTs.\n    /// Returns false otherwise.\n    function getSkipNFT(address owner) public view virtual returns (bool) {\n        AddressData storage d = _getDN404Storage().addressData[owner];\n        if (d.flags & _ADDRESS_DATA_INITIALIZED_FLAG == 0) {\n            return _hasCode(owner);\n        }\n        return d.flags & _ADDRESS_DATA_SKIP_NFT_FLAG != 0;\n    }\n\n    /// @dev Sets the caller's skipNFT flag to `skipNFT`. Returns true.\n    ///\n    /// Emits a {SkipNFTSet} event.\n    function setSkipNFT(bool skipNFT) public virtual returns (bool) {\n        _setSkipNFT(msg.sender, skipNFT);\n        return true;\n    }\n\n    /// @dev Internal function to set account `owner` skipNFT flag to `state`\n    ///\n    /// Initializes account `owner` AddressData if it is not currently initialized.\n    ///\n    /// Emits a {SkipNFTSet} event.\n    function _setSkipNFT(address owner, bool state) internal virtual {\n        AddressData storage d = _addressData(owner);\n        if ((d.flags & _ADDRESS_DATA_SKIP_NFT_FLAG != 0) != state) {\n            d.flags ^= _ADDRESS_DATA_SKIP_NFT_FLAG;\n        }\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(0x00, iszero(iszero(state)))\n            log2(0x00, 0x20, _SKIP_NFT_SET_EVENT_SIGNATURE, shr(96, shl(96, owner)))\n        }\n    }\n\n    /// @dev Returns a storage data pointer for account `owner` AddressData\n    ///\n    /// Initializes account `owner` AddressData if it is not currently initialized.\n    function _addressData(address owner) internal virtual returns (AddressData storage d) {\n        d = _getDN404Storage().addressData[owner];\n        unchecked {\n            if (d.flags & _ADDRESS_DATA_INITIALIZED_FLAG == 0) {\n                uint256 skipNFT = (_toUint(_hasCode(owner)) * _ADDRESS_DATA_SKIP_NFT_FLAG);\n                d.flags = uint8(skipNFT | _ADDRESS_DATA_INITIALIZED_FLAG);\n            }\n        }\n    }\n\n    /// @dev Returns the `addressAlias` of account `to`.\n    ///\n    /// Assigns and registers the next alias if `to` alias was not previously registered.\n    function _registerAndResolveAlias(AddressData storage toAddressData, address to)\n        internal\n        virtual\n        returns (uint32 addressAlias)\n    {\n        DN404Storage storage $ = _getDN404Storage();\n        addressAlias = toAddressData.addressAlias;\n        if (addressAlias == 0) {\n            unchecked {\n                addressAlias = ++$.numAliases;\n            }\n            toAddressData.addressAlias = addressAlias;\n            $.aliasToAddress[addressAlias] = to;\n            if (addressAlias == 0) revert(); // Overflow.\n        }\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                     MIRROR OPERATIONS                      */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns the address of the mirror NFT contract.\n    function mirrorERC721() public view virtual returns (address) {\n        return _getDN404Storage().mirrorERC721;\n    }\n\n    /// @dev Returns the total NFT supply.\n    function _totalNFTSupply() internal view virtual returns (uint256) {\n        return _getDN404Storage().totalNFTSupply;\n    }\n\n    /// @dev Returns `owner` NFT balance.\n    function _balanceOfNFT(address owner) internal view virtual returns (uint256) {\n        AddressData storage addressData = _getDN404Storage().addressData[owner];\n        return addressData.ownedLength + addressData.lockedLength;\n    }\n\n    /// @dev Returns the owner of token `id`.\n    /// Returns the zero address instead of reverting if the token does not exist.\n    function _ownerAt(uint256 id) internal view virtual returns (address) {\n        DN404Storage storage $ = _getDN404Storage();\n        return $.aliasToAddress[_get($.oo, _ownershipIndex(id))];\n    }\n\n    /// @dev Returns the owner of token `id`.\n    ///\n    /// Requirements:\n    /// - Token `id` must exist.\n    function _ownerOf(uint256 id) internal view virtual returns (address) {\n        if (!_exists(id)) revert TokenDoesNotExist();\n        return _ownerAt(id);\n    }\n\n    /// @dev Returns if token `id` exists.\n    function _exists(uint256 id) internal view virtual returns (bool) {\n        return _ownerAt(id) != address(0);\n    }\n\n    /// @dev Returns the account approved to manage token `id`.\n    ///\n    /// Requirements:\n    /// - Token `id` must exist.\n    function _getApproved(uint256 id) internal view virtual returns (address) {\n        if (!_exists(id)) revert TokenDoesNotExist();\n        return _getDN404Storage().nftApprovals[id];\n    }\n\n    /// @dev Sets `spender` as the approved account to manage token `id`, using `msgSender`.\n    ///\n    /// Requirements:\n    /// - `msgSender` must be the owner or an approved operator for the token owner.\n    function _approveNFT(address spender, uint256 id, address msgSender)\n        internal\n        virtual\n        returns (address owner)\n    {\n        DN404Storage storage $ = _getDN404Storage();\n\n        owner = $.aliasToAddress[_get($.oo, _ownershipIndex(id))];\n\n        if (msgSender != owner) {\n            if ($.operatorApprovals[msgSender][owner].value == 0) {\n                revert ApprovalCallerNotOwnerNorApproved();\n            }\n        }\n\n        $.nftApprovals[id] = spender;\n        LibBitmap.setTo($.mayHaveNFTApproval, id, spender != address(0));\n    }\n\n    function _removeNFTApproval(DN404Storage storage $, uint256 id) internal virtual {\n        if (LibBitmap.get($.mayHaveNFTApproval, id)) {\n            LibBitmap.setTo($.mayHaveNFTApproval, id, false);\n            delete $.nftApprovals[id];\n        }\n    }\n\n    /// @dev Approve or remove the `operator` as an operator for `msgSender`,\n    /// without authorization checks.\n    function _setApprovalForAll(address operator, bool approved, address msgSender)\n        internal\n        virtual\n    {\n        DN404Storage storage $ = _getDN404Storage();\n        Uint256Ref storage ref = $.operatorApprovals[operator][msgSender];\n        if (operator == address(this)) {\n            bool status = ref.value != 0;\n            AddressData storage senderAddressData = $.addressData[msgSender];\n            if (_toUint(approved) & _toUint(!status) != 0) {\n                // initialize when approving\n                senderAddressData.feePerNFTSnap = $.accFeePerNFT;\n                $.numExchangableNFT += senderAddressData.ownedLength;\n            } else if (_toUint(!approved) & _toUint(status) != 0) {\n                // refresh when removing approval\n                _pullFeeForTwo($, msgSender, msgSender);\n                $.numExchangableNFT -= senderAddressData.ownedLength;\n            }\n        }\n        ref.value = _toUint(approved); // `approved ? 1 : 0`\n    }\n\n    /// @dev Lock or unlock the `id`,\n    /// `msgSener` should be authorized as the operator of the owner of the NFT\n    function _setNFTLockState(uint256[] memory ids, bool lock, address msgSender)\n        internal\n        virtual\n    {\n        DN404Storage storage $ = _getDN404Storage();\n        _pullFeeForTwo($, msgSender, msgSender);\n\n        Uint32Map storage oo = $.oo;\n        LibBitmap.Bitmap storage tokenLocks = $.tokenLocks;\n\n        AddressData storage ownerAddressData = $.addressData[msgSender];\n        Uint32Map storage ownerLocked = $.locked[msgSender];\n        Uint32Map storage ownerOwned = $.owned[msgSender];\n        uint32 ownerAlias = ownerAddressData.addressAlias;\n        uint256 idLen = ids.length;\n\n        for (uint256 i; i < idLen; ++i) {\n            uint256 id = ids[i];\n\n            if (_get(oo, _ownershipIndex(id)) != ownerAlias) {\n                revert ApprovalCallerNotOwnerNorApproved();\n            }\n\n            uint32 ownedIndex = _get(oo, _ownedIndex(id));\n\n            if (LibBitmap.get(tokenLocks, id) == lock) revert TokenLockStatusNoChange();\n\n            if (!lock) {\n                // already locked, to unlock\n                LibBitmap.setTo(tokenLocks, id, false);\n\n                // swap with last NFT and pop the last\n                _delNFTAt(ownerLocked, oo, ownedIndex, --ownerAddressData.lockedLength);\n\n                uint256 n = ownerAddressData.ownedLength++;\n                _set(ownerOwned, n, uint32(id));\n                _set(oo, _ownedIndex(id), uint32(n));\n            } else {\n                // not locked, to lock\n                LibBitmap.setTo(tokenLocks, id, true);\n\n                // swap with last NFT and pop the last\n                _delNFTAt(ownerOwned, oo, ownedIndex, --ownerAddressData.ownedLength);\n\n                uint256 n = ownerAddressData.lockedLength++;\n                _set(ownerLocked, n, uint32(id));\n                _set(oo, _ownedIndex(id), uint32(n));\n            }\n        }\n\n        if (lock) $.numLockedNFT += uint32(idLen);\n        else $.numLockedNFT -= uint32(idLen);\n\n        if ($.operatorApprovals[address(this)][msgSender].value != 0) {\n            if (lock) $.numExchangableNFT -= uint32(ids.length);\n            else $.numExchangableNFT += uint32(ids.length);\n        }\n    }\n\n    /// @dev Returns the NFT IDs of `owner` in range `[begin, end)`.\n    /// Optimized for smaller bytecode size, as this function is intended for off-chain calling.\n    function _ownedIds(address owner, uint256 begin, uint256 end, bool locked)\n        internal\n        view\n        virtual\n        returns (uint256[] memory ids)\n    {\n        DN404Storage storage $ = _getDN404Storage();\n        (Uint32Map storage owned, uint256 n) = locked\n            ? ($.locked[owner], $.addressData[owner].lockedLength)\n            : ($.owned[owner], $.addressData[owner].ownedLength);\n        n = _min(n, end);\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Allocate one more word to store the offset when returning with assembly.\n            ids := mload(0x40)\n            mstore(0x20, owned.slot)\n            let i := begin\n            for {} lt(i, n) { i := add(i, 1) } {\n                mstore(0x00, shr(3, i))\n                let s := keccak256(0x00, 0x40) // Storage slot.\n                let id := and(0xffffffff, shr(shl(5, and(i, 7)), sload(s)))\n                mstore(add(add(ids, 0x20), shl(5, sub(i, begin))), id) // Append to.\n            }\n            mstore(ids, sub(i, begin)) // Store the length.\n            mstore(0x40, add(add(ids, 0x20), shl(5, sub(i, begin)))) // Allocate memory.\n        }\n    }\n\n    /// @dev Fallback modifier to dispatch calls from the mirror NFT contract\n    /// to internal functions in this contract.\n    modifier dn404Fallback() virtual {\n        DN404Storage storage $ = _getDN404Storage();\n\n        uint256 fnSelector = _calldataload(0x00) >> 224;\n        address mirror = $.mirrorERC721;\n\n        // `transferFromNFT(address,address,uint256,address)`.\n        if (fnSelector == 0xe5eb36c8) {\n            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();\n            _transferFromNFT(\n                address(uint160(_calldataload(0x04))), // `from`.\n                address(uint160(_calldataload(0x24))), // `to`.\n                _calldataload(0x44), // `id`.\n                address(uint160(_calldataload(0x64))) // `msgSender`.\n            );\n            _return(1);\n        }\n        // `setApprovalForAll(address,bool,address)`.\n        if (fnSelector == 0x813500fc) {\n            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();\n            _setApprovalForAll(\n                address(uint160(_calldataload(0x04))), // `spender`.\n                _calldataload(0x24) != 0, // `status`.\n                address(uint160(_calldataload(0x44))) // `msgSender`.\n            );\n            _return(1);\n        }\n        // `exchangeNFT(uint256,uint256,address)`.\n        if (fnSelector == 0x2c5966af) {\n            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();\n            (address x, address y, uint256 fee) = _exchangeNFT(\n                _calldataload(0x04), // `idX`\n                _calldataload(0x24), // `idY`\n                address(uint160(_calldataload(0x44))) // `msgSender`\n            );\n\n            /// @solidity memory-safe-assembly\n            assembly {\n                mstore(0x00, x)\n                mstore(0x20, y)\n                mstore(0x40, fee)\n                return(0x00, 0x60)\n            }\n        }\n        // `setNFTLockState(uint256,uint256[])`.\n        if (fnSelector == 0xb79cc1bd) {\n            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();\n\n            uint256 senderAndLockFlag = _calldataload(0x04);\n            uint256[] memory ids;\n            assembly {\n                let offset := add(0x24, calldataload(0x24))\n                let length := calldataload(sub(offset, 0x20))\n                ids := mload(0x40)\n                mstore(0x40, add(add(ids, 0x20), shl(5, length))) // Allocate memory.\n\n                mstore(ids, length) // Store array length\n                calldatacopy(add(ids, 0x20), offset, shl(5, length)) // Copy array elements\n            }\n\n            _setNFTLockState(\n                ids, // `ids`\n                uint8(senderAndLockFlag) != 0, // `lock`\n                address(uint160(senderAndLockFlag >> 96)) // `msgSender`\n            );\n            _return(1);\n        }\n        // `isApprovedForAll(address,address)`.\n        if (fnSelector == 0xe985e9c5) {\n            address owner = address(uint160(_calldataload(0x04)));\n            address spender = address(uint160(_calldataload(0x24)));\n            Uint256Ref storage ref = $.operatorApprovals[spender][owner];\n\n            _return(ref.value);\n        }\n        // `ownerOf(uint256)`.\n        if (fnSelector == 0x6352211e) {\n            _return(uint160(_ownerOf(_calldataload(0x04))));\n        }\n        // `ownerAt(uint256)`.\n        if (fnSelector == 0x24359879) {\n            _return(uint160(_ownerAt(_calldataload(0x04))));\n        }\n        // `approveNFT(address,uint256,address)`.\n        if (fnSelector == 0xd10b6e0c) {\n            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();\n            address owner = _approveNFT(\n                address(uint160(_calldataload(0x04))), // `spender`.\n                _calldataload(0x24), // `id`.\n                address(uint160(_calldataload(0x44))) // `msgSender`.\n            );\n            _return(uint160(owner));\n        }\n        // `ownedIds(uint256,uint256,uint256)`.\n        if (fnSelector == 0xf9b4b328) {\n            uint256 addrAndFlag = _calldataload(0x04);\n            /// @solidity memory-safe-assembly\n            assembly {\n                // Allocate one word to store the offset of the array in returndata.\n                mstore(0x40, add(mload(0x40), 0x20))\n            }\n\n            uint256[] memory ids = _ownedIds(\n                address(uint160(addrAndFlag >> 96)),\n                _calldataload(0x24),\n                _calldataload(0x44),\n                uint8(addrAndFlag) != 0\n            );\n            /// @solidity memory-safe-assembly\n            assembly {\n                // Memory safe, as we've advanced the free memory pointer by a word.\n                let p := sub(ids, 0x20)\n                mstore(p, 0x20) // Store the offset of the array in returndata.\n                return(p, add(0x40, shl(5, mload(ids))))\n            }\n        }\n        // `getApproved(uint256)`.\n        if (fnSelector == 0x081812fc) {\n            _return(uint160(_getApproved(_calldataload(0x04))));\n        }\n        // `balanceOfNFT(address)`.\n        if (fnSelector == 0xf5b100ea) {\n            _return(_balanceOfNFT(address(uint160(_calldataload(0x04)))));\n        }\n        // `totalNFTSupply()`.\n        if (fnSelector == 0xe2c79281) {\n            _return(_totalNFTSupply());\n        }\n        // `implementsDN404()`.\n        if (fnSelector == 0xb7a94eb8) {\n            _return(1);\n        }\n        _;\n    }\n\n    /// @dev Fallback function for calls from mirror NFT contract.\n    fallback() external payable virtual dn404Fallback {}\n\n    receive() external payable virtual {}\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                 INTERNAL / PRIVATE HELPERS                 */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns `i << 1`.\n    function _ownershipIndex(uint256 i) internal pure returns (uint256) {\n        unchecked {\n            return i << 1;\n        }\n    }\n\n    /// @dev Returns `(i << 1) + 1`.\n    function _ownedIndex(uint256 i) internal pure returns (uint256) {\n        unchecked {\n            return (i << 1) + 1;\n        }\n    }\n\n    function _delNFTAt(\n        Uint32Map storage owned,\n        Uint32Map storage oo,\n        uint256 toDelIndex,\n        uint256 lastIndex\n    ) internal {\n        if (toDelIndex != lastIndex) {\n            uint256 updatedId = _get(owned, lastIndex);\n            _set(owned, toDelIndex, uint32(updatedId));\n            _set(oo, _ownedIndex(updatedId), uint32(toDelIndex));\n        }\n    }\n\n    /// @dev Returns whether `amount` is a valid `totalSupply`.\n    function _totalSupplyOverflows(uint256 amount) internal view returns (bool result) {\n        uint256 unit = _unit();\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := iszero(iszero(or(shr(96, amount), lt(0xfffffffe, div(amount, unit)))))\n        }\n    }\n\n    /// @dev Struct containing direct transfer log data for {Transfer} events to be\n    /// emitted by the mirror NFT contract.\n    struct _DNDirectLogs {\n        uint256 offset;\n        address from;\n        address to;\n        uint256[] logs;\n    }\n\n    /// @dev Initiates memory allocation for direct logs with `n` log items.\n    function _directLogsMalloc(uint256 n, address from, address to)\n        private\n        pure\n        returns (_DNDirectLogs memory p)\n    {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Note that `p` implicitly allocates and advances the free memory pointer by\n            // 4 words, which we can safely mutate in `_packedLogsSend`.\n            let logs := mload(0x40)\n            mstore(logs, n) // Store the length.\n            let offset := add(0x20, logs) // Skip the word for `p.logs.length`.\n            mstore(0x40, add(offset, shl(5, n))) // Allocate memory.\n            mstore(add(0x60, p), logs) // Set `p.logs`.\n            mstore(add(0x40, p), shr(96, shl(96, to))) // Set `p.to`.\n            mstore(add(0x20, p), shr(96, shl(96, from))) // Set `p.from`.\n            mstore(p, offset) // Set `p.offset`.\n        }\n    }\n\n    /// @dev Adds a direct log item to `p` with token `id`.\n    function _directLogsAppend(_DNDirectLogs memory p, uint256 id) private pure {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let offset := mload(p)\n            mstore(offset, id)\n            mstore(p, add(offset, 0x20))\n        }\n    }\n\n    /// @dev Calls the `mirror` NFT contract to emit {Transfer} events for packed logs `p`.\n    function _directLogsSend(_DNDirectLogs memory p, address mirror) private {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let logs := mload(add(p, 0x60))\n            let n := add(0x84, shl(5, mload(logs))) // Length of calldata to send.\n            let o := sub(logs, 0x80) // Start of calldata to send.\n            mstore(o, 0x144027d3) // `logDirectTransfer(address,address,uint256[])`.\n            mstore(add(o, 0x20), mload(add(0x20, p)))\n            mstore(add(o, 0x40), mload(add(0x40, p)))\n            mstore(add(o, 0x60), 0x60) // Offset of `logs` in the calldata to send.\n            if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {\n                revert(o, 0x00)\n            }\n        }\n    }\n\n    /// emitted by the mirror NFT contract.\n    struct _PackedLogs {\n        uint256 offset;\n        uint256 addressAndBit;\n        uint256[] logs;\n    }\n\n    /// @dev Initiates memory allocation for packed logs with `n` log items.\n    function _packedLogsMalloc(uint256 n) internal pure returns (_PackedLogs memory p) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Note that `p` implicitly allocates and advances the free memory pointer by\n            // 2 words, which we can safely mutate in `_packedLogsSend`.\n            let logs := mload(0x40)\n            mstore(logs, n) // Store the length.\n            let offset := add(0x20, logs)\n            mstore(0x40, add(offset, shl(5, n))) // Allocate memory.\n            mstore(add(0x40, p), logs) // Set `p.logs`.\n            mstore(p, offset) // Set `p.offset`.\n        }\n    }\n\n    /// @dev Set the current address and the burn bit.\n    function _packedLogsSet(_PackedLogs memory p, address a, uint256 burnBit) internal pure {\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(add(p, 0x20), or(shl(96, a), burnBit))\n        }\n    }\n\n    /// @dev Adds a packed log item to `p` with token `id`.\n    function _packedLogsAppend(_PackedLogs memory p, uint256 id) internal pure {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let offset := mload(p)\n            mstore(offset, or(mload(add(p, 0x20)), shl(8, id)))\n            mstore(p, add(offset, 0x20))\n        }\n    }\n\n    function _packedLogsSend(_PackedLogs memory p, address mirror) internal {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let logs := mload(add(p, 0x40))\n            let o := sub(logs, 0x40) // Start of calldata to send.\n            mstore(o, 0x263c69d6) // `logTransfer(uint256[])`.\n            mstore(add(o, 0x20), 0x20) // Offset of `logs` in the calldata to send.\n            let n := add(0x44, shl(5, mload(logs))) // Length of calldata to send.\n            if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {\n                revert(o, 0x00)\n            }\n        }\n    }\n\n    /// @dev Struct of temporary variables for transfers.\n    struct _TransferTemps {\n        uint256 numNFTBurns;\n        uint256 numNFTMints;\n        uint256 fromBalance;\n        uint256 toBalance;\n        uint256 fromOwnedLength;\n        uint256 toOwnedLength;\n        uint256 totalSupply;\n        uint256 fromLockedLength;\n        uint256 toLockedLength;\n    }\n\n    /// @dev Returns if `a` has bytecode of non-zero length.\n    function _hasCode(address a) private view returns (bool result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := extcodesize(a) // Can handle dirty upper bits.\n        }\n    }\n\n    /// @dev Returns the calldata value at `offset`.\n    function _calldataload(uint256 offset) private pure returns (uint256 value) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            value := calldataload(offset)\n        }\n    }\n\n    /// @dev Executes a return opcode to return `x` and end the current call frame.\n    function _return(uint256 x) private pure {\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(0x00, x)\n            return(0x00, 0x20)\n        }\n    }\n\n    /// @dev Returns `max(0, x - y)`.\n    function _zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            z := mul(gt(x, y), sub(x, y))\n        }\n    }\n\n    /// @dev Returns `x < y ? x : y`.\n    function _min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            z := xor(x, mul(xor(x, y), lt(y, x)))\n        }\n    }\n\n    /// @dev Returns `b ? 1 : 0`.\n    function _toUint(bool b) internal pure returns (uint256 result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := iszero(iszero(b))\n        }\n    }\n\n    /// @dev Returns the uint32 value at `index` in `map`.\n    function _get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) {\n        result = uint32(map.map[index >> 3] >> ((index & 7) << 5));\n    }\n\n    /// @dev Updates the uint32 value at `index` in `map`.\n    function _set(Uint32Map storage map, uint256 index, uint32 value) internal {\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(0x20, map.slot)\n            mstore(0x00, shr(3, index))\n            let s := keccak256(0x00, 0x40) // Storage slot.\n            let o := shl(5, and(index, 7)) // Storage slot offset (bits).\n            let v := sload(s) // Storage slot value.\n            let m := 0xffffffff // Value mask.\n            sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))\n        }\n    }\n\n    /// @dev Sets the owner alias and the owned index together.\n    function _setOwnerAliasAndOwnedIndex(\n        Uint32Map storage map,\n        uint256 id,\n        uint32 ownership,\n        uint32 ownedIndex\n    ) internal {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let value := or(shl(32, ownedIndex), and(0xffffffff, ownership))\n            mstore(0x20, map.slot)\n            mstore(0x00, shr(2, id))\n            let s := keccak256(0x00, 0x40) // Storage slot.\n            let o := shl(6, and(id, 3)) // Storage slot offset (bits).\n            let v := sload(s) // Storage slot value.\n            let m := 0xffffffffffffffff // Value mask.\n            sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))\n        }\n    }\n}\n"
      },
      "src/DN404Mirror.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @title DN404Mirror\n/// @notice DN404Mirror provides an interface for interacting with the\n/// NFT tokens in a DN404 implementation.\n///\n/// @author vectorized.eth (@optimizoor)\n/// @author Quit (@0xQuit)\n/// @author Michael Amadi (@AmadiMichaels)\n/// @author cygaar (@0xCygaar)\n/// @author Thomas (@0xjustadev)\n/// @author Harrison (@PopPunkOnChain)\n///\n/// @dev Note:\n/// - The ERC721 data is stored in the base DN404 contract.\ncontract DN404Mirror {\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                           EVENTS                           */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Emitted when token `id` is transferred from `from` to `to`.\n    event Transfer(address indexed from, address indexed to, uint256 indexed id);\n\n    /// @dev Emitted when `owner` enables `account` to manage the `id` token.\n    event Approval(address indexed owner, address indexed account, uint256 indexed id);\n\n    /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.\n    event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);\n\n    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.\n    /// This is for marketplace signaling purposes. This contract has a `pullOwner()`\n    /// function that will sync the owner from the base contract.\n    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);\n\n    /// @dev Emitted when `owner` lock or unlock token `id`.\n    event UpdateLockState(address indexed owner, uint256 indexed id, bool lockStatus);\n\n    /// @dev Emitted when token `idX` and `idY` exchanged.\n    event Exchange(uint256 indexed idX, uint256 indexed idY, uint256 exchangeFee);\n\n    /// @dev `keccak256(bytes(\"Transfer(address,address,uint256)\"))`.\n    uint256 private constant _TRANSFER_EVENT_SIGNATURE =\n        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;\n\n    /// @dev `keccak256(bytes(\"Approval(address,address,uint256)\"))`.\n    uint256 private constant _APPROVAL_EVENT_SIGNATURE =\n        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;\n\n    /// @dev `keccak256(bytes(\"ApprovalForAll(address,address,bool)\"))`.\n    uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =\n        0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;\n\n    /// @dev `keccak256(bytes(\"UpdateLockState(address,uint256,bool)\"))`.\n    uint256 private constant _UPDATE_LOCK_STATE_EVENT_SIGNATURE =\n        0xcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac;\n\n    /// @dev `keccak256(bytes(\"Exchange(uint256,uint256,uint256)\"))`\n    uint256 private constant _EXCHANGE_EVENT_SIGNATURE =\n        0xbc43d7c0945f5a13a7bfa8ca7309e55f903f01d66c38c6d1353fe7ff9335d776;\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                        CUSTOM ERRORS                       */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Thrown when a call for an NFT function did not originate\n    /// from the base DN404 contract.\n    error SenderNotBase();\n\n    /// @dev Thrown when a call for an NFT function did not originate from the deployer.\n    error SenderNotDeployer();\n\n    /// @dev Thrown when transferring an NFT to a contract address that\n    /// does not implement ERC721Receiver.\n    error TransferToNonERC721ReceiverImplementer();\n\n    /// @dev Thrown when linking to the DN404 base contract and the\n    /// DN404 supportsInterface check fails or the call reverts.\n    error CannotLink();\n\n    /// @dev Thrown when a linkMirrorContract call is received and the\n    /// NFT mirror contract has already been linked to a DN404 base contract.\n    error AlreadyLinked();\n\n    /// @dev Thrown when retrieving the base DN404 address when a link has not\n    /// been established.\n    error NotLinked();\n\n    /// @dev The caller is not authorized to call the function.\n    error Unauthorized();\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                          STORAGE                           */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Struct contain the NFT mirror contract storage.\n    struct DN404NFTStorage {\n        address baseERC20;\n        address deployer;\n        address owner;\n    }\n\n    /// @dev Returns a storage pointer for DN404NFTStorage.\n    function _getDN404NFTStorage() internal pure virtual returns (DN404NFTStorage storage $) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // `uint72(bytes9(keccak256(\"DN404_MIRROR_STORAGE\")))`.\n            $.slot := 0x3602298b8c10b01230 // Truncate to 9 bytes to reduce bytecode size.\n        }\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                        CONSTRUCTOR                         */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    constructor(address deployer) {\n        // For non-proxies, we will store the deployer so that only the deployer can\n        // link the base contract.\n        _getDN404NFTStorage().deployer = deployer;\n    }\n\n    function _initializeDN404Mirror(address deployer) internal {\n        // For non-proxies, we will store the deployer so that only the deployer can\n        // link the base contract.\n        _getDN404NFTStorage().deployer = deployer;\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                     ERC721 OPERATIONS                      */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns the token collection name from the base DN404 contract.\n    function name() public view virtual returns (string memory result) {\n        return _readString(0x06fdde03, 0); // `name()`.\n    }\n\n    /// @dev Returns the token collection symbol from the base DN404 contract.\n    function symbol() public view virtual returns (string memory result) {\n        return _readString(0x95d89b41, 0); // `symbol()`.\n    }\n\n    /// @dev Returns the Uniform Resource Identifier (URI) for token `id` from\n    /// the base DN404 contract.\n    function tokenURI(uint256 id) public view virtual returns (string memory result) {\n        return _readString(0xc87b56dd, id); // `tokenURI()`.\n    }\n\n    /// @dev Returns the total NFT supply from the base DN404 contract.\n    function totalSupply() public view virtual returns (uint256 result) {\n        return _readWord(0xe2c79281, 0, 0); // `totalNFTSupply()`.\n    }\n\n    /// @dev Returns the number of NFT tokens owned by `nftOwner` from the base DN404 contract.\n    ///\n    /// Requirements:\n    /// - `nftOwner` must not be the zero address.\n    function balanceOf(address nftOwner) public view virtual returns (uint256 result) {\n        return _readWord(0xf5b100ea, uint160(nftOwner), 0); // `balanceOfNFT(address)`.\n    }\n\n    /// @dev Returns the owner of token `id` from the base DN404 contract.\n    ///\n    /// Requirements:\n    /// - Token `id` must exist.\n    function ownerOf(uint256 id) public view virtual returns (address result) {\n        return address(uint160(_readWord(0x6352211e, id, 0))); // `ownerOf(uint256)`.\n    }\n\n    /// @dev Returns the owner of token `id` from the base DN404 contract.\n    /// Returns `address(0)` instead of reverting if the token does not exist.\n    function ownerAt(uint256 id) public view virtual returns (address result) {\n        return address(uint160(_readWord(0x24359879, id, 0))); // `ownerAt(uint256)`.\n    }\n\n    /// @dev Sets `spender` as the approved account to manage token `id` in\n    /// the base DN404 contract.\n    ///\n    /// Requirements:\n    /// - Token `id` must exist.\n    /// - The caller must be the owner of the token,\n    ///   or an approved operator for the token owner.\n    ///\n    /// Emits an {Approval} event.\n    function approve(address spender, uint256 id) public virtual {\n        address base = baseERC20();\n        /// @solidity memory-safe-assembly\n        assembly {\n            spender := shr(96, shl(96, spender))\n            let m := mload(0x40)\n            mstore(0x00, 0xd10b6e0c) // `approveNFT(address,uint256,address)`.\n            mstore(0x20, spender)\n            mstore(0x40, id)\n            mstore(0x60, caller())\n            if iszero(\n                and(\n                    gt(returndatasize(), 0x1f),\n                    call(gas(), base, callvalue(), 0x1c, 0x64, 0x00, 0x20)\n                )\n            ) {\n                returndatacopy(m, 0x00, returndatasize())\n                revert(m, returndatasize())\n            }\n            mstore(0x40, m) // Restore the free memory pointer.\n            mstore(0x60, 0) // Restore the zero pointer.\n            // Emit the {Approval} event.\n            log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, shr(96, mload(0x0c)), spender, id)\n        }\n    }\n\n    /// @dev Returns the account approved to manage token `id` from\n    /// the base DN404 contract.\n    ///\n    /// Requirements:\n    /// - Token `id` must exist.\n    function getApproved(uint256 id) public view virtual returns (address) {\n        return address(uint160(_readWord(0x081812fc, id, 0))); // `getApproved(uint256)`.\n    }\n\n    /// @dev Sets whether `operator` is approved to manage the tokens of the caller in\n    /// the base DN404 contract.\n    ///\n    /// Emits an {ApprovalForAll} event.\n    function setApprovalForAll(address operator, bool approved) public virtual {\n        address base = baseERC20();\n        /// @solidity memory-safe-assembly\n        assembly {\n            operator := shr(96, shl(96, operator))\n            let m := mload(0x40)\n            mstore(0x00, 0x813500fc) // `setApprovalForAll(address,bool,address)`.\n            mstore(0x20, operator)\n            mstore(0x40, iszero(iszero(approved)))\n            mstore(0x60, caller())\n            if iszero(\n                and(eq(mload(0x00), 1), call(gas(), base, callvalue(), 0x1c, 0x64, 0x00, 0x20))\n            ) {\n                returndatacopy(m, 0x00, returndatasize())\n                revert(m, returndatasize())\n            }\n            // Emit the {ApprovalForAll} event.\n            // The `approved` value is already at 0x40.\n            log3(0x40, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), operator)\n            mstore(0x40, m) // Restore the free memory pointer.\n            mstore(0x60, 0) // Restore the zero pointer.\n        }\n    }\n\n    /// @dev Returns whether `operator` is approved to manage the tokens of `nftOwner` from\n    /// the base DN404 contract.\n    function isApprovedForAll(address nftOwner, address operator)\n        public\n        view\n        virtual\n        returns (bool result)\n    {\n        // `isApprovedForAll(address,address)`.\n        return _readWord(0xe985e9c5, uint160(nftOwner), uint160(operator)) != 0;\n    }\n\n    /// @dev Returns the owned token ids of `account` from the base DN404 contract.\n    function ownedIds(address account, uint256 begin, uint256 end)\n        public\n        view\n        virtual\n        returns (uint256[] memory)\n    {\n        return _ownedIds(account, begin, end, false);\n    }\n\n    /// @dev Returns the locked token ids of `account` from the base DN404 contract.\n    function lockedIds(address account, uint256 begin, uint256 end)\n        public\n        view\n        virtual\n        returns (uint256[] memory)\n    {\n        return _ownedIds(account, begin, end, true);\n    }\n\n    /// @dev Transfers token `id` from `from` to `to`.\n    ///\n    /// Requirements:\n    ///\n    /// - Token `id` must exist.\n    /// - `from` must be the owner of the token.\n    /// - `to` cannot be the zero address.\n    /// - The caller must be the owner of the token, or be approved to manage the token.\n    ///\n    /// Emits a {Transfer} event.\n    function transferFrom(address from, address to, uint256 id) public virtual {\n        address base = baseERC20();\n        /// @solidity memory-safe-assembly\n        assembly {\n            from := shr(96, shl(96, from))\n            to := shr(96, shl(96, to))\n            let m := mload(0x40)\n            mstore(m, 0xe5eb36c8) // `transferFromNFT(address,address,uint256,address)`.\n            mstore(add(m, 0x20), from)\n            mstore(add(m, 0x40), to)\n            mstore(add(m, 0x60), id)\n            mstore(add(m, 0x80), caller())\n            if iszero(\n                and(eq(mload(m), 1), call(gas(), base, callvalue(), add(m, 0x1c), 0x84, m, 0x20))\n            ) {\n                returndatacopy(m, 0x00, returndatasize())\n                revert(m, returndatasize())\n            }\n            // Emit the `from` unlock event.\n            mstore(m, 0x00)\n            log3(m, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, from, id)\n            // Emit the {Transfer} event.\n            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)\n            // Emit the `to` lock event\n            mstore(m, 0x01)\n            log3(m, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, to, id)\n        }\n    }\n\n    /// @dev Equivalent to `safeTransferFrom(from, to, id, \"\")`.\n    function safeTransferFrom(address from, address to, uint256 id) public virtual {\n        transferFrom(from, to, id);\n\n        if (_hasCode(to)) _checkOnERC721Received(from, to, id, \"\");\n    }\n\n    /// @dev Transfers token `id` from `from` to `to`.\n    ///\n    /// Requirements:\n    ///\n    /// - Token `id` must exist.\n    /// - `from` must be the owner of the token.\n    /// - `to` cannot be the zero address.\n    /// - The caller must be the owner of the token, or be approved to manage the token.\n    /// - If `to` refers to a smart contract, it must implement\n    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n    ///\n    /// Emits a {Transfer} event.\n    function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)\n        public\n        virtual\n    {\n        transferFrom(from, to, id);\n\n        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);\n    }\n\n    function updateLockState(uint256[] memory ids, bool lock) public virtual {\n        address base = baseERC20();\n\n        (bool success, bytes memory result) = base.call(\n            abi.encodeWithSignature(\n                \"setNFTLockState(uint256,uint256[])\",\n                uint256(uint160(msg.sender)) << 96 | (lock ? 1 : 0),\n                ids\n            )\n        );\n\n        // @solidity memory-safe-assembly\n        assembly {\n            if iszero(and(eq(mload(add(result, 0x20)), 1), success)) {\n                revert(add(result, 0x20), mload(result))\n            }\n            let idLen := mload(ids)\n\n            mstore(0x00, lock)\n            for {\n                let s := add(ids, 0x20)\n                let end := add(s, shl(5, idLen))\n            } iszero(eq(s, end)) { s := add(s, 0x20) } {\n                log3(0x00, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, caller(), mload(s))\n            }\n        }\n    }\n\n    function exchange(uint256 idX, uint256 idY) public virtual returns (uint256 exchangeFee) {\n        address base = baseERC20();\n        /// @solidity memory-safe-assembly\n        assembly {\n            let m := mload(0x40)\n            mstore(0x00, 0x2c5966af) // `exchangeNFT(uint256,uint256,address)`.\n            mstore(0x20, idX)\n            mstore(0x40, idY)\n            mstore(0x60, caller())\n            if iszero(\n                and(\n                    gt(returndatasize(), 0x5F),\n                    call(gas(), base, callvalue(), 0x1c, 0x64, 0x00, 0x60)\n                )\n            ) {\n                returndatacopy(m, 0x00, returndatasize())\n                revert(m, returndatasize())\n            }\n            // store return value\n            let x := mload(0x00)\n            let y := mload(0x20)\n            exchangeFee := mload(0x40)\n\n            // Emit the {Transfer} event.\n            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, x, y, idX)\n            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, y, caller(), idY)\n            // Emit the {Exchange} event.\n            log3(0x40, 0x20, _EXCHANGE_EVENT_SIGNATURE, idX, idY)\n            // Emit the `caller` lock event.\n            mstore(0x40, 0x01)\n            log3(0x40, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, caller(), idY)\n\n            mstore(0x40, m) // Restore the free memory pointer.\n            mstore(0x60, 0) // Restore the zero pointer.\n        }\n    }\n\n    /// @dev Returns true if this contract implements the interface defined by `interfaceId`.\n    /// See: https://eips.ethereum.org/EIPS/eip-165\n    /// This function call must use less than 30000 gas.\n    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let s := shr(224, interfaceId)\n            // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.\n            result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))\n        }\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                  OWNER SYNCING OPERATIONS                  */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns the `owner` of the contract, for marketplace signaling purposes.\n    function owner() public view virtual returns (address) {\n        return _getDN404NFTStorage().owner;\n    }\n\n    /// @dev Permissionless function to pull the owner from the base DN404 contract\n    /// if it implements ownable, for marketplace signaling purposes.\n    function pullOwner() public virtual {\n        address newOwner;\n        address base = baseERC20();\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(0x00, 0x8da5cb5b) // `owner()`.\n            if and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x04, 0x00, 0x20)) {\n                newOwner := shr(96, mload(0x0c))\n            }\n        }\n        DN404NFTStorage storage $ = _getDN404NFTStorage();\n        address oldOwner = $.owner;\n        if (oldOwner != newOwner) {\n            $.owner = newOwner;\n            emit OwnershipTransferred(oldOwner, newOwner);\n        }\n    }\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                     MIRROR OPERATIONS                      */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Returns the address of the base DN404 contract.\n    function baseERC20() public view virtual returns (address base) {\n        base = _getDN404NFTStorage().baseERC20;\n        if (base == address(0)) revert NotLinked();\n    }\n\n    /// @dev Fallback modifier to execute calls from the base DN404 contract.\n    modifier dn404NFTFallback() virtual {\n        DN404NFTStorage storage $ = _getDN404NFTStorage();\n\n        uint256 fnSelector = _calldataload(0x00) >> 224;\n\n        // `logTransfer(uint256[])`.\n        if (fnSelector == 0x263c69d6) {\n            if (msg.sender != $.baseERC20) revert SenderNotBase();\n            /// @solidity memory-safe-assembly\n            assembly {\n                // When returndatacopy copies 1 or more out-of-bounds bytes, it reverts.\n                returndatacopy(0x00, returndatasize(), lt(calldatasize(), 0x20))\n                let o := add(0x24, calldataload(0x04)) // Packed logs offset.\n                returndatacopy(0x00, returndatasize(), lt(calldatasize(), o))\n                let end := add(o, shl(5, calldataload(sub(o, 0x20))))\n                returndatacopy(0x00, returndatasize(), lt(calldatasize(), end))\n\n                for {} iszero(eq(o, end)) { o := add(0x20, o) } {\n                    let d := calldataload(o) // Entry in the packed logs.\n                    let a := shr(96, d) // The address.\n                    let b := and(1, d) // Whether it is a burn.\n                    log4(\n                        codesize(),\n                        0x00,\n                        _TRANSFER_EVENT_SIGNATURE,\n                        mul(a, b), // `from`.\n                        mul(a, iszero(b)), // `to`.\n                        shr(168, shl(160, d)) // `id`.\n                    )\n                }\n                mstore(0x00, 0x01)\n                return(0x00, 0x20)\n            }\n        }\n        // `logDirectTransfer(address,address,uint256[])`.\n        if (fnSelector == 0x144027d3) {\n            if (msg.sender != $.baseERC20) revert SenderNotBase();\n            /// @solidity memory-safe-assembly\n            assembly {\n                let from := calldataload(0x04)\n                let to := calldataload(0x24)\n                let o := add(0x24, calldataload(0x44)) // Direct logs offset.\n                let end := add(o, shl(5, calldataload(sub(o, 0x20))))\n\n                for {} iszero(eq(o, end)) { o := add(0x20, o) } {\n                    log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, calldataload(o))\n                }\n                mstore(0x00, 0x01)\n                return(0x00, 0x20)\n            }\n        }\n        // `linkMirrorContract(address)`.\n        if (fnSelector == 0x0f4599e5) {\n            if ($.deployer != address(0)) {\n                if (address(uint160(_calldataload(0x04))) != $.deployer) {\n                    revert SenderNotDeployer();\n                }\n            }\n            if ($.baseERC20 != address(0)) revert AlreadyLinked();\n            $.baseERC20 = msg.sender;\n            /// @solidity memory-safe-assembly\n            assembly {\n                mstore(0x00, 0x01)\n                return(0x00, 0x20)\n            }\n        }\n        _;\n    }\n\n    /// @dev Fallback function for calls from base DN404 contract.\n    fallback() external payable virtual dn404NFTFallback {}\n\n    receive() external payable virtual {}\n\n    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/\n    /*                      PRIVATE HELPERS                       */\n    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/\n\n    /// @dev Helper to read owned ids of a account from the base DN404 contract\n    function _ownedIds(address account, uint256 begin, uint256 end, bool locked)\n        private\n        view\n        returns (uint256[] memory result)\n    {\n        address base = baseERC20();\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := mload(0x40)\n            mstore(0x00, 0xf9b4b328) // `ownedIds(uint256,uint256,uint256)`.\n            mstore(0x20, or(shl(96, account), iszero(iszero(locked))))\n            mstore(0x40, begin)\n            mstore(0x60, end)\n            if iszero(staticcall(gas(), base, 0x1c, 0x64, 0x00, 0x00)) {\n                returndatacopy(result, 0x00, returndatasize())\n                revert(result, returndatasize())\n            }\n            returndatacopy(0x00, 0x00, 0x20) // Copy the offset of the array in returndata.\n            returndatacopy(result, mload(0x00), 0x20) // Copy the length of the array.\n            returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), shl(5, mload(result))) // Copy the array elements.\n            mstore(0x40, add(add(result, 0x20), shl(5, mload(result)))) // Allocate memory.\n            mstore(0x60, 0) // Restore the zero pointer.\n        }\n    }\n\n    /// @dev Helper to read a string from the base DN404 contract.\n    function _readString(uint256 fnSelector, uint256 arg0)\n        private\n        view\n        returns (string memory result)\n    {\n        address base = baseERC20();\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := mload(0x40)\n            mstore(0x00, fnSelector)\n            mstore(0x20, arg0)\n            if iszero(staticcall(gas(), base, 0x1c, 0x24, 0x00, 0x00)) {\n                returndatacopy(result, 0x00, returndatasize())\n                revert(result, returndatasize())\n            }\n            returndatacopy(0x00, 0x00, 0x20) // Copy the offset of the string in returndata.\n            returndatacopy(result, mload(0x00), 0x20) // Copy the length of the string.\n            returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result)) // Copy the string.\n            mstore(0x40, add(add(result, 0x20), mload(result))) // Allocate memory.\n        }\n    }\n\n    /// @dev Helper to read a word from the base DN404 contract.\n    function _readWord(uint256 fnSelector, uint256 arg0, uint256 arg1)\n        private\n        view\n        returns (uint256 result)\n    {\n        address base = baseERC20();\n        /// @solidity memory-safe-assembly\n        assembly {\n            let m := mload(0x40)\n            mstore(0x00, fnSelector)\n            mstore(0x20, arg0)\n            mstore(0x40, arg1)\n            if iszero(\n                and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x44, 0x00, 0x20))\n            ) {\n                returndatacopy(m, 0x00, returndatasize())\n                revert(m, returndatasize())\n            }\n            mstore(0x40, m) // Restore the free memory pointer.\n            result := mload(0x00)\n        }\n    }\n\n    /// @dev Returns the calldata value at `offset`.\n    function _calldataload(uint256 offset) private pure returns (uint256 value) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            value := calldataload(offset)\n        }\n    }\n\n    /// @dev Returns if `a` has bytecode of non-zero length.\n    function _hasCode(address a) private view returns (bool result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := extcodesize(a) // Can handle dirty upper bits.\n        }\n    }\n\n    /// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.\n    /// Reverts if the target does not support the function correctly.\n    function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)\n        private\n    {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Prepare the calldata.\n            let m := mload(0x40)\n            let onERC721ReceivedSelector := 0x150b7a02\n            mstore(m, onERC721ReceivedSelector)\n            mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.\n            mstore(add(m, 0x40), shr(96, shl(96, from)))\n            mstore(add(m, 0x60), id)\n            mstore(add(m, 0x80), 0x80)\n            let n := mload(data)\n            mstore(add(m, 0xa0), n)\n            if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) }\n            // Revert if the call reverts.\n            if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {\n                if returndatasize() {\n                    // Bubble up the revert if the call reverts.\n                    returndatacopy(m, 0x00, returndatasize())\n                    revert(m, returndatasize())\n                }\n            }\n            // Load the returndata and compare it.\n            if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {\n                mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.\n                revert(0x1c, 0x04)\n            }\n        }\n    }\n}\n"
      },
      "lib/solady/src/utils/UUPSUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice UUPS proxy mixin.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)\n/// @author Modified from OpenZeppelin\n/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)\n///\n/// Note:\n/// - This implementation is intended to be used with ERC1967 proxies.\n/// See: `LibClone.deployERC1967` and related functions.\n/// - This implementation is NOT compatible with legacy OpenZeppelin proxies\n/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.\nabstract contract UUPSUpgradeable {\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                       CUSTOM ERRORS                        */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev The upgrade failed.\n    error UpgradeFailed();\n\n    /// @dev The call is from an unauthorized call context.\n    error UnauthorizedCallContext();\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                         IMMUTABLES                         */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev For checking if the context is a delegate call.\n    uint256 private immutable __self = uint256(uint160(address(this)));\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                           EVENTS                           */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Emitted when the proxy's implementation is upgraded.\n    event Upgraded(address indexed implementation);\n\n    /// @dev `keccak256(bytes(\"Upgraded(address)\"))`.\n    uint256 private constant _UPGRADED_EVENT_SIGNATURE =\n        0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                          STORAGE                           */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev The ERC-1967 storage slot for the implementation in the proxy.\n    /// `uint256(keccak256(\"eip1967.proxy.implementation\")) - 1`.\n    bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =\n        0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                      UUPS OPERATIONS                       */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Please override this function to check if `msg.sender` is authorized\n    /// to upgrade the proxy to `newImplementation`, reverting if not.\n    /// ```\n    ///     function _authorizeUpgrade(address) internal override onlyOwner {}\n    /// ```\n    function _authorizeUpgrade(address newImplementation) internal virtual;\n\n    /// @dev Returns the storage slot used by the implementation,\n    /// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).\n    ///\n    /// Note: The `notDelegated` modifier prevents accidental upgrades to\n    /// an implementation that is a proxy contract.\n    function proxiableUUID() public view virtual notDelegated returns (bytes32) {\n        // This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.\n        return _ERC1967_IMPLEMENTATION_SLOT;\n    }\n\n    /// @dev Upgrades the proxy's implementation to `newImplementation`.\n    /// Emits a {Upgraded} event.\n    ///\n    /// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.\n    function upgradeToAndCall(address newImplementation, bytes calldata data)\n        public\n        payable\n        virtual\n        onlyProxy\n    {\n        _authorizeUpgrade(newImplementation);\n        /// @solidity memory-safe-assembly\n        assembly {\n            newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.\n            mstore(0x01, 0x52d1902d) // `proxiableUUID()`.\n            let s := _ERC1967_IMPLEMENTATION_SLOT\n            // Check if `newImplementation` implements `proxiableUUID` correctly.\n            if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {\n                mstore(0x01, 0x55299b49) // `UpgradeFailed()`.\n                revert(0x1d, 0x04)\n            }\n            // Emit the {Upgraded} event.\n            log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)\n            sstore(s, newImplementation) // Updates the implementation.\n\n            // Perform a delegatecall to `newImplementation` if `data` is non-empty.\n            if data.length {\n                // Forwards the `data` to `newImplementation` via delegatecall.\n                let m := mload(0x40)\n                calldatacopy(m, data.offset, data.length)\n                if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00))\n                {\n                    // Bubble up the revert if the call reverts.\n                    returndatacopy(m, 0x00, returndatasize())\n                    revert(m, returndatasize())\n                }\n            }\n        }\n    }\n\n    /// @dev Requires that the execution is performed through a proxy.\n    modifier onlyProxy() {\n        uint256 s = __self;\n        /// @solidity memory-safe-assembly\n        assembly {\n            // To enable use cases with an immutable default implementation in the bytecode,\n            // (see: ERC6551Proxy), we don't require that the proxy address must match the\n            // value stored in the implementation slot, which may not be initialized.\n            if eq(s, address()) {\n                mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.\n                revert(0x1c, 0x04)\n            }\n        }\n        _;\n    }\n\n    /// @dev Requires that the execution is NOT performed via delegatecall.\n    /// This is the opposite of `onlyProxy`.\n    modifier notDelegated() {\n        uint256 s = __self;\n        /// @solidity memory-safe-assembly\n        assembly {\n            if iszero(eq(s, address())) {\n                mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.\n                revert(0x1c, 0x04)\n            }\n        }\n        _;\n    }\n}\n"
      },
      "lib/solady/src/utils/LibBitmap.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {LibBit} from \"./LibBit.sol\";\n\n/// @notice Library for storage of packed unsigned booleans.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBitmap.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibBitmap.sol)\n/// @author Modified from Solidity-Bits (https://github.com/estarriolvetch/solidity-bits/blob/main/contracts/BitMaps.sol)\nlibrary LibBitmap {\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                         CONSTANTS                          */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev The constant returned when a bitmap scan does not find a result.\n    uint256 internal constant NOT_FOUND = type(uint256).max;\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                          STRUCTS                           */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev A bitmap in storage.\n    struct Bitmap {\n        mapping(uint256 => uint256) map;\n    }\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                         OPERATIONS                         */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Returns the boolean value of the bit at `index` in `bitmap`.\n    function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) {\n        // It is better to set `isSet` to either 0 or 1, than zero vs non-zero.\n        // Both cost the same amount of gas, but the former allows the returned value\n        // to be reused without cleaning the upper bits.\n        uint256 b = (bitmap.map[index >> 8] >> (index & 0xff)) & 1;\n        /// @solidity memory-safe-assembly\n        assembly {\n            isSet := b\n        }\n    }\n\n    /// @dev Updates the bit at `index` in `bitmap` to true.\n    function set(Bitmap storage bitmap, uint256 index) internal {\n        bitmap.map[index >> 8] |= (1 << (index & 0xff));\n    }\n\n    /// @dev Updates the bit at `index` in `bitmap` to false.\n    function unset(Bitmap storage bitmap, uint256 index) internal {\n        bitmap.map[index >> 8] &= ~(1 << (index & 0xff));\n    }\n\n    /// @dev Flips the bit at `index` in `bitmap`.\n    /// Returns the boolean result of the flipped bit.\n    function toggle(Bitmap storage bitmap, uint256 index) internal returns (bool newIsSet) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(0x20, bitmap.slot)\n            mstore(0x00, shr(8, index))\n            let storageSlot := keccak256(0x00, 0x40)\n            let shift := and(index, 0xff)\n            let storageValue := xor(sload(storageSlot), shl(shift, 1))\n            // It makes sense to return the `newIsSet`,\n            // as it allow us to skip an additional warm `sload`,\n            // and it costs minimal gas (about 15),\n            // which may be optimized away if the returned value is unused.\n            newIsSet := and(1, shr(shift, storageValue))\n            sstore(storageSlot, storageValue)\n        }\n    }\n\n    /// @dev Updates the bit at `index` in `bitmap` to `shouldSet`.\n    function setTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal {\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(0x20, bitmap.slot)\n            mstore(0x00, shr(8, index))\n            let storageSlot := keccak256(0x00, 0x40)\n            let storageValue := sload(storageSlot)\n            let shift := and(index, 0xff)\n            sstore(\n                storageSlot,\n                // Unsets the bit at `shift` via `and`, then sets its new value via `or`.\n                or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet))))\n            )\n        }\n    }\n\n    /// @dev Consecutively sets `amount` of bits starting from the bit at `start`.\n    function setBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let max := not(0)\n            let shift := and(start, 0xff)\n            mstore(0x20, bitmap.slot)\n            mstore(0x00, shr(8, start))\n            if iszero(lt(add(shift, amount), 257)) {\n                let storageSlot := keccak256(0x00, 0x40)\n                sstore(storageSlot, or(sload(storageSlot), shl(shift, max)))\n                let bucket := add(mload(0x00), 1)\n                let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))\n                amount := and(add(amount, shift), 0xff)\n                shift := 0\n                for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {\n                    mstore(0x00, bucket)\n                    sstore(keccak256(0x00, 0x40), max)\n                }\n                mstore(0x00, bucket)\n            }\n            let storageSlot := keccak256(0x00, 0x40)\n            sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max))))\n        }\n    }\n\n    /// @dev Consecutively unsets `amount` of bits starting from the bit at `start`.\n    function unsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let shift := and(start, 0xff)\n            mstore(0x20, bitmap.slot)\n            mstore(0x00, shr(8, start))\n            if iszero(lt(add(shift, amount), 257)) {\n                let storageSlot := keccak256(0x00, 0x40)\n                sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0)))))\n                let bucket := add(mload(0x00), 1)\n                let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))\n                amount := and(add(amount, shift), 0xff)\n                shift := 0\n                for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {\n                    mstore(0x00, bucket)\n                    sstore(keccak256(0x00, 0x40), 0)\n                }\n                mstore(0x00, bucket)\n            }\n            let storageSlot := keccak256(0x00, 0x40)\n            sstore(\n                storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0)))))\n            )\n        }\n    }\n\n    /// @dev Returns number of set bits within a range by\n    /// scanning `amount` of bits starting from the bit at `start`.\n    function popCount(Bitmap storage bitmap, uint256 start, uint256 amount)\n        internal\n        view\n        returns (uint256 count)\n    {\n        unchecked {\n            uint256 bucket = start >> 8;\n            uint256 shift = start & 0xff;\n            if (!(amount + shift < 257)) {\n                count = LibBit.popCount(bitmap.map[bucket] >> shift);\n                uint256 bucketEnd = bucket + ((amount + shift) >> 8);\n                amount = (amount + shift) & 0xff;\n                shift = 0;\n                for (++bucket; bucket != bucketEnd; ++bucket) {\n                    count += LibBit.popCount(bitmap.map[bucket]);\n                }\n            }\n            count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256 - amount));\n        }\n    }\n\n    /// @dev Returns the index of the most significant set bit before the bit at `before`.\n    /// If no set bit is found, returns `NOT_FOUND`.\n    function findLastSet(Bitmap storage bitmap, uint256 before)\n        internal\n        view\n        returns (uint256 setBitIndex)\n    {\n        uint256 bucket;\n        uint256 bucketBits;\n        /// @solidity memory-safe-assembly\n        assembly {\n            setBitIndex := not(0)\n            bucket := shr(8, before)\n            mstore(0x00, bucket)\n            mstore(0x20, bitmap.slot)\n            let offset := and(0xff, not(before)) // `256 - (255 & before) - 1`.\n            bucketBits := shr(offset, shl(offset, sload(keccak256(0x00, 0x40))))\n            if iszero(or(bucketBits, iszero(bucket))) {\n                for {} 1 {} {\n                    bucket := add(bucket, setBitIndex) // `sub(bucket, 1)`.\n                    mstore(0x00, bucket)\n                    bucketBits := sload(keccak256(0x00, 0x40))\n                    if or(bucketBits, iszero(bucket)) { break }\n                }\n            }\n        }\n        if (bucketBits != 0) {\n            setBitIndex = (bucket << 8) | LibBit.fls(bucketBits);\n            /// @solidity memory-safe-assembly\n            assembly {\n                setBitIndex := or(setBitIndex, sub(0, gt(setBitIndex, before)))\n            }\n        }\n    }\n}\n"
      },
      "lib/solady/src/utils/LibBit.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for bit twiddling and boolean operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)\n/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)\nlibrary LibBit {\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                  BIT TWIDDLING OPERATIONS                  */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Find last set.\n    /// Returns the index of the most significant bit of `x`,\n    /// counting from the least significant bit position.\n    /// If `x` is zero, returns 256.\n    function fls(uint256 x) internal pure returns (uint256 r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))\n            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))\n            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n            r := or(r, shl(4, lt(0xffff, shr(r, x))))\n            r := or(r, shl(3, lt(0xff, shr(r, x))))\n            // forgefmt: disable-next-item\n            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),\n                0x0706060506020504060203020504030106050205030304010505030400000000))\n        }\n    }\n\n    /// @dev Count leading zeros.\n    /// Returns the number of zeros preceding the most significant one bit.\n    /// If `x` is zero, returns 256.\n    function clz(uint256 x) internal pure returns (uint256 r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))\n            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))\n            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n            r := or(r, shl(4, lt(0xffff, shr(r, x))))\n            r := or(r, shl(3, lt(0xff, shr(r, x))))\n            // forgefmt: disable-next-item\n            r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),\n                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))\n        }\n    }\n\n    /// @dev Find first set.\n    /// Returns the index of the least significant bit of `x`,\n    /// counting from the least significant bit position.\n    /// If `x` is zero, returns 256.\n    /// Equivalent to `ctz` (count trailing zeros), which gives\n    /// the number of zeros following the least significant one bit.\n    function ffs(uint256 x) internal pure returns (uint256 r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Isolate the least significant bit.\n            let b := and(x, add(not(x), 1))\n\n            r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, b)))\n            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))\n            r := or(r, shl(5, lt(0xffffffff, shr(r, b))))\n\n            // For the remaining 32 bits, use a De Bruijn lookup.\n            // forgefmt: disable-next-item\n            r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),\n                0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))\n        }\n    }\n\n    /// @dev Returns the number of set bits in `x`.\n    function popCount(uint256 x) internal pure returns (uint256 c) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let max := not(0)\n            let isMax := eq(x, max)\n            x := sub(x, and(shr(1, x), div(max, 3)))\n            x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))\n            x := and(add(x, shr(4, x)), div(max, 17))\n            c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))\n        }\n    }\n\n    /// @dev Returns whether `x` is a power of 2.\n    function isPo2(uint256 x) internal pure returns (bool result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Equivalent to `x && !(x & (x - 1))`.\n            result := iszero(add(and(x, sub(x, 1)), iszero(x)))\n        }\n    }\n\n    /// @dev Returns `x` reversed at the bit level.\n    function reverseBits(uint256 x) internal pure returns (uint256 r) {\n        uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;\n        uint256 m1 = m0 ^ (m0 << 2);\n        uint256 m2 = m1 ^ (m1 << 1);\n        r = reverseBytes(x);\n        r = (m2 & (r >> 1)) | ((m2 & r) << 1);\n        r = (m1 & (r >> 2)) | ((m1 & r) << 2);\n        r = (m0 & (r >> 4)) | ((m0 & r) << 4);\n    }\n\n    /// @dev Returns `x` reversed at the byte level.\n    function reverseBytes(uint256 x) internal pure returns (uint256 r) {\n        unchecked {\n            // Computing masks on-the-fly reduces bytecode size by about 200 bytes.\n            uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == 0) >> 192);\n            uint256 m1 = m0 ^ (m0 << 32);\n            uint256 m2 = m1 ^ (m1 << 16);\n            uint256 m3 = m2 ^ (m2 << 8);\n            r = (m3 & (x >> 8)) | ((m3 & x) << 8);\n            r = (m2 & (r >> 16)) | ((m2 & r) << 16);\n            r = (m1 & (r >> 32)) | ((m1 & r) << 32);\n            r = (m0 & (r >> 64)) | ((m0 & r) << 64);\n            r = (r >> 128) | (r << 128);\n        }\n    }\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                     BOOLEAN OPERATIONS                     */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    // A Solidity bool on the stack or memory is represented as a 256-bit word.\n    // Non-zero values are true, zero is false.\n    // A clean bool is either 0 (false) or 1 (true) under the hood.\n    // Usually, if not always, the bool result of a regular Solidity expression,\n    // or the argument of a public/external function will be a clean bool.\n    // You can usually use the raw variants for more performance.\n    // If uncertain, test (best with exact compiler settings).\n    // Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).\n\n    /// @dev Returns `x & y`. Inputs must be clean.\n    function rawAnd(bool x, bool y) internal pure returns (bool z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            z := and(x, y)\n        }\n    }\n\n    /// @dev Returns `x & y`.\n    function and(bool x, bool y) internal pure returns (bool z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            z := and(iszero(iszero(x)), iszero(iszero(y)))\n        }\n    }\n\n    /// @dev Returns `x | y`. Inputs must be clean.\n    function rawOr(bool x, bool y) internal pure returns (bool z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            z := or(x, y)\n        }\n    }\n\n    /// @dev Returns `x | y`.\n    function or(bool x, bool y) internal pure returns (bool z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            z := or(iszero(iszero(x)), iszero(iszero(y)))\n        }\n    }\n\n    /// @dev Returns 1 if `b` is true, else 0. Input must be clean.\n    function rawToUint(bool b) internal pure returns (uint256 z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            z := b\n        }\n    }\n\n    /// @dev Returns 1 if `b` is true, else 0.\n    function toUint(bool b) internal pure returns (uint256 z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            z := iszero(iszero(b))\n        }\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "forge-std/=test/utils/forge-std/",
        "murky/=lib/murky/",
        "solady/=lib/solady/src/",
        "@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
        "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
        "@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
        "forge-std/=lib/forge-std/src/",
        "murky/=lib/murky/",
        "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
        "openzeppelin-contracts/=lib/murky/lib/openzeppelin-contracts/",
        "solady/=lib/solady/src/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 1000
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "viaIR": false,
      "libraries": {}
    }
  }}