ETH Price: $2,436.60 (+0.36%)

Token

Blastin Pepes (bPEPE)
 

Overview

Max Total Supply

0 bPEPE

Holders

2,902

Total Transfers

-

Market

Price

$0.00 @ 0.000000 ETH

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

Blastin Pepes are the cyberpunk amphibians of the digital frontier. Regardless of their faction, their sole mission is to propel CultureFi into the mainstream, blending vibrant subcultures with futuristic innovation.

Contract Source Code Verified (Exact Match)

Contract Name:
MBlastinPepes

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 10000 runs

Other Settings:
paris EvmVersion
File 1 of 17 : MBlastinPepes.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";

import "../thruster/INonfungiblePositionManager.sol";
import "../BT404NFTWrapper.sol";
import "../blast/AbstractBlastContract.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";

contract MBlastinPepes is BT404NFTWrapper, AbstractBlastContract, Ownable {
    using Strings for uint256;

    error TransferToLiquidityPoolDisabled();

    address private _liquidityPool;
    bool liquidityPoolTransferDisabled;

    constructor(address _mirrorERC721) Ownable(msg.sender) {
        _initializeDN404(0, address(0), _mirrorERC721);
        // Blast configuration
        __AbstractBlastContract_init();
        _configurePointsOperator(msg.sender);

        // Disable transfers to liquidity pool until the floor lock is created
        liquidityPoolTransferDisabled = true;
    }

    /// @dev Throws if called by any account other than the owner or the mirror contract
    modifier onlyOwnerOrMirror() {
        if (owner() != _msgSender() && mirrorERC721() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
        _;
    }

    function name() public view virtual override returns (string memory) {
        return "Blastin Pepes";
    }

    function symbol() public view virtual override returns (string memory) {
        return "bPEPE";
    }

    function tokenURI(
        uint256 id
    ) public view virtual override returns (string memory) {
        return string.concat("https://api.blastinpepes.xyz/md/", id.toString());
    }

    /// @dev returns the supply liquidity
    function returnSupplyLiquidity(uint256 _maxNumNfts) external onlyOwnerOrMirror {
        uint256 amount = _maxNumNfts * _unit();
        uint256 balance = balanceOf(address(this));
        if (balance == 0) {
            return;
        }
        if (amount > balance) {
            amount = balance;
        }
        _transfer(address(this), mirrorERC721(), amount);
    }

    /// @dev sets skipNFT for an account
    function setSkipNFTFor(address account, bool state) public onlyOwnerOrMirror {
        _setSkipNFT(account, state);
    }

    /// @dev enables liquidity pool transfers
    function enableLiquidityPoolTransfer() public onlyOwnerOrMirror {
        liquidityPoolTransferDisabled = false;
    }

    /// @dev Create a new liquidity pool for the token that will be used for floor lock
    function createLiquidityPool(address nonfungiblePositionManager, address weth) external onlyOwner {
        bool isToken0 = address(this) < weth;
        address token0 = isToken0 ? address(this) : weth;
        address token1 = isToken0 ? weth : address(this);

        // Price equates to 0.069 ETH / 1000 tokens
        uint160 sqrtPriceX96 = isToken0 ? 658118545356139457330439066 : 9537949932697673064997196578117;

        address pool = INonfungiblePositionManager(nonfungiblePositionManager).createAndInitializePoolIfNecessary(
            token0,
            token1,
            3000,
            sqrtPriceX96
        );

        _setSkipNFT(pool, true);
        _setSkipNFT(mirrorERC721(), true);

        _liquidityPool = pool;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                    INTERNAL FUNCTIONS                      */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @inheritdoc DN404
    function _validateReceiver(address to) internal view override {
        if (liquidityPoolTransferDisabled && to == _liquidityPool) {
            revert TransferToLiquidityPoolDisabled();
        }
    }

    /// @inheritdoc DN404
    function _mintNFT(
        address to,
        uint256[] memory ids,
        bool lock
    ) internal virtual override {
        if (to == address(0)) {
            revert TransferToZeroAddress();
        }

        DN404Storage storage $ = _getDN404Storage();
        // refresh fee for `to`
        _pullFeeForTwo($, to, to);

        Uint32Map storage oo = $.oo;
        LibBitmap.Bitmap storage tokenLocks = $.tokenLocks;

        AddressData storage toAddressData = _addressData(to);
        uint32 toAlias = _registerAndResolveAlias(toAddressData, to);
        (uint32 n, Uint32Map storage ownedMap) = lock
            ? (toAddressData.lockedLength, $.locked[to])
            : (toAddressData.ownedLength, $.owned[to]);

        uint256 numToMints = ids.length;
        if (!lock) {
            for (uint256 i; i < numToMints; ++i) {
                uint256 id = ids[i];

                address from = $.aliasToAddress[_get(oo, _ownershipIndex(id))];
                if (from != address(0)) {
                    revert TransferFromIncorrectOwner();
                }

                _set(ownedMap, n, uint32(id));
                _setOwnerAliasAndOwnedIndex(oo, id, toAlias, n++);

                if (lock != LibBitmap.get(tokenLocks, id))
                    LibBitmap.setTo(tokenLocks, id, lock);
            }

            $.totalNFTSupply += uint32(numToMints);
            if (lock) {
                toAddressData.lockedLength = n;
                $.numLockedNFT += uint32(numToMints);
            } else {
                toAddressData.ownedLength = n;
            }

            // update market shares
            if ($.operatorApprovals[address(this)][to].value > 0 && !lock) {
                $.numExchangableNFT += uint32(numToMints);
            }
        }

        uint256 units = _unit() * numToMints;
        {
            uint256 totalSupply_ = units + $.totalSupply;
            uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
            if (overflows | _toUint(totalSupply_ < units) != 0) {
                revert TotalSupplyOverflow();
            }

            $.totalSupply = uint96(totalSupply_);
            toAddressData.balance += uint96(units);
        }

        /// @solidity memory-safe-assembly
        assembly {
            // Emit the {Transfer} event.
            mstore(0x00, units)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
        }
    }

    /// @inheritdoc DN404
    function _burnNFT(
        address from,
        uint256[] memory ids
    ) internal virtual override {
        if (from == address(0)) {
            revert TransferFromIncorrectOwner();
        }

        DN404Storage storage $ = _getDN404Storage();
        // refresh fee for `from`
        _pullFeeForTwo($, from, from);

        AddressData storage fromAddressData = _addressData(from);
        (uint32 lockedLength, uint32 ownedLength) = (
            fromAddressData.lockedLength,
            fromAddressData.ownedLength
        );
        uint256 ownedBurned = 0;
        uint256 numToBurns = ids.length;
        {
            Uint32Map storage oo = $.oo;
            LibBitmap.Bitmap storage tokenLocks = $.tokenLocks;
            (Uint32Map storage locked, Uint32Map storage owned) = (
                $.locked[from],
                $.owned[from]
            );

            for (uint256 i; i < numToBurns; ++i) {
                uint256 id = ids[i];

                address owner = $.aliasToAddress[_get(oo, _ownershipIndex(id))];
                if (from != owner) {
                    revert TransferCallerNotOwnerNorApproved();
                }

                uint32 toBurnIdx = _get(oo, _ownedIndex(id));
                if (LibBitmap.get(tokenLocks, id)) {
                    LibBitmap.setTo(tokenLocks, id, false);
                    _delNFTAt(locked, oo, toBurnIdx, --lockedLength);
                } else {
                    _delNFTAt(owned, oo, toBurnIdx, --ownedLength);
                    ++ownedBurned;
                }
                _removeNFTApproval($, id);

                _setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
            }
        }

        unchecked {
            uint256 lockedBurned = numToBurns - ownedBurned;
            if (lockedBurned > 0) {
                fromAddressData.lockedLength = lockedLength;
                $.numLockedNFT -= uint32(lockedBurned);
            }
            if (ownedBurned > 0) {
                fromAddressData.ownedLength = ownedLength;
            }

            // update market shares
            if (
                $.operatorApprovals[address(this)][from].value > 0 &&
                ownedBurned > 0
            ) {
                $.numExchangableNFT -= uint32(ownedBurned);
            }
        }

        uint256 units = _unit() * numToBurns;
        unchecked {
            $.totalNFTSupply -= uint32(numToBurns);
            $.totalSupply -= uint96(units);
            fromAddressData.balance -= uint96(units);
        }

        /// @solidity memory-safe-assembly
        assembly {
            // Emit the {Transfer} event.
            mstore(0x00, units)
            log3(
                0x00,
                0x20,
                _TRANSFER_EVENT_SIGNATURE,
                shr(96, shl(96, from)),
                0
            )
        }
    }
}

File 2 of 17 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 3 of 17 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 4 of 17 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 5 of 17 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 6 of 17 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @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 towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (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 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                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.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 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.

            uint256 twos = denominator & (0 - denominator);
            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 (unsignedRoundsUp(rounding) && 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
     * towards zero.
     *
     * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 7 of 17 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @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 8 of 17 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @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), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(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) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        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 bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 9 of 17 : AbstractBlastContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./IBlast.sol";
import "./BlastConstants.sol";

/// @title AbstractBlastContract
/// @notice An abstract contract that can configure/claim Blast points/yield
abstract contract AbstractBlastContract {
    /// @dev initializer for proxy
    function __AbstractBlastContract_init() internal {
        if (getChainId() == 81457) {
            IBlast blast = _getBlast();
            blast.configure(YieldMode.CLAIMABLE, GasMode.CLAIMABLE, msg.sender);
        }
    }

    /// @dev configure the points operator
    function _configurePointsOperator(address _operator) internal {
        if (getChainId() == 81457) {
            IBlastPoints(BlastConstants.BLAST_POINTS).configurePointsOperator(_operator);
        }
    }

    /// @dev returns the address of the Blast contract
    function _getBlast() internal pure returns (IBlast) {
        return IBlast(BlastConstants.BLAST);
    }

    function getChainId() private view returns (uint256) {
        uint256 id;
        assembly {
            id := chainid()
        }
        return id;
    }
}

File 10 of 17 : BlastConstants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

library BlastConstants {
    /// @notice Address of the Blast predeploy.
    address internal constant BLAST = 0x4300000000000000000000000000000000000002;

    /// @notice Address of the USDB predeploy.
    address internal constant USDB = 0x4300000000000000000000000000000000000003;

    /// @notice Address of the WETH predeploy.
    address internal constant WETH = 0x4300000000000000000000000000000000000004;

    /// @notice Address of the Blast points manager.
    address internal constant BLAST_POINTS = 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800;
}

File 11 of 17 : IBlast.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

enum YieldMode {
    AUTOMATIC,
    VOID,
    CLAIMABLE
}

enum GasMode {
    VOID,
    CLAIMABLE 
}

interface IBlast{
    // configure
    function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
    function configure(YieldMode _yield, GasMode gasMode, address governor) external;

    // base configuration options
    function configureClaimableYield() external;
    function configureClaimableYieldOnBehalf(address contractAddress) external;
    function configureAutomaticYield() external;
    function configureAutomaticYieldOnBehalf(address contractAddress) external;
    function configureVoidYield() external;
    function configureVoidYieldOnBehalf(address contractAddress) external;
    function configureClaimableGas() external;
    function configureClaimableGasOnBehalf(address contractAddress) external;
    function configureVoidGas() external;
    function configureVoidGasOnBehalf(address contractAddress) external;
    function configureGovernor(address _governor) external;
    function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;

    // claim yield
    function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);
    function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);

    // claim gas
    function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
    function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) external returns (uint256);
    function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);
    function claimGas(address contractAddress, address recipientOfGas, uint256 gasToClaim, uint256 gasSecondsToConsume) external returns (uint256);

    // read functions
    function readClaimableYield(address contractAddress) external view returns (uint256);
    function readYieldConfiguration(address contractAddress) external view returns (uint8);
    function readGasParams(address contractAddress) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}

interface IERC20Rebasing {
  // changes the yield mode of the caller and update the balance
  // to reflect the configuration
  function configure(YieldMode) external returns (uint256);
  // "claimable" yield mode accounts can call this this claim their yield
  // to another address
  function claim(address recipient, uint256 amount) external returns (uint256);
  // read the claimable amount for an account
  function getClaimableAmount(address account) external view returns (uint256);
}

interface IWETHRebasing is IERC20Rebasing {
    function deposit() external payable;
    function withdraw(uint) external;
}

interface IBlastPoints {
	function configurePointsOperator(address operator) external;
}

File 12 of 17 : BT404NFTWrapper.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {LibBitmap} from "./solady/utils/LibBitmap.sol";
import "./DN404.sol";

abstract contract BT404NFTWrapper is DN404 {

    /// @dev Amount of token balance that is equal to one NFT.
    function unit() external pure returns (uint256) {
        return _unit();
    }

    /// @dev Amount of token balance that is equal to one NFT.
    function _unit() internal pure override returns (uint256) {
        return 1000 * 10 ** 18; // 1 NFT = 1000 Tokens
    }

    function _mintNFT(address to, uint256[] memory ids, bool lock) internal virtual override {
        if (to == address(0)) {
            revert TransferToZeroAddress();
        }

        DN404Storage storage $ = _getDN404Storage();
        // refresh fee for `to`
        _pullFeeForTwo($, to, to);

        Uint32Map storage oo = $.oo;
        LibBitmap.Bitmap storage tokenLocks = $.tokenLocks;

        AddressData storage toAddressData = _addressData(to);
        uint32 toAlias = _registerAndResolveAlias(toAddressData, to);
        (uint32 n, Uint32Map storage ownedMap) = lock
            ? (toAddressData.lockedLength, $.locked[to])
            : (toAddressData.ownedLength, $.owned[to]);

        uint256 numToMints = ids.length;
        for (uint256 i; i < numToMints; ++i) {
            uint256 id = ids[i];

            address from = $.aliasToAddress[_get(oo, _ownershipIndex(id))];
            if (from != address(0)) {
                revert TransferFromIncorrectOwner();
            }

            _set(ownedMap, n, uint32(id));
            _setOwnerAliasAndOwnedIndex(oo, id, toAlias, n++);

            if (lock != LibBitmap.get(tokenLocks, id)) LibBitmap.setTo(tokenLocks, id, lock);
        }

        $.totalNFTSupply += uint32(numToMints);
        if (lock) {
            toAddressData.lockedLength = n;
            $.numLockedNFT += uint32(numToMints);
        } else {
            toAddressData.ownedLength = n;
        }

        // update market shares
        if ($.operatorApprovals[address(this)][to].value > 0 && !lock) {
            $.numExchangableNFT += uint32(numToMints);
        }

        uint256 units = _unit() * numToMints;
        {
            uint256 totalSupply_ = units + $.totalSupply;
            uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
            if (overflows | _toUint(totalSupply_ < units) != 0) {
                revert TotalSupplyOverflow();
            }

            $.totalSupply = uint96(totalSupply_);
            toAddressData.balance += uint96(units);
        }

        /// @solidity memory-safe-assembly
        assembly {
            // Emit the {Transfer} event.
            mstore(0x00, units)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
        }
    }

    function _burnNFT(address from, uint256[] memory ids) internal virtual override {
        if (from == address(0)) {
            revert TransferFromIncorrectOwner();
        }

        DN404Storage storage $ = _getDN404Storage();
        // refresh fee for `from`
        _pullFeeForTwo($, from, from);

        AddressData storage fromAddressData = _addressData(from);
        (uint32 lockedLength, uint32 ownedLength) =
            (fromAddressData.lockedLength, fromAddressData.ownedLength);
        uint256 ownedBurned = 0;
        uint256 numToBurns = ids.length;
        {
            Uint32Map storage oo = $.oo;
            LibBitmap.Bitmap storage tokenLocks = $.tokenLocks;
            (Uint32Map storage locked, Uint32Map storage owned) = ($.locked[from], $.owned[from]);

            for (uint256 i; i < numToBurns; ++i) {
                uint256 id = ids[i];

                address owner = $.aliasToAddress[_get(oo, _ownershipIndex(id))];
                if (from != owner) {
                    revert TransferCallerNotOwnerNorApproved();
                }

                uint32 toBurnIdx = _get(oo, _ownedIndex(id));
                if (LibBitmap.get(tokenLocks, id)) {
                    LibBitmap.setTo(tokenLocks, id, false);
                    _delNFTAt(locked, oo, toBurnIdx, --lockedLength);
                } else {
                    _delNFTAt(owned, oo, toBurnIdx, --ownedLength);
                    ++ownedBurned;
                }
                _removeNFTApproval($, id);

                _setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
            }
        }

        unchecked {
            uint256 lockedBurned = numToBurns - ownedBurned;
            if (lockedBurned > 0) {
                fromAddressData.lockedLength = lockedLength;
                $.numLockedNFT -= uint32(lockedBurned);
            }
            if (ownedBurned > 0) {
                fromAddressData.ownedLength = ownedLength;
            }

            // update market shares
            if ($.operatorApprovals[address(this)][from].value > 0 && ownedBurned > 0) {
                $.numExchangableNFT -= uint32(ownedBurned);
            }
        }

        uint256 units = _unit() * numToBurns;
        unchecked {
            $.totalNFTSupply -= uint32(numToBurns);
            $.totalSupply -= uint96(units);
            fromAddressData.balance -= uint96(units);
        }

        /// @solidity memory-safe-assembly
        assembly {
            // Emit the {Transfer} event.
            mstore(0x00, units)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
    }
}

File 13 of 17 : DN404.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {LibBitmap} from "./solady/utils/LibBitmap.sol";

/// @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 {
    using LibBitmap for LibBitmap.Bitmap;
    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                           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 `target` sets their skipNFT flag to `status`.
    event SkipNFTSet(address indexed target, bool status);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 internal constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 internal constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /// @dev `keccak256(bytes("SkipNFTSet(address,bool)"))`.
    uint256 internal constant _SKIP_NFT_SET_EVENT_SIGNATURE =
        0xb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d6420393;

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                        CUSTOM ERRORS                       */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Thrown when attempting to double-initialize the contract.
    error DNAlreadyInitialized();

    /// @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 cannot be zero.
    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 Thrown when exchanging the NFTs that locked.
    error ExchangeTokenLocked();

    /// @dev Thrown when exchanging the same NFTs
    error ExchangeSameToken();

    /// @dev Thrown when attempting to lock the NFTs that locked,
    ///      or to unlock the NFTs that unlocked.
    error TokenLockStatusNoChange();

    /// @dev Thrown when transferring tokens but the balance is insufficient to to maintain locked NFTs.
    error InsufficientBalanceToMaintainLockedTokens();

    /// @dev Thrown when buy/sell with invalid price.
    error InvalidSalePrice();

    /// @dev Throw when NFT is not locked.
    error TokenNotLocked();

    /// @dev Throw when buy/sell but the address is not matched.
    error InvalidSellerOrBuyer();

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                         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 address to keep bid funds.
    address internal constant _BID_FUNDS_TMP_ACCOUNT = address(0x1);

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                          STORAGE                           */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Struct containing an address's token data and settings.
    struct AddressData {
        // Auxiliary data.
        uint56 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 locked.
        uint32 lockedLength;
        // The number of NFT tokens owned.
        uint32 ownedLength;
        // The token balance in wei.
        uint96 balance;
        // snapshot of `accFeePerNFT` when the account fee accrued
        uint96 feePerNFTSnap;
    }

    /// @dev Struct represents the offer to sell an NFT.
    struct NFTOffer {
        uint32 seller;
        uint32 sellTo;
        uint96 minTokens;
    }

    /// @dev Struct represents the bid to buy an NFT.
    struct NFTBid {
        uint32 bidder;
        uint96 tokens;
    }

    /// @dev A uint32 map in storage.
    struct Uint32Map {
        mapping(uint256 => uint256) map;
    }

    /// @dev A struct to wrap a uint256 in storage.
    struct Uint256Ref {
        uint256 value;
    }

    /// @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;
        // Total number of NFT IDs in the burned pool.
        uint32 burnedPoolSize;
        // Total supply of minted NFTs.
        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.
        mapping(address => mapping(address => Uint256Ref)) operatorApprovals;
        // Mapping of NFT approvals to approved operators.
        mapping(uint256 => address) nftApprovals;
        // Bitmap of whether an non-zero NFT approval may exist.
        LibBitmap.Bitmap mayHaveNFTApproval;
        // Mapping of user allowances for ERC20 spenders.
        mapping(address => mapping(address => Uint256Ref)) allowance;
        // Mapping of NFT IDs owned by an address.
        mapping(address => Uint32Map) owned;
        // Mapping of NFT token IDs locked by an address.
        mapping(address => Uint32Map) locked;
        // The pool of burned NFT IDs.
        Uint32Map burnedPool;
        // Even indices: owner aliases. Odd indices: owned indices.
        // if NFT token was locked, owned indices are ref to `locked`, otherwise `owned`
        Uint32Map oo;
        // Mapping of user account AddressData.
        mapping(address => AddressData) addressData;
        // Mapping of NFT token to locked flag
        LibBitmap.Bitmap tokenLocks;
        // The number of NFT tokens locked globally.
        uint32 numLockedNFT;
        // The number of NFT tokens approved to `this` globally.
        uint32 numExchangableNFT;
        // Fee rate to charged per NFT when exchange unlocking NFTs
        uint16 exchangeNFTFeeBips;
        // accumulated fee per unlocked NFT should receive
        uint96 accFeePerNFT;
        // Slot gap.
        uint80 __gap;
        // Fee rate to charged per NFT when trading through market(bid/ask).
        uint16 marketFeeBips;
        // Mapping of NFT to sale offers.
        mapping(uint256 => NFTOffer) offers;
        // Mapping of NFT to buy bids.
        mapping(uint256 => NFTBid) bids;
    }

    /// @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
    ) internal virtual {
        DN404Storage storage $ = _getDN404Storage();

        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, caller())
            if iszero(and(eq(mload(0x00), 1), call(gas(), mirror, 0, 0x1c, 0x24, 0x00, 0x20))) {
                mstore(0x00, 0xd125259c) // `LinkMirrorContractFailed()`.
                revert(0x1c, 0x04)
            }
        }

        $.mirrorERC721 = mirror;

        if (_unit() < 10 ** decimals() || _unit() > 10 ** 24) revert InvalidUnit();

        _setSkipNFT(_BID_FUNDS_TMP_ACCOUNT, true);

        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) public view virtual returns (string memory);

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                      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) {
        return _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) {
        DN404Storage storage $ = _getDN404Storage();
        _pullFeeForTwo($, msg.sender, to);
        _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) {
        DN404Storage storage $ = _getDN404Storage();
        Uint256Ref storage a = $.allowance[from][msg.sender];

        uint256 allowed = a.value;
        if (allowed != type(uint256).max) {
            if (amount > allowed) revert InsufficientAllowance();
            unchecked {
                a.value = allowed - amount;
            }
        }
        _pullFeeForTwo($, from, to);
        _transfer(from, to, amount);
        return true;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Validates if the receiver is allowed to receive tokens.
    function _validateReceiver(address to) internal virtual {}

    /// @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();
        _validateReceiver(to);

        DN404Storage storage $ = _getDN404Storage();

        AddressData storage fromAddressData = _addressData(from);
        AddressData storage toAddressData = _addressData(to);

        _TransferTemps memory t;
        t.fromOwnedLength = fromAddressData.ownedLength;
        t.toOwnedLength = toAddressData.ownedLength;
        t.totalSupply = $.totalSupply;

        if (amount > (t.fromBalance = fromAddressData.balance)) {
            revert InsufficientBalance();
        }

        unchecked {
            t.fromBalance -= amount;

            t.fromLockedLength = fromAddressData.lockedLength;
            // need enough token to maintain locked NFTs
            if (t.fromBalance < t.fromLockedLength * _unit()) {
                revert InsufficientBalanceToMaintainLockedTokens();
            }

            fromAddressData.balance = uint96(t.fromBalance);
            toAddressData.balance = uint96(t.toBalance = toAddressData.balance + amount);

            t.numNFTBurns =
                _zeroFloorSub(t.fromOwnedLength + t.fromLockedLength, t.fromBalance / _unit());

            if (toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG == 0) {
                if (from == to) t.toOwnedLength = t.fromOwnedLength - t.numNFTBurns;
                t.numNFTMints = _zeroFloorSub(
                    t.toBalance / _unit(),
                    t.toOwnedLength + toAddressData.lockedLength // balance needed for locked and owned
                );
            }

            {
                // cache `address(this)` approvals
                mapping(address => Uint256Ref) storage thisOperatorApprovals =
                    $.operatorApprovals[address(this)];
                // `from` burns NFTs
                if (thisOperatorApprovals[from].value != 0) {
                    $.numExchangableNFT -= uint32(t.numNFTBurns);
                }
                // `to`mints NFTs
                if (thisOperatorApprovals[to].value != 0) {
                    $.numExchangableNFT += uint32(t.numNFTMints);
                }
            }

            $.totalNFTSupply = uint32(uint256($.totalNFTSupply) + t.numNFTMints - t.numNFTBurns);
            Uint32Map storage oo = $.oo;
            {
                uint256 n = _min(t.numNFTBurns, t.numNFTMints);
                if (n != 0) {
                    t.numNFTBurns -= n;
                    t.numNFTMints -= n;

                    if (from == to) {
                        t.toOwnedLength += n;
                    } else {
                        _DNDirectLogs memory directLogs = _directLogsMalloc(n, from, to);
                        Uint32Map storage fromOwned = $.owned[from];
                        Uint32Map storage toOwned = $.owned[to];
                        uint32 toAlias = _registerAndResolveAlias(toAddressData, to);
                        // Direct transfer loop.
                        do {
                            uint256 id = _get(fromOwned, --t.fromOwnedLength);
                            _set(toOwned, t.toOwnedLength, uint32(id));
                            _setOwnerAliasAndOwnedIndex(oo, id, toAlias, uint32(t.toOwnedLength++));
                            _removeNFTApproval($, id);
                            _directLogsAppend(directLogs, id);
                        } while (--n != 0);

                        _directLogsSend(directLogs, $.mirrorERC721);
                        fromAddressData.ownedLength = uint32(t.fromOwnedLength);
                        toAddressData.ownedLength = uint32(t.toOwnedLength);
                    }
                }
            }

            _PackedLogs memory packedLogs = _packedLogsMalloc(t.numNFTBurns + t.numNFTMints);
            uint256 burnedPoolSize = $.burnedPoolSize;
            if (t.numNFTBurns != 0) {
                _packedLogsSet(packedLogs, from, 1);
                Uint32Map storage fromOwned = $.owned[from];
                uint256 fromIndex = t.fromOwnedLength;
                uint256 fromEnd = fromIndex - t.numNFTBurns;
                fromAddressData.ownedLength = uint32(fromEnd);
                // Burn loop.
                do {
                    uint256 id = _get(fromOwned, --fromIndex);
                    _setOwnerAliasAndOwnedIndex(oo, id, 0, 0);
                    _set($.burnedPool, burnedPoolSize++, uint32(id));
                    _removeNFTApproval($, id);
                    _packedLogsAppend(packedLogs, id);
                } while (fromIndex != fromEnd);
            }

            if (t.numNFTMints != 0) {
                _packedLogsSet(packedLogs, to, 0);
                uint256 nextTokenId = $.nextTokenId;
                Uint32Map storage toOwned = $.owned[to];
                uint256 toIndex = t.toOwnedLength;
                uint256 toEnd = toIndex + t.numNFTMints;
                uint32 toAlias = _registerAndResolveAlias(toAddressData, to);
                toAddressData.ownedLength = uint32(toEnd);
                // Mint loop.
                do {
                    uint256 id;
                    if (burnedPoolSize != 0) {
                        id = _get($.burnedPool, --burnedPoolSize);
                    } else {
                        id = nextTokenId;
                        nextTokenId = id + 1;
                    }
                    _set(toOwned, toIndex, uint32(id));
                    _setOwnerAliasAndOwnedIndex(oo, id, toAlias, uint32(toIndex++));
                    _packedLogsAppend(packedLogs, id);
                } while (toIndex != toEnd);

                $.nextTokenId = uint32(nextTokenId);
            }

            if (packedLogs.logs.length != 0) {
                $.burnedPoolSize = uint32(burnedPoolSize);
                _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();
        Uint32Map storage oo = $.oo;

        if (from != $.aliasToAddress[_get(oo, _ownershipIndex(id))]) {
            revert TransferFromIncorrectOwner();
        }

        if (msgSender != from) {
            if ($.operatorApprovals[msgSender][from].value == 0) {
                if (msgSender != $.nftApprovals[id]) {
                    revert TransferCallerNotOwnerNorApproved();
                }
            }
        }

        AddressData storage fromAddressData = _addressData(from);
        AddressData storage toAddressData = _addressData(to);

        uint256 unit = _unit();

        fromAddressData.balance -= uint96(unit);

        unchecked {
            toAddressData.balance += uint96(unit);

            _removeNFTApproval($, id);
            _clearNFTOffer($, id);

            uint32 toTransferIdx = _get(oo, _ownedIndex(id));
            if (LibBitmap.get($.tokenLocks, id)) {
                // operate `locked` map
                // delete transferred NFT
                _delNFTAt($.locked[from], oo, toTransferIdx, --fromAddressData.lockedLength);
            } else {
                if ($.operatorApprovals[address(this)][from].value != 0) {
                    // The unlocked NFTs amount of account `from` will decrease, collecting fees first
                    _pullFeeForTwo($, from, from);
                    // `from` lock 1 NFT
                    --$.numExchangableNFT;
                }

                // operate `owned` map
                // delete transferred NFT
                _delNFTAt($.owned[from], oo, toTransferIdx, --fromAddressData.ownedLength);

                // lock
                LibBitmap.setTo($.tokenLocks, id, true);
                ++$.numLockedNFT;
            }

            // transfer ownership
            // lock the NFT by default for ERC721 transfer
            uint256 n = toAddressData.lockedLength++;
            _set($.locked[to], n, uint32(id));
            _setOwnerAliasAndOwnedIndex(
                oo, id, _registerAndResolveAlias(toAddressData, to), uint32(n)
            );
        }
        /// @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))
            )
        }
    }

    function _exchangeNFT(uint256 idX, uint256 idY, address msgSender)
        internal
        virtual
        returns (address x, address y, uint256 exchangeFee)
    {
        if (idX == idY) revert ExchangeSameToken();

        DN404Storage storage $ = _getDN404Storage();

        LibBitmap.Bitmap storage tokenLocks = $.tokenLocks;
        if (_toUint(LibBitmap.get(tokenLocks, idX)) | _toUint(LibBitmap.get(tokenLocks, idY)) != 0)
        {
            revert ExchangeTokenLocked();
        }

        x = _ownerOf(idX);
        y = _ownerOf(idY);
        // Only owner or spender can operate the token `idX`
        if (msgSender != x) {
            if ($.operatorApprovals[msgSender][x].value == 0) {
                if (msgSender != $.nftApprovals[idX]) {
                    revert TransferCallerNotOwnerNorApproved();
                }
            }
        }

        mapping(address => Uint256Ref) storage thisOperatorApprovals =
            $.operatorApprovals[address(this)];
        if (thisOperatorApprovals[y].value == 0) {
            revert ApprovalCallerNotOwnerNorApproved();
        }

        _removeNFTApproval($, idX);
        _removeNFTApproval($, idY);

        // collecting fees for account `x` and `y` first
        _pullFeeForTwo($, x, y);

        Uint32Map storage oo = $.oo;

        // idY to account x, then lock
        // must transfer `idY` firstly, otherwise the ownedIndex of `idX` is wrong
        AddressData storage xAddressData = _addressData(x);
        {
            uint256 xIndex = _get(oo, _ownedIndex(idX));
            // remove NFT `idX` from account `x`
            _delNFTAt($.owned[x], oo, xIndex, --xAddressData.ownedLength);
        }

        // snapshot owned index of `idY`
        uint256 yIndex = _get(oo, _ownedIndex(idY));

        {
            // append `idY` to `locked`
            uint256 n = xAddressData.lockedLength++;
            _set($.locked[x], n, uint32(idY));
            _setOwnerAliasAndOwnedIndex(oo, idY, xAddressData.addressAlias, uint32(n));

            // lock `idY`
            LibBitmap.setTo(tokenLocks, idY, true);
            ++$.numLockedNFT;
        }

        // idX to account y
        AddressData storage yAddressData = _addressData(y);
        _setOwnerAliasAndOwnedIndex(oo, idX, yAddressData.addressAlias, uint32(yIndex));
        _set($.owned[y], yIndex, uint32(idX));

        // transfer nft first, then token, otherwise specified NFT transfer may not success
        // fee charges in percentage of the unit
        exchangeFee = $.exchangeNFTFeeBips;
        if (exchangeFee > 0) {
            // Only refresh when the balance of `msgSender` will be changed.
            if (msgSender != x) _pullFeeForTwo($, msgSender, msgSender);
            unchecked {
                exchangeFee *= _unit() / 10000;
                _transfer(msgSender, address(this), exchangeFee);
                $.accFeePerNFT += uint96(exchangeFee / $.numExchangableNFT);
            }
        }

        // If `msgSender` exchanged on behalf of `x`, `msgSender` receive the NFT.
        if (msgSender != x) _transferFromNFT(x, msgSender, idY, x);
        // x lock 1 NFT
        if (thisOperatorApprovals[x].value != 0) {
            --$.numExchangableNFT;
        }
    }

    function _pullFeeForTwo(DN404Storage storage $, address account1, address account2)
        internal
        virtual
    {
        // Cannot receive fee if `address(this)` has no operator approvals
        mapping(address => Uint256Ref) storage thisOperatorApprovals =
            $.operatorApprovals[address(this)];
        uint256 accFeePerNFT;
        uint256 accruedFee1;
        if (thisOperatorApprovals[account1].value > 0) {
            accFeePerNFT = $.accFeePerNFT;
            AddressData storage addressData = $.addressData[account1];
            // only unlocked NFTs receive fee
            accruedFee1 = (accFeePerNFT - addressData.feePerNFTSnap) * (addressData.ownedLength);
            addressData.feePerNFTSnap = uint96(accFeePerNFT);
        }
        if (account2 != account1) {
            if (thisOperatorApprovals[account2].value > 0) {
                if (accFeePerNFT == 0) {
                    accFeePerNFT = $.accFeePerNFT;
                }
                AddressData storage addressData = $.addressData[account2];
                // only unlocked NFTs receive fee
                uint256 accrued =
                    (accFeePerNFT - addressData.feePerNFTSnap) * (addressData.ownedLength);
                addressData.feePerNFTSnap = uint96(accFeePerNFT);
                if (accrued > 0) {
                    _transfer(address(this), account2, accrued);
                }
            }
        }
        if (accruedFee1 > 0) {
            _transfer(address(this), account1, accruedFee1);
        }
    }

    /// @dev Internal function for minting new NFTs.
    function _mintNFT(address, uint256[] memory, bool) internal virtual {
        revert(); // Revert if called, implementation should be provided by inheriting contracts.
    }

    /// @dev Internal function for burning existing NFTs.
    function _burnNFT(address, uint256[] memory) internal virtual {
        revert(); // Revert if called, implementation should be provided by inheriting contracts.
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                 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 {
        Uint256Ref storage ref = _getDN404Storage().allowance[owner][spender];

        ref.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 (uint56) {
        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, uint56 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 (d.flags & _ADDRESS_DATA_INITIALIZED_FLAG == 0) {
            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 (d.flags & _ADDRESS_DATA_INITIALIZED_FLAG == 0) {
                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 (addressAlias == 0) {
            unchecked {
                addressAlias = ++$.numAliases;
            }
            toAddressData.addressAlias = addressAlias;
            $.aliasToAddress[addressAlias] = to;
            if (addressAlias == 0) 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) {
        AddressData storage addressData = _getDN404Storage().addressData[owner];
        return addressData.ownedLength + addressData.lockedLength;
    }

    /// @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(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 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(id))];

        if (msgSender != owner) {
            if ($.operatorApprovals[msgSender][owner].value == 0) {
                revert ApprovalCallerNotOwnerNorApproved();
            }
        }

        $.nftApprovals[id] = spender;
        LibBitmap.setTo($.mayHaveNFTApproval, id, spender != address(0));
    }

    function _removeNFTApproval(DN404Storage storage $, uint256 id) internal virtual {
        if (LibBitmap.get($.mayHaveNFTApproval, id)) {
            LibBitmap.setTo($.mayHaveNFTApproval, id, false);
            delete $.nftApprovals[id];
        }
    }

    /// @dev Approve or remove the `operator` as an operator for `msgSender`,
    /// without authorization checks.
    function _setApprovalForAll(address operator, bool approved, address msgSender)
        internal
        virtual
    {
        DN404Storage storage $ = _getDN404Storage();
        Uint256Ref storage ref = $.operatorApprovals[operator][msgSender];
        if (operator == address(this)) {
            bool status = ref.value != 0;
            AddressData storage senderAddressData = $.addressData[msgSender];
            if (_toUint(approved) & _toUint(!status) != 0) {
                // initialize when approving
                senderAddressData.feePerNFTSnap = $.accFeePerNFT;
                $.numExchangableNFT += senderAddressData.ownedLength;
            } else if (_toUint(!approved) & _toUint(status) != 0) {
                // refresh when removing approval
                _pullFeeForTwo($, msgSender, msgSender);
                $.numExchangableNFT -= senderAddressData.ownedLength;
            }
        }
        ref.value = _toUint(approved); // `approved ? 1 : 0`
    }

    /// @dev Lock or unlock the `id`,
    /// `msgSener` should be authorized as the operator of the owner of the NFT
    function _setNFTLockState(uint256[] memory ids, bool lock, address msgSender)
        internal
        virtual
    {
        DN404Storage storage $ = _getDN404Storage();
        _pullFeeForTwo($, msgSender, msgSender);

        Uint32Map storage oo = $.oo;
        LibBitmap.Bitmap storage tokenLocks = $.tokenLocks;

        AddressData storage ownerAddressData = $.addressData[msgSender];
        Uint32Map storage ownerLocked = $.locked[msgSender];
        Uint32Map storage ownerOwned = $.owned[msgSender];
        uint32 ownerAlias = ownerAddressData.addressAlias;
        uint256 idLen = ids.length;

        for (uint256 i; i < idLen; ++i) {
            uint256 id = ids[i];

            if (_get(oo, _ownershipIndex(id)) != ownerAlias) {
                revert ApprovalCallerNotOwnerNorApproved();
            }

            uint32 ownedIndex = _get(oo, _ownedIndex(id));

            if (LibBitmap.get(tokenLocks, id) == lock) revert TokenLockStatusNoChange();

            if (!lock) {
                // already locked, to unlock
                LibBitmap.setTo(tokenLocks, id, false);

                // swap with last NFT and pop the last
                _delNFTAt(ownerLocked, oo, ownedIndex, --ownerAddressData.lockedLength);

                uint256 n = ownerAddressData.ownedLength++;
                _set(ownerOwned, n, uint32(id));
                _set(oo, _ownedIndex(id), uint32(n));
            } else {
                // not locked, to lock
                LibBitmap.setTo(tokenLocks, id, true);

                // swap with last NFT and pop the last
                _delNFTAt(ownerOwned, oo, ownedIndex, --ownerAddressData.ownedLength);

                uint256 n = ownerAddressData.lockedLength++;
                _set(ownerLocked, n, uint32(id));
                _set(oo, _ownedIndex(id), uint32(n));
            }
        }

        if (lock) $.numLockedNFT += uint32(idLen);
        else $.numLockedNFT -= uint32(idLen);

        if ($.operatorApprovals[address(this)][msgSender].value != 0) {
            if (lock) $.numExchangableNFT -= uint32(ids.length);
            else $.numExchangableNFT += uint32(ids.length);
        }
    }

    /// @dev Returns the NFT IDs of `owner` in range `[begin, end)`.
    /// Optimized for smaller bytecode size, as this function is intended for off-chain calling.
    function _ownedIds(address owner, uint256 begin, uint256 end, bool locked)
        internal
        view
        virtual
        returns (uint256[] memory ids)
    {
        DN404Storage storage $ = _getDN404Storage();
        (Uint32Map storage owned, uint256 n) = locked
            ? ($.locked[owner], $.addressData[owner].lockedLength)
            : ($.owned[owner], $.addressData[owner].ownedLength);
        n = _min(n, end);
        /// @solidity memory-safe-assembly
        assembly {
            // Allocate one more word to store the offset when returning with assembly.
            ids := mload(0x40)
            mstore(0x20, owned.slot)
            let i := begin
            for {} lt(i, n) { i := add(i, 1) } {
                mstore(0x00, shr(3, i))
                let s := keccak256(0x00, 0x40) // 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.
        }
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                NFT OFFER BID FUNCTIONS                 */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
    struct NFTOrder {
        uint256 id;
        uint256 tokenUnits;
    }

    function _clearNFTOffer(DN404Storage storage $, uint256 id) private {
        // Clear exist offer if needed.
        if ($.offers[id].seller != 0) delete $.offers[id];
    }

    /// @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;
        address mirror = $.mirrorERC721;

        // `transferFromNFT(address,address,uint256,address)`.
        if (fnSelector == 0xe5eb36c8) {
            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
            _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);
        }
        // `setNFTLockState(uint256,uint256[])`.
        if (fnSelector == 0xb79cc1bd) {
            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();

            uint256 senderAndLockFlag = _calldataload(0x04);

            _setNFTLockState(
                _calldatacopyArray(_calldataload(0x24) + 0x04), // `ids`
                uint8(senderAndLockFlag) != 0, // `lock`
                address(uint160(senderAndLockFlag >> 96)) // `msgSender`
            );
            _return(1);
        }
        // `mintNFT(uint256,uint256[])`
        if (fnSelector == 0x3e0446a1) {
            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
            uint256 senderAndLockFlag = _calldataload(0x04);
            _mintNFT(
                address(uint160(senderAndLockFlag >> 96)), // `to`
                _calldatacopyArray(_calldataload(0x24) + 0x04), // `ids`
                uint8(senderAndLockFlag) != 0 // `lock`
            );
            _return(1);
        }
        // `burnNFT(address,uint256[])`
        if (fnSelector == 0x86529a61) {
            if (msg.sender != $.mirrorERC721) revert SenderNotMirror();

            _burnNFT(
                address(uint160(_calldataload(0x04))), // `from`
                _calldatacopyArray(_calldataload(0x24) + 0x04) // `ids`
            );
            _return(1);
        }
        // `isApprovedForAll(address,address)`.
        if (fnSelector == 0xe985e9c5) {
            address owner = address(uint160(_calldataload(0x04)));
            address spender = address(uint160(_calldataload(0x24)));
            Uint256Ref storage ref = $.operatorApprovals[spender][owner];

            _return(ref.value);
        }
        // `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));
        }
        // `ownedIds(uint256,uint256,uint256)`.
        if (fnSelector == 0xf9b4b328) {
            uint256 addrAndFlag = _calldataload(0x04);
            /// @solidity memory-safe-assembly
            assembly {
                // Allocate one word to store the offset of the array in returndata.
                mstore(0x40, add(mload(0x40), 0x20))
            }

            uint256[] memory ids = _ownedIds(
                address(uint160(addrAndFlag >> 96)),
                _calldataload(0x24),
                _calldataload(0x44),
                uint8(addrAndFlag) != 0
            );
            /// @solidity memory-safe-assembly
            assembly {
                // Memory safe, as we've advanced the free memory pointer by a word.
                let p := sub(ids, 0x20)
                mstore(p, 0x20) // Store the offset of the array in returndata.
                return(p, add(0x40, shl(5, mload(ids))))
            }
        }
        // `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());
        }
        // `implementsDN404()`.
        if (fnSelector == 0xb7a94eb8) {
            _return(1);
        }
        _;
    }

    /// @dev Fallback function for calls from mirror NFT contract.
    fallback() external payable virtual dn404Fallback {}

    receive() external payable virtual {}

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                 INTERNAL / PRIVATE HELPERS                 */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns `i << 1`.
    function _ownershipIndex(uint256 i) internal pure returns (uint256) {
        unchecked {
            return i << 1;
        }
    }

    /// @dev Returns `(i << 1) + 1`.
    function _ownedIndex(uint256 i) internal pure returns (uint256) {
        unchecked {
            return (i << 1) + 1;
        }
    }

    function _delNFTAt(
        Uint32Map storage owned,
        Uint32Map storage oo,
        uint256 toDelIndex,
        uint256 lastIndex
    ) internal {
        if (toDelIndex != lastIndex) {
            uint256 updatedId = _get(owned, lastIndex);
            _set(owned, toDelIndex, uint32(updatedId));
            _set(oo, _ownedIndex(updatedId), uint32(toDelIndex));
        }
    }

    /// @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 Struct containing direct transfer log data for {Transfer} events to be
    /// emitted by the mirror NFT contract.
    struct _DNDirectLogs {
        uint256 offset;
        address from;
        address to;
        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 {
            // Note that `p` implicitly allocates and advances the free memory pointer by
            // 4 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(0x60, p), logs) // Set `p.logs`.
            mstore(add(0x40, p), shr(96, shl(96, to))) // Set `p.to`.
            mstore(add(0x20, p), shr(96, shl(96, from))) // Set `p.from`.
            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, 0x60))
            let n := add(0x84, shl(5, mload(logs))) // Length of calldata to send.
            let o := sub(logs, 0x80) // Start of calldata to send.
            mstore(o, 0x144027d3) // `logDirectTransfer(address,address,uint256[])`.
            mstore(add(o, 0x20), mload(add(0x20, p)))
            mstore(add(o, 0x40), mload(add(0x40, p)))
            mstore(add(o, 0x60), 0x60) // Offset of `logs` in the calldata to send.
            if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {
                revert(o, 0x00)
            }
        }
    }

    /// emitted by the mirror NFT contract.
    struct _PackedLogs {
        uint256 offset;
        uint256 addressAndBit;
        uint256[] logs;
    }

    /// @dev Initiates memory allocation for packed logs with `n` log items.
    function _packedLogsMalloc(uint256 n) internal pure returns (_PackedLogs memory p) {
        /// @solidity memory-safe-assembly
        assembly {
            // Note that `p` implicitly allocates and advances the free memory pointer by
            // 2 words, which we can safely mutate in `_packedLogsSend`.
            let logs := mload(0x40)
            mstore(logs, n) // Store the length.
            let offset := add(0x20, logs)
            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(_PackedLogs memory p, address a, uint256 burnBit) internal pure {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(add(p, 0x20), or(shl(96, a), burnBit))
        }
    }

    /// @dev Adds a packed log item to `p` with token `id`.
    function _packedLogsAppend(_PackedLogs memory p, uint256 id) internal pure {
        /// @solidity memory-safe-assembly
        assembly {
            let offset := mload(p)
            mstore(offset, or(mload(add(p, 0x20)), shl(8, id)))
            mstore(p, add(offset, 0x20))
        }
    }

    function _packedLogsSend(_PackedLogs memory p, address mirror) internal {
        /// @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 _TransferTemps {
        uint256 numNFTBurns;
        uint256 numNFTMints;
        uint256 fromBalance;
        uint256 toBalance;
        uint256 fromOwnedLength;
        uint256 toOwnedLength;
        uint256 totalSupply;
        uint256 fromLockedLength;
        uint256 toLockedLength;
    }

    /// @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)
        }
    }

    function _calldatacopyArray(uint256 offset) private pure returns (uint256[] memory value) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := calldataload(offset)
            value := mload(0x40)
            mstore(0x40, add(add(value, 0x20), shl(5, length))) // Allocate memory.

            mstore(value, length) // Store array length
            calldatacopy(add(value, 0x20), add(offset, 0x20), shl(5, length)) // Copy array elements
        }
    }

    /// @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)
        }
    }

    /// @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 the uint32 value at `index` in `map`.
    function _get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) {
        result = uint32(map.map[index >> 3] >> ((index & 7) << 5));
    }

    /// @dev Updates the uint32 value at `index` in `map`.
    function _set(Uint32Map storage map, uint256 index, uint32 value) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, map.slot)
            mstore(0x00, shr(3, index))
            let s := keccak256(0x00, 0x40) // Storage slot.
            let o := shl(5, and(index, 7)) // Storage slot offset (bits).
            let v := sload(s) // Storage slot value.
            let m := 0xffffffff // Value mask.
            sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
        }
    }

    /// @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 value := or(shl(32, ownedIndex), and(0xffffffff, ownership))
            mstore(0x20, map.slot)
            mstore(0x00, shr(2, id))
            let s := keccak256(0x00, 0x40) // Storage slot.
            let o := shl(6, and(id, 3)) // Storage slot offset (bits).
            let v := sload(s) // Storage slot value.
            let m := 0xffffffffffffffff // Value mask.
            sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
        }
    }
}

File 14 of 17 : LibBit.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  BIT TWIDDLING OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Find last set.
    /// Returns the index of the most significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    function fls(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Count leading zeros.
    /// Returns the number of zeros preceding the most significant one bit.
    /// If `x` is zero, returns 256.
    function clz(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
        }
    }

    /// @dev Find first set.
    /// Returns the index of the least significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    /// Equivalent to `ctz` (count trailing zeros), which gives
    /// the number of zeros following the least significant one bit.
    function ffs(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Isolate the least significant bit.
            x := and(x, add(not(x), 1))
            // For the upper 3 bits of the result, use a De Bruijn-like lookup.
            // Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
            // forgefmt: disable-next-item
            r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
                0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
                0x8040405543005266443200005020610674053026020000107506200176117077)))
            // For the lower 5 bits of the result, use a De Bruijn lookup.
            // forgefmt: disable-next-item
            r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
                0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
        }
    }

    /// @dev Returns the number of set bits in `x`.
    function popCount(uint256 x) internal pure returns (uint256 c) {
        /// @solidity memory-safe-assembly
        assembly {
            let max := not(0)
            let isMax := eq(x, max)
            x := sub(x, and(shr(1, x), div(max, 3)))
            x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
            x := and(add(x, shr(4, x)), div(max, 17))
            c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
        }
    }

    /// @dev Returns whether `x` is a power of 2.
    function isPo2(uint256 x) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `x && !(x & (x - 1))`.
            result := iszero(add(and(x, sub(x, 1)), iszero(x)))
        }
    }

    /// @dev Returns `x` reversed at the bit level.
    function reverseBits(uint256 x) internal pure returns (uint256 r) {
        uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
        uint256 m1 = m0 ^ (m0 << 2);
        uint256 m2 = m1 ^ (m1 << 1);
        r = reverseBytes(x);
        r = (m2 & (r >> 1)) | ((m2 & r) << 1);
        r = (m1 & (r >> 2)) | ((m1 & r) << 2);
        r = (m0 & (r >> 4)) | ((m0 & r) << 4);
    }

    /// @dev Returns `x` reversed at the byte level.
    function reverseBytes(uint256 x) internal pure returns (uint256 r) {
        unchecked {
            // Computing masks on-the-fly reduces bytecode size by about 200 bytes.
            uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == 0) >> 192);
            uint256 m1 = m0 ^ (m0 << 32);
            uint256 m2 = m1 ^ (m1 << 16);
            uint256 m3 = m2 ^ (m2 << 8);
            r = (m3 & (x >> 8)) | ((m3 & x) << 8);
            r = (m2 & (r >> 16)) | ((m2 & r) << 16);
            r = (m1 & (r >> 32)) | ((m1 & r) << 32);
            r = (m0 & (r >> 64)) | ((m0 & r) << 64);
            r = (r >> 128) | (r << 128);
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     BOOLEAN OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // A Solidity bool on the stack or memory is represented as a 256-bit word.
    // Non-zero values are true, zero is false.
    // A clean bool is either 0 (false) or 1 (true) under the hood.
    // Usually, if not always, the bool result of a regular Solidity expression,
    // or the argument of a public/external function will be a clean bool.
    // You can usually use the raw variants for more performance.
    // If uncertain, test (best with exact compiler settings).
    // Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).

    /// @dev Returns `x & y`. Inputs must be clean.
    function rawAnd(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := and(x, y)
        }
    }

    /// @dev Returns `x & y`.
    function and(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := and(iszero(iszero(x)), iszero(iszero(y)))
        }
    }

    /// @dev Returns `x | y`. Inputs must be clean.
    function rawOr(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, y)
        }
    }

    /// @dev Returns `x | y`.
    function or(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(iszero(iszero(x)), iszero(iszero(y)))
        }
    }

    /// @dev Returns 1 if `b` is true, else 0. Input must be clean.
    function rawToUint(bool b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := b
        }
    }

    /// @dev Returns 1 if `b` is true, else 0.
    function toUint(bool b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := iszero(iszero(b))
        }
    }
}

File 15 of 17 : LibBitmap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {LibBit} from "./LibBit.sol";

/// @notice Library for storage of packed unsigned booleans.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solidity-Bits (https://github.com/estarriolvetch/solidity-bits/blob/main/contracts/BitMaps.sol)
library LibBitmap {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The constant returned when a bitmap scan does not find a result.
    uint256 internal constant NOT_FOUND = type(uint256).max;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STRUCTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev A bitmap in storage.
    struct Bitmap {
        mapping(uint256 => uint256) map;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         OPERATIONS                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the boolean value of the bit at `index` in `bitmap`.
    function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) {
        // It is better to set `isSet` to either 0 or 1, than zero vs non-zero.
        // Both cost the same amount of gas, but the former allows the returned value
        // to be reused without cleaning the upper bits.
        uint256 b = (bitmap.map[index >> 8] >> (index & 0xff)) & 1;
        /// @solidity memory-safe-assembly
        assembly {
            isSet := b
        }
    }

    /// @dev Updates the bit at `index` in `bitmap` to true.
    function set(Bitmap storage bitmap, uint256 index) internal {
        bitmap.map[index >> 8] |= (1 << (index & 0xff));
    }

    /// @dev Updates the bit at `index` in `bitmap` to false.
    function unset(Bitmap storage bitmap, uint256 index) internal {
        bitmap.map[index >> 8] &= ~(1 << (index & 0xff));
    }

    /// @dev Flips the bit at `index` in `bitmap`.
    /// Returns the boolean result of the flipped bit.
    function toggle(Bitmap storage bitmap, uint256 index) internal returns (bool newIsSet) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, bitmap.slot)
            mstore(0x00, shr(8, index))
            let storageSlot := keccak256(0x00, 0x40)
            let shift := and(index, 0xff)
            let storageValue := xor(sload(storageSlot), shl(shift, 1))
            // It makes sense to return the `newIsSet`,
            // as it allow us to skip an additional warm `sload`,
            // and it costs minimal gas (about 15),
            // which may be optimized away if the returned value is unused.
            newIsSet := and(1, shr(shift, storageValue))
            sstore(storageSlot, storageValue)
        }
    }

    /// @dev Updates the bit at `index` in `bitmap` to `shouldSet`.
    function setTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, bitmap.slot)
            mstore(0x00, shr(8, index))
            let storageSlot := keccak256(0x00, 0x40)
            let storageValue := sload(storageSlot)
            let shift := and(index, 0xff)
            sstore(
                storageSlot,
                // Unsets the bit at `shift` via `and`, then sets its new value via `or`.
                or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet))))
            )
        }
    }

    /// @dev Consecutively sets `amount` of bits starting from the bit at `start`.
    function setBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let max := not(0)
            let shift := and(start, 0xff)
            mstore(0x20, bitmap.slot)
            mstore(0x00, shr(8, start))
            if iszero(lt(add(shift, amount), 257)) {
                let storageSlot := keccak256(0x00, 0x40)
                sstore(storageSlot, or(sload(storageSlot), shl(shift, max)))
                let bucket := add(mload(0x00), 1)
                let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
                amount := and(add(amount, shift), 0xff)
                shift := 0
                for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
                    mstore(0x00, bucket)
                    sstore(keccak256(0x00, 0x40), max)
                }
                mstore(0x00, bucket)
            }
            let storageSlot := keccak256(0x00, 0x40)
            sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max))))
        }
    }

    /// @dev Consecutively unsets `amount` of bits starting from the bit at `start`.
    function unsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let shift := and(start, 0xff)
            mstore(0x20, bitmap.slot)
            mstore(0x00, shr(8, start))
            if iszero(lt(add(shift, amount), 257)) {
                let storageSlot := keccak256(0x00, 0x40)
                sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0)))))
                let bucket := add(mload(0x00), 1)
                let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
                amount := and(add(amount, shift), 0xff)
                shift := 0
                for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
                    mstore(0x00, bucket)
                    sstore(keccak256(0x00, 0x40), 0)
                }
                mstore(0x00, bucket)
            }
            let storageSlot := keccak256(0x00, 0x40)
            sstore(
                storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0)))))
            )
        }
    }

    /// @dev Returns number of set bits within a range by
    /// scanning `amount` of bits starting from the bit at `start`.
    function popCount(Bitmap storage bitmap, uint256 start, uint256 amount)
        internal
        view
        returns (uint256 count)
    {
        unchecked {
            uint256 bucket = start >> 8;
            uint256 shift = start & 0xff;
            if (!(amount + shift < 257)) {
                count = LibBit.popCount(bitmap.map[bucket] >> shift);
                uint256 bucketEnd = bucket + ((amount + shift) >> 8);
                amount = (amount + shift) & 0xff;
                shift = 0;
                for (++bucket; bucket != bucketEnd; ++bucket) {
                    count += LibBit.popCount(bitmap.map[bucket]);
                }
            }
            count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256 - amount));
        }
    }

    /// @dev Returns the index of the most significant set bit in `[0..upTo]`.
    /// If no set bit is found, returns `NOT_FOUND`.
    function findLastSet(Bitmap storage bitmap, uint256 upTo)
        internal
        view
        returns (uint256 setBitIndex)
    {
        uint256 bucket;
        uint256 bucketBits;
        /// @solidity memory-safe-assembly
        assembly {
            setBitIndex := not(0)
            bucket := shr(8, upTo)
            mstore(0x00, bucket)
            mstore(0x20, bitmap.slot)
            let offset := and(0xff, not(upTo)) // `256 - (255 & upTo) - 1`.
            bucketBits := shr(offset, shl(offset, sload(keccak256(0x00, 0x40))))
            if iszero(or(bucketBits, iszero(bucket))) {
                for {} 1 {} {
                    bucket := add(bucket, setBitIndex) // `sub(bucket, 1)`.
                    mstore(0x00, bucket)
                    bucketBits := sload(keccak256(0x00, 0x40))
                    if or(bucketBits, iszero(bucket)) { break }
                }
            }
        }
        if (bucketBits != 0) {
            setBitIndex = (bucket << 8) | LibBit.fls(bucketBits);
            /// @solidity memory-safe-assembly
            assembly {
                setBitIndex := or(setBitIndex, sub(0, gt(setBitIndex, upTo)))
            }
        }
    }
}

File 16 of 17 : INonfungiblePositionManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./IPoolInitializer.sol";

/// @title Non-fungible token for positions
/// @notice Wraps Thruster CLMM positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager is IPoolInitializer, IERC721 {
    /// @notice Returns the position information associated with a given token ID.
    /// @dev Throws if the token ID is not valid.
    /// @param tokenId The ID of the token that represents the position
    /// @return nonce The nonce for permits
    /// @return operator The address that is approved for spending
    /// @return token0 The address of the token0 for a specific pool
    /// @return token1 The address of the token1 for a specific pool
    /// @return fee The fee associated with the pool
    /// @return tickLower The lower end of the tick range for the position
    /// @return tickUpper The higher end of the tick range for the position
    /// @return liquidity The liquidity of the position
    /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
    /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
    /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
    /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
    function positions(uint256 tokenId)
        external
        view
        returns (
            uint96 nonce,
            address operator,
            address token0,
            address token1,
            uint24 fee,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    /// @notice Creates a new position wrapped in a NFT
    /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
    /// a method does not exist, i.e. the pool is assumed to be initialized.
    /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
    /// @return tokenId The ID of the token that represents the minted position
    /// @return liquidity The amount of liquidity for this position
    /// @return amount0 The amount of token0
    /// @return amount1 The amount of token1
    function mint(MintParams calldata params)
        external
        payable
        returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
    /// @param params tokenId The ID of the token for which liquidity is being increased,
    /// amount0Desired The desired amount of token0 to be spent,
    /// amount1Desired The desired amount of token1 to be spent,
    /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
    /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return liquidity The new liquidity amount as a result of the increase
    /// @return amount0 The amount of token0 to acheive resulting liquidity
    /// @return amount1 The amount of token1 to acheive resulting liquidity
    function increaseLiquidity(IncreaseLiquidityParams calldata params)
        external
        payable
        returns (uint128 liquidity, uint256 amount0, uint256 amount1);

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Decreases the amount of liquidity in a position and accounts it to the position
    /// @param params tokenId The ID of the token for which liquidity is being decreased,
    /// amount The amount by which liquidity will be decreased,
    /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
    /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return amount0 The amount of token0 accounted to the position's tokens owed
    /// @return amount1 The amount of token1 accounted to the position's tokens owed
    function decreaseLiquidity(DecreaseLiquidityParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
    /// @param params tokenId The ID of the NFT for which tokens are being collected,
    /// recipient The account that should receive the tokens,
    /// amount0Max The maximum amount of token0 to collect,
    /// amount1Max The maximum amount of token1 to collect
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
    /// must be collected first.
    /// @param tokenId The ID of the token that is being burned
    function burn(uint256 tokenId) external payable;
}

File 17 of 17 : IPoolInitializer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @title Creates and initializes V3 Pools
/// @notice Provides a method for creating and initializing a pool, if necessary, for bundling with other methods that
/// require the pool to exist.
interface IPoolInitializer {
    /// @notice Creates a new pool if it does not exist, then initializes if not initialized
    /// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool
    /// @param token0 The contract address of token0 of the pool
    /// @param token1 The contract address of token1 of the pool
    /// @param fee The fee amount of the v3 pool for the specified token pair
    /// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value
    /// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary
    function createAndInitializePoolIfNecessary(
        address token0,
        address token1,
        uint24 fee,
        uint160 sqrtPriceX96
    ) external payable returns (address pool);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_mirrorERC721","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"DNAlreadyInitialized","type":"error"},{"inputs":[],"name":"ExchangeSameToken","type":"error"},{"inputs":[],"name":"ExchangeTokenLocked","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientBalanceToMaintainLockedTokens","type":"error"},{"inputs":[],"name":"InvalidSalePrice","type":"error"},{"inputs":[],"name":"InvalidSellerOrBuyer","type":"error"},{"inputs":[],"name":"InvalidUnit","type":"error"},{"inputs":[],"name":"LinkMirrorContractFailed","type":"error"},{"inputs":[],"name":"MirrorAddressIsZero","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"SenderNotMirror","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TokenLockStatusNoChange","type":"error"},{"inputs":[],"name":"TokenNotLocked","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToLiquidityPoolDisabled","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"SkipNFTSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nonfungiblePositionManager","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"name":"createLiquidityPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"enableLiquidityPoolTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getSkipNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mirrorERC721","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxNumNfts","type":"uint256"}],"name":"returnSupplyLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"skipNFT","type":"bool"}],"name":"setSkipNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setSkipNFTFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040523480156200001157600080fd5b5060405162004747380380620047478339810160408190526200003491620004b4565b33806200005b57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200006681620000a4565b506200007560008083620000f4565b6200007f620002fb565b6200008a3362000379565b506001805460ff60a01b1916600160a01b1790556200066e565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b68a20d6e21d0e52553095468a20d6e21d0e5255308906001600160a01b0316156200013257604051633ab534b960e21b815260040160405180910390fd5b6001600160a01b0382166200015a576040516339a84a7b60e01b815260040160405180910390fd5b630f4599e560005233602052602060006024601c6000865af1600160005114166200018d5763d125259c6000526004601cfd5b6001810180546001600160a01b0319166001600160a01b038416179055620001b3601290565b620001c090600a620005fb565b683635c9adc5dea000001080620001d5575060005b15620001f45760405163265f13bd60e21b815260040160405180910390fd5b62000201600180620003c8565b8315620002f5576001600160a01b0383166200023057604051633a954ecd60e21b815260040160405180910390fd5b606084901c683635c9adc5dea00000850463fffffffe101715620002675760405163e5cfe95760e01b815260040160405180910390fd5b8054600160801b600160e01b031916600160801b6001600160601b038616021781556000620002968462000451565b80546001600160601b038716600160a01b026001600160a01b0391821617825560008781529192508516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602082a3620002f3846001620003c8565b505b50505050565b4662013e3103620003775760405163c8992e6160e01b815273430000000000000000000000000000000000000290819063c8992e61906200034790600290600190339060040162000622565b600060405180830381600087803b1580156200036257600080fd5b505af1158015620002f3573d6000803e3d6000fd5b565b4662013e3103620003c5576040516336b91f2b60e01b81526001600160a01b0382166004820152732536fe9ab3f511540f2f9e2ec2a805005c3dd800906336b91f2b9060240162000347565b50565b6000620003d58362000451565b805490915067010000000000000090046002161515821515146200041957805460ff67010000000000000080830482166002189091160260ff60381b199091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b6001600160a01b038116600090815268a20d6e21d0e525531360205260408120805490916701000000000000009091046001169003620004af57805460ff60381b191667010000000000000060ff843b151560020260011716021781555b919050565b600060208284031215620004c757600080fd5b81516001600160a01b0381168114620004df57600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b808511156200053d578160001904821115620005215762000521620004e6565b808516156200052f57918102915b93841c939080029062000501565b509250929050565b6000826200055657506001620005f5565b816200056557506000620005f5565b81600181146200057e57600281146200058957620005a9565b6001915050620005f5565b60ff8411156200059d576200059d620004e6565b50506001821b620005f5565b5060208310610133831016604e8410600b8410161715620005ce575081810a620005f5565b620005da8383620004fc565b8060001904821115620005f157620005f1620004e6565b0290505b92915050565b6000620004df60ff84168362000545565b634e487b7160e01b600052602160045260246000fd5b60608101600385106200063957620006396200060c565b848252600284106200064f576200064f6200060c565b60208201939093526001600160a01b0391909116604090910152919050565b6140c9806200067e6000396000f3fe60806040526004361061016e5760003560e01c806370a08231116100cb578063a0ae8d051161007f578063c87b56dd11610059578063c87b56dd146108d3578063dd62ed3e146108f3578063f2fde38b1461094157610175565b8063a0ae8d051461087e578063a9059cbb14610893578063b001bebf146108b357610175565b80638da5cb5b116100b05780638da5cb5b146107fe578063907af6c01461081c57806395d89b411461083857610175565b806370a0823114610785578063715018a6146107e957610175565b806323b872dd116101225780632a6a935d116101075780632a6a935d1461070f578063313ce5671461072f5780634ef41efc1461074b57610175565b806323b872dd146106cf578063274e430b146106ef57610175565b8063095ea7b311610153578063095ea7b31461063657806318160ddd146106665780632384d3d0146106af57610175565b806306fdde03146105be57806307a19f721461061657610175565b3661017557005b68a20d6e21d0e52553095468a20d6e21d0e52553089060003560e01c906001600160a01b031663e5eb36c882900361020b5760018301546001600160a01b031633146101ed576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610201600435602435604435606435610961565b61020b6001610ef7565b8163813500fc036102795760018301546001600160a01b0316331461025c576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61026f6004356024351515604435610f01565b6102796001610ef7565b8163b79cc1bd036103035760018301546001600160a01b031633146102ca576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004356102f76102e76024355b6102e2906004613cc0565b61109d565b60ff83161515606084901c6110c8565b6103016001610ef7565b505b81633e0446a1036103815760018301546001600160a01b03163314610354576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600435610375606082901c61036a6024356102d7565b60ff8416151561159e565b61037f6001610ef7565b505b816386529a61036103f25760018301546001600160a01b031633146103d2576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e86004356103e36024356102d7565b611b32565b6103f26001610ef7565b8163e985e9c50361043a576001600160a01b036024358181166000908152600386016020908152604080832060043595861684529091529020805461043690610ef7565b5050505b81636352211e0361046157610461610453600435612026565b6001600160a01b0316610ef7565b8163243598790361047a5761047a610453600435612076565b8163d10b6e0c036104f45760018301546001600160a01b031633146104cb576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006104de6004356024356044356120cb565b90506104f2816001600160a01b0316610ef7565b505b8163f9b4b3280361053b576040805160200190526004356000610526606083901c60243560443560ff86161515612201565b90506020810360208152815160051b60400181f35b8163081812fc0361055457610554610453600435612316565b8163f5b100ea036105725761057261056d60043561237b565b610ef7565b8163e2c79281036105a75768a20d6e21d0e5255308546105a7906c01000000000000000000000000900463ffffffff16610ef7565b8163b7a94eb8036105bc576105bc6001610ef7565b005b3480156105ca57600080fd5b5060408051808201909152600d81527f426c617374696e2050657065730000000000000000000000000000000000000060208201525b60405161060d9190613cf7565b60405180910390f35b34801561062257600080fd5b506105bc610631366004613d6d565b6123e0565b34801561064257600080fd5b50610656610651366004613da2565b612475565b604051901515815260200161060d565b34801561067257600080fd5b5068a20d6e21d0e52553085470010000000000000000000000000000000090046bffffffffffffffffffffffff165b60405190815260200161060d565b3480156106bb57600080fd5b506105bc6106ca366004613dce565b61248b565b3480156106db57600080fd5b506106566106ea366004613e07565b61260d565b3480156106fb57600080fd5b5061065661070a366004613e48565b6126b2565b34801561071b57600080fd5b5061065661072a366004613e65565b612709565b34801561073b57600080fd5b506040516012815260200161060d565b34801561075757600080fd5b5068a20d6e21d0e5255309546001600160a01b03165b6040516001600160a01b03909116815260200161060d565b34801561079157600080fd5b506106a16107a0366004613e48565b6001600160a01b0316600090815268a20d6e21d0e525531360205260409020547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1690565b3480156107f557600080fd5b506105bc61271d565b34801561080a57600080fd5b506000546001600160a01b031661076d565b34801561082857600080fd5b50683635c9adc5dea000006106a1565b34801561084457600080fd5b5060408051808201909152600581527f62504550450000000000000000000000000000000000000000000000000000006020820152610600565b34801561088a57600080fd5b506105bc612731565b34801561089f57600080fd5b506106566108ae366004613da2565b6127a3565b3480156108bf57600080fd5b506105bc6108ce366004613e80565b6127cf565b3480156108df57600080fd5b506106006108ee366004613e80565b6128b3565b3480156108ff57600080fd5b506106a161090e366004613dce565b6001600160a01b03918216600090815268a20d6e21d0e525530e6020908152604080832093909416825291909152205490565b34801561094d57600080fd5b506105bc61095c366004613e48565b6128e4565b6001600160a01b0383166109a1576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b68a20d6e21d0e525530868a20d6e21d0e525531268a20d6e21d0e525530a60006109ee83600188901b5b600381901c600090815260209290925260409091205460059190911b60e0161c90565b63ffffffff1681526020810191909152604001600020546001600160a01b03878116911614610a49576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b856001600160a01b0316836001600160a01b031614610ae7576001600160a01b0380841660009081526003840160209081526040808320938a168352929052908120549003610ae75760008481526004830160205260409020546001600160a01b03848116911614610ae7576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610af287612938565b90506000610aff87612938565b8254909150683635c9adc5dea000009081908490601490610b479084907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16613e99565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915583546001600160a01b0381167401000000000000000000000000000000000000000091829004831685019092160217835550610ba885886129b5565b610bb28588612a34565b6000610bc38560018a811b016109cb565b600889901c6000908152600c8801602052604090205490915060ff89161c60011615610c64576001600160a01b038a166000908152600887016020526040902084547fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff811663ffffffff6c01000000000000000000000000928390048116600019018116928302919091178755610c5f92918891851690612a8a565b610dbb565b30600090815260038701602090815260408083206001600160a01b038e16845290915290205415610ce157610c9a868b8c612b16565b600d8601805460001963ffffffff64010000000080840482169290920116027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9091161790555b6001600160a01b038a166000908152600787016020526040902084547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff811663ffffffff700100000000000000000000000000000000928390048116600019018116928302919091178755610d5b92918891851690612a8a565b600c8601602052600888901c6000908152604090208054600160ff8b161b8019909116179055600d8601805463ffffffff808216600101167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009091161790555b8254600163ffffffff6c010000000000000000000000008084048216928301909116027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9092169190911784556001600160a01b038a1660009081526008880160205260409020610e5c90828b826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b610eb2868a610e6b878e612d2e565b848163ffffffff168160201b17846020528360021c60005260406000206003851660061b815467ffffffffffffffff8482841c188116831b82188455505050505050505050565b5050806000528760601b60601c8960601b60601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a3505050505050505050565b8060005260206000f35b6001600160a01b03838116600081815268a20d6e21d0e525530b6020908152604080832094861683529390529190912068a20d6e21d0e5255308913090036110925780546001600160a01b0384166000908152600b840160205260409020901580159190861515161561101e57600d840180546001830180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff6a0100000000000000000000909304929092169190911790558154815463ffffffff70010000000000000000000000000000000090920482169291600491610ffb918591640100000000900416613ebe565b92506101000a81548163ffffffff021916908363ffffffff16021790555061108f565b8115158615161561108f57611034848687612b16565b8054600d8501805463ffffffff700100000000000000000000000000000000909304831692600491611070918591640100000000900416613edb565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b50505b831515905550505050565b6060813560405191508060051b60208301016040528082528060051b60208401602084013750919050565b68a20d6e21d0e52553086110dd818380612b16565b6001600160a01b0382166000908152600b8201602090815260408083206008850183528184206007860190935290832081548851600a870195600c880195939268010000000000000000900463ffffffff1691905b818110156114535760008c828151811061114e5761114e613ef8565b602002602001015190508363ffffffff1661116d8a6109cb8460011b90565b63ffffffff16146111aa576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111bb8a600184811b016109cb565b600883901c600090815260208b905260409020549091508d15159060ff84161c600116151503611217576040517f4590a8d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8c611361576020899052600882901c6000908152604090208054600160ff85161b19169055611290878b8363ffffffff168b600001600c81819054906101000a900463ffffffff1661126890613f27565b91906101000a81548163ffffffff021916908363ffffffff160217905563ffffffff16612a8a565b8754600090700100000000000000000000000000000000900463ffffffff168960106112bb83613f47565b91906101000a81548163ffffffff021916908363ffffffff16021790555063ffffffff16905061131b878285826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b61135b8b600185811b015b83826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b50611449565b6020899052600882901c6000908152604090208054600160ff85161b80199091161790556113b1868b8363ffffffff168b600001601081819054906101000a900463ffffffff1661126890613f27565b87546000906c01000000000000000000000000900463ffffffff1689600c6113d883613f47565b91906101000a81548163ffffffff021916908363ffffffff16021790555063ffffffff169050611438888285826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b6114478b600185811b01611326565b505b5050600101611132565b50891561149a57600d8801805482919060009061147790849063ffffffff16613ebe565b92506101000a81548163ffffffff021916908363ffffffff1602179055506114d6565b600d880180548291906000906114b790849063ffffffff16613edb565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b30600090815260038901602090815260408083206001600160a01b038d1684529091529020541561159157891561154e578a51600d8901805460049061152b908490640100000000900463ffffffff16613edb565b92506101000a81548163ffffffff021916908363ffffffff160217905550611591565b8a51600d89018054600490611572908490640100000000900463ffffffff16613ebe565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b5050505050505050505050565b6001600160a01b0383166115de576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b68a20d6e21d0e52553086115f3818580612b16565b600a8101600c8201600061160687612938565b905060006116148289612d2e565b90506000808761165b5783546001600160a01b038b166000908152600789016020526040902070010000000000000000000000000000000090910463ffffffff1690611690565b83546001600160a01b038b16600090815260088901602052604090206c0100000000000000000000000090910463ffffffff16905b8a519193509150886119a35760005b8181101561181c5760008b82815181106116bb576116bb613ef8565b6020026020010151905060008a60020160006116db8c6109cb8660011b90565b63ffffffff1681526020810191909152604001600020546001600160a01b031690508015611735576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020859052631fffffff600387901c16600090815260409020805460e0600589901b1681811c851863ffffffff16901b1890556117c08a83898961177881613f47565b9a508163ffffffff168160201b17846020528360021c60005260406000206003851660061b815467ffffffffffffffff8482841c188116831b82188455505050505050505050565b600882901c600090815260208a9052604090205460ff83161c60011615158c151514611812576020899052600882901c6000908152604090208054600160ff851690811b199091168e151590911b1790555b505060010161169f565b50875481908990600c906118479084906c01000000000000000000000000900463ffffffff16613ebe565b92506101000a81548163ffffffff021916908363ffffffff16021790555088156118e857845463ffffffff8085166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff909216919091178655600d8901805483926000916118c591859116613ebe565b92506101000a81548163ffffffff021916908363ffffffff16021790555061192a565b84547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000063ffffffff8516021785555b30600090815260038901602090815260408083206001600160a01b038f1684529091529020541580159061195c575088155b156119a3578088600d0160048282829054906101000a900463ffffffff166119849190613ebe565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b60006119b882683635c9adc5dea00000613f6a565b89549091506000906119ec9070010000000000000000000000000000000090046bffffffffffffffffffffffff1683613cc0565b9050606081901c683635c9adc5dea00000820463fffffffe10171515828210811715611a44576040517fe5cfe95700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8a546bffffffffffffffffffffffff808416700100000000000000000000000000000000027fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff909216919091178c55885484918a91601491611ac19185917401000000000000000000000000000000000000000090910416613f81565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505050806000528b60601b60601c60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a3505050505050505050505050565b6001600160a01b038216611b72576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b68a20d6e21d0e5255308611b87818480612b16565b6000611b9284612938565b805484516001600160a01b0387166000908152600886016020908152604080832060078901909252822094955063ffffffff6c0100000000000000000000000085048116957001000000000000000000000000000000009095041693919291600a880191600c890191855b85811015611d8b5760008c8281518110611c1957611c19613ef8565b6020026020010151905060008c6002016000611c39896109cb8660011b90565b63ffffffff1681526020810191909152604001600020546001600160a01b0390811691508f168114611c97576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ca888600185811b016109cb565b600884901c60009081526020899052604090205490915060ff84161c60011615611d15576020879052600883901c6000908152604090208054600160ff86161b19169055611d1086898363ffffffff168f611d0290613f27565b9f508f63ffffffff16612a8a565b611d44565b611d38858963ffffffff8416611d2a8f613f27565b9e508e63ffffffff16612a8a565b611d418a613fa6565b99505b611d4e8e846129b5565b6020889052600283901c600090815260409020805460c0600686901b1681811c67ffffffffffffffff16901b189055505050806001019050611bfd565b505050838303915050818314611e1857855463ffffffff8087166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff909216919091178755600d880180548083168490039092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009092169190911790555b8215611e605785547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000063ffffffff8616021786555b30600090815260038801602090815260408083206001600160a01b038d16845290915290205415801590611e945750600083115b15611ede57600d8701805463ffffffff6401000000008083048216879003909116027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9091161790555b506000611ef482683635c9adc5dea00000613f6a565b87546bffffffffffffffffffffffff70010000000000000000000000000000000063ffffffff6c010000000000000000000000008085048216889003909116027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff84168117829004831685900383169091027fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff9091167fffffffff00000000000000000000000000000000ffffffffffffffffffffffff9093169290921791909117895587547401000000000000000000000000000000000000000080820483168490039092169091026001600160a01b0391821617885560008281529192508a167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a3505050505050505050565b600061203182612e16565b612067576040517fceea21b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61207082612076565b92915050565b600068a20d6e21d0e525530868a20d6e21d0e525530a826120a468a20d6e21d0e5255312600187901b6109cb565b63ffffffff1681526020810191909152604001600020546001600160a01b03169392505050565b600068a20d6e21d0e525530868a20d6e21d0e525530a826120f968a20d6e21d0e5255312600188901b6109cb565b63ffffffff1681526020810191909152604001600020546001600160a01b03908116925083168214612187576001600160a01b03808416600090815260038301602090815260408083209386168352929052908120549003612187576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152600482016020908152604080832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038a1690811790915560058501909252600887901c83529091208054600160ff881690811b19909116921515901b919091179055509392505050565b606068a20d6e21d0e52553086000808461225c576001600160a01b03881660009081526007840160209081526040808320600b870190925290912054700100000000000000000000000000000000900463ffffffff1661229b565b6001600160a01b03881660009081526008840160209081526040808320600b8701909252909120546c01000000000000000000000000900463ffffffff165b6040516020839052955090925063ffffffff1686811887821102189050865b818110156122f9578060031c600052604060002080546007831660051b1c63ffffffff1690508089830360051b602088010152506001810190506122ba565b9690960380845260051b8301602001604052509095945050505050565b600061232182612e16565b612357576040517fceea21b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600090815268a20d6e21d0e525530c60205260409020546001600160a01b031690565b6001600160a01b038116600090815268a20d6e21d0e52553136020526040812080546123d39063ffffffff6c010000000000000000000000008204811691700100000000000000000000000000000000900416613ebe565b63ffffffff169392505050565b6000546001600160a01b0316331480159061241d57503361241168a20d6e21d0e5255309546001600160a01b031690565b6001600160a01b031614155b1561246757335b6040517f118cdaa70000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024015b60405180910390fd5b6124718282612e33565b5050565b6000612482338484612ed4565b50600192915050565b612493612f36565b6001600160a01b03811630106000816124ac57826124ae565b305b90506000826124bd57306124bf565b835b90506000836124db576c7862c74490707c52727f6abd456124e9565b6b02206205d4d70b66e052639a5b6040517f13ead5620000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301528481166024830152610bb860448301526cffffffffffffffffffffffffff929092166064820181905292506000918816906313ead562906084016020604051808303816000875af1158015612578573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259c9190613fc0565b90506125a9816001612e33565b6125cd6125c668a20d6e21d0e5255309546001600160a01b031690565b6001612e33565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055505050505050565b6001600160a01b038316600090815268a20d6e21d0e525530e602090815260408083203384529091528120805468a20d6e21d0e52553089190600019811461268f5780851115612689576040517f13be252b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84810382555b61269a838888612b16565b6126a5878787612f4e565b5060019695505050505050565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604081208054670100000000000000900460011682036126f257823b5b9392505050565b546701000000000000009004600216151592915050565b60006127153383612e33565b506001919050565b612725612f36565b61272f6000613966565b565b6000546001600160a01b0316331480159061276e57503361276268a20d6e21d0e5255309546001600160a01b031690565b6001600160a01b031614155b156127795733612424565b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff169055565b600068a20d6e21d0e52553086127ba813386612b16565b6127c5338585612f4e565b5060019392505050565b6000546001600160a01b0316331480159061280c57503361280068a20d6e21d0e5255309546001600160a01b031690565b6001600160a01b031614155b156128175733612424565b600061282c683635c9adc5dea0000083613f6a565b30600090815268a20d6e21d0e52553136020526040812054919250907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1690508060000361287d57505050565b80821115612889578091505b6128ad306128a768a20d6e21d0e5255309546001600160a01b031690565b84612f4e565b50505b50565b60606128be826139ce565b6040516020016128ce9190613fdd565b6040516020818303038152906040529050919050565b6128ec612f36565b6001600160a01b03811661292f576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526000600482015260240161245e565b6128b081613966565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604081208054909167010000000000000090910460011690036129b05780547fffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff1667010000000000000060ff843b151560020260011716021781555b919050565b600881901c600090815260058301602052604090205460ff82161c600116156124715760058201602052600881901c6000908152604090208054600160ff84161b191690556000818152600483016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555050565b6000818152600f8301602052604090205463ffffffff1615612471576000818152600f83016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555050565b808214612b1057600381811c6000908152602086815260408083205486851c84528184208054600589811b60e090811683811c8b841b83169690961c63ffffffff81811697909718871690911b909318909355948a9052600190811b6401fffffffe16019586901c85529190932080549490921b1683811c861890921690911b90911890555b50505050565b30600090815260038401602090815260408083206001600160a01b038616845291829052822054909190819015612c0057600d8601546001600160a01b0386166000908152600b880160205260409020805460018201546a01000000000000000000009093046bffffffffffffffffffffffff9081169550919270010000000000000000000000000000000090910463ffffffff1691612bb7911685614022565b612bc19190613f6a565b60019190910180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff851617905590505b846001600160a01b0316846001600160a01b031614612d15576001600160a01b03841660009081526020849052604090205415612d155781600003612c6357600d8601546a010000000000000000000090046bffffffffffffffffffffffff1691505b6001600160a01b0384166000908152600b8701602052604081208054600182015491929170010000000000000000000000000000000090910463ffffffff1690612cbb906bffffffffffffffffffffffff1686614022565b612cc59190613f6a565b6001830180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff871617905590508015612d1257612d12308783612f4e565b50505b8015612d2657612d26308683612f4e565b505050505050565b815468010000000000000000900463ffffffff1668a20d6e21d0e52553086000829003612e0f5780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000008116600163ffffffff92831601918216908117835585547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff166801000000000000000082021786556000818152600284016020526040812080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0388161790559193509003612e0f57600080fd5b5092915050565b600080612e2283612076565b6001600160a01b0316141592915050565b6000612e3e83612938565b80549091506701000000000000009004600216151582151514612e9c57805460ff6701000000000000008083048216600218909116027fffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff9091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b6001600160a01b03838116600081815268a20d6e21d0e525530e6020908152604080832094871680845294825282208581558583529392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a350505050565b6000546001600160a01b0316331461272f5733612424565b6001600160a01b038216612f8e576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f9782613a6e565b68a20d6e21d0e52553086000612fac85612938565b90506000612fb985612938565b905061300a6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b825470010000000000000000000000000000000080820463ffffffff908116608085015284548290041660a08401528554046bffffffffffffffffffffffff90811660c08401527401000000000000000000000000000000000000000090910416604082018190528511156130ab576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040810180518690039081905283546c01000000000000000000000000900463ffffffff1660e08301819052683635c9adc5dea0000002111561311a576040517f2e33c5e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604081015183546bffffffffffffffffffffffff918216740100000000000000000000000000000000000000009081026001600160a01b039283161786558454818104841689016060860181905290931602911617825560e081015160808201516131ac9101613190683635c9adc5dea0000090565b8360400151816131a2576131a2614035565b0480821191030290565b81528154670100000000000000900460021660000361323f57856001600160a01b0316876001600160a01b0316036131ec57805160808201510360a08201525b613239683635c9adc5dea0000082606001518161320b5761320b614035565b845460a085015163ffffffff6c01000000000000000000000000909204919091160191900481810391100290565b60208201525b30600090815260038501602090815260408083206001600160a01b038b1684529182905290912054156132b7578151600d8601805463ffffffff640100000000808304821694909403169092027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9092169190911790555b6001600160a01b03871660009081526020829052604090205415613322576020820151600d8601805463ffffffff6401000000008083048216909401169092027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9092169190911790555b5080516020820151855463ffffffff6c0100000000000000000000000080830482168401859003909116027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff909116178655600a8601916000918082189082110218905080156135a4578251819003835260208301805182900390526001600160a01b03808916908a16036133c05760a083018051820190526135a4565b604080516080810182526000808252602080830182815283850183815260608086019081528651888152600589901b810185019097528690526001600160a01b03808f169091528e1690529290920181526001600160a01b03808c16600090815260078a016020526040808220928d1682528120929350909190613444888d612d2e565b90505b6080870180516000190190819052600381901c600090815260208590526040812054909160051b60e0161c63ffffffff1690506134b8838960a0015183826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b60a088018051600181019091526020888152600283901c600090815260409020805460c0600686901b1681811c9490931b63ffffffff8716179390931867ffffffffffffffff1690911b90911890556135118b826129b5565b84518181526020018552506000198501946000036001016134475760018a01546135459085906001600160a01b0316613adc565b5050506080840151865463ffffffff9182167001000000000000000000000000000000009081027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff92831617895560a087015188549316029116178555505b5060006135ba8360200151846000015101613b2c565b8654845191925068010000000000000000900463ffffffff1690156137115760608a901b60011760208301526001600160a01b038a166000908152600788016020526040902060808501518551885463ffffffff918303918216700100000000000000000000000000000000027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff9091161789555b60001991909101600381901c600090815260208490526040812054919291600584901b60e0161c6020889052633fffffff600282901c16600090815260409020805460c0600684901b1681811c67ffffffffffffffff16901b18905563ffffffff16905060098b01602052600385901c600090815260409020805460e0600588901b1681811c841863ffffffff16901b1890556001909401936136f28b826129b5565b8551602080880151600884901b1782520186525080820361364f575050505b6020840151156138c557606089901b602083015286546001600160a01b038a1660009081526007890160209081526040822060a08801519188015164010000000090940463ffffffff169390929082019061376c8a8f612d2e565b8a547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000063ffffffff851602178b5590505b600086156137ee5760001996909601600381901c600090815260098e016020526040902054909690600588901b60e0161c63ffffffff1690506137f5565b5060018501945b6020859052600384901c600090815260409020805460e0600587901b1681811c841863ffffffff16901b1890556020898152600282901c600090815260409020805460c0600685901b1681811c9388901b63ffffffff8716179390931867ffffffffffffffff1690921b909118905560019093019287516020808a0151600884901b178252018852508183036137b0575050895463ffffffff909316640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff90931692909217895550505b604082015151156139225786547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff166801000000000000000063ffffffff83160217875560018701546139229083906001600160a01b0316613b73565b876000528860601b60601c8a60601b60601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a350505050505050505050565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b606060006139db83613baf565b600101905060008167ffffffffffffffff8111156139fb576139fb614064565b6040519080825280601f01601f191660200182016040528015613a25576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8504945084613a2f57509392505050565b60015474010000000000000000000000000000000000000000900460ff168015613aa557506001546001600160a01b038281169116145b156128b0576040517f86122ad400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060820151805160051b60840160808203915063144027d38252836020015160208301528360400151604083015260608083015260208282601c85016000875af1600183511416612b1057600082fd5b613b5060405180606001604052806000815260200160008152602001606081525090565b604051828152806020018360051b81016040528183604001528083525050919050565b60408201516040810363263c69d68152602080820152815160051b604401915060208183601c84016000875af1600182511416612b1057600081fd5b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310613bf8577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310613c24576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310613c4257662386f26fc10000830492506010015b6305f5e1008310613c5a576305f5e100830492506008015b6127108310613c6e57612710830492506004015b60648310613c80576064830492506002015b600a83106120705760010192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561207057612070613c91565b60005b83811015613cee578181015183820152602001613cd6565b50506000910152565b6020815260008251806020840152613d16816040850160208701613cd3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6001600160a01b03811681146128b057600080fd5b803580151581146129b057600080fd5b60008060408385031215613d8057600080fd5b8235613d8b81613d48565b9150613d9960208401613d5d565b90509250929050565b60008060408385031215613db557600080fd5b8235613dc081613d48565b946020939093013593505050565b60008060408385031215613de157600080fd5b8235613dec81613d48565b91506020830135613dfc81613d48565b809150509250929050565b600080600060608486031215613e1c57600080fd5b8335613e2781613d48565b92506020840135613e3781613d48565b929592945050506040919091013590565b600060208284031215613e5a57600080fd5b81356126eb81613d48565b600060208284031215613e7757600080fd5b6126eb82613d5d565b600060208284031215613e9257600080fd5b5035919050565b6bffffffffffffffffffffffff828116828216039080821115612e0f57612e0f613c91565b63ffffffff818116838216019080821115612e0f57612e0f613c91565b63ffffffff828116828216039080821115612e0f57612e0f613c91565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600063ffffffff821680613f3d57613f3d613c91565b6000190192915050565b600063ffffffff808316818103613f6057613f60613c91565b6001019392505050565b808202811582820484141761207057612070613c91565b6bffffffffffffffffffffffff818116838216019080821115612e0f57612e0f613c91565b60006000198203613fb957613fb9613c91565b5060010190565b600060208284031215613fd257600080fd5b81516126eb81613d48565b7f68747470733a2f2f6170692e626c617374696e70657065732e78797a2f6d642f815260008251614015816020850160208701613cd3565b9190910160200192915050565b8181038181111561207057612070613c91565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea2646970667358221220fa07488a4ba45a800d6833c12e2647064dfa00c0a6f97657047d896311a74fd664736f6c634300081800330000000000000000000000000cd7e55f61d393866db74afd6fdf49d81b82b9be

Deployed Bytecode

0x60806040526004361061016e5760003560e01c806370a08231116100cb578063a0ae8d051161007f578063c87b56dd11610059578063c87b56dd146108d3578063dd62ed3e146108f3578063f2fde38b1461094157610175565b8063a0ae8d051461087e578063a9059cbb14610893578063b001bebf146108b357610175565b80638da5cb5b116100b05780638da5cb5b146107fe578063907af6c01461081c57806395d89b411461083857610175565b806370a0823114610785578063715018a6146107e957610175565b806323b872dd116101225780632a6a935d116101075780632a6a935d1461070f578063313ce5671461072f5780634ef41efc1461074b57610175565b806323b872dd146106cf578063274e430b146106ef57610175565b8063095ea7b311610153578063095ea7b31461063657806318160ddd146106665780632384d3d0146106af57610175565b806306fdde03146105be57806307a19f721461061657610175565b3661017557005b68a20d6e21d0e52553095468a20d6e21d0e52553089060003560e01c906001600160a01b031663e5eb36c882900361020b5760018301546001600160a01b031633146101ed576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610201600435602435604435606435610961565b61020b6001610ef7565b8163813500fc036102795760018301546001600160a01b0316331461025c576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61026f6004356024351515604435610f01565b6102796001610ef7565b8163b79cc1bd036103035760018301546001600160a01b031633146102ca576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004356102f76102e76024355b6102e2906004613cc0565b61109d565b60ff83161515606084901c6110c8565b6103016001610ef7565b505b81633e0446a1036103815760018301546001600160a01b03163314610354576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600435610375606082901c61036a6024356102d7565b60ff8416151561159e565b61037f6001610ef7565b505b816386529a61036103f25760018301546001600160a01b031633146103d2576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e86004356103e36024356102d7565b611b32565b6103f26001610ef7565b8163e985e9c50361043a576001600160a01b036024358181166000908152600386016020908152604080832060043595861684529091529020805461043690610ef7565b5050505b81636352211e0361046157610461610453600435612026565b6001600160a01b0316610ef7565b8163243598790361047a5761047a610453600435612076565b8163d10b6e0c036104f45760018301546001600160a01b031633146104cb576040517fce5a776b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006104de6004356024356044356120cb565b90506104f2816001600160a01b0316610ef7565b505b8163f9b4b3280361053b576040805160200190526004356000610526606083901c60243560443560ff86161515612201565b90506020810360208152815160051b60400181f35b8163081812fc0361055457610554610453600435612316565b8163f5b100ea036105725761057261056d60043561237b565b610ef7565b8163e2c79281036105a75768a20d6e21d0e5255308546105a7906c01000000000000000000000000900463ffffffff16610ef7565b8163b7a94eb8036105bc576105bc6001610ef7565b005b3480156105ca57600080fd5b5060408051808201909152600d81527f426c617374696e2050657065730000000000000000000000000000000000000060208201525b60405161060d9190613cf7565b60405180910390f35b34801561062257600080fd5b506105bc610631366004613d6d565b6123e0565b34801561064257600080fd5b50610656610651366004613da2565b612475565b604051901515815260200161060d565b34801561067257600080fd5b5068a20d6e21d0e52553085470010000000000000000000000000000000090046bffffffffffffffffffffffff165b60405190815260200161060d565b3480156106bb57600080fd5b506105bc6106ca366004613dce565b61248b565b3480156106db57600080fd5b506106566106ea366004613e07565b61260d565b3480156106fb57600080fd5b5061065661070a366004613e48565b6126b2565b34801561071b57600080fd5b5061065661072a366004613e65565b612709565b34801561073b57600080fd5b506040516012815260200161060d565b34801561075757600080fd5b5068a20d6e21d0e5255309546001600160a01b03165b6040516001600160a01b03909116815260200161060d565b34801561079157600080fd5b506106a16107a0366004613e48565b6001600160a01b0316600090815268a20d6e21d0e525531360205260409020547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1690565b3480156107f557600080fd5b506105bc61271d565b34801561080a57600080fd5b506000546001600160a01b031661076d565b34801561082857600080fd5b50683635c9adc5dea000006106a1565b34801561084457600080fd5b5060408051808201909152600581527f62504550450000000000000000000000000000000000000000000000000000006020820152610600565b34801561088a57600080fd5b506105bc612731565b34801561089f57600080fd5b506106566108ae366004613da2565b6127a3565b3480156108bf57600080fd5b506105bc6108ce366004613e80565b6127cf565b3480156108df57600080fd5b506106006108ee366004613e80565b6128b3565b3480156108ff57600080fd5b506106a161090e366004613dce565b6001600160a01b03918216600090815268a20d6e21d0e525530e6020908152604080832093909416825291909152205490565b34801561094d57600080fd5b506105bc61095c366004613e48565b6128e4565b6001600160a01b0383166109a1576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b68a20d6e21d0e525530868a20d6e21d0e525531268a20d6e21d0e525530a60006109ee83600188901b5b600381901c600090815260209290925260409091205460059190911b60e0161c90565b63ffffffff1681526020810191909152604001600020546001600160a01b03878116911614610a49576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b856001600160a01b0316836001600160a01b031614610ae7576001600160a01b0380841660009081526003840160209081526040808320938a168352929052908120549003610ae75760008481526004830160205260409020546001600160a01b03848116911614610ae7576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610af287612938565b90506000610aff87612938565b8254909150683635c9adc5dea000009081908490601490610b479084907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16613e99565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915583546001600160a01b0381167401000000000000000000000000000000000000000091829004831685019092160217835550610ba885886129b5565b610bb28588612a34565b6000610bc38560018a811b016109cb565b600889901c6000908152600c8801602052604090205490915060ff89161c60011615610c64576001600160a01b038a166000908152600887016020526040902084547fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff811663ffffffff6c01000000000000000000000000928390048116600019018116928302919091178755610c5f92918891851690612a8a565b610dbb565b30600090815260038701602090815260408083206001600160a01b038e16845290915290205415610ce157610c9a868b8c612b16565b600d8601805460001963ffffffff64010000000080840482169290920116027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9091161790555b6001600160a01b038a166000908152600787016020526040902084547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff811663ffffffff700100000000000000000000000000000000928390048116600019018116928302919091178755610d5b92918891851690612a8a565b600c8601602052600888901c6000908152604090208054600160ff8b161b8019909116179055600d8601805463ffffffff808216600101167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009091161790555b8254600163ffffffff6c010000000000000000000000008084048216928301909116027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9092169190911784556001600160a01b038a1660009081526008880160205260409020610e5c90828b826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b610eb2868a610e6b878e612d2e565b848163ffffffff168160201b17846020528360021c60005260406000206003851660061b815467ffffffffffffffff8482841c188116831b82188455505050505050505050565b5050806000528760601b60601c8960601b60601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a3505050505050505050565b8060005260206000f35b6001600160a01b03838116600081815268a20d6e21d0e525530b6020908152604080832094861683529390529190912068a20d6e21d0e5255308913090036110925780546001600160a01b0384166000908152600b840160205260409020901580159190861515161561101e57600d840180546001830180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff6a0100000000000000000000909304929092169190911790558154815463ffffffff70010000000000000000000000000000000090920482169291600491610ffb918591640100000000900416613ebe565b92506101000a81548163ffffffff021916908363ffffffff16021790555061108f565b8115158615161561108f57611034848687612b16565b8054600d8501805463ffffffff700100000000000000000000000000000000909304831692600491611070918591640100000000900416613edb565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b50505b831515905550505050565b6060813560405191508060051b60208301016040528082528060051b60208401602084013750919050565b68a20d6e21d0e52553086110dd818380612b16565b6001600160a01b0382166000908152600b8201602090815260408083206008850183528184206007860190935290832081548851600a870195600c880195939268010000000000000000900463ffffffff1691905b818110156114535760008c828151811061114e5761114e613ef8565b602002602001015190508363ffffffff1661116d8a6109cb8460011b90565b63ffffffff16146111aa576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111bb8a600184811b016109cb565b600883901c600090815260208b905260409020549091508d15159060ff84161c600116151503611217576040517f4590a8d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8c611361576020899052600882901c6000908152604090208054600160ff85161b19169055611290878b8363ffffffff168b600001600c81819054906101000a900463ffffffff1661126890613f27565b91906101000a81548163ffffffff021916908363ffffffff160217905563ffffffff16612a8a565b8754600090700100000000000000000000000000000000900463ffffffff168960106112bb83613f47565b91906101000a81548163ffffffff021916908363ffffffff16021790555063ffffffff16905061131b878285826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b61135b8b600185811b015b83826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b50611449565b6020899052600882901c6000908152604090208054600160ff85161b80199091161790556113b1868b8363ffffffff168b600001601081819054906101000a900463ffffffff1661126890613f27565b87546000906c01000000000000000000000000900463ffffffff1689600c6113d883613f47565b91906101000a81548163ffffffff021916908363ffffffff16021790555063ffffffff169050611438888285826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b6114478b600185811b01611326565b505b5050600101611132565b50891561149a57600d8801805482919060009061147790849063ffffffff16613ebe565b92506101000a81548163ffffffff021916908363ffffffff1602179055506114d6565b600d880180548291906000906114b790849063ffffffff16613edb565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b30600090815260038901602090815260408083206001600160a01b038d1684529091529020541561159157891561154e578a51600d8901805460049061152b908490640100000000900463ffffffff16613edb565b92506101000a81548163ffffffff021916908363ffffffff160217905550611591565b8a51600d89018054600490611572908490640100000000900463ffffffff16613ebe565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b5050505050505050505050565b6001600160a01b0383166115de576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b68a20d6e21d0e52553086115f3818580612b16565b600a8101600c8201600061160687612938565b905060006116148289612d2e565b90506000808761165b5783546001600160a01b038b166000908152600789016020526040902070010000000000000000000000000000000090910463ffffffff1690611690565b83546001600160a01b038b16600090815260088901602052604090206c0100000000000000000000000090910463ffffffff16905b8a519193509150886119a35760005b8181101561181c5760008b82815181106116bb576116bb613ef8565b6020026020010151905060008a60020160006116db8c6109cb8660011b90565b63ffffffff1681526020810191909152604001600020546001600160a01b031690508015611735576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020859052631fffffff600387901c16600090815260409020805460e0600589901b1681811c851863ffffffff16901b1890556117c08a83898961177881613f47565b9a508163ffffffff168160201b17846020528360021c60005260406000206003851660061b815467ffffffffffffffff8482841c188116831b82188455505050505050505050565b600882901c600090815260208a9052604090205460ff83161c60011615158c151514611812576020899052600882901c6000908152604090208054600160ff851690811b199091168e151590911b1790555b505060010161169f565b50875481908990600c906118479084906c01000000000000000000000000900463ffffffff16613ebe565b92506101000a81548163ffffffff021916908363ffffffff16021790555088156118e857845463ffffffff8085166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff909216919091178655600d8901805483926000916118c591859116613ebe565b92506101000a81548163ffffffff021916908363ffffffff16021790555061192a565b84547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000063ffffffff8516021785555b30600090815260038901602090815260408083206001600160a01b038f1684529091529020541580159061195c575088155b156119a3578088600d0160048282829054906101000a900463ffffffff166119849190613ebe565b92506101000a81548163ffffffff021916908363ffffffff1602179055505b60006119b882683635c9adc5dea00000613f6a565b89549091506000906119ec9070010000000000000000000000000000000090046bffffffffffffffffffffffff1683613cc0565b9050606081901c683635c9adc5dea00000820463fffffffe10171515828210811715611a44576040517fe5cfe95700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8a546bffffffffffffffffffffffff808416700100000000000000000000000000000000027fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff909216919091178c55885484918a91601491611ac19185917401000000000000000000000000000000000000000090910416613f81565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505050806000528b60601b60601c60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a3505050505050505050505050565b6001600160a01b038216611b72576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b68a20d6e21d0e5255308611b87818480612b16565b6000611b9284612938565b805484516001600160a01b0387166000908152600886016020908152604080832060078901909252822094955063ffffffff6c0100000000000000000000000085048116957001000000000000000000000000000000009095041693919291600a880191600c890191855b85811015611d8b5760008c8281518110611c1957611c19613ef8565b6020026020010151905060008c6002016000611c39896109cb8660011b90565b63ffffffff1681526020810191909152604001600020546001600160a01b0390811691508f168114611c97576040517f59c896be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ca888600185811b016109cb565b600884901c60009081526020899052604090205490915060ff84161c60011615611d15576020879052600883901c6000908152604090208054600160ff86161b19169055611d1086898363ffffffff168f611d0290613f27565b9f508f63ffffffff16612a8a565b611d44565b611d38858963ffffffff8416611d2a8f613f27565b9e508e63ffffffff16612a8a565b611d418a613fa6565b99505b611d4e8e846129b5565b6020889052600283901c600090815260409020805460c0600686901b1681811c67ffffffffffffffff16901b189055505050806001019050611bfd565b505050838303915050818314611e1857855463ffffffff8087166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff909216919091178755600d880180548083168490039092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009092169190911790555b8215611e605785547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000063ffffffff8616021786555b30600090815260038801602090815260408083206001600160a01b038d16845290915290205415801590611e945750600083115b15611ede57600d8701805463ffffffff6401000000008083048216879003909116027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9091161790555b506000611ef482683635c9adc5dea00000613f6a565b87546bffffffffffffffffffffffff70010000000000000000000000000000000063ffffffff6c010000000000000000000000008085048216889003909116027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff84168117829004831685900383169091027fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff9091167fffffffff00000000000000000000000000000000ffffffffffffffffffffffff9093169290921791909117895587547401000000000000000000000000000000000000000080820483168490039092169091026001600160a01b0391821617885560008281529192508a167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a3505050505050505050565b600061203182612e16565b612067576040517fceea21b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61207082612076565b92915050565b600068a20d6e21d0e525530868a20d6e21d0e525530a826120a468a20d6e21d0e5255312600187901b6109cb565b63ffffffff1681526020810191909152604001600020546001600160a01b03169392505050565b600068a20d6e21d0e525530868a20d6e21d0e525530a826120f968a20d6e21d0e5255312600188901b6109cb565b63ffffffff1681526020810191909152604001600020546001600160a01b03908116925083168214612187576001600160a01b03808416600090815260038301602090815260408083209386168352929052908120549003612187576040517fcfb3b94200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152600482016020908152604080832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038a1690811790915560058501909252600887901c83529091208054600160ff881690811b19909116921515901b919091179055509392505050565b606068a20d6e21d0e52553086000808461225c576001600160a01b03881660009081526007840160209081526040808320600b870190925290912054700100000000000000000000000000000000900463ffffffff1661229b565b6001600160a01b03881660009081526008840160209081526040808320600b8701909252909120546c01000000000000000000000000900463ffffffff165b6040516020839052955090925063ffffffff1686811887821102189050865b818110156122f9578060031c600052604060002080546007831660051b1c63ffffffff1690508089830360051b602088010152506001810190506122ba565b9690960380845260051b8301602001604052509095945050505050565b600061232182612e16565b612357576040517fceea21b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600090815268a20d6e21d0e525530c60205260409020546001600160a01b031690565b6001600160a01b038116600090815268a20d6e21d0e52553136020526040812080546123d39063ffffffff6c010000000000000000000000008204811691700100000000000000000000000000000000900416613ebe565b63ffffffff169392505050565b6000546001600160a01b0316331480159061241d57503361241168a20d6e21d0e5255309546001600160a01b031690565b6001600160a01b031614155b1561246757335b6040517f118cdaa70000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024015b60405180910390fd5b6124718282612e33565b5050565b6000612482338484612ed4565b50600192915050565b612493612f36565b6001600160a01b03811630106000816124ac57826124ae565b305b90506000826124bd57306124bf565b835b90506000836124db576c7862c74490707c52727f6abd456124e9565b6b02206205d4d70b66e052639a5b6040517f13ead5620000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301528481166024830152610bb860448301526cffffffffffffffffffffffffff929092166064820181905292506000918816906313ead562906084016020604051808303816000875af1158015612578573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259c9190613fc0565b90506125a9816001612e33565b6125cd6125c668a20d6e21d0e5255309546001600160a01b031690565b6001612e33565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055505050505050565b6001600160a01b038316600090815268a20d6e21d0e525530e602090815260408083203384529091528120805468a20d6e21d0e52553089190600019811461268f5780851115612689576040517f13be252b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84810382555b61269a838888612b16565b6126a5878787612f4e565b5060019695505050505050565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604081208054670100000000000000900460011682036126f257823b5b9392505050565b546701000000000000009004600216151592915050565b60006127153383612e33565b506001919050565b612725612f36565b61272f6000613966565b565b6000546001600160a01b0316331480159061276e57503361276268a20d6e21d0e5255309546001600160a01b031690565b6001600160a01b031614155b156127795733612424565b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff169055565b600068a20d6e21d0e52553086127ba813386612b16565b6127c5338585612f4e565b5060019392505050565b6000546001600160a01b0316331480159061280c57503361280068a20d6e21d0e5255309546001600160a01b031690565b6001600160a01b031614155b156128175733612424565b600061282c683635c9adc5dea0000083613f6a565b30600090815268a20d6e21d0e52553136020526040812054919250907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1690508060000361287d57505050565b80821115612889578091505b6128ad306128a768a20d6e21d0e5255309546001600160a01b031690565b84612f4e565b50505b50565b60606128be826139ce565b6040516020016128ce9190613fdd565b6040516020818303038152906040529050919050565b6128ec612f36565b6001600160a01b03811661292f576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526000600482015260240161245e565b6128b081613966565b6001600160a01b038116600090815268a20d6e21d0e5255313602052604081208054909167010000000000000090910460011690036129b05780547fffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff1667010000000000000060ff843b151560020260011716021781555b919050565b600881901c600090815260058301602052604090205460ff82161c600116156124715760058201602052600881901c6000908152604090208054600160ff84161b191690556000818152600483016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555050565b6000818152600f8301602052604090205463ffffffff1615612471576000818152600f83016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555050565b808214612b1057600381811c6000908152602086815260408083205486851c84528184208054600589811b60e090811683811c8b841b83169690961c63ffffffff81811697909718871690911b909318909355948a9052600190811b6401fffffffe16019586901c85529190932080549490921b1683811c861890921690911b90911890555b50505050565b30600090815260038401602090815260408083206001600160a01b038616845291829052822054909190819015612c0057600d8601546001600160a01b0386166000908152600b880160205260409020805460018201546a01000000000000000000009093046bffffffffffffffffffffffff9081169550919270010000000000000000000000000000000090910463ffffffff1691612bb7911685614022565b612bc19190613f6a565b60019190910180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff851617905590505b846001600160a01b0316846001600160a01b031614612d15576001600160a01b03841660009081526020849052604090205415612d155781600003612c6357600d8601546a010000000000000000000090046bffffffffffffffffffffffff1691505b6001600160a01b0384166000908152600b8701602052604081208054600182015491929170010000000000000000000000000000000090910463ffffffff1690612cbb906bffffffffffffffffffffffff1686614022565b612cc59190613f6a565b6001830180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff871617905590508015612d1257612d12308783612f4e565b50505b8015612d2657612d26308683612f4e565b505050505050565b815468010000000000000000900463ffffffff1668a20d6e21d0e52553086000829003612e0f5780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000008116600163ffffffff92831601918216908117835585547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff166801000000000000000082021786556000818152600284016020526040812080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0388161790559193509003612e0f57600080fd5b5092915050565b600080612e2283612076565b6001600160a01b0316141592915050565b6000612e3e83612938565b80549091506701000000000000009004600216151582151514612e9c57805460ff6701000000000000008083048216600218909116027fffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff9091161781555b8115156000528260601b60601c7fb5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d642039360206000a2505050565b6001600160a01b03838116600081815268a20d6e21d0e525530e6020908152604080832094871680845294825282208581558583529392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a350505050565b6000546001600160a01b0316331461272f5733612424565b6001600160a01b038216612f8e576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f9782613a6e565b68a20d6e21d0e52553086000612fac85612938565b90506000612fb985612938565b905061300a6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b825470010000000000000000000000000000000080820463ffffffff908116608085015284548290041660a08401528554046bffffffffffffffffffffffff90811660c08401527401000000000000000000000000000000000000000090910416604082018190528511156130ab576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040810180518690039081905283546c01000000000000000000000000900463ffffffff1660e08301819052683635c9adc5dea0000002111561311a576040517f2e33c5e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604081015183546bffffffffffffffffffffffff918216740100000000000000000000000000000000000000009081026001600160a01b039283161786558454818104841689016060860181905290931602911617825560e081015160808201516131ac9101613190683635c9adc5dea0000090565b8360400151816131a2576131a2614035565b0480821191030290565b81528154670100000000000000900460021660000361323f57856001600160a01b0316876001600160a01b0316036131ec57805160808201510360a08201525b613239683635c9adc5dea0000082606001518161320b5761320b614035565b845460a085015163ffffffff6c01000000000000000000000000909204919091160191900481810391100290565b60208201525b30600090815260038501602090815260408083206001600160a01b038b1684529182905290912054156132b7578151600d8601805463ffffffff640100000000808304821694909403169092027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9092169190911790555b6001600160a01b03871660009081526020829052604090205415613322576020820151600d8601805463ffffffff6401000000008083048216909401169092027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9092169190911790555b5080516020820151855463ffffffff6c0100000000000000000000000080830482168401859003909116027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff909116178655600a8601916000918082189082110218905080156135a4578251819003835260208301805182900390526001600160a01b03808916908a16036133c05760a083018051820190526135a4565b604080516080810182526000808252602080830182815283850183815260608086019081528651888152600589901b810185019097528690526001600160a01b03808f169091528e1690529290920181526001600160a01b03808c16600090815260078a016020526040808220928d1682528120929350909190613444888d612d2e565b90505b6080870180516000190190819052600381901c600090815260208590526040812054909160051b60e0161c63ffffffff1690506134b8838960a0015183826020528160031c60005260406000206007831660051b815463ffffffff8482841c188116831b8218845550505050505050565b60a088018051600181019091526020888152600283901c600090815260409020805460c0600686901b1681811c9490931b63ffffffff8716179390931867ffffffffffffffff1690911b90911890556135118b826129b5565b84518181526020018552506000198501946000036001016134475760018a01546135459085906001600160a01b0316613adc565b5050506080840151865463ffffffff9182167001000000000000000000000000000000009081027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff92831617895560a087015188549316029116178555505b5060006135ba8360200151846000015101613b2c565b8654845191925068010000000000000000900463ffffffff1690156137115760608a901b60011760208301526001600160a01b038a166000908152600788016020526040902060808501518551885463ffffffff918303918216700100000000000000000000000000000000027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff9091161789555b60001991909101600381901c600090815260208490526040812054919291600584901b60e0161c6020889052633fffffff600282901c16600090815260409020805460c0600684901b1681811c67ffffffffffffffff16901b18905563ffffffff16905060098b01602052600385901c600090815260409020805460e0600588901b1681811c841863ffffffff16901b1890556001909401936136f28b826129b5565b8551602080880151600884901b1782520186525080820361364f575050505b6020840151156138c557606089901b602083015286546001600160a01b038a1660009081526007890160209081526040822060a08801519188015164010000000090940463ffffffff169390929082019061376c8a8f612d2e565b8a547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000063ffffffff851602178b5590505b600086156137ee5760001996909601600381901c600090815260098e016020526040902054909690600588901b60e0161c63ffffffff1690506137f5565b5060018501945b6020859052600384901c600090815260409020805460e0600587901b1681811c841863ffffffff16901b1890556020898152600282901c600090815260409020805460c0600685901b1681811c9388901b63ffffffff8716179390931867ffffffffffffffff1690921b909118905560019093019287516020808a0151600884901b178252018852508183036137b0575050895463ffffffff909316640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff90931692909217895550505b604082015151156139225786547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff166801000000000000000063ffffffff83160217875560018701546139229083906001600160a01b0316613b73565b876000528860601b60601c8a60601b60601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a350505050505050505050565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b606060006139db83613baf565b600101905060008167ffffffffffffffff8111156139fb576139fb614064565b6040519080825280601f01601f191660200182016040528015613a25576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8504945084613a2f57509392505050565b60015474010000000000000000000000000000000000000000900460ff168015613aa557506001546001600160a01b038281169116145b156128b0576040517f86122ad400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060820151805160051b60840160808203915063144027d38252836020015160208301528360400151604083015260608083015260208282601c85016000875af1600183511416612b1057600082fd5b613b5060405180606001604052806000815260200160008152602001606081525090565b604051828152806020018360051b81016040528183604001528083525050919050565b60408201516040810363263c69d68152602080820152815160051b604401915060208183601c84016000875af1600182511416612b1057600081fd5b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310613bf8577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310613c24576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310613c4257662386f26fc10000830492506010015b6305f5e1008310613c5a576305f5e100830492506008015b6127108310613c6e57612710830492506004015b60648310613c80576064830492506002015b600a83106120705760010192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561207057612070613c91565b60005b83811015613cee578181015183820152602001613cd6565b50506000910152565b6020815260008251806020840152613d16816040850160208701613cd3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6001600160a01b03811681146128b057600080fd5b803580151581146129b057600080fd5b60008060408385031215613d8057600080fd5b8235613d8b81613d48565b9150613d9960208401613d5d565b90509250929050565b60008060408385031215613db557600080fd5b8235613dc081613d48565b946020939093013593505050565b60008060408385031215613de157600080fd5b8235613dec81613d48565b91506020830135613dfc81613d48565b809150509250929050565b600080600060608486031215613e1c57600080fd5b8335613e2781613d48565b92506020840135613e3781613d48565b929592945050506040919091013590565b600060208284031215613e5a57600080fd5b81356126eb81613d48565b600060208284031215613e7757600080fd5b6126eb82613d5d565b600060208284031215613e9257600080fd5b5035919050565b6bffffffffffffffffffffffff828116828216039080821115612e0f57612e0f613c91565b63ffffffff818116838216019080821115612e0f57612e0f613c91565b63ffffffff828116828216039080821115612e0f57612e0f613c91565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600063ffffffff821680613f3d57613f3d613c91565b6000190192915050565b600063ffffffff808316818103613f6057613f60613c91565b6001019392505050565b808202811582820484141761207057612070613c91565b6bffffffffffffffffffffffff818116838216019080821115612e0f57612e0f613c91565b60006000198203613fb957613fb9613c91565b5060010190565b600060208284031215613fd257600080fd5b81516126eb81613d48565b7f68747470733a2f2f6170692e626c617374696e70657065732e78797a2f6d642f815260008251614015816020850160208701613cd3565b9190910160200192915050565b8181038181111561207057612070613c91565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea2646970667358221220fa07488a4ba45a800d6833c12e2647064dfa00c0a6f97657047d896311a74fd664736f6c63430008180033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000000cd7e55f61d393866db74afd6fdf49d81b82b9be

-----Decoded View---------------
Arg [0] : _mirrorERC721 (address): 0x0cd7e55f61D393866db74afd6fdf49D81b82b9Be

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000000cd7e55f61d393866db74afd6fdf49d81b82b9be


[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.