ETH Price: $3,462.05 (+4.35%)

Contract

0xfFe53829e5d367D8e3671bBfb95fe0E024f0d8b6
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Artworks212395542024-11-22 0:01:1139 hrs ago1732233671IN
0xfFe53829...024f0d8b6
0 ETH0.000545211.95728756
Set Artwork212392622024-11-21 23:02:1140 hrs ago1732230131IN
0xfFe53829...024f0d8b6
0 ETH0.0004959514.19070076
Set Artworks212335012024-11-21 3:43:472 days ago1732160627IN
0xfFe53829...024f0d8b6
0 ETH0.00039969.79520666
Set Artwork212288272024-11-20 12:03:353 days ago1732104215IN
0xfFe53829...024f0d8b6
0 ETH0.000401611.49504078
Set Artwork212246002024-11-19 21:54:353 days ago1732053275IN
0xfFe53829...024f0d8b6
0 ETH0.000590116.88480314
Set Artwork212216012024-11-19 11:52:474 days ago1732017167IN
0xfFe53829...024f0d8b6
0 ETH0.0003914711.2013819
Set Artwork212027462024-11-16 20:47:596 days ago1731790079IN
0xfFe53829...024f0d8b6
0 ETH0.0004113811.7710159
Set Artwork211772942024-11-13 7:31:4710 days ago1731483107IN
0xfFe53829...024f0d8b6
0 ETH0.0006417618.36292591
Set Artwork211770952024-11-13 6:51:5910 days ago1731480719IN
0xfFe53829...024f0d8b6
0 ETH0.0006669719.08430523
Set Artwork211770332024-11-13 6:39:2310 days ago1731479963IN
0xfFe53829...024f0d8b6
0 ETH0.0008748125.03969135
Set Artworks211709792024-11-12 10:23:1111 days ago1731406991IN
0xfFe53829...024f0d8b6
0 ETH0.0055007436.93161151
Set Artworks211602252024-11-10 22:21:3512 days ago1731277295IN
0xfFe53829...024f0d8b6
0 ETH0.0007808117.12461323
Set Artwork211488242024-11-09 8:12:4714 days ago1731139967IN
0xfFe53829...024f0d8b6
0 ETH0.0004283412.26061515
Set Artwork211087152024-11-03 17:50:2319 days ago1730656223IN
0xfFe53829...024f0d8b6
0 ETH0.000179525.1366744
Set Artworks210890432024-10-31 23:54:5922 days ago1730418899IN
0xfFe53829...024f0d8b6
0 ETH0.000471598.5838738
Set Artworks210759672024-10-30 4:06:3524 days ago1730261195IN
0xfFe53829...024f0d8b6
0 ETH0.0005574412.22582434
Set Artwork210412702024-10-25 7:55:3529 days ago1729842935IN
0xfFe53829...024f0d8b6
0 ETH0.000239396.85461233
Set Artworks210412602024-10-25 7:53:3529 days ago1729842815IN
0xfFe53829...024f0d8b6
0 ETH0.000332177.28705228
Set Artworks210214992024-10-22 13:44:4732 days ago1729604687IN
0xfFe53829...024f0d8b6
0 ETH0.0023133218.37724824
Set Artwork210109632024-10-21 2:27:5933 days ago1729477679IN
0xfFe53829...024f0d8b6
0 ETH0.000332539.51823882
Set Artwork209891912024-10-18 1:33:3536 days ago1729215215IN
0xfFe53829...024f0d8b6
0 ETH0.0005023514.37875901
Set Artwork209766012024-10-16 7:22:2338 days ago1729063343IN
0xfFe53829...024f0d8b6
0 ETH0.0003850611.02167838
Set Artwork209696002024-10-15 7:53:2339 days ago1728978803IN
0xfFe53829...024f0d8b6
0 ETH0.000473313.54275729
Set Artwork209658312024-10-14 19:15:4739 days ago1728933347IN
0xfFe53829...024f0d8b6
0 ETH0.0009635327.56976302
Set Artworks209300902024-10-09 19:20:2344 days ago1728501623IN
0xfFe53829...024f0d8b6
0 ETH0.0133861325.65283631
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MoonbirdsRenderer

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 7 : MoonbirdsRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Ownable} from "solady/auth/Ownable.sol";
import {LibString} from "solady/utils/LibString.sol";
import {LibBitmap} from "solady/utils/LibBitmap.sol";
import {ILegacyMoonbirdsRenderer, Attribute} from "../interfaces/ILegacyMoonbirdsRenderer.sol";
import {IERC721Ownership} from "../interfaces/IERC721Ownership.sol";

contract MoonbirdsRenderer is Ownable {
    using LibString for uint256;
    using LibBitmap for LibBitmap.Bitmap;

    error NotOwnerOfToken(uint256 tokenId);
    error ArrayLengthMismatch();

    ILegacyMoonbirdsRenderer public immutable legacyRenderer;
    IERC721Ownership public immutable moonbirds;

    string public baseUri;
    string public animationBaseUri;

    LibBitmap.Bitmap private _useNewArtwork;

    uint32 private _scaleFactor = 15;

    constructor(
        address moonbirds_,
        address legacyRenderer_,
        string memory baseUri_,
        string memory animationBaseUri_
    ) {
        _initializeOwner(msg.sender);
        moonbirds = IERC721Ownership(moonbirds_);
        legacyRenderer = ILegacyMoonbirdsRenderer(legacyRenderer_);
        baseUri = baseUri_;
        animationBaseUri = animationBaseUri_;
    }

    function tokenURI(uint256 tokenId) external view returns (string memory) {
        string memory tokenIdString = tokenId.toString();

        return
            string.concat(
                'data:application/json;utf-8,{"name":"Moonbirds #',
                tokenIdString,
                '","external_url":"https://proof.xyz/moonbirds/',
                tokenIdString,
                '","animation_url":"',
                animationUri(tokenId),
                '","image":"',
                artworkUri(tokenId),
                '","alternate_image":"',
                alternateArtworkUri(tokenId),
                '","attributes":',
                attributesJson(tokenId),
                "}"
            );
    }

    function attributesJson(
        uint256 tokenId
    ) public view returns (string memory) {
        Attribute[] memory attrs = legacyRenderer.attributes(tokenId);

        string memory result = "[";
        for (uint256 i = 0; i < attrs.length; i++) {
            string memory comma = i == 0 ? "" : ",";
            string memory end = i == attrs.length - 1 ? "]" : "";
            result = string.concat(
                result,
                comma,
                '{"trait_type":"',
                attrs[i].name,
                '","value":"',
                attrs[i].value,
                '"}',
                end
            );
        }
        return result;
    }

    function artworkUri(uint256 tokenId) public view returns (string memory) {
        if (_useNewArtwork.get(tokenId)) {
            return
                string(abi.encodePacked(baseUri, tokenId.toString(), ".png"));
        }
        return legacyRenderer.artworkURI(tokenId, _scaleFactor);
    }

    function alternateArtworkUri(
        uint256 tokenId
    ) public view returns (string memory) {
        if (_useNewArtwork.get(tokenId)) {
            return legacyRenderer.artworkURI(tokenId, _scaleFactor);
        }

        return string(abi.encodePacked(baseUri, tokenId.toString(), ".png"));
    }

    function animationUri(uint256 tokenId) public view returns (string memory) {
        string memory useNew = _useNewArtwork.get(tokenId) ? "true" : "false";

        return
            string(
                abi.encodePacked(
                    animationBaseUri,
                    "?tokenId=",
                    tokenId.toString(),
                    "&useNewArtwork=",
                    useNew
                )
            );
    }

    function setArtwork(uint256 tokenId, bool useNew) public {
        if (moonbirds.ownerOf(tokenId) != msg.sender) {
            revert NotOwnerOfToken(tokenId);
        }
        _useNewArtwork.setTo(tokenId, useNew);
    }

    function setArtworks(
        uint256[] calldata tokenIds,
        bool[] calldata values
    ) external {
        uint256 length = tokenIds.length;
        if (length != values.length) {
            revert ArrayLengthMismatch();
        }

        for (uint256 i = 0; i < length; ++i) {
            setArtwork(tokenIds[i], values[i]);
        }
    }

    function setBaseUri(string memory baseUri_) external onlyOwner {
        baseUri = baseUri_;
    }

    function useNewArtwork(uint256 tokenId) external view returns (bool) {
        return _useNewArtwork.get(tokenId);
    }

    function setAnimationBaseUri(
        string memory animationBaseUri_
    ) external onlyOwner {
        animationBaseUri = animationBaseUri_;
    }

    function setScaleFactor(uint32 scaleFactor) external onlyOwner {
        _scaleFactor = scaleFactor;
    }
}

File 2 of 7 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

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

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

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

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

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

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 3 of 7 : LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The length of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /// @dev The length of the string is more than 32 bytes.
    error TooBigForSmallString();

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

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = type(uint256).max;

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

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits.
            str := add(mload(0x40), 0x80)
            // Update the free memory pointer to allocate.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            let w := not(0) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 1)`.
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory str) {
        if (value >= 0) {
            return toString(uint256(value));
        }
        unchecked {
            str = toString(~uint256(value) + 1);
        }
        /// @solidity memory-safe-assembly
        assembly {
            // We still have some spare memory space on the left,
            // as we have allocated 3 words (96 bytes) for up to 78 digits.
            let length := mload(str) // Load the string length.
            mstore(str, 0x2d) // Store the '-' character.
            str := sub(str, 1) // Move back the string pointer by a byte.
            mstore(str, add(length, 1)) // Update the string length.
        }
    }

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

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexStringNoPrefix(uint256 value, uint256 length)
        internal
        pure
        returns (string memory str)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let start := sub(str, add(length, length))
            let w := not(1) // Tsk.
            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {} 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(str, start)) { break }
            }

            if temp {
                mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                revert(0x1c, 0x04)
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x".
    /// The output excludes leading "0" from the `toHexString` output.
    /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
    function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
            str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
            mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output excludes leading "0" from the `toHexStringNoPrefix` output.
    /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
    function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
            let strLength := mload(str) // Get the length.
            str := add(str, o) // Move the pointer, accounting for leading zero.
            mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2` bytes.
    function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            str := add(mload(0x40), 0x80)
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let w := not(1) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(temp) { break }
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksummed(address value) internal pure returns (string memory str) {
        str = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(str, 0x22)
            let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
            let t := shl(240, 136) // `0b10001000 << 240`
            for { let i := 0 } 1 {} {
                mstore(add(i, i), mul(t, byte(i, hashed)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
            mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
            o := add(o, 0x20)
            mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            str := mload(0x40)

            // Allocate the memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(str, 0x80))

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            str := add(str, 2)
            mstore(str, 40)

            let o := add(str, 0x20)
            mstore(add(o, 40), 0)

            value := shl(96, value)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let i := 0 } 1 {} {
                let p := add(o, add(i, i))
                let temp := byte(i, value)
                mstore8(add(p, 1), mload(and(temp, 15)))
                mstore8(p, mload(shr(4, temp)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexString(bytes memory raw) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(raw)
            str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(str, add(length, length)) // Store the length of the output.

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let o := add(str, 0x20)
            let end := add(raw, length)

            for {} iszero(eq(raw, end)) {} {
                raw := add(raw, 1)
                mstore8(add(o, 1), mload(and(mload(raw), 15)))
                mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                o := add(o, 2)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate the memory.
        }
    }

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

    /// @dev Returns the number of UTF characters in the string.
    function runeCount(string memory s) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                mstore(0x00, div(not(0), 255))
                mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for { result := 1 } 1 { result := add(result, 1) } {
                    o := add(o, byte(0, mload(shr(250, mload(o)))))
                    if iszero(lt(o, end)) { break }
                }
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string.
    /// (i.e. all characters codes are in [0..127])
    function is7BitASCII(string memory s) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(7, div(not(0), 255))
            result := 1
            let n := mload(s)
            if n {
                let o := add(s, 0x20)
                let end := add(o, n)
                let last := mload(end)
                mstore(end, 0)
                for {} 1 {} {
                    if and(mask, mload(o)) {
                        result := 0
                        break
                    }
                    o := add(o, 0x20)
                    if iszero(lt(o, end)) { break }
                }
                mstore(end, last)
            }
        }
    }

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

    // For performance and bytecode compactness, byte string operations are restricted
    // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
    // Usage of byte string operations on charsets with runes spanning two or more bytes
    // can lead to undefined behavior.

    /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
    function replace(string memory subject, string memory search, string memory replacement)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        for { let o := 0 } 1 {} {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            if iszero(lt(o, replacementLength)) { break }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            for {} lt(subject, subjectEnd) {} {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
            mstore(last, 0)
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
            mstore(result, k) // Store the length.
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let subjectLength := mload(subject) } 1 {} {
                if iszero(mload(search)) {
                    if iszero(gt(from, subjectLength)) {
                        result := from
                        break
                    }
                    result := subjectLength
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)

                result := not(0) // Initialize to `NOT_FOUND`.

                subject := add(subjectStart, from)
                let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)

                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(add(search, 0x20))

                if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }

                if iszero(lt(searchLength, 0x20)) {
                    for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        if iszero(lt(subject, end)) { break }
                    }
                    break
                }
                for {} 1 {} {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                result := not(0) // Initialize to `NOT_FOUND`.
                let searchLength := mload(search)
                if gt(searchLength, mload(subject)) { break }
                let w := result

                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) { from := fromMax }

                let end := add(add(subject, 0x20), w)
                subject := add(add(subject, 0x20), from)
                if iszero(gt(subject, end)) { break }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(end, 1))
                        break
                    }
                    subject := add(subject, w) // `sub(subject, 1)`.
                    if iszero(gt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns true if `search` is found in `subject`, false otherwise.
    function contains(string memory subject, string memory search) internal pure returns (bool) {
        return indexOf(subject, search) != NOT_FOUND;
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(
                    keccak256(add(subject, 0x20), searchLength),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    for { let o := 0 } 1 {} {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        if iszero(lt(o, subjectLength)) { break }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    if iszero(times) { break }
                }
                mstore(output, 0) // Zeroize the slot after the string.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength) // Store the length.
                // Allocate the memory.
                mstore(0x40, add(result, add(resultLength, 0x20)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(string memory subject, uint256 start, uint256 end)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) { end := subjectLength }
            if iszero(gt(subjectLength, start)) { start := subjectLength }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                let w := not(0x1f)
                // Copy the `subject` one word at a time, backwards.
                for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start)
        internal
        pure
        returns (string memory result)
    {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256[] memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter)
        internal
        pure
        returns (string[] memory result)
    {
        uint256[] memory indices = indicesOf(subject, delimiter);
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            let indexPtr := add(indices, 0x20)
            let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
            mstore(add(indicesEnd, w), mload(subject))
            mstore(indices, add(mload(indices), 1))
            let prevIndex := 0
            for {} 1 {} {
                let index := mload(indexPtr)
                mstore(indexPtr, 0x60)
                if iszero(eq(index, prevIndex)) {
                    let element := mload(0x40)
                    let elementLength := sub(index, prevIndex)
                    mstore(element, elementLength)
                    // Copy the `subject` one word at a time, backwards.
                    for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
                        mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                        o := add(o, w) // `sub(o, 0x20)`.
                        if iszero(o) { break }
                    }
                    // Zeroize the slot after the string.
                    mstore(add(add(element, 0x20), elementLength), 0)
                    // Allocate memory for the length and the bytes,
                    // rounded up to a multiple of 32.
                    mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
                    // Store the `element` into the array.
                    mstore(indexPtr, element)
                }
                prevIndex := add(index, mload(delimiter))
                indexPtr := add(indexPtr, 0x20)
                if iszero(lt(indexPtr, indicesEnd)) { break }
            }
            result := indices
            if iszero(mload(delimiter)) {
                result := add(indices, 0x20)
                mstore(result, sub(mload(indices), 2))
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            for { let o := and(add(aLength, 0x20), w) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let bLength := mload(b)
            let output := add(result, aLength)
            // Copy `b` one word at a time, backwards.
            for { let o := and(add(bLength, 0x20), w) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Stores the length.
            mstore(result, totalLength)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 0x1f), w))
        }
    }

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function toCase(string memory subject, bool toUpper)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(subject)
            if length {
                result := add(mload(0x40), 0x20)
                subject := add(subject, 1)
                let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                let w := not(0)
                for { let o := length } 1 {} {
                    o := add(o, w)
                    let b := and(0xff, mload(add(subject, o)))
                    mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                    if iszero(o) { break }
                }
                result := mload(0x40)
                mstore(result, length) // Store the length.
                let last := add(add(result, 0x20), length)
                mstore(last, 0) // Zeroize the slot after the string.
                mstore(0x40, add(last, 0x20)) // Allocate the memory.
            }
        }
    }

    /// @dev Returns a string from a small bytes32 string.
    /// `s` must be null-terminated, or behavior will be undefined.
    function fromSmallString(bytes32 s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let n := 0
            for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
            mstore(result, n)
            let o := add(result, 0x20)
            mstore(o, s)
            mstore(add(o, n), 0)
            mstore(0x40, add(result, 0x40))
        }
    }

    /// @dev Returns the small string, with all bytes after the first null byte zeroized.
    function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
            mstore(0x00, s)
            mstore(result, 0x00)
            result := mload(0x00)
        }
    }

    /// @dev Returns the string as a normalized null-terminated small string.
    function toSmallString(string memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(s)
            if iszero(lt(result, 33)) {
                mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
                revert(0x1c, 0x04)
            }
            result := shl(shl(3, sub(32, result)), mload(add(s, result)))
        }
    }

    /// @dev Returns a lowercased copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function lower(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function upper(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, true);
    }

    /// @dev Escapes the string to be used within HTML tags.
    function escapeHTML(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(s, mload(s))
            result := add(mload(0x40), 0x20)
            // Store the bytes of the packed offsets and strides into the scratch space.
            // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
            mstore(0x1f, 0x900094)
            mstore(0x08, 0xc0000000a6ab)
            // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
            mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) {
                    mstore8(result, c)
                    result := add(result, 1)
                    continue
                }
                let t := shr(248, mload(c))
                mstore(result, mload(and(t, 0x1f)))
                result := add(result, shr(5, t))
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
    function escapeJSON(string memory s, bool addDoubleQuotes)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(s, mload(s))
            result := add(mload(0x40), 0x20)
            if addDoubleQuotes {
                mstore8(result, 34)
                result := add(1, result)
            }
            // Store "\\u0000" in scratch space.
            // Store "0123456789abcdef" in scratch space.
            // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
            // into the scratch space.
            mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
            // Bitmask for detecting `["\"","\\"]`.
            let e := or(shl(0x22, 1), shl(0x5c, 1))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                if iszero(lt(c, 0x20)) {
                    if iszero(and(shl(c, 1), e)) {
                        // Not in `["\"","\\"]`.
                        mstore8(result, c)
                        result := add(result, 1)
                        continue
                    }
                    mstore8(result, 0x5c) // "\\".
                    mstore8(add(result, 1), c)
                    result := add(result, 2)
                    continue
                }
                if iszero(and(shl(c, 1), 0x3700)) {
                    // Not in `["\b","\t","\n","\f","\d"]`.
                    mstore8(0x1d, mload(shr(4, c))) // Hex value.
                    mstore8(0x1e, mload(and(c, 15))) // Hex value.
                    mstore(result, mload(0x19)) // "\\u00XX".
                    result := add(result, 6)
                    continue
                }
                mstore8(result, 0x5c) // "\\".
                mstore8(add(result, 1), mload(add(c, 8)))
                result := add(result, 2)
            }
            if addDoubleQuotes {
                mstore8(result, 34)
                result := add(1, result)
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(string memory s) internal pure returns (string memory result) {
        result = escapeJSON(s, false);
    }

    /// @dev Returns whether `a` equals `b`.
    function eq(string memory a, string memory b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
        }
    }

    /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
    function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // These should be evaluated on compile time, as far as possible.
            let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
            let x := not(or(m, or(b, add(m, and(b, m)))))
            let r := shl(7, iszero(iszero(shr(128, x))))
            r := or(r, shl(6, iszero(iszero(shr(64, 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
            result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
                xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
        }
    }

    /// @dev Packs a single string with its length into a single word.
    /// Returns `bytes32(0)` if the length is zero or greater than 31.
    function packOne(string memory a) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // We don't need to zero right pad the string,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    // Load the length and the bytes.
                    mload(add(a, 0x1f)),
                    // `length != 0 && length < 32`. Abuses underflow.
                    // Assumes that the length is valid and within the block gas limit.
                    lt(sub(mload(a), 1), 0x1f)
                )
        }
    }

    /// @dev Unpacks a string packed using {packOne}.
    /// Returns the empty string if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packOne}, the output behavior is undefined.
    function unpackOne(bytes32 packed) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            result := mload(0x40)
            // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(0x40, add(result, 0x40))
            // Zeroize the length slot.
            mstore(result, 0)
            // Store the length and bytes.
            mstore(add(result, 0x1f), packed)
            // Right pad with zeroes.
            mstore(add(add(result, 0x20), mload(result)), 0)
        }
    }

    /// @dev Packs two strings with their lengths into a single word.
    /// Returns `bytes32(0)` if combined length is zero or greater than 30.
    function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let aLength := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    // Load the length and the bytes of `a` and `b`.
                    or(
                        shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
                        mload(sub(add(b, 0x1e), aLength))
                    ),
                    // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                    // Assumes that the lengths are valid and within the block gas limit.
                    lt(sub(add(aLength, mload(b)), 1), 0x1e)
                )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behavior is undefined.
    function unpackTwo(bytes32 packed)
        internal
        pure
        returns (string memory resultA, string memory resultB)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            resultA := mload(0x40)
            resultB := add(resultA, 0x40)
            // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
            mstore(0x40, add(resultB, 0x40))
            // Zeroize the length slots.
            mstore(resultA, 0)
            mstore(resultB, 0)
            // Store the lengths and bytes.
            mstore(add(resultA, 0x1f), packed)
            mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
            // Right pad with zeroes.
            mstore(add(add(resultA, 0x20), mload(resultA)), 0)
            mstore(add(add(resultB, 0x20), mload(resultB)), 0)
        }
    }

    /// @dev Directly returns `a` without copying.
    function directReturn(string memory a) internal pure {
        assembly {
            // Assumes that the string does not start from the scratch space.
            let retStart := sub(a, 0x20)
            let retSize := add(mload(a), 0x40)
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(retStart, retSize), 0)
            // Store the return offset.
            mstore(retStart, 0x20)
            // End the transaction, returning the string.
            return(retStart, retSize)
        }
    }
}

File 4 of 7 : LibBitmap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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 5 of 7 : ILegacyMoonbirdsRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

struct Attribute {
    string name;
    string value;
}

interface ILegacyMoonbirdsRenderer {
    function artworkURI(uint256 tokenId, uint32 scaleFactor) external view returns (string memory);
    function attributes(uint256 tokenId) external view returns (Attribute[] memory);
}

File 6 of 7 : IERC721Ownership.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC721Ownership {
    function ownerOf(uint256 tokenId) external view returns (address);
    function balanceOf(address owner) external view returns (uint256);
}

File 7 of 7 : LibBit.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @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.
            let b := and(x, add(not(x), 1))

            r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, b)))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, b))))

            // For the remaining 32 bits, use a De Bruijn lookup.
            // forgefmt: disable-next-item
            r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
                0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
        }
    }

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

Settings
{
  "remappings": [
    "ds-test/=lib/erc721s/lib/forge-std/lib/ds-test/src/",
    "erc721a/=lib/erc721s/lib/erc721a/contracts/",
    "erc721s/=lib/erc721s/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin/=lib/erc721s/lib/openzeppelin/",
    "solady/=lib/solady/src/",
    "soladymocks/=lib/solady/test/utils/mocks/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"moonbirds_","type":"address"},{"internalType":"address","name":"legacyRenderer_","type":"address"},{"internalType":"string","name":"baseUri_","type":"string"},{"internalType":"string","name":"animationBaseUri_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotOwnerOfToken","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"alternateArtworkUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"animationBaseUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"animationUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"artworkUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"attributesJson","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"legacyRenderer","outputs":[{"internalType":"contract ILegacyMoonbirdsRenderer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"moonbirds","outputs":[{"internalType":"contract IERC721Ownership","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"animationBaseUri_","type":"string"}],"name":"setAnimationBaseUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"useNew","type":"bool"}],"name":"setArtwork","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"bool[]","name":"values","type":"bool[]"}],"name":"setArtworks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseUri_","type":"string"}],"name":"setBaseUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"scaleFactor","type":"uint32"}],"name":"setScaleFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"useNewArtwork","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60c06040526003805463ffffffff1916600f1790553480156200002157600080fd5b50604051620019b7380380620019b78339810160408190526200004491620001ab565b6200004f336200008a565b6001600160a01b0380851660a05283166080526000620000708382620002cc565b5060016200007f8282620002cc565b505050505062000398565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b80516001600160a01b0381168114620000de57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200010b57600080fd5b81516001600160401b0380821115620001285762000128620000e3565b604051601f8301601f19908116603f01168101908282118183101715620001535762000153620000e3565b81604052838152602092508660208588010111156200017157600080fd5b600091505b8382101562000195578582018301518183018401529082019062000176565b6000602085830101528094505050505092915050565b60008060008060808587031215620001c257600080fd5b620001cd85620000c6565b9350620001dd60208601620000c6565b60408601519093506001600160401b0380821115620001fb57600080fd5b6200020988838901620000f9565b935060608701519150808211156200022057600080fd5b506200022f87828801620000f9565b91505092959194509250565b600181811c908216806200025057607f821691505b6020821081036200027157634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620002c7576000816000526020600020601f850160051c81016020861015620002a25750805b601f850160051c820191505b81811015620002c357828155600101620002ae565b5050505b505050565b81516001600160401b03811115620002e857620002e8620000e3565b6200030081620002f984546200023b565b8462000277565b602080601f8311600181146200033857600084156200031f5750858301515b600019600386901b1c1916600185901b178555620002c3565b600085815260208120601f198616915b82811015620003695788860151825594840194600190910190840162000348565b5085821015620003885787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a0516115dd620003da600039600081816102640152610a6e01526000818161037b015281816104800152818161056c015261062201526115dd6000f3fe6080604052600436106101355760003560e01c80638a0349fb116100ab578063c87b56dd1161006f578063c87b56dd14610329578063cb36c11714610349578063de26bc3514610369578063f04e283e1461039d578063f2fde38b146103b0578063fee81cf4146103c357600080fd5b80638a0349fb146102a65780638da5cb5b146102bb5780639abc8320146102d4578063a0bcfc7f146102e9578063c443d4fa1461030957600080fd5b80633d44c286116100fd5780633d44c286146101da57806354d1f13d146101fa5780636b1e676b146102025780636c24a457146102325780636f7013a814610252578063715018a61461029e57600080fd5b80630dbc4e741461013a5780631680ed7b146101705780631db6151a1461019257806325692962146101b257806327b3a84b146101ba575b600080fd5b34801561014657600080fd5b5061015a610155366004610c3a565b610404565b6040516101679190610c77565b60405180910390f35b34801561017c57600080fd5b5061019061018b366004610caa565b6104fe565b005b34801561019e57600080fd5b5061015a6101ad366004610c3a565b610522565b6101906105ae565b3480156101c657600080fd5b5061015a6101d5366004610c3a565b6105fe565b3480156101e657600080fd5b506101906101f5366004610d23565b6107b1565b610190610830565b34801561020e57600080fd5b5061022261021d366004610c3a565b61086c565b6040519015158152602001610167565b34801561023e57600080fd5b5061015a61024d366004610c3a565b61088d565b34801561025e57600080fd5b506102867f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610167565b610190610929565b3480156102b257600080fd5b5061015a61093d565b3480156102c757600080fd5b50638b78c6d81954610286565b3480156102e057600080fd5b5061015a6109cb565b3480156102f557600080fd5b50610190610304366004610e27565b6109d8565b34801561031557600080fd5b50610190610324366004610e27565b6109f0565b34801561033557600080fd5b5061015a610344366004610c3a565b610a04565b34801561035557600080fd5b50610190610364366004610ebc565b610a4e565b34801561037557600080fd5b506102867f000000000000000000000000000000000000000000000000000000000000000081565b6101906103ab366004610efd565b610b36565b6101906103be366004610efd565b610b76565b3480156103cf57600080fd5b506103f66103de366004610efd565b63389a75e1600c908152600091909152602090205490565b604051908152602001610167565b600881901c60009081526002602052604090205460609060ff83161c6001161561045a57600061043383610b9d565b604051602001610444929190610fe3565b6040516020818303038152906040529050919050565b6003546040516359bd015360e01b81526004810184905263ffffffff90911660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906359bd0153906044015b600060405180830381865afa1580156104d0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104f89190810190611064565b92915050565b610506610be1565b6003805463ffffffff191663ffffffff92909216919091179055565b600881901c60009081526002602052604090205460609060ff83161c600116156105a3576003546040516359bd015360e01b81526004810184905263ffffffff90911660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906359bd0153906044016104b3565b600061043383610b9d565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b60405163682ee63560e11b8152600481018290526060906000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d05dcc6a90602401600060405180830381865afa158015610669573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106919190810190611099565b6040805180820190915260018152605b60f81b602082015290915060005b82518110156107a957600081156106df57604051806040016040528060018152602001600b60fa1b8152506106f0565b604051806020016040528060008152505b905060006001855161070291906111b7565b831461071d5760405180602001604052806000815250610738565b604051806040016040528060018152602001605d60f81b8152505b9050838286858151811061074e5761074e6111d8565b60200260200101516000015187868151811061076c5761076c6111d8565b6020026020010151602001518460405160200161078d9594939291906111ee565b60408051808303601f19018152919052935050506001016106af565b509392505050565b828181146107d25760405163512509d360e11b815260040160405180910390fd5b60005b81811015610828576108208686838181106107f2576107f26111d8565b9050602002013585858481811061080b5761080b6111d8565b9050602002016020810190610364919061129d565b6001016107d5565b505050505050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b600881901c60009081526002602052604081205460ff83161c6001166104f8565b600881901c6000908152600260205260408120546060919060ff84161c6001166108d4576040518060400160405280600581526020016466616c736560d81b8152506108f2565b604051806040016040528060048152602001637472756560e01b8152505b905060016108ff84610b9d565b82604051602001610912939291906112b8565b604051602081830303815290604052915050919050565b610931610be1565b61093b6000610bfc565b565b6001805461094a90610f1a565b80601f016020809104026020016040519081016040528092919081815260200182805461097690610f1a565b80156109c35780601f10610998576101008083540402835291602001916109c3565b820191906000526020600020905b8154815290600101906020018083116109a657829003601f168201915b505050505081565b6000805461094a90610f1a565b6109e0610be1565b60006109ec8282611370565b5050565b6109f8610be1565b60016109ec8282611370565b60606000610a1183610b9d565b90508081610a1e8561088d565b610a2786610404565b610a3087610522565b610a39886105fe565b60405160200161091296959493929190611430565b6040516331a9108f60e11b81526004810183905233906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa158015610ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad9919061158a565b6001600160a01b031614610b0757604051633b94a19960e01b81526004810183905260240160405180910390fd5b6002602052600882901c6000908152604090208054600160ff851690811b1990911683151590911b1790555050565b610b3e610be1565b63389a75e1600c52806000526020600c208054421115610b6657636f5e88186000526004601cfd5b60009055610b7381610bfc565b50565b610b7e610be1565b8060601b610b9457637448fbae6000526004601cfd5b610b7381610bfc565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480610bb8575050819003601f19909101908152919050565b638b78c6d81954331461093b576382b429006000526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b600060208284031215610c4c57600080fd5b5035919050565b60005b83811015610c6e578181015183820152602001610c56565b50506000910152565b6020815260008251806020840152610c96816040850160208701610c53565b601f01601f19169190910160400192915050565b600060208284031215610cbc57600080fd5b813563ffffffff81168114610cd057600080fd5b9392505050565b60008083601f840112610ce957600080fd5b50813567ffffffffffffffff811115610d0157600080fd5b6020830191508360208260051b8501011115610d1c57600080fd5b9250929050565b60008060008060408587031215610d3957600080fd5b843567ffffffffffffffff80821115610d5157600080fd5b610d5d88838901610cd7565b90965094506020870135915080821115610d7657600080fd5b50610d8387828801610cd7565b95989497509550505050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610dc857610dc8610d8f565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610df757610df7610d8f565b604052919050565b600067ffffffffffffffff821115610e1957610e19610d8f565b50601f01601f191660200190565b600060208284031215610e3957600080fd5b813567ffffffffffffffff811115610e5057600080fd5b8201601f81018413610e6157600080fd5b8035610e74610e6f82610dff565b610dce565b818152856020838501011115610e8957600080fd5b81602084016020830137600091810160200191909152949350505050565b80358015158114610eb757600080fd5b919050565b60008060408385031215610ecf57600080fd5b82359150610edf60208401610ea7565b90509250929050565b6001600160a01b0381168114610b7357600080fd5b600060208284031215610f0f57600080fd5b8135610cd081610ee8565b600181811c90821680610f2e57607f821691505b602082108103610f4e57634e487b7160e01b600052602260045260246000fd5b50919050565b60008154610f6181610f1a565b60018281168015610f795760018114610f8e57610fbd565b60ff1984168752821515830287019450610fbd565b8560005260208060002060005b85811015610fb45781548a820152908401908201610f9b565b50505082870194505b5050505092915050565b60008151610fd9818560208601610c53565b9290920192915050565b6000610fef8285610f54565b8351610fff818360208801610c53565b632e706e6760e01b9101908152600401949350505050565b600082601f83011261102857600080fd5b8151611036610e6f82610dff565b81815284602083860101111561104b57600080fd5b61105c826020830160208701610c53565b949350505050565b60006020828403121561107657600080fd5b815167ffffffffffffffff81111561108d57600080fd5b61105c84828501611017565b600060208083850312156110ac57600080fd5b825167ffffffffffffffff808211156110c457600080fd5b818501915085601f8301126110d857600080fd5b8151818111156110ea576110ea610d8f565b8060051b6110f9858201610dce565b918252838101850191858101908984111561111357600080fd5b86860192505b838310156111aa578251858111156111315760008081fd5b86016040818c03601f19018113156111495760008081fd5b611151610da5565b89830151888111156111635760008081fd5b6111718e8c83870101611017565b8252509082015190878211156111875760008081fd5b6111958d8b84860101611017565b818b0152845250509186019190860190611119565b9998505050505050505050565b818103818111156104f857634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008651611200818460208b01610c53565b865190830190611214818360208b01610c53565b6e3d913a3930b4ba2fba3cb832911d1160891b9101908152855161123f81600f840160208a01610c53565b6a1116113b30b63ab2911d1160a91b600f9290910191820152845161126b81601a840160208901610c53565b61227d60f01b601a9290910191820152835161128e81601c840160208801610c53565b01601c01979650505050505050565b6000602082840312156112af57600080fd5b610cd082610ea7565b60006112c48286610f54565b683f746f6b656e49643d60b81b815284516112e6816009840160208901610c53565b6e267573654e6577417274776f726b3d60881b600992909101918201528351611316816018840160208801610c53565b0160180195945050505050565b601f82111561136b576000816000526020600020601f850160051c8101602086101561134c5750805b601f850160051c820191505b8181101561082857828155600101611358565b505050565b815167ffffffffffffffff81111561138a5761138a610d8f565b61139e816113988454610f1a565b84611323565b602080601f8311600181146113d357600084156113bb5750858301515b600019600386901b1c1916600185901b178555610828565b600085815260208120601f198616915b82811015611402578886015182559484019460019091019084016113e3565b50858210156114205787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b7574662d382c7b226e6181526f6d65223a224d6f6f6e6269726473202360801b602082015260008751611481816030850160208c01610c53565b7f222c2265787465726e616c5f75726c223a2268747470733a2f2f70726f6f662e6030918401918201526d78797a2f6d6f6f6e62697264732f60901b605082015287516114d581605e840160208c01610c53565b016114f8605e82017211161130b734b6b0ba34b7b72fbab936111d1160691b9052565b6115056071820188610fc7565b6a11161134b6b0b3b2911d1160a91b81529050611525600b820187610fc7565b7411161130b63a32b93730ba32afb4b6b0b3b2911d1160591b8152905061154f6015820186610fc7565b6e11161130ba3a3934b13aba32b9911d60891b81529050611573600f820185610fc7565b607d60f81b81526001019998505050505050505050565b60006020828403121561159c57600080fd5b8151610cd081610ee856fea2646970667358221220234147f4d670181f998dfd884314b4bf3c11051a8f8dfb24319113047664948d64736f6c6343000817003300000000000000000000000023581767a106ae21c074b2276d25e5c3e136a68b000000000000000000000000b1befc9e7b76c1e846ebbf3e6e1ab029c86e7435000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000003568747470733a2f2f636f6c6c656374696f6e2d6173736574732e70726f6f662e78797a2f6d6f6f6e62697264732f696d616765732f0000000000000000000000000000000000000000000000000000000000000000000000000000000000003868747470733a2f2f636f6c6c656374696f6e2d6173736574732e70726f6f662e78797a2f6d6f6f6e62697264732f656d6265642e68746d6c0000000000000000

Deployed Bytecode

0x6080604052600436106101355760003560e01c80638a0349fb116100ab578063c87b56dd1161006f578063c87b56dd14610329578063cb36c11714610349578063de26bc3514610369578063f04e283e1461039d578063f2fde38b146103b0578063fee81cf4146103c357600080fd5b80638a0349fb146102a65780638da5cb5b146102bb5780639abc8320146102d4578063a0bcfc7f146102e9578063c443d4fa1461030957600080fd5b80633d44c286116100fd5780633d44c286146101da57806354d1f13d146101fa5780636b1e676b146102025780636c24a457146102325780636f7013a814610252578063715018a61461029e57600080fd5b80630dbc4e741461013a5780631680ed7b146101705780631db6151a1461019257806325692962146101b257806327b3a84b146101ba575b600080fd5b34801561014657600080fd5b5061015a610155366004610c3a565b610404565b6040516101679190610c77565b60405180910390f35b34801561017c57600080fd5b5061019061018b366004610caa565b6104fe565b005b34801561019e57600080fd5b5061015a6101ad366004610c3a565b610522565b6101906105ae565b3480156101c657600080fd5b5061015a6101d5366004610c3a565b6105fe565b3480156101e657600080fd5b506101906101f5366004610d23565b6107b1565b610190610830565b34801561020e57600080fd5b5061022261021d366004610c3a565b61086c565b6040519015158152602001610167565b34801561023e57600080fd5b5061015a61024d366004610c3a565b61088d565b34801561025e57600080fd5b506102867f00000000000000000000000023581767a106ae21c074b2276d25e5c3e136a68b81565b6040516001600160a01b039091168152602001610167565b610190610929565b3480156102b257600080fd5b5061015a61093d565b3480156102c757600080fd5b50638b78c6d81954610286565b3480156102e057600080fd5b5061015a6109cb565b3480156102f557600080fd5b50610190610304366004610e27565b6109d8565b34801561031557600080fd5b50610190610324366004610e27565b6109f0565b34801561033557600080fd5b5061015a610344366004610c3a565b610a04565b34801561035557600080fd5b50610190610364366004610ebc565b610a4e565b34801561037557600080fd5b506102867f000000000000000000000000b1befc9e7b76c1e846ebbf3e6e1ab029c86e743581565b6101906103ab366004610efd565b610b36565b6101906103be366004610efd565b610b76565b3480156103cf57600080fd5b506103f66103de366004610efd565b63389a75e1600c908152600091909152602090205490565b604051908152602001610167565b600881901c60009081526002602052604090205460609060ff83161c6001161561045a57600061043383610b9d565b604051602001610444929190610fe3565b6040516020818303038152906040529050919050565b6003546040516359bd015360e01b81526004810184905263ffffffff90911660248201527f000000000000000000000000b1befc9e7b76c1e846ebbf3e6e1ab029c86e74356001600160a01b0316906359bd0153906044015b600060405180830381865afa1580156104d0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104f89190810190611064565b92915050565b610506610be1565b6003805463ffffffff191663ffffffff92909216919091179055565b600881901c60009081526002602052604090205460609060ff83161c600116156105a3576003546040516359bd015360e01b81526004810184905263ffffffff90911660248201527f000000000000000000000000b1befc9e7b76c1e846ebbf3e6e1ab029c86e74356001600160a01b0316906359bd0153906044016104b3565b600061043383610b9d565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b60405163682ee63560e11b8152600481018290526060906000906001600160a01b037f000000000000000000000000b1befc9e7b76c1e846ebbf3e6e1ab029c86e7435169063d05dcc6a90602401600060405180830381865afa158015610669573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106919190810190611099565b6040805180820190915260018152605b60f81b602082015290915060005b82518110156107a957600081156106df57604051806040016040528060018152602001600b60fa1b8152506106f0565b604051806020016040528060008152505b905060006001855161070291906111b7565b831461071d5760405180602001604052806000815250610738565b604051806040016040528060018152602001605d60f81b8152505b9050838286858151811061074e5761074e6111d8565b60200260200101516000015187868151811061076c5761076c6111d8565b6020026020010151602001518460405160200161078d9594939291906111ee565b60408051808303601f19018152919052935050506001016106af565b509392505050565b828181146107d25760405163512509d360e11b815260040160405180910390fd5b60005b81811015610828576108208686838181106107f2576107f26111d8565b9050602002013585858481811061080b5761080b6111d8565b9050602002016020810190610364919061129d565b6001016107d5565b505050505050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b600881901c60009081526002602052604081205460ff83161c6001166104f8565b600881901c6000908152600260205260408120546060919060ff84161c6001166108d4576040518060400160405280600581526020016466616c736560d81b8152506108f2565b604051806040016040528060048152602001637472756560e01b8152505b905060016108ff84610b9d565b82604051602001610912939291906112b8565b604051602081830303815290604052915050919050565b610931610be1565b61093b6000610bfc565b565b6001805461094a90610f1a565b80601f016020809104026020016040519081016040528092919081815260200182805461097690610f1a565b80156109c35780601f10610998576101008083540402835291602001916109c3565b820191906000526020600020905b8154815290600101906020018083116109a657829003601f168201915b505050505081565b6000805461094a90610f1a565b6109e0610be1565b60006109ec8282611370565b5050565b6109f8610be1565b60016109ec8282611370565b60606000610a1183610b9d565b90508081610a1e8561088d565b610a2786610404565b610a3087610522565b610a39886105fe565b60405160200161091296959493929190611430565b6040516331a9108f60e11b81526004810183905233906001600160a01b037f00000000000000000000000023581767a106ae21c074b2276d25e5c3e136a68b1690636352211e90602401602060405180830381865afa158015610ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad9919061158a565b6001600160a01b031614610b0757604051633b94a19960e01b81526004810183905260240160405180910390fd5b6002602052600882901c6000908152604090208054600160ff851690811b1990911683151590911b1790555050565b610b3e610be1565b63389a75e1600c52806000526020600c208054421115610b6657636f5e88186000526004601cfd5b60009055610b7381610bfc565b50565b610b7e610be1565b8060601b610b9457637448fbae6000526004601cfd5b610b7381610bfc565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480610bb8575050819003601f19909101908152919050565b638b78c6d81954331461093b576382b429006000526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b600060208284031215610c4c57600080fd5b5035919050565b60005b83811015610c6e578181015183820152602001610c56565b50506000910152565b6020815260008251806020840152610c96816040850160208701610c53565b601f01601f19169190910160400192915050565b600060208284031215610cbc57600080fd5b813563ffffffff81168114610cd057600080fd5b9392505050565b60008083601f840112610ce957600080fd5b50813567ffffffffffffffff811115610d0157600080fd5b6020830191508360208260051b8501011115610d1c57600080fd5b9250929050565b60008060008060408587031215610d3957600080fd5b843567ffffffffffffffff80821115610d5157600080fd5b610d5d88838901610cd7565b90965094506020870135915080821115610d7657600080fd5b50610d8387828801610cd7565b95989497509550505050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610dc857610dc8610d8f565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610df757610df7610d8f565b604052919050565b600067ffffffffffffffff821115610e1957610e19610d8f565b50601f01601f191660200190565b600060208284031215610e3957600080fd5b813567ffffffffffffffff811115610e5057600080fd5b8201601f81018413610e6157600080fd5b8035610e74610e6f82610dff565b610dce565b818152856020838501011115610e8957600080fd5b81602084016020830137600091810160200191909152949350505050565b80358015158114610eb757600080fd5b919050565b60008060408385031215610ecf57600080fd5b82359150610edf60208401610ea7565b90509250929050565b6001600160a01b0381168114610b7357600080fd5b600060208284031215610f0f57600080fd5b8135610cd081610ee8565b600181811c90821680610f2e57607f821691505b602082108103610f4e57634e487b7160e01b600052602260045260246000fd5b50919050565b60008154610f6181610f1a565b60018281168015610f795760018114610f8e57610fbd565b60ff1984168752821515830287019450610fbd565b8560005260208060002060005b85811015610fb45781548a820152908401908201610f9b565b50505082870194505b5050505092915050565b60008151610fd9818560208601610c53565b9290920192915050565b6000610fef8285610f54565b8351610fff818360208801610c53565b632e706e6760e01b9101908152600401949350505050565b600082601f83011261102857600080fd5b8151611036610e6f82610dff565b81815284602083860101111561104b57600080fd5b61105c826020830160208701610c53565b949350505050565b60006020828403121561107657600080fd5b815167ffffffffffffffff81111561108d57600080fd5b61105c84828501611017565b600060208083850312156110ac57600080fd5b825167ffffffffffffffff808211156110c457600080fd5b818501915085601f8301126110d857600080fd5b8151818111156110ea576110ea610d8f565b8060051b6110f9858201610dce565b918252838101850191858101908984111561111357600080fd5b86860192505b838310156111aa578251858111156111315760008081fd5b86016040818c03601f19018113156111495760008081fd5b611151610da5565b89830151888111156111635760008081fd5b6111718e8c83870101611017565b8252509082015190878211156111875760008081fd5b6111958d8b84860101611017565b818b0152845250509186019190860190611119565b9998505050505050505050565b818103818111156104f857634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008651611200818460208b01610c53565b865190830190611214818360208b01610c53565b6e3d913a3930b4ba2fba3cb832911d1160891b9101908152855161123f81600f840160208a01610c53565b6a1116113b30b63ab2911d1160a91b600f9290910191820152845161126b81601a840160208901610c53565b61227d60f01b601a9290910191820152835161128e81601c840160208801610c53565b01601c01979650505050505050565b6000602082840312156112af57600080fd5b610cd082610ea7565b60006112c48286610f54565b683f746f6b656e49643d60b81b815284516112e6816009840160208901610c53565b6e267573654e6577417274776f726b3d60881b600992909101918201528351611316816018840160208801610c53565b0160180195945050505050565b601f82111561136b576000816000526020600020601f850160051c8101602086101561134c5750805b601f850160051c820191505b8181101561082857828155600101611358565b505050565b815167ffffffffffffffff81111561138a5761138a610d8f565b61139e816113988454610f1a565b84611323565b602080601f8311600181146113d357600084156113bb5750858301515b600019600386901b1c1916600185901b178555610828565b600085815260208120601f198616915b82811015611402578886015182559484019460019091019084016113e3565b50858210156114205787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b7574662d382c7b226e6181526f6d65223a224d6f6f6e6269726473202360801b602082015260008751611481816030850160208c01610c53565b7f222c2265787465726e616c5f75726c223a2268747470733a2f2f70726f6f662e6030918401918201526d78797a2f6d6f6f6e62697264732f60901b605082015287516114d581605e840160208c01610c53565b016114f8605e82017211161130b734b6b0ba34b7b72fbab936111d1160691b9052565b6115056071820188610fc7565b6a11161134b6b0b3b2911d1160a91b81529050611525600b820187610fc7565b7411161130b63a32b93730ba32afb4b6b0b3b2911d1160591b8152905061154f6015820186610fc7565b6e11161130ba3a3934b13aba32b9911d60891b81529050611573600f820185610fc7565b607d60f81b81526001019998505050505050505050565b60006020828403121561159c57600080fd5b8151610cd081610ee856fea2646970667358221220234147f4d670181f998dfd884314b4bf3c11051a8f8dfb24319113047664948d64736f6c63430008170033

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

00000000000000000000000023581767a106ae21c074b2276d25e5c3e136a68b000000000000000000000000b1befc9e7b76c1e846ebbf3e6e1ab029c86e7435000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000003568747470733a2f2f636f6c6c656374696f6e2d6173736574732e70726f6f662e78797a2f6d6f6f6e62697264732f696d616765732f0000000000000000000000000000000000000000000000000000000000000000000000000000000000003868747470733a2f2f636f6c6c656374696f6e2d6173736574732e70726f6f662e78797a2f6d6f6f6e62697264732f656d6265642e68746d6c0000000000000000

-----Decoded View---------------
Arg [0] : moonbirds_ (address): 0x23581767a106ae21c074b2276D25e5C3e136a68b
Arg [1] : legacyRenderer_ (address): 0xb1bEfc9E7B76C1e846EBBf3e6E1Ab029C86e7435
Arg [2] : baseUri_ (string): https://collection-assets.proof.xyz/moonbirds/images/
Arg [3] : animationBaseUri_ (string): https://collection-assets.proof.xyz/moonbirds/embed.html

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 00000000000000000000000023581767a106ae21c074b2276d25e5c3e136a68b
Arg [1] : 000000000000000000000000b1befc9e7b76c1e846ebbf3e6e1ab029c86e7435
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [5] : 68747470733a2f2f636f6c6c656374696f6e2d6173736574732e70726f6f662e
Arg [6] : 78797a2f6d6f6f6e62697264732f696d616765732f0000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000038
Arg [8] : 68747470733a2f2f636f6c6c656374696f6e2d6173736574732e70726f6f662e
Arg [9] : 78797a2f6d6f6f6e62697264732f656d6265642e68746d6c0000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.