creation_sourcecode: // Sources flattened with hardhat v2.22.2 https://hardhat.org
  
  // SPDX-License-Identifier: MIT
  
  // File @openzeppelin/contracts-upgradeable/interfaces/draft-IERC1822Upgradeable.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
   * proxy whose upgrades are fully controlled by the current implementation.
   */
  interface IERC1822ProxiableUpgradeable {
      /**
       * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
       * address.
       *
       * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
       * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
       * function revert if invoked through a proxy.
       */
      function proxiableUUID() external view returns (bytes32);
  }
  
  
  // File @openzeppelin/contracts-upgradeable/interfaces/IERC1967Upgradeable.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
   *
   * _Available since v4.8.3._
   */
  interface IERC1967Upgradeable {
      /**
       * @dev Emitted when the implementation is upgraded.
       */
      event Upgraded(address indexed implementation);
  
      /**
       * @dev Emitted when the admin account has changed.
       */
      event AdminChanged(address previousAdmin, address newAdmin);
  
      /**
       * @dev Emitted when the beacon is changed.
       */
      event BeaconUpgraded(address indexed beacon);
  }
  
  
  // File @openzeppelin/contracts-upgradeable/proxy/beacon/IBeaconUpgradeable.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev This is the interface that {BeaconProxy} expects of its beacon.
   */
  interface IBeaconUpgradeable {
      /**
       * @dev Must return an address that can be used as a delegate call target.
       *
       * {BeaconProxy} will check that this address is a contract.
       */
      function implementation() external view returns (address);
  }
  
  
  // File @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
  
  pragma solidity ^0.8.1;
  
  /**
   * @dev Collection of functions related to the address type
   */
  library AddressUpgradeable {
      /**
       * @dev Returns true if `account` is a contract.
       *
       * [IMPORTANT]
       * ====
       * It is unsafe to assume that an address for which this function returns
       * false is an externally-owned account (EOA) and not a contract.
       *
       * Among others, `isContract` will return false for the following
       * types of addresses:
       *
       *  - an externally-owned account
       *  - a contract in construction
       *  - an address where a contract will be created
       *  - an address where a contract lived, but was destroyed
       *
       * Furthermore, `isContract` will also return true if the target contract within
       * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
       * which only has an effect at the end of a transaction.
       * ====
       *
       * [IMPORTANT]
       * ====
       * You shouldn't rely on `isContract` to protect against flash loan attacks!
       *
       * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
       * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
       * constructor.
       * ====
       */
      function isContract(address account) internal view returns (bool) {
          // This method relies on extcodesize/address.code.length, which returns 0
          // for contracts in construction, since the code is only stored at the end
          // of the constructor execution.
  
          return account.code.length > 0;
      }
  
      /**
       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
       * `recipient`, forwarding all available gas and reverting on errors.
       *
       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
       * of certain opcodes, possibly making contracts go over the 2300 gas limit
       * imposed by `transfer`, making them unable to receive funds via
       * `transfer`. {sendValue} removes this limitation.
       *
       * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
       *
       * IMPORTANT: because control is transferred to `recipient`, care must be
       * taken to not create reentrancy vulnerabilities. Consider using
       * {ReentrancyGuard} or the
       * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
       */
      function sendValue(address payable recipient, uint256 amount) internal {
          require(address(this).balance >= amount, "Address: insufficient balance");
  
          (bool success, ) = recipient.call{value: amount}("");
          require(success, "Address: unable to send value, recipient may have reverted");
      }
  
      /**
       * @dev Performs a Solidity function call using a low level `call`. A
       * plain `call` is an unsafe replacement for a function call: use this
       * function instead.
       *
       * If `target` reverts with a revert reason, it is bubbled up by this
       * function (like regular Solidity function calls).
       *
       * Returns the raw returned data. To convert to the expected return value,
       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
       *
       * Requirements:
       *
       * - `target` must be a contract.
       * - calling `target` with `data` must not revert.
       *
       * _Available since v3.1._
       */
      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionCallWithValue(target, data, 0, "Address: low-level call failed");
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
       * `errorMessage` as a fallback revert reason when `target` reverts.
       *
       * _Available since v3.1._
       */
      function functionCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          return functionCallWithValue(target, data, 0, errorMessage);
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
       * but also transferring `value` wei to `target`.
       *
       * Requirements:
       *
       * - the calling contract must have an ETH balance of at least `value`.
       * - the called Solidity function must be `payable`.
       *
       * _Available since v3.1._
       */
      function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
          return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
      }
  
      /**
       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
       * with `errorMessage` as a fallback revert reason when `target` reverts.
       *
       * _Available since v3.1._
       */
      function functionCallWithValue(
          address target,
          bytes memory data,
          uint256 value,
          string memory errorMessage
      ) internal returns (bytes memory) {
          require(address(this).balance >= value, "Address: insufficient balance for call");
          (bool success, bytes memory returndata) = target.call{value: value}(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
       * but performing a static call.
       *
       * _Available since v3.3._
       */
      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
          return functionStaticCall(target, data, "Address: low-level static call failed");
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
       * but performing a static call.
       *
       * _Available since v3.3._
       */
      function functionStaticCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal view returns (bytes memory) {
          (bool success, bytes memory returndata) = target.staticcall(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
       * but performing a delegate call.
       *
       * _Available since v3.4._
       */
      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionDelegateCall(target, data, "Address: low-level delegate call failed");
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
       * but performing a delegate call.
       *
       * _Available since v3.4._
       */
      function functionDelegateCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          (bool success, bytes memory returndata) = target.delegatecall(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
  
      /**
       * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
       * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
       *
       * _Available since v4.8._
       */
      function verifyCallResultFromTarget(
          address target,
          bool success,
          bytes memory returndata,
          string memory errorMessage
      ) internal view returns (bytes memory) {
          if (success) {
              if (returndata.length == 0) {
                  // only check isContract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  require(isContract(target), "Address: call to non-contract");
              }
              return returndata;
          } else {
              _revert(returndata, errorMessage);
          }
      }
  
      /**
       * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
       * revert reason or using the provided one.
       *
       * _Available since v4.3._
       */
      function verifyCallResult(
          bool success,
          bytes memory returndata,
          string memory errorMessage
      ) internal pure returns (bytes memory) {
          if (success) {
              return returndata;
          } else {
              _revert(returndata, errorMessage);
          }
      }
  
      function _revert(bytes memory returndata, string memory errorMessage) private pure {
          // Look for revert reason and bubble it up if present
          if (returndata.length > 0) {
              // The easiest way to bubble the revert reason is using memory via assembly
              /// @solidity memory-safe-assembly
              assembly {
                  let returndata_size := mload(returndata)
                  revert(add(32, returndata), returndata_size)
              }
          } else {
              revert(errorMessage);
          }
      }
  }
  
  
  // File @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
  
  pragma solidity ^0.8.2;
  
  /**
   * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
   * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
   * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
   * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
   *
   * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
   * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
   * case an upgrade adds a module that needs to be initialized.
   *
   * For example:
   *
   * [.hljs-theme-light.nopadding]
   * ```solidity
   * contract MyToken is ERC20Upgradeable {
   *     function initialize() initializer public {
   *         __ERC20_init("MyToken", "MTK");
   *     }
   * }
   *
   * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
   *     function initializeV2() reinitializer(2) public {
   *         __ERC20Permit_init("MyToken");
   *     }
   * }
   * ```
   *
   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
   *
   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
   *
   * [CAUTION]
   * ====
   * Avoid leaving a contract uninitialized.
   *
   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
   * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
   * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
   *
   * [.hljs-theme-light.nopadding]
   * ```
   * /// @custom:oz-upgrades-unsafe-allow constructor
   * constructor() {
   *     _disableInitializers();
   * }
   * ```
   * ====
   */
  abstract contract Initializable {
      /**
       * @dev Indicates that the contract has been initialized.
       * @custom:oz-retyped-from bool
       */
      uint8 private _initialized;
  
      /**
       * @dev Indicates that the contract is in the process of being initialized.
       */
      bool private _initializing;
  
      /**
       * @dev Triggered when the contract has been initialized or reinitialized.
       */
      event Initialized(uint8 version);
  
      /**
       * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
       * `onlyInitializing` functions can be used to initialize parent contracts.
       *
       * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
       * constructor.
       *
       * Emits an {Initialized} event.
       */
      modifier initializer() {
          bool isTopLevelCall = !_initializing;
          require(
              (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
              "Initializable: contract is already initialized"
          );
          _initialized = 1;
          if (isTopLevelCall) {
              _initializing = true;
          }
          _;
          if (isTopLevelCall) {
              _initializing = false;
              emit Initialized(1);
          }
      }
  
      /**
       * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
       * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
       * used to initialize parent contracts.
       *
       * A reinitializer may be used after the original initialization step. This is essential to configure modules that
       * are added through upgrades and that require initialization.
       *
       * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
       * cannot be nested. If one is invoked in the context of another, execution will revert.
       *
       * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
       * a contract, executing them in the right order is up to the developer or operator.
       *
       * WARNING: setting the version to 255 will prevent any future reinitialization.
       *
       * Emits an {Initialized} event.
       */
      modifier reinitializer(uint8 version) {
          require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
          _initialized = version;
          _initializing = true;
          _;
          _initializing = false;
          emit Initialized(version);
      }
  
      /**
       * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
       * {initializer} and {reinitializer} modifiers, directly or indirectly.
       */
      modifier onlyInitializing() {
          require(_initializing, "Initializable: contract is not initializing");
          _;
      }
  
      /**
       * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
       * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
       * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
       * through proxies.
       *
       * Emits an {Initialized} event the first time it is successfully executed.
       */
      function _disableInitializers() internal virtual {
          require(!_initializing, "Initializable: contract is initializing");
          if (_initialized != type(uint8).max) {
              _initialized = type(uint8).max;
              emit Initialized(type(uint8).max);
          }
      }
  
      /**
       * @dev Returns the highest version that has been initialized. See {reinitializer}.
       */
      function _getInitializedVersion() internal view returns (uint8) {
          return _initialized;
      }
  
      /**
       * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
       */
      function _isInitializing() internal view returns (bool) {
          return _initializing;
      }
  }
  
  
  // File @openzeppelin/contracts-upgradeable/utils/StorageSlotUpgradeable.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
  // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Library for reading and writing primitive types to specific storage slots.
   *
   * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
   * This library helps with reading and writing to such slots without the need for inline assembly.
   *
   * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
   *
   * Example usage to set ERC1967 implementation slot:
   * ```solidity
   * contract ERC1967 {
   *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
   *
   *     function _getImplementation() internal view returns (address) {
   *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
   *     }
   *
   *     function _setImplementation(address newImplementation) internal {
   *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
   *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
   *     }
   * }
   * ```
   *
   * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
   * _Available since v4.9 for `string`, `bytes`._
   */
  library StorageSlotUpgradeable {
      struct AddressSlot {
          address value;
      }
  
      struct BooleanSlot {
          bool value;
      }
  
      struct Bytes32Slot {
          bytes32 value;
      }
  
      struct Uint256Slot {
          uint256 value;
      }
  
      struct StringSlot {
          string value;
      }
  
      struct BytesSlot {
          bytes value;
      }
  
      /**
       * @dev Returns an `AddressSlot` with member `value` located at `slot`.
       */
      function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
       */
      function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
       */
      function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
       */
      function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `StringSlot` with member `value` located at `slot`.
       */
      function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
       */
      function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := store.slot
          }
      }
  
      /**
       * @dev Returns an `BytesSlot` with member `value` located at `slot`.
       */
      function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
       */
      function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := store.slot
          }
      }
  }
  
  
  // File @openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
  
  pragma solidity ^0.8.2;
  
  
  
  
  
  
  /**
   * @dev This abstract contract provides getters and event emitting update functions for
   * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
   *
   * _Available since v4.1._
   */
  abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
      // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
      bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
  
      /**
       * @dev Storage slot with the address of the current implementation.
       * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
       * validated in the constructor.
       */
      bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
  
      function __ERC1967Upgrade_init() internal onlyInitializing {
      }
  
      function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
      }
      /**
       * @dev Returns the current implementation address.
       */
      function _getImplementation() internal view returns (address) {
          return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
      }
  
      /**
       * @dev Stores a new address in the EIP1967 implementation slot.
       */
      function _setImplementation(address newImplementation) private {
          require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
          StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
      }
  
      /**
       * @dev Perform implementation upgrade
       *
       * Emits an {Upgraded} event.
       */
      function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
      }
  
      /**
       * @dev Perform implementation upgrade with additional setup call.
       *
       * Emits an {Upgraded} event.
       */
      function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
          _upgradeTo(newImplementation);
          if (data.length > 0 || forceCall) {
              AddressUpgradeable.functionDelegateCall(newImplementation, data);
          }
      }
  
      /**
       * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
       *
       * Emits an {Upgraded} event.
       */
      function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
          // Upgrades from old implementations will perform a rollback test. This test requires the new
          // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
          // this special case will break upgrade paths from old UUPS implementation to new ones.
          if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
              _setImplementation(newImplementation);
          } else {
              try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                  require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
              } catch {
                  revert("ERC1967Upgrade: new implementation is not UUPS");
              }
              _upgradeToAndCall(newImplementation, data, forceCall);
          }
      }
  
      /**
       * @dev Storage slot with the admin of the contract.
       * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
       * validated in the constructor.
       */
      bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
  
      /**
       * @dev Returns the current admin.
       */
      function _getAdmin() internal view returns (address) {
          return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
      }
  
      /**
       * @dev Stores a new address in the EIP1967 admin slot.
       */
      function _setAdmin(address newAdmin) private {
          require(newAdmin != address(0), "ERC1967: new admin is the zero address");
          StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
      }
  
      /**
       * @dev Changes the admin of the proxy.
       *
       * Emits an {AdminChanged} event.
       */
      function _changeAdmin(address newAdmin) internal {
          emit AdminChanged(_getAdmin(), newAdmin);
          _setAdmin(newAdmin);
      }
  
      /**
       * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
       * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
       */
      bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
  
      /**
       * @dev Returns the current beacon.
       */
      function _getBeacon() internal view returns (address) {
          return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
      }
  
      /**
       * @dev Stores a new beacon in the EIP1967 beacon slot.
       */
      function _setBeacon(address newBeacon) private {
          require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
          require(
              AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
              "ERC1967: beacon implementation is not a contract"
          );
          StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
      }
  
      /**
       * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
       * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
       *
       * Emits a {BeaconUpgraded} event.
       */
      function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
          _setBeacon(newBeacon);
          emit BeaconUpgraded(newBeacon);
          if (data.length > 0 || forceCall) {
              AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
          }
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[50] private __gap;
  }
  
  
  // File @openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)
  
  pragma solidity ^0.8.0;
  
  
  
  /**
   * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
   * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
   *
   * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
   * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
   * `UUPSUpgradeable` with a custom implementation of upgrades.
   *
   * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
   *
   * _Available since v4.1._
   */
  abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
      /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
      address private immutable __self = address(this);
  
      /**
       * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
       * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
       * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
       * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
       * fail.
       */
      modifier onlyProxy() {
          require(address(this) != __self, "Function must be called through delegatecall");
          require(_getImplementation() == __self, "Function must be called through active proxy");
          _;
      }
  
      /**
       * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
       * callable on the implementing contract but not through proxies.
       */
      modifier notDelegated() {
          require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
          _;
      }
  
      function __UUPSUpgradeable_init() internal onlyInitializing {
      }
  
      function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
      }
      /**
       * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
       * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
       *
       * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
       * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
       * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
       */
      function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
          return _IMPLEMENTATION_SLOT;
      }
  
      /**
       * @dev Upgrade the implementation of the proxy to `newImplementation`.
       *
       * Calls {_authorizeUpgrade}.
       *
       * Emits an {Upgraded} event.
       *
       * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
       */
      function upgradeTo(address newImplementation) public virtual onlyProxy {
          _authorizeUpgrade(newImplementation);
          _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
      }
  
      /**
       * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
       * encoded in `data`.
       *
       * Calls {_authorizeUpgrade}.
       *
       * Emits an {Upgraded} event.
       *
       * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
       */
      function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
          _authorizeUpgrade(newImplementation);
          _upgradeToAndCallUUPS(newImplementation, data, true);
      }
  
      /**
       * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
       * {upgradeTo} and {upgradeToAndCall}.
       *
       * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
       *
       * ```solidity
       * function _authorizeUpgrade(address) internal override onlyOwner {}
       * ```
       */
      function _authorizeUpgrade(address newImplementation) internal virtual;
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[50] private __gap;
  }
  
  
  // File @openzeppelin/contracts/utils/math/Math.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Standard math utilities missing in the Solidity language.
   */
  library Math {
      enum Rounding {
          Down, // Toward negative infinity
          Up, // Toward infinity
          Zero // Toward zero
      }
  
      /**
       * @dev Returns the largest of two numbers.
       */
      function max(uint256 a, uint256 b) internal pure returns (uint256) {
          return a > b ? a : b;
      }
  
      /**
       * @dev Returns the smallest of two numbers.
       */
      function min(uint256 a, uint256 b) internal pure returns (uint256) {
          return a < b ? a : b;
      }
  
      /**
       * @dev Returns the average of two numbers. The result is rounded towards
       * zero.
       */
      function average(uint256 a, uint256 b) internal pure returns (uint256) {
          // (a + b) / 2 can overflow.
          return (a & b) + (a ^ b) / 2;
      }
  
      /**
       * @dev Returns the ceiling of the division of two numbers.
       *
       * This differs from standard division with `/` in that it rounds up instead
       * of rounding down.
       */
      function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
          // (a + b - 1) / b can overflow on addition, so we distribute.
          return a == 0 ? 0 : (a - 1) / b + 1;
      }
  
      /**
       * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
       * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
       * with further edits by Uniswap Labs also under MIT license.
       */
      function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
          unchecked {
              // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
              // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
              // variables such that product = prod1 * 2^256 + prod0.
              uint256 prod0; // Least significant 256 bits of the product
              uint256 prod1; // Most significant 256 bits of the product
              assembly {
                  let mm := mulmod(x, y, not(0))
                  prod0 := mul(x, y)
                  prod1 := sub(sub(mm, prod0), lt(mm, prod0))
              }
  
              // Handle non-overflow cases, 256 by 256 division.
              if (prod1 == 0) {
                  // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                  // The surrounding unchecked block does not change this fact.
                  // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                  return prod0 / denominator;
              }
  
              // Make sure the result is less than 2^256. Also prevents denominator == 0.
              require(denominator > prod1, "Math: mulDiv overflow");
  
              ///////////////////////////////////////////////
              // 512 by 256 division.
              ///////////////////////////////////////////////
  
              // Make division exact by subtracting the remainder from [prod1 prod0].
              uint256 remainder;
              assembly {
                  // Compute remainder using mulmod.
                  remainder := mulmod(x, y, denominator)
  
                  // Subtract 256 bit number from 512 bit number.
                  prod1 := sub(prod1, gt(remainder, prod0))
                  prod0 := sub(prod0, remainder)
              }
  
              // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
              // See https://cs.stackexchange.com/q/138556/92363.
  
              // Does not overflow because the denominator cannot be zero at this stage in the function.
              uint256 twos = denominator & (~denominator + 1);
              assembly {
                  // Divide denominator by twos.
                  denominator := div(denominator, twos)
  
                  // Divide [prod1 prod0] by twos.
                  prod0 := div(prod0, twos)
  
                  // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                  twos := add(div(sub(0, twos), twos), 1)
              }
  
              // Shift in bits from prod1 into prod0.
              prod0 |= prod1 * twos;
  
              // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
              // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
              // four bits. That is, denominator * inv = 1 mod 2^4.
              uint256 inverse = (3 * denominator) ^ 2;
  
              // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
              // in modular arithmetic, doubling the correct bits in each step.
              inverse *= 2 - denominator * inverse; // inverse mod 2^8
              inverse *= 2 - denominator * inverse; // inverse mod 2^16
              inverse *= 2 - denominator * inverse; // inverse mod 2^32
              inverse *= 2 - denominator * inverse; // inverse mod 2^64
              inverse *= 2 - denominator * inverse; // inverse mod 2^128
              inverse *= 2 - denominator * inverse; // inverse mod 2^256
  
              // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
              // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
              // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
              // is no longer required.
              result = prod0 * inverse;
              return result;
          }
      }
  
      /**
       * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
       */
      function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
          uint256 result = mulDiv(x, y, denominator);
          if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
              result += 1;
          }
          return result;
      }
  
      /**
       * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
       *
       * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
       */
      function sqrt(uint256 a) internal pure returns (uint256) {
          if (a == 0) {
              return 0;
          }
  
          // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
          //
          // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
          // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
          //
          // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
          // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
          // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
          //
          // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
          uint256 result = 1 << (log2(a) >> 1);
  
          // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
          // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
          // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
          // into the expected uint128 result.
          unchecked {
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              return min(result, a / result);
          }
      }
  
      /**
       * @notice Calculates sqrt(a), following the selected rounding direction.
       */
      function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
          unchecked {
              uint256 result = sqrt(a);
              return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
          }
      }
  
      /**
       * @dev Return the log in base 2, rounded down, of a positive value.
       * Returns 0 if given 0.
       */
      function log2(uint256 value) internal pure returns (uint256) {
          uint256 result = 0;
          unchecked {
              if (value >> 128 > 0) {
                  value >>= 128;
                  result += 128;
              }
              if (value >> 64 > 0) {
                  value >>= 64;
                  result += 64;
              }
              if (value >> 32 > 0) {
                  value >>= 32;
                  result += 32;
              }
              if (value >> 16 > 0) {
                  value >>= 16;
                  result += 16;
              }
              if (value >> 8 > 0) {
                  value >>= 8;
                  result += 8;
              }
              if (value >> 4 > 0) {
                  value >>= 4;
                  result += 4;
              }
              if (value >> 2 > 0) {
                  value >>= 2;
                  result += 2;
              }
              if (value >> 1 > 0) {
                  result += 1;
              }
          }
          return result;
      }
  
      /**
       * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
       * Returns 0 if given 0.
       */
      function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
          unchecked {
              uint256 result = log2(value);
              return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
          }
      }
  
      /**
       * @dev Return the log in base 10, rounded down, of a positive value.
       * Returns 0 if given 0.
       */
      function log10(uint256 value) internal pure returns (uint256) {
          uint256 result = 0;
          unchecked {
              if (value >= 10 ** 64) {
                  value /= 10 ** 64;
                  result += 64;
              }
              if (value >= 10 ** 32) {
                  value /= 10 ** 32;
                  result += 32;
              }
              if (value >= 10 ** 16) {
                  value /= 10 ** 16;
                  result += 16;
              }
              if (value >= 10 ** 8) {
                  value /= 10 ** 8;
                  result += 8;
              }
              if (value >= 10 ** 4) {
                  value /= 10 ** 4;
                  result += 4;
              }
              if (value >= 10 ** 2) {
                  value /= 10 ** 2;
                  result += 2;
              }
              if (value >= 10 ** 1) {
                  result += 1;
              }
          }
          return result;
      }
  
      /**
       * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
       * Returns 0 if given 0.
       */
      function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
          unchecked {
              uint256 result = log10(value);
              return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
          }
      }
  
      /**
       * @dev Return the log in base 256, rounded down, of a positive value.
       * Returns 0 if given 0.
       *
       * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
       */
      function log256(uint256 value) internal pure returns (uint256) {
          uint256 result = 0;
          unchecked {
              if (value >> 128 > 0) {
                  value >>= 128;
                  result += 16;
              }
              if (value >> 64 > 0) {
                  value >>= 64;
                  result += 8;
              }
              if (value >> 32 > 0) {
                  value >>= 32;
                  result += 4;
              }
              if (value >> 16 > 0) {
                  value >>= 16;
                  result += 2;
              }
              if (value >> 8 > 0) {
                  result += 1;
              }
          }
          return result;
      }
  
      /**
       * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
       * Returns 0 if given 0.
       */
      function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
          unchecked {
              uint256 result = log256(value);
              return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
          }
      }
  }
  
  
  // File @openzeppelin/contracts/utils/math/SignedMath.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Standard signed math utilities missing in the Solidity language.
   */
  library SignedMath {
      /**
       * @dev Returns the largest of two signed numbers.
       */
      function max(int256 a, int256 b) internal pure returns (int256) {
          return a > b ? a : b;
      }
  
      /**
       * @dev Returns the smallest of two signed numbers.
       */
      function min(int256 a, int256 b) internal pure returns (int256) {
          return a < b ? a : b;
      }
  
      /**
       * @dev Returns the average of two signed numbers without overflow.
       * The result is rounded towards zero.
       */
      function average(int256 a, int256 b) internal pure returns (int256) {
          // Formula from the book "Hacker's Delight"
          int256 x = (a & b) + ((a ^ b) >> 1);
          return x + (int256(uint256(x) >> 255) & (a ^ b));
      }
  
      /**
       * @dev Returns the absolute unsigned value of a signed value.
       */
      function abs(int256 n) internal pure returns (uint256) {
          unchecked {
              // must be unchecked in order to support `n = type(int256).min`
              return uint256(n >= 0 ? n : -n);
          }
      }
  }
  
  
  // File @openzeppelin/contracts/utils/Strings.sol@v4.9.6
  
  // Original license: SPDX_License_Identifier: MIT
  // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
  
  pragma solidity ^0.8.0;
  
  
  /**
   * @dev String operations.
   */
  library Strings {
      bytes16 private constant _SYMBOLS = "0123456789abcdef";
      uint8 private constant _ADDRESS_LENGTH = 20;
  
      /**
       * @dev Converts a `uint256` to its ASCII `string` decimal representation.
       */
      function toString(uint256 value) internal pure returns (string memory) {
          unchecked {
              uint256 length = Math.log10(value) + 1;
              string memory buffer = new string(length);
              uint256 ptr;
              /// @solidity memory-safe-assembly
              assembly {
                  ptr := add(buffer, add(32, length))
              }
              while (true) {
                  ptr--;
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                  }
                  value /= 10;
                  if (value == 0) break;
              }
              return buffer;
          }
      }
  
      /**
       * @dev Converts a `int256` to its ASCII `string` decimal representation.
       */
      function toString(int256 value) internal pure returns (string memory) {
          return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
      }
  
      /**
       * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
       */
      function toHexString(uint256 value) internal pure returns (string memory) {
          unchecked {
              return toHexString(value, Math.log256(value) + 1);
          }
      }
  
      /**
       * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
       */
      function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
          bytes memory buffer = new bytes(2 * length + 2);
          buffer[0] = "0";
          buffer[1] = "x";
          for (uint256 i = 2 * length + 1; i > 1; --i) {
              buffer[i] = _SYMBOLS[value & 0xf];
              value >>= 4;
          }
          require(value == 0, "Strings: hex length insufficient");
          return string(buffer);
      }
  
      /**
       * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
       */
      function toHexString(address addr) internal pure returns (string memory) {
          return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
      }
  
      /**
       * @dev Returns true if the two strings are equal.
       */
      function equal(string memory a, string memory b) internal pure returns (bool) {
          return keccak256(bytes(a)) == keccak256(bytes(b));
      }
  }
  
  
  // File contracts/pass/dn404/DN404.sol
  
  // Original license: SPDX_License_Identifier: MIT
  pragma solidity ^0.8.17;
  
  /// @title DN404
  /// @notice DN404 is a hybrid ERC20 and ERC721 implementation that mints
  /// and burns NFTs based on an account's ERC20 token balance.
  ///
  /// @author vectorized.eth (@optimizoor)
  /// @author Quit (@0xQuit)
  /// @author Michael Amadi (@AmadiMichaels)
  /// @author cygaar (@0xCygaar)
  /// @author Thomas (@0xjustadev)
  /// @author Harrison (@PopPunkOnChain)
  ///
  /// @dev Note:
  /// - The ERC721 data is stored in this base DN404 contract, however a
  ///   DN404Mirror contract ***MUST*** be deployed and linked during
  ///   initialization.
  abstract contract DN404 is Initializable {
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                           EVENTS                           */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
      event Transfer(address indexed from, address indexed to, uint256 amount);
  
      /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
      event Approval(address indexed owner, address indexed spender, uint256 amount);
  
      /// @dev Emitted when `owner` sets their skipNFT flag to `status`.
      event SkipNFTSet(address indexed owner, bool status);
  
      /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
      uint256 private constant _TRANSFER_EVENT_SIGNATURE =
          0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
  
      /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
      uint256 private constant _APPROVAL_EVENT_SIGNATURE =
          0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
  
      /// @dev `keccak256(bytes("SkipNFTSet(address,bool)"))`.
      uint256 private constant _SKIP_NFT_SET_EVENT_SIGNATURE =
          0xb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d6420393;
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                        CUSTOM ERRORS                       */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Thrown when attempting to double-initialize the contract.
      error DNAlreadyInitialized();
  
      /// @dev The function can only be called after the contract has been initialized.
      error DNNotInitialized();
  
      /// @dev Thrown when attempting to transfer or burn more tokens than sender's balance.
      error InsufficientBalance();
  
      /// @dev Thrown when a spender attempts to transfer tokens with an insufficient allowance.
      error InsufficientAllowance();
  
      /// @dev Thrown when minting an amount of tokens that would overflow the max tokens.
      error TotalSupplyOverflow();
  
      /// @dev The unit must be greater than zero and less than `2**96`.
      error InvalidUnit();
  
      /// @dev Thrown when the caller for a fallback NFT function is not the mirror contract.
      error SenderNotMirror();
  
      /// @dev Thrown when attempting to transfer tokens to the zero address.
      error TransferToZeroAddress();
  
      /// @dev Thrown when the mirror address provided for initialization is the zero address.
      error MirrorAddressIsZero();
  
      /// @dev Thrown when the link call to the mirror contract reverts.
      error LinkMirrorContractFailed();
  
      /// @dev Thrown when setting an NFT token approval
      /// and the caller is not the owner or an approved operator.
      error ApprovalCallerNotOwnerNorApproved();
  
      /// @dev Thrown when transferring an NFT
      /// and the caller is not the owner or an approved operator.
      error TransferCallerNotOwnerNorApproved();
  
      /// @dev Thrown when transferring an NFT and the from address is not the current owner.
      error TransferFromIncorrectOwner();
  
      /// @dev Thrown when checking the owner or approved address for a non-existent NFT.
      error TokenDoesNotExist();
  
      /// @dev The function selector is not recognized.
      error FnSelectorNotRecognized();
  
      /// @dev Thrown when the program is paused
      error Paused();
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                         CONSTANTS                          */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev The flag to denote that the address data is initialized.
      uint8 internal constant _ADDRESS_DATA_INITIALIZED_FLAG = 1 << 0;
  
      /// @dev The flag to denote that the address should skip NFTs.
      uint8 internal constant _ADDRESS_DATA_SKIP_NFT_FLAG = 1 << 1;
  
      /// @dev The flag to denote that the address has overridden the default Permit2 allowance.
      uint8 internal constant _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG = 1 << 2;
  
      /// @dev The canonical Permit2 address.
      /// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
      /// To enable, override `_givePermit2DefaultInfiniteAllowance()`.
      /// [Github](https://github.com/Uniswap/permit2)
      /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
      address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                          STORAGE                           */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Struct containing an address's token data and settings.
      struct AddressData {
          // Auxiliary data.
          uint88 aux;
          // Flags for `initialized` and `skipNFT`.
          uint8 flags;
          // The alias for the address. Zero means absence of an alias.
          uint32 addressAlias;
          // The number of NFT tokens.
          uint32 ownedLength;
          // The token balance in wei.
          uint96 balance;
      }
  
      /// @dev A uint32 map in storage.
      struct Uint32Map {
          uint256 spacer;
      }
  
      /// @dev A bitmap in storage.
      struct Bitmap {
          uint256 spacer;
      }
  
      /// @dev A struct to wrap a uint256 in storage.
      struct Uint256Ref {
          uint256 value;
      }
  
      /// @dev A mapping of an address pair to a Uint256Ref.
      struct AddressPairToUint256RefMap {
          uint256 spacer;
      }
  
      /// @dev Struct containing the base token contract storage.
      struct DN404Storage {
          // Current number of address aliases assigned.
          uint32 numAliases;
          // Next NFT ID to assign for a mint.
          uint32 nextTokenId;
          // The head of the burned pool.
          uint32 burnedPoolHead;
          // The tail of the burned pool.
          uint32 burnedPoolTail;
          // Total number of NFTs in existence.
          uint32 totalNFTSupply;
          // Total supply of tokens.
          uint96 totalSupply;
          // Address of the NFT mirror contract.
          address mirrorERC721;
          // Mapping of a user alias number to their address.
          mapping(uint32 => address) aliasToAddress;
          // Mapping of user operator approvals for NFTs.
          AddressPairToUint256RefMap operatorApprovals;
          // Mapping of NFT approvals to approved operators.
          mapping(uint256 => address) nftApprovals;
          // Bitmap of whether an non-zero NFT approval may exist.
          Bitmap mayHaveNFTApproval;
          // Bitmap of whether a NFT ID exists. Ignored if `_useExistsLookup()` returns false.
          Bitmap exists;
          // Mapping of user allowances for ERC20 spenders.
          AddressPairToUint256RefMap allowance;
          // Mapping of NFT IDs owned by an address.
          mapping(address => Uint32Map) owned;
          // The pool of burned NFT IDs.
          Uint32Map burnedPool;
          // Even indices: owner aliases. Odd indices: owned indices.
          Uint32Map oo;
          // Mapping of user account AddressData.
          mapping(address => AddressData) addressData;
      }
  
      /// @dev Returns a storage pointer for DN404Storage.
      function _getDN404Storage() internal pure virtual returns (DN404Storage storage $) {
          /// @solidity memory-safe-assembly
          assembly {
              // `uint72(bytes9(keccak256("DN404_STORAGE")))`.
              $.slot := 0xa20d6e21d0e5255308 // Truncate to 9 bytes to reduce bytecode size.
          }
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                         INITIALIZER                        */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Initializes the DN404 contract with an
      /// `initialTokenSupply`, `initialTokenOwner` and `mirror` NFT contract address.
      function _initializeDN404(
          uint256 initialTokenSupply,
          address initialSupplyOwner,
          address mirror,
          address deployer
      ) internal onlyInitializing {
          DN404Storage storage $ = _getDN404Storage();
  
          unchecked {
              if (_unit() - 1 >= 2 ** 96 - 1) revert InvalidUnit();
          }
          if ($.mirrorERC721 != address(0)) revert DNAlreadyInitialized();
          if (mirror == address(0)) revert MirrorAddressIsZero();
  
          /// @solidity memory-safe-assembly
          assembly {
              // Make the call to link the mirror contract.
              mstore(0x00, 0x0f4599e5) // `linkMirrorContract(address)`.
              mstore(0x20, deployer)
              if iszero(and(eq(mload(0x00), 1), call(gas(), mirror, 0, 0x1c, 0x24, 0x00, 0x20))) {
                  mstore(0x00, 0xd125259c) // `LinkMirrorContractFailed()`.
                  revert(0x1c, 0x04)
              }
          }
  
          $.nextTokenId = 1;
          $.mirrorERC721 = mirror;
  
          if (initialTokenSupply != 0) {
              if (initialSupplyOwner == address(0)) revert TransferToZeroAddress();
              if (_totalSupplyOverflows(initialTokenSupply)) revert TotalSupplyOverflow();
  
              $.totalSupply = uint96(initialTokenSupply);
              AddressData storage initialOwnerAddressData = _addressData(initialSupplyOwner);
              initialOwnerAddressData.balance = uint96(initialTokenSupply);
  
              /// @solidity memory-safe-assembly
              assembly {
                  // Emit the {Transfer} event.
                  mstore(0x00, initialTokenSupply)
                  log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, initialSupplyOwner)))
              }
  
              _setSkipNFT(initialSupplyOwner, true);
          }
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*               BASE UNIT FUNCTION TO OVERRIDE               */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Amount of token balance that is equal to one NFT.
      function _unit() internal view virtual returns (uint256) {
          return 10 ** 18;
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*               METADATA FUNCTIONS TO OVERRIDE               */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Returns the name of the token.
      function name() public view virtual returns (string memory);
  
      /// @dev Returns the symbol of the token.
      function symbol() public view virtual returns (string memory);
  
      /// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
      function _tokenURI(uint256 id) internal view virtual returns (string memory);
  
      /// @dev Returns the name of the token.
      function _isPaused() internal view virtual returns (bool);
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                       CONFIGURABLES                        */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Returns if direct NFT transfers should be used during ERC20 transfers
      /// whenever possible, instead of burning and re-minting.
      function _useDirectTransfersIfPossible() internal view virtual returns (bool) {
          return true;
      }
  
      /// @dev Returns if burns should be added to the burn pool.
      /// This returns false by default, which means the NFT IDs are re-minted in a cycle.
      function _addToBurnedPool(uint256 totalNFTSupplyAfterBurn, uint256 totalSupplyAfterBurn)
          internal
          view
          virtual
          returns (bool)
      {
          // Silence unused variable compiler warning.
          totalSupplyAfterBurn = totalNFTSupplyAfterBurn;
          return true;
      }
  
      /// @dev Returns whether to use the exists bitmap for more efficient
      /// scanning of an empty token ID slot.
      /// Recommended for collections that do not use the burn pool,
      /// and are expected to have nearly all possible NFTs materialized.
      ///
      /// Note: The returned value must be constant after initialization.
      function _useExistsLookup() internal view virtual returns (bool) {
          return true;
      }
  
      /// @dev Hook that is called after any NFT token transfers, including minting and burning.
      function _afterNFTTransfer(address from, address to, uint256 id) internal virtual {}
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                      ERC20 OPERATIONS                      */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Returns the decimals places of the token. Always 18.
      function decimals() public pure returns (uint8) {
          return 18;
      }
  
      /// @dev Returns the amount of tokens in existence.
      function totalSupply() public view virtual returns (uint256) {
          return uint256(_getDN404Storage().totalSupply);
      }
  
      /// @dev Returns the amount of tokens owned by `owner`.
      function balanceOf(address owner) public view virtual returns (uint256) {
          return _getDN404Storage().addressData[owner].balance;
      }
  
      /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
      function allowance(address owner, address spender) public view returns (uint256) {
          if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) {
              uint8 flags = _getDN404Storage().addressData[owner].flags;
              if (_isZero(flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG)) return type(uint256).max;
          }
          return _ref(_getDN404Storage().allowance, owner, spender).value;
      }
  
      /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
      ///
      /// Emits a {Approval} event.
      function approve(address spender, uint256 amount) public virtual returns (bool) {
          _approve(msg.sender, spender, amount);
          return true;
      }
  
      /// @dev Transfer `amount` tokens from the caller to `to`.
      ///
      /// Will burn sender NFTs if balance after transfer is less than
      /// the amount required to support the current NFT balance.
      ///
      /// Will mint NFTs to `to` if the recipient's new balance supports
      /// additional NFTs ***AND*** the `to` address's skipNFT flag is
      /// set to false.
      ///
      /// Requirements:
      /// - `from` must at least have `amount`.
      ///
      /// Emits a {Transfer} event.
      function transfer(address to, uint256 amount) public virtual returns (bool) {
          if (_isPaused()) revert Paused();
          _transfer(msg.sender, to, amount);
          return true;
      }
  
      /// @dev Transfers `amount` tokens from `from` to `to`.
      ///
      /// Note: Does not update the allowance if it is the maximum uint256 value.
      ///
      /// Will burn sender NFTs if balance after transfer is less than
      /// the amount required to support the current NFT balance.
      ///
      /// Will mint NFTs to `to` if the recipient's new balance supports
      /// additional NFTs ***AND*** the `to` address's skipNFT flag is
      /// set to false.
      ///
      /// Requirements:
      /// - `from` must at least have `amount`.
      /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
      ///
      /// Emits a {Transfer} event.
      function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
          if (_isPaused()) revert Paused();
          Uint256Ref storage a = _ref(_getDN404Storage().allowance, from, msg.sender);
  
          uint256 allowed = _givePermit2DefaultInfiniteAllowance() && msg.sender == _PERMIT2
              && _isZero(_getDN404Storage().addressData[from].flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG)
              ? type(uint256).max
              : a.value;
  
          if (allowed != type(uint256).max) {
              if (amount > allowed) revert InsufficientAllowance();
              unchecked {
                  a.value = allowed - amount;
              }
          }
          _transfer(from, to, amount);
          return true;
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                          PERMIT2                           */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Whether Permit2 has infinite allowances by default for all owners.
      /// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
      /// To enable, override this function to return true.
      function _givePermit2DefaultInfiniteAllowance() internal view virtual returns (bool) {
          return false;
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                  INTERNAL MINT FUNCTIONS                   */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Mints `amount` tokens to `to`, increasing the total supply.
      /// just support 1 unit token mint here
      /// Will mint NFTs to `to` if the recipient's new balance supports
      /// additional NFTs ***AND*** the `to` address's skipNFT flag is set to false.
      ///
      /// Emits a {Transfer} event.
      function _mint(address to, uint256 amount) internal virtual returns (uint256 id){
          if (_isPaused()) revert Paused();
          if (to == address(0)) revert TransferToZeroAddress();
  
          AddressData storage toAddressData = _addressData(to);
          DN404Storage storage $ = _getDN404Storage();
          if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
  
          _DNMintTemps memory t;
          unchecked {
              uint256 toBalance = uint256(toAddressData.balance) + amount;
              toAddressData.balance = uint96(toBalance);
              t.toEnd = toBalance / _unit();
          }
          uint256 maxId;
          unchecked {
              uint256 totalSupply_ = uint256($.totalSupply) + amount;
              $.totalSupply = uint96(totalSupply_);
              uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
              if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow();
              maxId = totalSupply_ / _unit();
          }
          unchecked {
              if (_isZero(toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG)) {
                  Uint32Map storage toOwned = $.owned[to];
                  Uint32Map storage oo = $.oo;
                  uint256 toIndex = toAddressData.ownedLength;
                  _DNPackedLogs memory packedLogs = _packedLogsMalloc(_zeroFloorSub(t.toEnd, toIndex));
  
                  if (packedLogs.logs.length != 0) {
                      _packedLogsSet(packedLogs, to, 0);
                      $.totalNFTSupply += uint32(packedLogs.logs.length);
                      toAddressData.ownedLength = uint32(t.toEnd);
                      t.toAlias = _registerAndResolveAlias(toAddressData, to);
                      uint32 burnedPoolHead = $.burnedPoolHead;
                      t.burnedPoolTail = $.burnedPoolTail;
                      t.nextTokenId = _wrapNFTId($.nextTokenId, maxId);
                      // Mint loop.
                      do {
                          if (burnedPoolHead != t.burnedPoolTail) {
                              id = _get($.burnedPool, burnedPoolHead++);
                          } else {
                              id = t.nextTokenId;
                              while (_get(oo, _ownershipIndex(id)) != 0) {
                                  id = _useExistsLookup()
                                      ? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId)
                                      : _wrapNFTId(id + 1, maxId);
                              }
                              t.nextTokenId = _wrapNFTId(id + 1, maxId);
                          }
                          if (_useExistsLookup()) _set($.exists, id, true);
                          _set(toOwned, toIndex, uint32(id));
                          _setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
                          _packedLogsAppend(packedLogs, id);
                          _afterNFTTransfer(address(0), to, id);
                      } while (toIndex != t.toEnd);
  
                      $.nextTokenId = uint32(t.nextTokenId);
                      $.burnedPoolHead = burnedPoolHead;
                      _packedLogsSend(packedLogs, $.mirrorERC721);
                  }
              }
          }
          /// @solidity memory-safe-assembly
          assembly {
              // Emit the {Transfer} event.
              mstore(0x00, amount)
              log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
          }
      }
  
      /// @dev Mints `amount` tokens to `to`, increasing the total supply.
      /// This variant mints NFT tokens starting from ID `preTotalSupply / _unit() + 1`.
      /// This variant will not touch the `burnedPool` and `nextTokenId`.
      ///
      /// Will mint NFTs to `to` if the recipient's new balance supports
      /// additional NFTs ***AND*** the `to` address's skipNFT flag is set to false.
      ///
      /// Emits a {Transfer} event.
      function _mintNext(address to, uint256 amount) internal virtual {
          if (to == address(0)) revert TransferToZeroAddress();
  
          AddressData storage toAddressData = _addressData(to);
          DN404Storage storage $ = _getDN404Storage();
          if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
  
          _DNMintTemps memory t;
          unchecked {
              uint256 toBalance = uint256(toAddressData.balance) + amount;
              toAddressData.balance = uint96(toBalance);
              t.toEnd = toBalance / _unit();
          }
          uint256 startId;
          uint256 maxId;
          unchecked {
              uint256 preTotalSupply = uint256($.totalSupply);
              startId = preTotalSupply / _unit() + 1;
              uint256 totalSupply_ = uint256(preTotalSupply) + amount;
              $.totalSupply = uint96(totalSupply_);
              uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
              if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow();
              maxId = totalSupply_ / _unit();
          }
          unchecked {
              if (_isZero(toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG)) {
                  Uint32Map storage toOwned = $.owned[to];
                  Uint32Map storage oo = $.oo;
                  uint256 toIndex = toAddressData.ownedLength;
                  _DNPackedLogs memory packedLogs = _packedLogsMalloc(_zeroFloorSub(t.toEnd, toIndex));
  
                  if (packedLogs.logs.length != 0) {
                      _packedLogsSet(packedLogs, to, 0);
                      $.totalNFTSupply += uint32(packedLogs.logs.length);
                      toAddressData.ownedLength = uint32(t.toEnd);
                      t.toAlias = _registerAndResolveAlias(toAddressData, to);
                      // Mint loop.
                      do {
                          uint256 id = startId;
                          while (_get(oo, _ownershipIndex(id)) != 0) {
                              id = _useExistsLookup()
                                  ? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId)
                                  : _wrapNFTId(id + 1, maxId);
                          }
                          startId = _wrapNFTId(id + 1, maxId);
                          if (_useExistsLookup()) _set($.exists, id, true);
                          _set(toOwned, toIndex, uint32(id));
                          _setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
                          _packedLogsAppend(packedLogs, id);
                          _afterNFTTransfer(address(0), to, id);
                      } while (toIndex != t.toEnd);
  
                      _packedLogsSend(packedLogs, $.mirrorERC721);
                  }
              }
          }
          /// @solidity memory-safe-assembly
          assembly {
              // Emit the {Transfer} event.
              mstore(0x00, amount)
              log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
          }
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                  INTERNAL BURN FUNCTIONS                   */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Burns `amount` tokens from `from`, reducing the total supply.
      ///
      /// Will burn sender NFTs if balance after transfer is less than
      /// the amount required to support the current NFT balance.
      ///
      /// Emits a {Transfer} event.
      function _burn(address from, uint256 amount) internal virtual {
          DN404Storage storage $ = _getDN404Storage();
          if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
  
          AddressData storage fromAddressData = $.addressData[from];
          uint256 fromBalance = fromAddressData.balance;
          if (amount > fromBalance) revert InsufficientBalance();
  
          unchecked {
              fromAddressData.balance = uint96(fromBalance -= amount);
              uint256 totalSupply_ = uint256($.totalSupply) - amount;
              $.totalSupply = uint96(totalSupply_);
  
              Uint32Map storage fromOwned = $.owned[from];
              uint256 fromIndex = fromAddressData.ownedLength;
              uint256 numNFTBurns = _zeroFloorSub(fromIndex, fromBalance / _unit());
  
              if (numNFTBurns != 0) {
                  _DNPackedLogs memory packedLogs = _packedLogsMalloc(numNFTBurns);
                  _packedLogsSet(packedLogs, from, 1);
                  bool addToBurnedPool;
                  {
                      uint256 totalNFTSupply = uint256($.totalNFTSupply) - numNFTBurns;
                      $.totalNFTSupply = uint32(totalNFTSupply);
                      addToBurnedPool = _addToBurnedPool(totalNFTSupply, totalSupply_);
                  }
  
                  Uint32Map storage oo = $.oo;
                  uint256 fromEnd = fromIndex - numNFTBurns;
                  fromAddressData.ownedLength = uint32(fromEnd);
                  uint32 burnedPoolTail = $.burnedPoolTail;
                  // Burn loop.
                  do {
                      uint256 id = _get(fromOwned, --fromIndex);
                      _setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
                      _packedLogsAppend(packedLogs, id);
                      if (_useExistsLookup()) _set($.exists, id, false);
                      if (addToBurnedPool) _set($.burnedPool, burnedPoolTail++, uint32(id));
                      if (_get($.mayHaveNFTApproval, id)) {
                          _set($.mayHaveNFTApproval, id, false);
                          delete $.nftApprovals[id];
                      }
                      _afterNFTTransfer(from, address(0), id);
                  } while (fromIndex != fromEnd);
  
                  if (addToBurnedPool) $.burnedPoolTail = burnedPoolTail;
                  _packedLogsSend(packedLogs, $.mirrorERC721);
              }
          }
          /// @solidity memory-safe-assembly
          assembly {
              // Emit the {Transfer} event.
              mstore(0x00, amount)
              log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
          }
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                INTERNAL TRANSFER FUNCTIONS                 */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Moves `amount` of tokens from `from` to `to`.
      ///
      /// Will burn sender NFTs if balance after transfer is less than
      /// the amount required to support the current NFT balance.
      ///
      /// Will mint NFTs to `to` if the recipient's new balance supports
      /// additional NFTs ***AND*** the `to` address's skipNFT flag is
      /// set to false.
      ///
      /// Emits a {Transfer} event.
      function _transfer(address from, address to, uint256 amount) internal virtual {
          if (to == address(0)) revert TransferToZeroAddress();
  
          DN404Storage storage $ = _getDN404Storage();
          AddressData storage fromAddressData = $.addressData[from];
          AddressData storage toAddressData = _addressData(to);
          if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
  
          _DNTransferTemps memory t;
          t.fromOwnedLength = fromAddressData.ownedLength;
          t.toOwnedLength = toAddressData.ownedLength;
  
          unchecked {
              {
                  uint256 fromBalance = fromAddressData.balance;
                  if (amount > fromBalance) revert InsufficientBalance();
                  fromAddressData.balance = uint96(fromBalance -= amount);
  
                  uint256 toBalance = uint256(toAddressData.balance) + amount;
                  toAddressData.balance = uint96(toBalance);
                  t.numNFTBurns = _zeroFloorSub(t.fromOwnedLength, fromBalance / _unit());
  
                  if (_isZero(toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG)) {
                      if (from == to) t.toOwnedLength = t.fromOwnedLength - t.numNFTBurns;
                      t.numNFTMints = _zeroFloorSub(toBalance / _unit(), t.toOwnedLength);
                  }
              }
  
              while (_useDirectTransfersIfPossible()) {
                  uint256 n = _min(t.fromOwnedLength, _min(t.numNFTBurns, t.numNFTMints));
                  if (_isZero(n)) break;
                  t.numNFTBurns -= n;
                  t.numNFTMints -= n;
                  if (from == to) {
                      t.toOwnedLength += n;
                      break;
                  }
                  _DNDirectLogs memory directLogs = _directLogsMalloc(n, from, to);
                  Uint32Map storage fromOwned = $.owned[from];
                  Uint32Map storage toOwned = $.owned[to];
                  t.toAlias = _registerAndResolveAlias(toAddressData, to);
                  uint256 toIndex = t.toOwnedLength;
                  n = toIndex + n;
                  // Direct transfer loop.
                  do {
                      uint256 id = _get(fromOwned, --t.fromOwnedLength);
                      _set(toOwned, toIndex, uint32(id));
                      _setOwnerAliasAndOwnedIndex($.oo, id, t.toAlias, uint32(toIndex));
                      _directLogsAppend(directLogs, id);
                      if (_get($.mayHaveNFTApproval, id)) {
                          _set($.mayHaveNFTApproval, id, false);
                          delete $.nftApprovals[id];
                      }
                      _afterNFTTransfer(from, to, id);
                  } while (++toIndex != n);
  
                  toAddressData.ownedLength = uint32(t.toOwnedLength = toIndex);
                  fromAddressData.ownedLength = uint32(t.fromOwnedLength);
                  _directLogsSend(directLogs, $.mirrorERC721);
                  break;
              }
  
              t.totalNFTSupply = uint256($.totalNFTSupply) + t.numNFTMints - t.numNFTBurns;
              $.totalNFTSupply = uint32(t.totalNFTSupply);
  
              Uint32Map storage oo = $.oo;
              _DNPackedLogs memory packedLogs = _packedLogsMalloc(t.numNFTBurns + t.numNFTMints);
  
              t.burnedPoolTail = $.burnedPoolTail;
              if (t.numNFTBurns != 0) {
                  _packedLogsSet(packedLogs, from, 1);
                  bool addToBurnedPool = _addToBurnedPool(t.totalNFTSupply, $.totalSupply);
                  Uint32Map storage fromOwned = $.owned[from];
                  uint256 fromIndex = t.fromOwnedLength;
                  fromAddressData.ownedLength = uint32(t.fromEnd = fromIndex - t.numNFTBurns);
                  uint32 burnedPoolTail = t.burnedPoolTail;
                  // Burn loop.
                  do {
                      uint256 id = _get(fromOwned, --fromIndex);
                      _setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
                      _packedLogsAppend(packedLogs, id);
                      if (_useExistsLookup()) _set($.exists, id, false);
                      if (addToBurnedPool) _set($.burnedPool, burnedPoolTail++, uint32(id));
                      if (_get($.mayHaveNFTApproval, id)) {
                          _set($.mayHaveNFTApproval, id, false);
                          delete $.nftApprovals[id];
                      }
                      _afterNFTTransfer(from, address(0), id);
                  } while (fromIndex != t.fromEnd);
  
                  if (addToBurnedPool) $.burnedPoolTail = (t.burnedPoolTail = burnedPoolTail);
              }
  
              if (t.numNFTMints != 0) {
                  _packedLogsSet(packedLogs, to, 0);
                  Uint32Map storage toOwned = $.owned[to];
                  t.toAlias = _registerAndResolveAlias(toAddressData, to);
                  uint256 maxId = $.totalSupply / _unit();
                  t.nextTokenId = _wrapNFTId($.nextTokenId, maxId);
                  uint256 toIndex = t.toOwnedLength;
                  toAddressData.ownedLength = uint32(t.toEnd = toIndex + t.numNFTMints);
                  uint32 burnedPoolHead = $.burnedPoolHead;
                  // Mint loop.
                  do {
                      uint256 id;
                      if (burnedPoolHead != t.burnedPoolTail) {
                          id = _get($.burnedPool, burnedPoolHead++);
                      } else {
                          id = t.nextTokenId;
                          while (_get(oo, _ownershipIndex(id)) != 0) {
                              id = _useExistsLookup()
                                  ? _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId)
                                  : _wrapNFTId(id + 1, maxId);
                          }
                          t.nextTokenId = _wrapNFTId(id + 1, maxId);
                      }
                      if (_useExistsLookup()) _set($.exists, id, true);
                      _set(toOwned, toIndex, uint32(id));
                      _setOwnerAliasAndOwnedIndex(oo, id, t.toAlias, uint32(toIndex++));
                      _packedLogsAppend(packedLogs, id);
                      _afterNFTTransfer(address(0), to, id);
                  } while (toIndex != t.toEnd);
  
                  $.burnedPoolHead = burnedPoolHead;
                  $.nextTokenId = uint32(t.nextTokenId);
              }
  
              if (packedLogs.logs.length != 0) _packedLogsSend(packedLogs, $.mirrorERC721);
          }
          /// @solidity memory-safe-assembly
          assembly {
              // Emit the {Transfer} event.
              mstore(0x00, amount)
              // forgefmt: disable-next-item
              log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)))
          }
      }
  
      /// @dev Transfers token `id` from `from` to `to`.
      ///
      /// Requirements:
      ///
      /// - Call must originate from the mirror contract.
      /// - Token `id` must exist.
      /// - `from` must be the owner of the token.
      /// - `to` cannot be the zero address.
      ///   `msgSender` must be the owner of the token, or be approved to manage the token.
      ///
      /// Emits a {Transfer} event.
      function _transferFromNFT(address from, address to, uint256 id, address msgSender)
          internal
          virtual
      {
          if (to == address(0)) revert TransferToZeroAddress();
  
          DN404Storage storage $ = _getDN404Storage();
          if ($.mirrorERC721 == address(0)) revert DNNotInitialized();
  
          Uint32Map storage oo = $.oo;
  
          if (from != $.aliasToAddress[_get(oo, _ownershipIndex(_restrictNFTId(id)))]) {
              revert TransferFromIncorrectOwner();
          }
  
          if (msgSender != from) {
              if (!_isApprovedForAll(from, msgSender)) {
                  if (_getApproved(id) != msgSender) {
                      revert TransferCallerNotOwnerNorApproved();
                  }
              }
          }
  
          AddressData storage fromAddressData = $.addressData[from];
          AddressData storage toAddressData = $.addressData[to];
  
          uint256 unit = _unit();
          mapping(address => Uint32Map) storage owned = $.owned;
  
          unchecked {
              uint256 fromBalance = fromAddressData.balance;
              if (unit > fromBalance) revert InsufficientBalance();
              fromAddressData.balance = uint96(fromBalance - unit);
              toAddressData.balance += uint96(unit);
          }
          if (_get($.mayHaveNFTApproval, id)) {
              _set($.mayHaveNFTApproval, id, false);
              delete $.nftApprovals[id];
          }
          unchecked {
              Uint32Map storage fromOwned = owned[from];
              uint32 updatedId = _get(fromOwned, --fromAddressData.ownedLength);
              uint32 i = _get(oo, _ownedIndex(id));
              _set(fromOwned, i, updatedId);
              _set(oo, _ownedIndex(updatedId), i);
          }
          unchecked {
              uint32 n = toAddressData.ownedLength++;
              _set(owned[to], n, uint32(id));
              _setOwnerAliasAndOwnedIndex(oo, id, _registerAndResolveAlias(toAddressData, to), n);
          }
          _afterNFTTransfer(from, to, id);
          /// @solidity memory-safe-assembly
          assembly {
              // Emit the {Transfer} event.
              mstore(0x00, unit)
              // forgefmt: disable-next-item
              log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)))
          }
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                 INTERNAL APPROVE FUNCTIONS                 */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
      ///
      /// Emits a {Approval} event.
      function _approve(address owner, address spender, uint256 amount) internal virtual {
          if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) {
              _getDN404Storage().addressData[owner].flags |= _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG;
          }
          _ref(_getDN404Storage().allowance, owner, spender).value = amount;
          /// @solidity memory-safe-assembly
          assembly {
              // Emit the {Approval} event.
              mstore(0x00, amount)
              // forgefmt: disable-next-item
              log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, shl(96, owner)), shr(96, shl(96, spender)))
          }
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                 DATA HITCHHIKING FUNCTIONS                 */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Returns the auxiliary data for `owner`.
      /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
      /// Auxiliary data can be set for any address, even if it does not have any tokens.
      function _getAux(address owner) internal view virtual returns (uint88) {
          return _getDN404Storage().addressData[owner].aux;
      }
  
      /// @dev Set the auxiliary data for `owner` to `value`.
      /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
      /// Auxiliary data can be set for any address, even if it does not have any tokens.
      function _setAux(address owner, uint88 value) internal virtual {
          _getDN404Storage().addressData[owner].aux = value;
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                     SKIP NFT FUNCTIONS                     */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Returns true if minting and transferring ERC20s to `owner` will skip minting NFTs.
      /// Returns false otherwise.
      function getSkipNFT(address owner) public view virtual returns (bool) {
          AddressData storage d = _getDN404Storage().addressData[owner];
          if (_isZero(d.flags & _ADDRESS_DATA_INITIALIZED_FLAG)) return _hasCode(owner);
          return d.flags & _ADDRESS_DATA_SKIP_NFT_FLAG != 0;
      }
  
      /// @dev Sets the caller's skipNFT flag to `skipNFT`. Returns true.
      ///
      /// Emits a {SkipNFTSet} event.
      function setSkipNFT(bool skipNFT) public virtual returns (bool) {
          _setSkipNFT(msg.sender, skipNFT);
          return true;
      }
  
      /// @dev Internal function to set account `owner` skipNFT flag to `state`
      ///
      /// Initializes account `owner` AddressData if it is not currently initialized.
      ///
      /// Emits a {SkipNFTSet} event.
      function _setSkipNFT(address owner, bool state) internal virtual {
          AddressData storage d = _addressData(owner);
          if ((d.flags & _ADDRESS_DATA_SKIP_NFT_FLAG != 0) != state) {
              d.flags ^= _ADDRESS_DATA_SKIP_NFT_FLAG;
          }
          /// @solidity memory-safe-assembly
          assembly {
              mstore(0x00, iszero(iszero(state)))
              log2(0x00, 0x20, _SKIP_NFT_SET_EVENT_SIGNATURE, shr(96, shl(96, owner)))
          }
      }
  
      /// @dev Returns a storage data pointer for account `owner` AddressData
      ///
      /// Initializes account `owner` AddressData if it is not currently initialized.
      function _addressData(address owner) internal virtual returns (AddressData storage d) {
          d = _getDN404Storage().addressData[owner];
          unchecked {
              if (_isZero(d.flags & _ADDRESS_DATA_INITIALIZED_FLAG)) {
                  uint256 skipNFT = _toUint(_hasCode(owner)) * _ADDRESS_DATA_SKIP_NFT_FLAG;
                  d.flags = uint8(skipNFT | _ADDRESS_DATA_INITIALIZED_FLAG);
              }
          }
      }
  
      /// @dev Returns the `addressAlias` of account `to`.
      ///
      /// Assigns and registers the next alias if `to` alias was not previously registered.
      function _registerAndResolveAlias(AddressData storage toAddressData, address to)
          internal
          virtual
          returns (uint32 addressAlias)
      {
          DN404Storage storage $ = _getDN404Storage();
          addressAlias = toAddressData.addressAlias;
          if (_isZero(addressAlias)) {
              unchecked {
                  addressAlias = ++$.numAliases;
              }
              toAddressData.addressAlias = addressAlias;
              $.aliasToAddress[addressAlias] = to;
              if (_isZero(addressAlias)) revert(); // Overflow.
          }
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                     MIRROR OPERATIONS                      */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Returns the address of the mirror NFT contract.
      function mirrorERC721() public view virtual returns (address) {
          return _getDN404Storage().mirrorERC721;
      }
  
      /// @dev Returns the total NFT supply.
      function _totalNFTSupply() internal view virtual returns (uint256) {
          return _getDN404Storage().totalNFTSupply;
      }
  
      /// @dev Returns `owner` NFT balance.
      function _balanceOfNFT(address owner) internal view virtual returns (uint256) {
          return _getDN404Storage().addressData[owner].ownedLength;
      }
  
      /// @dev Returns the owner of token `id`.
      /// Returns the zero address instead of reverting if the token does not exist.
      function _ownerAt(uint256 id) internal view virtual returns (address) {
          DN404Storage storage $ = _getDN404Storage();
          return $.aliasToAddress[_get($.oo, _ownershipIndex(_restrictNFTId(id)))];
      }
  
      /// @dev Returns the owner of token `id`.
      ///
      /// Requirements:
      /// - Token `id` must exist.
      function _ownerOf(uint256 id) internal view virtual returns (address) {
          if (!_exists(id)) revert TokenDoesNotExist();
          return _ownerAt(id);
      }
  
      /// @dev Returns whether `operator` is approved to manage the NFT tokens of `owner`.
      function _isApprovedForAll(address owner, address operator)
          internal
          view
          virtual
          returns (bool)
      {
          return !_isZero(_ref(_getDN404Storage().operatorApprovals, owner, operator).value);
      }
  
      /// @dev Returns if token `id` exists.
      function _exists(uint256 id) internal view virtual returns (bool) {
          return _ownerAt(id) != address(0);
      }
  
      /// @dev Returns the account approved to manage token `id`.
      ///
      /// Requirements:
      /// - Token `id` must exist.
      function _getApproved(uint256 id) internal view virtual returns (address) {
          if (!_exists(id)) revert TokenDoesNotExist();
          return _getDN404Storage().nftApprovals[id];
      }
  
      /// @dev Sets `spender` as the approved account to manage token `id`, using `msgSender`.
      ///
      /// Requirements:
      /// - `msgSender` must be the owner or an approved operator for the token owner.
      function _approveNFT(address spender, uint256 id, address msgSender)
          internal
          virtual
          returns (address owner)
      {
          DN404Storage storage $ = _getDN404Storage();
  
          owner = $.aliasToAddress[_get($.oo, _ownershipIndex(_restrictNFTId(id)))];
  
          if (msgSender != owner) {
              if (!_isApprovedForAll(owner, msgSender)) {
                  revert ApprovalCallerNotOwnerNorApproved();
              }
          }
  
          $.nftApprovals[id] = spender;
          _set($.mayHaveNFTApproval, id, spender != address(0));
      }
  
      /// @dev Approve or remove the `operator` as an operator for `msgSender`,
      /// without authorization checks.
      function _setApprovalForAll(address operator, bool approved, address msgSender)
          internal
          virtual
      {
          _ref(_getDN404Storage().operatorApprovals, msgSender, operator).value = _toUint(approved);
      }
  
      /// @dev Returns the NFT IDs of `owner` in the range `[begin..end)` (exclusive of `end`).
      /// `begin` and `end` are indices in the owner's token ID array, not the entire token range.
      /// Optimized for smaller bytecode size, as this function is intended for off-chain calling.
      function _ownedIds(address owner, uint256 begin, uint256 end)
          internal
          view
          virtual
          returns (uint256[] memory ids)
      {
          DN404Storage storage $ = _getDN404Storage();
          Uint32Map storage owned = $.owned[owner];
          end = _min($.addressData[owner].ownedLength, end);
          /// @solidity memory-safe-assembly
          assembly {
              ids := mload(0x40)
              let i := begin
              for {} lt(i, end) { i := add(i, 1) } {
                  let s := add(shl(96, owned.slot), shr(3, i)) // Storage slot.
                  let id := and(0xffffffff, shr(shl(5, and(i, 7)), sload(s)))
                  mstore(add(add(ids, 0x20), shl(5, sub(i, begin))), id) // Append to.
              }
              mstore(ids, sub(i, begin)) // Store the length.
              mstore(0x40, add(add(ids, 0x20), shl(5, sub(i, begin)))) // Allocate memory.
          }
      }
  
      /// @dev Fallback modifier to dispatch calls from the mirror NFT contract
      /// to internal functions in this contract.
      modifier dn404Fallback() virtual {
          DN404Storage storage $ = _getDN404Storage();
  
          uint256 fnSelector = _calldataload(0x00) >> 224;
  
          // `transferFromNFT(address,address,uint256,address)`.
          if (fnSelector == 0xe5eb36c8) {
              if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
              if (_isPaused()) revert Paused();
              _transferFromNFT(
                  address(uint160(_calldataload(0x04))), // `from`.
                  address(uint160(_calldataload(0x24))), // `to`.
                  _calldataload(0x44), // `id`.
                  address(uint160(_calldataload(0x64))) // `msgSender`.
              );
              _return(1);
          }
          // `setApprovalForAll(address,bool,address)`.
          if (fnSelector == 0x813500fc) {
              if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
              _setApprovalForAll(
                  address(uint160(_calldataload(0x04))), // `spender`.
                  _calldataload(0x24) != 0, // `status`.
                  address(uint160(_calldataload(0x44))) // `msgSender`.
              );
              _return(1);
          }
          // `isApprovedForAll(address,address)`.
          if (fnSelector == 0xe985e9c5) {
              bool result = _isApprovedForAll(
                  address(uint160(_calldataload(0x04))), // `owner`.
                  address(uint160(_calldataload(0x24))) // `operator`.
              );
              _return(_toUint(result));
          }
          // `ownerOf(uint256)`.
          if (fnSelector == 0x6352211e) {
              _return(uint160(_ownerOf(_calldataload(0x04))));
          }
          // `ownerAt(uint256)`.
          if (fnSelector == 0x24359879) {
              _return(uint160(_ownerAt(_calldataload(0x04))));
          }
          // `approveNFT(address,uint256,address)`.
          if (fnSelector == 0xd10b6e0c) {
              if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
              address owner = _approveNFT(
                  address(uint160(_calldataload(0x04))), // `spender`.
                  _calldataload(0x24), // `id`.
                  address(uint160(_calldataload(0x44))) // `msgSender`.
              );
              _return(uint160(owner));
          }
          // `getApproved(uint256)`.
          if (fnSelector == 0x081812fc) {
              _return(uint160(_getApproved(_calldataload(0x04))));
          }
          // `balanceOfNFT(address)`.
          if (fnSelector == 0xf5b100ea) {
              _return(_balanceOfNFT(address(uint160(_calldataload(0x04)))));
          }
          // `totalNFTSupply()`.
          if (fnSelector == 0xe2c79281) {
              _return(_totalNFTSupply());
          }
          // `tokenURI(uint256)`.
          if (fnSelector == 0xc87b56dd) {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x40, add(mload(0x40), 0x20))
              }
              string memory uri = _tokenURI(_calldataload(0x04));
              /// @solidity memory-safe-assembly
              assembly {
                  // Memory safe, as we've advanced the free memory pointer by a word.
                  let o := sub(uri, 0x20)
                  mstore(o, 0x20) // Store the offset of `uri`.
                  return(o, add(0x60, mload(uri)))
              }
          }
          // `implementsDN404()`.
          if (fnSelector == 0xb7a94eb8) {
              _return(1);
          }
          _;
      }
  
      /// @dev Fallback function for calls from mirror NFT contract.
      /// Override this if you need to implement your custom
      /// fallback with utilities like Solady's `LibZip.cdFallback()`.
      /// And always remember to always wrap the fallback with `dn404Fallback`.
      fallback() external payable virtual dn404Fallback {
          revert FnSelectorNotRecognized(); // Not mandatory. Just for quality of life.
      }
  
      /// @dev This is to silence the compiler warning.
      /// Override and remove the revert if you want your contract to receive ETH via receive.
      receive() external payable virtual {
          if (msg.value != 0) revert();
      }
  
      /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
      /*                 INTERNAL / PRIVATE HELPERS                 */
      /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
  
      /// @dev Returns `(i - 1) << 1`.
      function _ownershipIndex(uint256 i) internal pure returns (uint256) {
          unchecked {
              return (i - 1) << 1; // Minus 1 as token IDs start from 1.
          }
      }
  
      /// @dev Returns `((i - 1) << 1) + 1`.
      function _ownedIndex(uint256 i) internal pure returns (uint256) {
          unchecked {
              return ((i - 1) << 1) + 1; // Minus 1 as token IDs start from 1.
          }
      }
  
      /// @dev Returns the uint32 value at `index` in `map`.
      function _get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) {
          /// @solidity memory-safe-assembly
          assembly {
              let s := add(shl(96, map.slot), shr(3, index)) // Storage slot.
              result := and(0xffffffff, shr(shl(5, and(index, 7)), sload(s)))
          }
      }
  
      /// @dev Updates the uint32 value at `index` in `map`.
      function _set(Uint32Map storage map, uint256 index, uint32 value) internal {
          /// @solidity memory-safe-assembly
          assembly {
              let s := add(shl(96, map.slot), shr(3, index)) // Storage slot.
              let o := shl(5, and(index, 7)) // Storage slot offset (bits).
              let v := sload(s) // Storage slot value.
              sstore(s, xor(v, shl(o, and(0xffffffff, xor(value, shr(o, v))))))
          }
      }
  
      /// @dev Sets the owner alias and the owned index together.
      function _setOwnerAliasAndOwnedIndex(
          Uint32Map storage map,
          uint256 id,
          uint32 ownership,
          uint32 ownedIndex
      ) internal {
          /// @solidity memory-safe-assembly
          assembly {
              let i := sub(id, 1) // Index of the uint64 combined value.
              let s := add(shl(96, map.slot), shr(2, i)) // Storage slot.
              let v := sload(s) // Storage slot value.
              let o := shl(6, and(i, 3)) // Storage slot offset (bits).
              let combined := or(shl(32, ownedIndex), and(0xffffffff, ownership))
              sstore(s, xor(v, shl(o, and(0xffffffffffffffff, xor(shr(o, v), combined)))))
          }
      }
  
      /// @dev Returns the boolean value of the bit at `index` in `bitmap`.
      function _get(Bitmap storage bitmap, uint256 index) internal view returns (bool result) {
          /// @solidity memory-safe-assembly
          assembly {
              let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot.
              result := and(1, shr(and(0xff, index), sload(s)))
          }
      }
  
      /// @dev Updates the bit at `index` in `bitmap` to `value`.
      function _set(Bitmap storage bitmap, uint256 index, bool value) internal {
          /// @solidity memory-safe-assembly
          assembly {
              let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot.
              let o := and(0xff, index) // Storage slot offset (bits).
              sstore(s, or(and(sload(s), not(shl(o, 1))), shl(o, iszero(iszero(value)))))
          }
      }
  
      /// @dev Returns the index of the least significant unset bit in `[begin..upTo]`.
      /// If no set bit is found, returns `type(uint256).max`.
      function _findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 upTo)
          internal
          view
          returns (uint256 unsetBitIndex)
      {
          /// @solidity memory-safe-assembly
          assembly {
              unsetBitIndex := not(0) // Initialize to `type(uint256).max`.
              let s := shl(96, bitmap.slot) // Storage offset of the bitmap.
              let bucket := add(s, shr(8, begin))
              let negBits := shl(and(0xff, begin), shr(and(0xff, begin), not(sload(bucket))))
              if iszero(negBits) {
                  let lastBucket := add(s, shr(8, upTo))
                  for {} 1 {} {
                      bucket := add(bucket, 1)
                      negBits := not(sload(bucket))
                      if or(negBits, gt(bucket, lastBucket)) { break }
                  }
                  if gt(bucket, lastBucket) {
                      negBits := shr(and(0xff, not(upTo)), shl(and(0xff, not(upTo)), negBits))
                  }
              }
              if negBits {
                  // Find-first-set routine.
                  let b := and(negBits, add(not(negBits), 1)) // Isolate the least significant bit.
                  let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, b))
                  r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
                  r := or(r, shl(5, lt(0xffffffff, shr(r, b))))
                  // For the remaining 32 bits, use a De Bruijn lookup.
                  // forgefmt: disable-next-item
                  r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
                      0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
                  r := or(shl(8, sub(bucket, s)), r)
                  unsetBitIndex := or(r, sub(0, or(gt(r, upTo), lt(r, begin))))
              }
          }
      }
  
      /// @dev Returns a storage reference to the value at (`a0`, `a1`) in `map`.
      function _ref(AddressPairToUint256RefMap storage map, address a0, address a1)
          internal
          pure
          returns (Uint256Ref storage ref)
      {
          /// @solidity memory-safe-assembly
          assembly {
              mstore(0x28, a1)
              mstore(0x14, a0)
              mstore(0x00, map.slot)
              ref.slot := keccak256(0x00, 0x48)
              // Clear the part of the free memory pointer that was overwritten.
              mstore(0x28, 0x00)
          }
      }
  
      /// @dev Wraps the NFT ID.
      function _wrapNFTId(uint256 id, uint256 maxId) internal pure returns (uint256 result) {
          /// @solidity memory-safe-assembly
          assembly {
              result := or(mul(iszero(gt(id, maxId)), id), gt(id, maxId))
          }
      }
  
      /// @dev Returns `id > type(uint32).max ? 0 : id`.
      function _restrictNFTId(uint256 id) internal pure returns (uint256 result) {
          /// @solidity memory-safe-assembly
          assembly {
              result := mul(id, lt(id, 0x100000000))
          }
      }
  
      /// @dev Returns whether `amount` is a valid `totalSupply`.
      function _totalSupplyOverflows(uint256 amount) internal view returns (bool result) {
          uint256 unit = _unit();
          /// @solidity memory-safe-assembly
          assembly {
              result := iszero(iszero(or(shr(96, amount), lt(0xfffffffe, div(amount, unit)))))
          }
      }
  
      /// @dev Returns `max(0, x - y)`.
      function _zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
          /// @solidity memory-safe-assembly
          assembly {
              z := mul(gt(x, y), sub(x, y))
          }
      }
  
      /// @dev Returns `x < y ? x : y`.
      function _min(uint256 x, uint256 y) internal pure returns (uint256 z) {
          /// @solidity memory-safe-assembly
          assembly {
              z := xor(x, mul(xor(x, y), lt(y, x)))
          }
      }
  
      /// @dev Returns `b ? 1 : 0`.
      function _toUint(bool b) internal pure returns (uint256 result) {
          /// @solidity memory-safe-assembly
          assembly {
              result := iszero(iszero(b))
          }
      }
  
      /// @dev Returns `b == 0`. This is because solc is sometimes dumb.
      function _isZero(uint256 x) internal pure returns (bool result) {
          /// @solidity memory-safe-assembly
          assembly {
              result := iszero(x)
          }
      }
  
      /// @dev Struct containing direct transfer log data for {Transfer} events to be
      /// emitted by the mirror NFT contract.
      struct _DNDirectLogs {
          uint256 offset;
          uint256[] logs;
      }
  
      /// @dev Initiates memory allocation for direct logs with `n` log items.
      function _directLogsMalloc(uint256 n, address from, address to)
          private
          pure
          returns (_DNDirectLogs memory p)
      {
          /// @solidity memory-safe-assembly
          assembly {
              let m := mload(0x40)
              mstore(m, 0x144027d3) // `logDirectTransfer(address,address,uint256[])`.
              mstore(add(m, 0x20), shr(96, shl(96, from)))
              mstore(add(m, 0x40), shr(96, shl(96, to)))
              mstore(add(m, 0x60), 0x60) // Offset of `logs` in the calldata to send.
              // Skip 4 words: `fnSelector`, `from`, `to`, `calldataLogsOffset`.
              let logs := add(0x80, m)
              mstore(logs, n) // Store the length.
              let offset := add(0x20, logs) // Skip the word for `p.logs.length`.
              mstore(0x40, add(offset, shl(5, n))) // Allocate memory.
              mstore(add(0x20, p), logs) // Set `p.logs`.
              mstore(p, offset) // Set `p.offset`.
          }
      }
  
      /// @dev Adds a direct log item to `p` with token `id`.
      function _directLogsAppend(_DNDirectLogs memory p, uint256 id) private pure {
          /// @solidity memory-safe-assembly
          assembly {
              let offset := mload(p)
              mstore(offset, id)
              mstore(p, add(offset, 0x20))
          }
      }
  
      /// @dev Calls the `mirror` NFT contract to emit {Transfer} events for packed logs `p`.
      function _directLogsSend(_DNDirectLogs memory p, address mirror) private {
          /// @solidity memory-safe-assembly
          assembly {
              let logs := mload(add(p, 0x20))
              let n := add(0x84, shl(5, mload(logs))) // Length of calldata to send.
              let o := sub(logs, 0x80) // Start of calldata to send.
              if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {
                  revert(o, 0x00)
              }
          }
      }
  
      /// @dev Struct containing packed log data for {Transfer} events to be
      /// emitted by the mirror NFT contract.
      struct _DNPackedLogs {
          uint256 offset;
          uint256 addressAndBit;
          uint256[] logs;
      }
  
      /// @dev Initiates memory allocation for packed logs with `n` log items.
      function _packedLogsMalloc(uint256 n) private pure returns (_DNPackedLogs memory p) {
          /// @solidity memory-safe-assembly
          assembly {
              // Note that `p` implicitly allocates and advances the free memory pointer by
              // 3 words, which we can safely mutate in `_packedLogsSend`.
              let logs := mload(0x40)
              mstore(logs, n) // Store the length.
              let offset := add(0x20, logs) // Skip the word for `p.logs.length`.
              mstore(0x40, add(offset, shl(5, n))) // Allocate memory.
              mstore(add(0x40, p), logs) // Set `p.logs`.
              mstore(p, offset) // Set `p.offset`.
          }
      }
  
      /// @dev Set the current address and the burn bit.
      function _packedLogsSet(_DNPackedLogs memory p, address a, uint256 burnBit) private pure {
          /// @solidity memory-safe-assembly
          assembly {
              mstore(add(p, 0x20), or(shl(96, a), burnBit)) // Set `p.addressAndBit`.
          }
      }
  
      /// @dev Adds a packed log item to `p` with token `id`.
      function _packedLogsAppend(_DNPackedLogs memory p, uint256 id) private pure {
          /// @solidity memory-safe-assembly
          assembly {
              let offset := mload(p)
              mstore(offset, or(mload(add(p, 0x20)), shl(8, id))) // `p.addressAndBit | (id << 8)`.
              mstore(p, add(offset, 0x20))
          }
      }
  
      /// @dev Calls the `mirror` NFT contract to emit {Transfer} events for packed logs `p`.
      function _packedLogsSend(_DNPackedLogs memory p, address mirror) private {
          /// @solidity memory-safe-assembly
          assembly {
              let logs := mload(add(p, 0x40))
              let o := sub(logs, 0x40) // Start of calldata to send.
              mstore(o, 0x263c69d6) // `logTransfer(uint256[])`.
              mstore(add(o, 0x20), 0x20) // Offset of `logs` in the calldata to send.
              let n := add(0x44, shl(5, mload(logs))) // Length of calldata to send.
              if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {
                  revert(o, 0x00)
              }
          }
      }
  
      /// @dev Struct of temporary variables for transfers.
      struct _DNTransferTemps {
          uint256 numNFTBurns;
          uint256 numNFTMints;
          uint256 fromOwnedLength;
          uint256 toOwnedLength;
          uint256 totalNFTSupply;
          uint256 fromEnd;
          uint256 toEnd;
          uint32 toAlias;
          uint256 nextTokenId;
          uint32 burnedPoolTail;
      }
  
      /// @dev Struct of temporary variables for mints.
      struct _DNMintTemps {
          uint256 nextTokenId;
          uint32 burnedPoolTail;
          uint256 toEnd;
          uint32 toAlias;
      }
  
      /// @dev Returns if `a` has bytecode of non-zero length.
      function _hasCode(address a) private view returns (bool result) {
          /// @solidity memory-safe-assembly
          assembly {
              result := extcodesize(a) // Can handle dirty upper bits.
          }
      }
  
      /// @dev Returns the calldata value at `offset`.
      function _calldataload(uint256 offset) private pure returns (uint256 value) {
          /// @solidity memory-safe-assembly
          assembly {
              value := calldataload(offset)
          }
      }
  
      /// @dev Executes a return opcode to return `x` and end the current call frame.
      function _return(uint256 x) private pure {
          /// @solidity memory-safe-assembly
          assembly {
              mstore(0x00, x)
              return(0x00, 0x20)
          }
      }
  }
  
  
  // File contracts/pass/VelocityPass2Token.sol
  
  // Original license: SPDX_License_Identifier: MIT
  pragma solidity ^0.8.17;
  
  
  
  /**
   * @title VelocityPass2Token
   * @notice Sample DN404 contract that demonstrates the owner selling fungible tokens.
   * When a user has at least one base unit (10^18) amount of tokens, they will automatically receive an NFT.
   * NFTs are minted as an address accumulates each base unit amount of tokens.
   */
  contract VelocityPass2Token is DN404, UUPSUpgradeable {
      using Strings for uint256;
  
      address public owner;
      address public minter;
      uint256 public maxSupply;
      uint256 public totalMinted;
      string private _name;
      string private _symbol;
      string private _baseURI;
  
      bool private _paused;
      uint32 private _unitMultiple;
      uint32 private _splitIndex;
  
      modifier onlyOwner() {
          require(owner == msg.sender, "Ownable: not owner");
          _;
      }
  
      modifier onlyMinter() {
          require(minter == msg.sender, "Minter: not minter");
          _;
      }
  
      constructor() {
          _disableInitializers();
      }
  
      // required by the OZ UUPS module
      function _authorizeUpgrade(address) internal override onlyOwner {}
  
      function initialize(
          string memory name_,
          string memory symbol_,
          string memory baseURI_,
          uint32 unitMultiple_,
          uint32 splitIndex_,
          uint256 maxSupply_,
          address mirror_,
          address owner_,
          address minter_
          ) external initializer {
          require(mirror_ != address(0), "Pls set the mirror");
          _unitMultiple = unitMultiple_;
  
          _initializeDN404(0, address(0), mirror_, owner_);
          __UUPSUpgradeable_init();
  
          _name = name_;
          _symbol = symbol_;
          _baseURI = baseURI_;
          owner = owner_;
          minter = minter_;
          maxSupply = maxSupply_;
          _splitIndex = splitIndex_;
      }
  
      function transferOwnership(address newOwner) external onlyOwner {
          require(newOwner != address(0), "Ownable: new owner is zero");
          owner = newOwner;
      }
  
      function setMinter(address minter_) external onlyOwner {
          minter = minter_;
      }
  
      function pause() external onlyOwner {
          _paused = true;
      }
  
      function unPause() external onlyOwner {
          _paused = false;
      }
  
      function name() public view override returns (string memory) {
          return _name;
      }
  
      function symbol() public view override returns (string memory) {
          return _symbol;
      }
  
      function maxTokenSupply() public view returns (uint256) {
          return maxSupply * _unit();
      }
  
      function _tokenURI(uint256 tokenId) internal view override returns (string memory result) {
          require(_exists(tokenId), "ERC721: invalid token ID");
  
          uint256 index = 1;
  
          if (tokenId <= _splitIndex) {
              index = 0;
          }
          
          if (bytes(_baseURI).length != 0) {
              result = string(abi.encodePacked(_baseURI, index.toString()));
          }
      }
  
      function _unit() internal override view returns (uint256) {
          return uint256(_unitMultiple) * (10 ** 18);
      }
  
      function _isPaused() internal override view virtual returns (bool) {
          return _paused;
      }
  
      /*
      here the projectId and sender make no sense for the interface
      */
      function mint(address to, uint256 projectId, address sender) public onlyMinter returns (uint256 tokenId){
          require(totalMinted < maxSupply, "exceed max limit");
  
          //mint 1 unit token every time, so return one nft token id
          tokenId = _mint(to, _unit());
          unchecked {
              ++totalMinted;
          }
      }
  
      function setBaseURI(string calldata baseURI_) public onlyOwner {
          _baseURI = baseURI_;
      }
  
      function setSkipNFTSpecial(address user, bool skipNFT) public onlyOwner returns (bool) {
          _setSkipNFT(user, skipNFT);
          return true;
      }
  
      function airdrop(address[] calldata recipients, uint256[] calldata quantities) public onlyOwner {
          require(recipients.length == quantities.length);
          require(totalMinted < maxSupply, "exceed max limit");
  
          for (uint256 i = 0; i < recipients.length;) {
              _mint(recipients[i], _unit() * quantities[i]);
              unchecked {
                  ++i;
                  ++totalMinted;
              }
          }
      }
  
      function batchTransfer(address[] calldata recipients, uint256[] calldata quantities) public {
          require(recipients.length == quantities.length);
          if (_isPaused()) revert Paused();
  
          for (uint256 i = 0; i < recipients.length;) {
              _transfer(msg.sender, recipients[i], quantities[i]);
              unchecked {
                  ++i;
              }
          }
      }
  }