ETH Price: $2,989.67 (-0.77%)
Gas: 6 Gwei

Token

Together (TGR)
 

Overview

Max Total Supply

1,000 TGR

Holders

313

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Filtered by Token Holder
4evernfts.eth
Balance
2 TGR
0x48ce3a928ba1db7555efd2315b98f5e6a948a973
Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

Together is a 3-star onchain NFT collection exploring the creation of art as a collective.

# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
Together

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 20 runs

Other Settings:
paris EvmVersion
File 1 of 18 : Together.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

import { ERC721A } from "@erc721a/ERC721A.sol";
import { NFTEventsAndErrors } from "./NFTEventsAndErrors.sol";
import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
import { IERC2981 } from "@openzeppelin/contracts/interfaces/IERC2981.sol";
import { IERC721 } from "@openzeppelin/contracts/interfaces/IERC721.sol";
import { Utils } from "./utils/Utils.sol";
import { Constants } from "./utils/Constants.sol";
import { ColorfulArt } from "./utils/ColorfulArt.sol";
import { LibString } from "./utils/LibString.sol";
import { AllowList } from "./utils/AllowList.sol";

/// @title Together NFT
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice Together is an onchain NFT where the art of each token builds on the art of all previous tokens.
contract Together is ColorfulArt, ERC721A, NFTEventsAndErrors, Constants, AllowList {
  using LibString for uint256;

  bool public publicMintEnabled;
  uint16 internal immutable _allowListMintMaxTotal;
  uint8 internal immutable _allowListMintMaxPerWallet;
  mapping(address user => uint8 minted) internal _allowListMinted;

  constructor(
    bytes32 allowListMerkleRoot,
    uint16 allowListMintMaxTotalVal,
    uint8 allowListMintMaxPerWalletVal
  ) AllowList(allowListMerkleRoot) ERC721A("Together", "TGR") {
    _allowListMintMaxTotal = allowListMintMaxTotalVal;
    _allowListMintMaxPerWallet = allowListMintMaxPerWalletVal;
  }

  // Commence

  /// @notice Update public mint enabled.
  function updatePublicMintEnabled(bool _publicMintEnabled) external onlyOwner {
    publicMintEnabled = _publicMintEnabled;
  }

  function commence() external onlyOwner {
    // Checks

    // Check if already commenced
    if (commenced) {
      revert AlreadyCommenced();
    }

    // Effects

    // Update commenced to be true
    commenced = true;

    // Mint initial tokens
    _coreMint(msg.sender, _MINT_AMOUNT_DURING_COMMENCE);
  }

  // Mint

  function _coreMint(address to, uint8 amount) internal {
    // Checks
    if (!commenced) {
      // Check mint has started
      revert MintNotStarted();
    }

    uint256 nextTokenIdToBeMinted = _nextTokenId();

    unchecked {
      if (MAX_POINTS_TOTAL + 1 < nextTokenIdToBeMinted + amount) {
        // Check max supply not exceeded
        revert MaxSupplyReached();
      }
    }

    // Effects

    // Set colors
    unchecked {
      for (uint256 i = nextTokenIdToBeMinted; i < nextTokenIdToBeMinted + amount; ) {
        tokenToColor[i] = Utils.getPackedRGBColor(keccak256(abi.encodePacked(block.prevrandao, i)));
        ++i;
      }
    }

    // Perform mint
    _mint(to, amount);
  }

  /// @notice Mint tokens for allowlist minters.
  /// @param proof proof
  /// @param amount amount of tokens to mint
  function mintAllowList(bytes32[] calldata proof, uint8 amount) external payable onlyAllowListed(proof) {
    // Checks

    unchecked {
      if (amount * PRICE != msg.value) {
        // Check payment by sender is correct
        revert IncorrectPayment();
      }

      if (_totalMinted() + amount > _allowListMintMaxTotal) {
        // Check allowlist mint total is not exceeding max allowed to be minted during allowlist phase
        revert AllowListMintCapExceeded();
      }

      if (_allowListMinted[msg.sender] + amount > _allowListMintMaxPerWallet) {
        // Check wallet is not exceeding max allowed during allowlist phase
        revert AllowListMintCapPerWalletExceeded();
      }
    }

    // Effects

    // Increase allowlist minted by amount
    unchecked {
      _allowListMinted[msg.sender] += amount;
    }

    // Perform mint
    _coreMint(msg.sender, amount);
  }

  /// @notice Mint tokens.
  /// @param amount amount of tokens to mint
  function mintPublic(uint8 amount) external payable {
    // Checks
    if (!publicMintEnabled) {
      // Check public mint enabled
      revert PublicMintNotEnabled();
    }

    if (amount > _MAX_PUBLIC_MINT_AMOUNT_PER_TRANSACTION) {
      revert PublicMintMaxPerTransactionExceeded();
    }

    unchecked {
      if (amount * PRICE != msg.value) {
        // Check payment by sender is correct
        revert IncorrectPayment();
      }
    }

    // Effects

    _coreMint(msg.sender, amount);
  }

  function _startTokenId() internal pure override returns (uint256) {
    return 1;
  }

  // Withdraw

  /// @notice Withdraw all ETH from the contract to the vault.
  function withdraw() external {
    (bool success, ) = _VAULT_ADDRESS.call{ value: address(this).balance }("");
    require(success);
  }

  // Metadata

  /// @notice Set the background color of your token.
  /// @param tokenId Together token id
  /// @param xxyyzzTokenId XXYYZZ token id (this will be what your Together
  /// token's background color is set to)
  function colorBackground(uint256 tokenId, uint24 xxyyzzTokenId) external {
    // Checks
    if (ownerOf(tokenId) != msg.sender) {
      // Revert if msg.sender does not own this token
      revert MsgSenderNotTokenOwner();
    }
    if (
      // Background color can always be reset to black
      xxyyzzTokenId != 0 &&
      // For any other color, check if msg.sender owns that xxyyzz token
      IERC721(_XXYYZZ_TOKEN_ADDRESS).ownerOf(xxyyzzTokenId) != msg.sender
    ) {
      // Revert if msg.sender is not owner of the input xxyyzz token
      revert MsgSenderDoesNotOwnXXYYZZToken();
    }

    // Effects
    // Update token background to xxyyzz token color
    tokenToBackgroundColor[tokenId] = xxyyzzTokenId;

    emit MetadataUpdate(tokenId);
  }

  function _getTraits(uint256 tokenId) internal view returns (string memory) {
    unchecked {
      uint256 innerShapePoints = tokenId % MAX_POINTS_PER_POLYGON;
      return
        string.concat(
          "[",
          Utils.getTrait("Total Points", tokenId.toString(), true, true),
          Utils.getTrait("Depth", (tokenId / MAX_POINTS_PER_POLYGON).toString(), true, true),
          Utils.getTrait(
            "Inner Shape Points",
            (innerShapePoints > 0 ? innerShapePoints : MAX_POINTS_PER_POLYGON).toString(),
            true,
            true
          ),
          Utils.getTrait("New Line Color", Utils.getRGBStr(tokenToColor[tokenId]), false, true),
          Utils.getTrait("Background Color", Utils.getRGBStr(tokenToBackgroundColor[tokenId]), false, false),
          "]"
        );
    }
  }

  /// @notice Get token uri for a particular token.
  /// @param tokenId token id
  /// @return tokenURI
  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
    if (!_exists(tokenId)) {
      revert URIQueryForNonexistentToken();
    }

    string memory artSvg = art(uint16(tokenId));

    return
      Utils.formatTokenURI(
        tokenId,
        Utils.svgToURI(artSvg),
        string.concat(
          "data:text/html;base64,",
          Utils.encodeBase64(
            bytes(
              string.concat(
                '<html style="overflow:hidden"><body style="margin:0">',
                artSvg,
                '<script>let a=!1,b=!1,o=Array.from(document.getElementsByTagName("line")).map(e=>e.style.stroke),s=e=>new Promise(t=>setTimeout(t,e)),c=()=>Math.floor(256*Math.random());document.body.addEventListener("click",async()=>{if(!a||b)return;b=!0;let e=document.getElementsByTagName("line");for(;a;){for(let t=0;t<e.length;t++)e[t].style.stroke=`rgb(${c()},${c()},${c()})`;await s(50)}for(let l=0;l<e.length;l++)e[l].style.stroke=o[l];b=!1},!0),document.body.addEventListener("click",async()=>{if(a)return;a=!0;let e=document.getElementsByTagName("line");for(let t=0;t<2*e.length;t++){let l=t<e.length?"0":"1",n=t<e.length?t:2*e.length-t-1;"m"!==e[n].id&&(e[n].style.strokeOpacity=l);let g=e.length%100;await s((t<e.length?t>=e.length-g:t>e.length&&t-e.length<=g)?20+(100-g)/100*75:10)}a=!1},!0);</script></body></html>'
              )
            )
          )
        ),
        _getTraits(tokenId)
      );
  }

  // Royalties

  function royaltyInfo(uint256, uint256 salePrice) external pure returns (address receiver, uint256 royaltyAmount) {
    unchecked {
      return (_VAULT_ADDRESS, (salePrice * 250) / 10_000);
    }
  }

  // IERC165

  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721A) returns (bool) {
    return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
  }
}

File 2 of 18 : ERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import "./IERC721A.sol";

/**
 * @dev Interface of ERC721 token receiver.
 */
interface ERC721A__IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    )
        external
        returns (bytes4);
}

/**
 * @title ERC721A
 *
 * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
 * Non-Fungible Token Standard, including the Metadata extension.
 * Optimized for lower gas during batch mints.
 *
 * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
 * starting from `_startTokenId()`.
 *
 * Assumptions:
 *
 * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
 * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
 */
contract ERC721A is IERC721A {
    // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
    struct TokenApprovalRef {
        address value;
    }

    // =============================================================
    //                           CONSTANTS
    // =============================================================

    // Mask of an entry in packed address data.
    uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;

    // The bit position of `numberMinted` in packed address data.
    uint256 private constant _BITPOS_NUMBER_MINTED = 64;

    // The bit position of `numberBurned` in packed address data.
    uint256 private constant _BITPOS_NUMBER_BURNED = 128;

    // The bit position of `aux` in packed address data.
    uint256 private constant _BITPOS_AUX = 192;

    // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
    uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;

    // The bit position of `startTimestamp` in packed ownership.
    uint256 private constant _BITPOS_START_TIMESTAMP = 160;

    // The bit mask of the `burned` bit in packed ownership.
    uint256 private constant _BITMASK_BURNED = 1 << 224;

    // The bit position of the `nextInitialized` bit in packed ownership.
    uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;

    // The bit mask of the `nextInitialized` bit in packed ownership.
    uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;

    // The bit position of `extraData` in packed ownership.
    uint256 private constant _BITPOS_EXTRA_DATA = 232;

    // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
    uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;

    // The mask of the lower 160 bits for addresses.
    uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;

    // The maximum `quantity` that can be minted with {_mintERC2309}.
    // This limit is to prevent overflows on the address data entries.
    // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
    // is required to cause an overflow, which is unrealistic.
    uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;

    // The `Transfer` event signature is given by:
    // `keccak256(bytes("Transfer(address,address,uint256)"))`.
    bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    // =============================================================
    //                            STORAGE
    // =============================================================

    // The next token ID to be minted.
    uint256 private _currentIndex;

    // The number of tokens burned.
    uint256 private _burnCounter;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to ownership details
    // An empty struct value does not necessarily mean the token is unowned.
    // See {_packedOwnershipOf} implementation for details.
    //
    // Bits Layout:
    // - [0..159]   `addr`
    // - [160..223] `startTimestamp`
    // - [224]      `burned`
    // - [225]      `nextInitialized`
    // - [232..255] `extraData`
    mapping(uint256 => uint256) private _packedOwnerships;

    // Mapping owner address to address data.
    //
    // Bits Layout:
    // - [0..63]    `balance`
    // - [64..127]  `numberMinted`
    // - [128..191] `numberBurned`
    // - [192..255] `aux`
    mapping(address => uint256) private _packedAddressData;

    // Mapping from token ID to approved address.
    mapping(uint256 => TokenApprovalRef) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // =============================================================
    //                          CONSTRUCTOR
    // =============================================================

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _currentIndex = _startTokenId();
    }

    // =============================================================
    //                   TOKEN COUNTING OPERATIONS
    // =============================================================

    /**
     * @dev Returns the starting token ID.
     * To change the starting token ID, please override this function.
     */
    function _startTokenId() internal view virtual returns (uint256) {
        return 1;
    }

    /**
     * @dev Returns the next token ID to be minted.
     */
    function _nextTokenId() internal view virtual returns (uint256) {
        return _currentIndex;
    }

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        // Counter underflow is impossible as _burnCounter cannot be incremented
        // more than `_currentIndex - _startTokenId()` times.
        unchecked {
            return _currentIndex - _burnCounter - _startTokenId();
        }
    }

    /**
     * @dev Returns the total amount of tokens minted in the contract.
     */
    function _totalMinted() internal view virtual returns (uint256) {
        // Counter underflow is impossible as `_currentIndex` does not decrement,
        // and it is initialized to `_startTokenId()`.
        unchecked {
            return _currentIndex - _startTokenId();
        }
    }

    /**
     * @dev Returns the total number of tokens burned.
     */
    function _totalBurned() internal view virtual returns (uint256) {
        return _burnCounter;
    }

    // =============================================================
    //                    ADDRESS DATA OPERATIONS
    // =============================================================

    /**
     * @dev Returns the number of tokens in `owner`'s account.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        if (owner == address(0)) revert BalanceQueryForZeroAddress();
        return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the number of tokens minted by `owner`.
     */
    function _numberMinted(address owner) internal view returns (uint256) {
        return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the number of tokens burned by or on behalf of `owner`.
     */
    function _numberBurned(address owner) internal view returns (uint256) {
        return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
     */
    function _getAux(address owner) internal view returns (uint64) {
        return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
    }

    /**
     * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
     * If there are multiple variables, please pack them into a uint64.
     */
    function _setAux(address owner, uint64 aux) internal virtual {
        uint256 packed = _packedAddressData[owner];
        uint256 auxCasted;
        // Cast `aux` with assembly to avoid redundant masking.
        assembly {
            auxCasted := aux
        }
        packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
        _packedAddressData[owner] = packed;
    }

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        // The interface IDs are constants representing the first 4 bytes
        // of the XOR of all function selectors in the interface.
        // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
        // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
        return interfaceId == 0x01ffc9a7 // ERC165 interface ID for ERC165.
            || interfaceId == 0x80ac58cd // ERC165 interface ID for ERC721.
            || interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
    }

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @dev Returns the token collection name.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256) public view virtual override returns (string memory) {
        return "";
    }

    // =============================================================
    //                     OWNERSHIPS OPERATIONS
    // =============================================================

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        return address(uint160(_packedOwnershipOf(tokenId)));
    }

    /**
     * @dev Gas spent here starts off proportional to the maximum mint batch size.
     * It gradually moves to O(1) as tokens get transferred around over time.
     */
    function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
        return _unpackedOwnership(_packedOwnershipOf(tokenId));
    }

    /**
     * @dev Returns the unpacked `TokenOwnership` struct at `index`.
     */
    function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
        return _unpackedOwnership(_packedOwnerships[index]);
    }

    /**
     * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
     */
    function _initializeOwnershipAt(uint256 index) internal virtual {
        if (_packedOwnerships[index] == 0) {
            _packedOwnerships[index] = _packedOwnershipOf(index);
        }
    }

    /**
     * Returns the packed ownership data of `tokenId`.
     */
    function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
        uint256 curr = tokenId;

        unchecked {
            if (_startTokenId() <= curr) {
                if (curr < _currentIndex) {
                    uint256 packed = _packedOwnerships[curr];
                    // If not burned.
                    if (packed & _BITMASK_BURNED == 0) {
                        // Invariant:
                        // There will always be an initialized ownership slot
                        // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                        // before an unintialized ownership slot
                        // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                        // Hence, `curr` will not underflow.
                        //
                        // We can directly compare the packed value.
                        // If the address is zero, packed will be zero.
                        while (packed == 0) {
                            packed = _packedOwnerships[--curr];
                        }
                        return packed;
                    }
                }
            }
        }
        revert OwnerQueryForNonexistentToken();
    }

    /**
     * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
     */
    function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
        ownership.addr = address(uint160(packed));
        ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
        ownership.burned = packed & _BITMASK_BURNED != 0;
        ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
    }

    /**
     * @dev Packs ownership data into a single uint256.
     */
    function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
        assembly {
            // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
            owner := and(owner, _BITMASK_ADDRESS)
            // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
            result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
        }
    }

    /**
     * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
     */
    function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
        // For branchless setting of the `nextInitialized` flag.
        assembly {
            // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
            result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
        }
    }

    // =============================================================
    //                      APPROVAL OPERATIONS
    // =============================================================

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

        if (_msgSenderERC721A() != owner) {
            if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                revert ApprovalCallerNotOwnerNorApproved();
            }
        }

        _tokenApprovals[tokenId].value = to;
        emit Approval(owner, to, tokenId);
    }

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();

        return _tokenApprovals[tokenId].value;
    }

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

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

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted. See {_mint}.
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _startTokenId() <= tokenId && tokenId < _currentIndex // If within bounds,
            && _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
    }

    /**
     * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
     */
    function _isSenderApprovedOrOwner(
        address approvedAddress,
        address owner,
        address msgSender
    )
        private
        pure
        returns (bool result)
    {
        assembly {
            // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
            owner := and(owner, _BITMASK_ADDRESS)
            // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
            msgSender := and(msgSender, _BITMASK_ADDRESS)
            // `msgSender == owner || msgSender == approvedAddress`.
            result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
        }
    }

    /**
     * @dev Returns the storage slot and value for the approved address of `tokenId`.
     */
    function _getApprovedSlotAndAddress(uint256 tokenId)
        private
        view
        returns (uint256 approvedAddressSlot, address approvedAddress)
    {
        TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
        // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
        assembly {
            approvedAddressSlot := tokenApproval.slot
            approvedAddress := sload(approvedAddressSlot)
        }
    }

    // =============================================================
    //                      TRANSFER OPERATIONS
    // =============================================================

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token
     * by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) public payable virtual override {
        uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

        if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();

        (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);

        // The nested ifs save around 20+ gas over a compound boolean condition.
        if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A())) {
            if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
        }

        if (to == address(0)) revert TransferToZeroAddress();

        _beforeTokenTransfers(from, to, tokenId, 1);

        // Clear approvals from the previous owner.
        assembly {
            if approvedAddress {
                // This is equivalent to `delete _tokenApprovals[tokenId]`.
                sstore(approvedAddressSlot, 0)
            }
        }

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
        unchecked {
            // We can directly increment and decrement the balances.
            --_packedAddressData[from]; // Updates: `balance -= 1`.
            ++_packedAddressData[to]; // Updates: `balance += 1`.

            // Updates:
            // - `address` to the next owner.
            // - `startTimestamp` to the timestamp of transfering.
            // - `burned` to `false`.
            // - `nextInitialized` to `true`.
            _packedOwnerships[tokenId] =
                _packOwnershipData(to, _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked));

            // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
            if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                uint256 nextTokenId = tokenId + 1;
                // If the next slot's address is zero and not burned (i.e. packed value is zero).
                if (_packedOwnerships[nextTokenId] == 0) {
                    // If the next slot is within bounds.
                    if (nextTokenId != _currentIndex) {
                        // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                        _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                    }
                }
            }
        }

        emit Transfer(from, to, tokenId);
        _afterTokenTransfers(from, to, tokenId, 1);
    }

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public payable virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token
     * by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    )
        public
        payable
        virtual
        override
    {
        transferFrom(from, to, tokenId);
        if (to.code.length != 0) {
            if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                revert TransferToNonERC721ReceiverImplementer();
            }
        }
    }

    /**
     * @dev Hook that is called before a set of serially-ordered token IDs
     * are about to be transferred. This includes minting.
     * And also called before burning one token.
     *
     * `startTokenId` - the first token ID to be transferred.
     * `quantity` - the amount to be transferred.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual { }

    /**
     * @dev Hook that is called after a set of serially-ordered token IDs
     * have been transferred. This includes minting.
     * And also called after one token has been burned.
     *
     * `startTokenId` - the first token ID to be transferred.
     * `quantity` - the amount to be transferred.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
     * transferred to `to`.
     * - When `from` is zero, `tokenId` has been minted for `to`.
     * - When `to` is zero, `tokenId` has been burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual { }

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
     *
     * `from` - Previous owner of the given token ID.
     * `to` - Target address that will receive the token.
     * `tokenId` - Token ID to be transferred.
     * `_data` - Optional data to send along with the call.
     *
     * Returns whether the call correctly returned the expected magic value.
     */
    function _checkContractOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    )
        private
        returns (bool)
    {
        try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
            bytes4 retval
        ) {
            return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
        } catch (bytes memory reason) {
            if (reason.length == 0) {
                revert TransferToNonERC721ReceiverImplementer();
            } else {
                assembly {
                    revert(add(32, reason), mload(reason))
                }
            }
        }
    }

    // =============================================================
    //                        MINT OPERATIONS
    // =============================================================

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {Transfer} event for each mint.
     */
    function _mint(address to, uint256 quantity) internal virtual {
        uint256 startTokenId = _currentIndex;
        if (quantity == 0) revert MintZeroQuantity();

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are incredibly unrealistic.
        // `balance` and `numberMinted` have a maximum limit of 2**64.
        // `tokenId` has a maximum limit of 2**256.
        unchecked {
            // Updates:
            // - `balance += quantity`.
            // - `numberMinted += quantity`.
            //
            // We can directly add to the `balance` and `numberMinted`.
            _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

            // Updates:
            // - `address` to the owner.
            // - `startTimestamp` to the timestamp of minting.
            // - `burned` to `false`.
            // - `nextInitialized` to `quantity == 1`.
            _packedOwnerships[startTokenId] =
                _packOwnershipData(to, _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0));

            uint256 toMasked;
            uint256 end = startTokenId + quantity;

            // Use assembly to loop and emit the `Transfer` event for gas savings.
            // The duplicated `log4` removes an extra check and reduces stack juggling.
            // The assembly, together with the surrounding Solidity code, have been
            // delicately arranged to nudge the compiler into producing optimized opcodes.
            assembly {
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                toMasked := and(to, _BITMASK_ADDRESS)
                // Emit the `Transfer` event.
                log4(
                    0, // Start of data (0, since no data).
                    0, // End of data (0, since no data).
                    _TRANSFER_EVENT_SIGNATURE, // Signature.
                    0, // `address(0)`.
                    toMasked, // `to`.
                    startTokenId // `tokenId`.
                )

                // The `iszero(eq(,))` check ensures that large values of `quantity`
                // that overflows uint256 will make the loop run out of gas.
                // The compiler will optimize the `iszero` away for performance.
                for { let tokenId := add(startTokenId, 1) } iszero(eq(tokenId, end)) { tokenId := add(tokenId, 1) } {
                    // Emit the `Transfer` event. Similar to above.
                    log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                }
            }
            if (toMasked == 0) revert MintToZeroAddress();

            _currentIndex = end;
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * This function is intended for efficient minting only during contract creation.
     *
     * It emits only one {ConsecutiveTransfer} as defined in
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
     * instead of a sequence of {Transfer} event(s).
     *
     * Calling this function outside of contract creation WILL make your contract
     * non-compliant with the ERC721 standard.
     * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
     * {ConsecutiveTransfer} event is only permissible during contract creation.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {ConsecutiveTransfer} event.
     */
    function _mintERC2309(address to, uint256 quantity) internal virtual {
        uint256 startTokenId = _currentIndex;
        if (to == address(0)) revert MintToZeroAddress();
        if (quantity == 0) revert MintZeroQuantity();
        if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
        unchecked {
            // Updates:
            // - `balance += quantity`.
            // - `numberMinted += quantity`.
            //
            // We can directly add to the `balance` and `numberMinted`.
            _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

            // Updates:
            // - `address` to the owner.
            // - `startTimestamp` to the timestamp of minting.
            // - `burned` to `false`.
            // - `nextInitialized` to `quantity == 1`.
            _packedOwnerships[startTokenId] =
                _packOwnershipData(to, _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0));

            emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);

            _currentIndex = startTokenId + quantity;
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Safely mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
     * - `quantity` must be greater than 0.
     *
     * See {_mint}.
     *
     * Emits a {Transfer} event for each mint.
     */
    function _safeMint(address to, uint256 quantity, bytes memory _data) internal virtual {
        _mint(to, quantity);

        unchecked {
            if (to.code.length != 0) {
                uint256 end = _currentIndex;
                uint256 index = end - quantity;
                do {
                    if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                        revert TransferToNonERC721ReceiverImplementer();
                    }
                } while (index < end);
                // Reentrancy protection.
                if (_currentIndex != end) revert();
            }
        }
    }

    /**
     * @dev Equivalent to `_safeMint(to, quantity, '')`.
     */
    function _safeMint(address to, uint256 quantity) internal virtual {
        _safeMint(to, quantity, "");
    }

    // =============================================================
    //                        BURN OPERATIONS
    // =============================================================

    /**
     * @dev Equivalent to `_burn(tokenId, false)`.
     */
    function _burn(uint256 tokenId) internal virtual {
        _burn(tokenId, false);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
        uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

        address from = address(uint160(prevOwnershipPacked));

        (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);

        if (approvalCheck) {
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A())) {
                if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
            }
        }

        _beforeTokenTransfers(from, address(0), tokenId, 1);

        // Clear approvals from the previous owner.
        assembly {
            if approvedAddress {
                // This is equivalent to `delete _tokenApprovals[tokenId]`.
                sstore(approvedAddressSlot, 0)
            }
        }

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
        unchecked {
            // Updates:
            // - `balance -= 1`.
            // - `numberBurned += 1`.
            //
            // We can directly decrement the balance, and increment the number burned.
            // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
            _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;

            // Updates:
            // - `address` to the last owner.
            // - `startTimestamp` to the timestamp of burning.
            // - `burned` to `true`.
            // - `nextInitialized` to `true`.
            _packedOwnerships[tokenId] = _packOwnershipData(
                from,
                (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
            );

            // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
            if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                uint256 nextTokenId = tokenId + 1;
                // If the next slot's address is zero and not burned (i.e. packed value is zero).
                if (_packedOwnerships[nextTokenId] == 0) {
                    // If the next slot is within bounds.
                    if (nextTokenId != _currentIndex) {
                        // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                        _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                    }
                }
            }
        }

        emit Transfer(from, address(0), tokenId);
        _afterTokenTransfers(from, address(0), tokenId, 1);

        // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
        unchecked {
            _burnCounter++;
        }
    }

    // =============================================================
    //                     EXTRA DATA OPERATIONS
    // =============================================================

    /**
     * @dev Directly sets the extra data for the ownership data `index`.
     */
    function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
        uint256 packed = _packedOwnerships[index];
        if (packed == 0) revert OwnershipNotInitializedForExtraData();
        uint256 extraDataCasted;
        // Cast `extraData` with assembly to avoid redundant masking.
        assembly {
            extraDataCasted := extraData
        }
        packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
        _packedOwnerships[index] = packed;
    }

    /**
     * @dev Called during each token transfer to set the 24bit `extraData` field.
     * Intended to be overridden by the cosumer contract.
     *
     * `previousExtraData` - the value of `extraData` before transfer.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _extraData(address from, address to, uint24 previousExtraData) internal view virtual returns (uint24) { }

    /**
     * @dev Returns the next extra data for the packed ownership data.
     * The returned result is shifted into position.
     */
    function _nextExtraData(address from, address to, uint256 prevOwnershipPacked) private view returns (uint256) {
        uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
        return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
    }

    // =============================================================
    //                       OTHER OPERATIONS
    // =============================================================

    /**
     * @dev Returns the message sender (defaults to `msg.sender`).
     *
     * If you are writing GSN compatible contracts, you need to override this function.
     */
    function _msgSenderERC721A() internal view virtual returns (address) {
        return msg.sender;
    }

    /**
     * @dev Converts a uint256 to its ASCII string decimal representation.
     */
    function _toString(uint256 value) internal pure virtual returns (string memory str) {
        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. Total: 5 * 0x20 = 0xa0.
            let m := add(mload(0x40), 0xa0)
            // Update the free memory pointer to allocate.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

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

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 { } {
                str := 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)
                // prettier-ignore
                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)
        }
    }
}

File 3 of 18 : NFTEventsAndErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

interface NFTEventsAndErrors {
  error AlreadyCommenced();
  error PublicMintNotEnabled();
  error AllowListMintCapPerWalletExceeded();
  error AllowListMintCapExceeded();
  error PublicMintMaxPerTransactionExceeded();
  error MintNotStarted();
  error MaxSupplyReached();
  error IncorrectPayment();
  error MsgSenderDoesNotOwnXXYYZZToken();
  error MsgSenderNotTokenOwner();
  event MetadataUpdate(uint256 tokenId);
}

File 4 of 18 : Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 32)

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

File 5 of 18 : IERC2981.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 *
 * _Available since v4.5._
 */
interface IERC2981 is IERC165 {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}

File 6 of 18 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/IERC721.sol";

File 7 of 18 : Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

import { LibPRNG } from "./LibPRNG.sol";
import { LibString } from "./LibString.sol";

/// @title Utils
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice Utility functions for constructing onchain Together NFT
library Utils {
  using LibPRNG for LibPRNG.PRNG;
  using LibString for uint256;

  string internal constant BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  function svgToURI(string memory _source) internal pure returns (string memory) {
    return string.concat("data:image/svg+xml;base64,", encodeBase64(bytes(_source)));
  }

  function formatTokenURI(
    uint256 _tokenId,
    string memory _imageURI,
    string memory _animationURI,
    string memory _properties
  ) internal pure returns (string memory) {
    return
      string.concat(
        "data:application/json;base64,",
        encodeBase64(
          bytes(
            string.concat(
              '{"name":"Together #',
              _tokenId.toString(),
              '","description":"Together is a generative onchain NFT with colorful art formed by its minters. The art of each token builds on the art of all previous tokens plus a new line generated at the time of mint. Tap once to unwind your art. Tap twice to unwind your mind.","attributes":',
              _properties,
              ',"image":"',
              _imageURI,
              '","animation_url":"',
              _animationURI,
              '"}'
            )
          )
        )
      );
  }

  function getTrait(
    string memory traitType,
    string memory value,
    bool isNumberValue,
    bool includeTrailingComma
  ) internal pure returns (string memory) {
    return
      string.concat(
        '{"trait_type":"',
        traitType,
        '","value":',
        isNumberValue ? value : string.concat('"', value, '"'),
        "}",
        includeTrailingComma ? "," : ""
      );
  }

  // Encode some bytes in base64
  // https://gist.github.com/mbvissers/8ba9ac1eca9ed0ef6973bd49b3c999ba
  function encodeBase64(bytes memory data) internal pure returns (string memory) {
    if (data.length == 0) return "";

    // load the table into memory
    string memory table = BASE64_TABLE;

    unchecked {
      // multiply by 4/3 rounded up
      uint256 encodedLen = 4 * ((data.length + 2) / 3);

      // add some extra buffer at the end required for the writing
      string memory result = new string(encodedLen + 32);

      assembly {
        // set the actual output length
        mstore(result, encodedLen)

        // prepare the lookup table
        let tablePtr := add(table, 1)

        // input ptr
        let dataPtr := data
        let endPtr := add(dataPtr, mload(data))

        // result ptr, jump over length
        let resultPtr := add(result, 32)

        // run over the input, 3 bytes at a time
        for {

        } lt(dataPtr, endPtr) {

        } {
          dataPtr := add(dataPtr, 3)

          // read 3 bytes
          let input := mload(dataPtr)

          // write 4 characters
          mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F)))))
          resultPtr := add(resultPtr, 1)
          mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F)))))
          resultPtr := add(resultPtr, 1)
          mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(6, input), 0x3F)))))
          resultPtr := add(resultPtr, 1)
          mstore(resultPtr, shl(248, mload(add(tablePtr, and(input, 0x3F)))))
          resultPtr := add(resultPtr, 1)
        }

        // padding with '='
        switch mod(mload(data), 3)
        case 1 {
          mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
        }
        case 2 {
          mstore(sub(resultPtr, 1), shl(248, 0x3d))
        }
      }

      return result;
    }
  }

  function getPackedRGBColor(bytes32 seed) internal pure returns (uint24) {
    LibPRNG.PRNG memory prng;
    prng.seed(seed);
    unchecked {
      return (uint24(prng.uniform(256)) << 16) + (uint24(prng.uniform(256)) << 8) + uint8(prng.uniform(256));
    }
  }

  function getRGBStr(uint24 packedRgb) internal pure returns (string memory) {
    return string.concat("#", uint256(packedRgb).toHexStringNoPrefix(3));
  }
}

File 8 of 18 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

/// @title Constants
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice Constants for constructing onchain Together NFT
contract Constants {
  uint256 public constant PRICE = 0.01 ether;

  address payable internal constant _VAULT_ADDRESS = payable(address(0x39Ab90066cec746A032D67e4fe3378f16294CF6b));
  address internal constant _XXYYZZ_TOKEN_ADDRESS = address(0xFf6000a85baAc9c4854fAA7155e70BA850BF726b);
  uint8 internal constant _MINT_AMOUNT_DURING_COMMENCE = 5;
  uint8 internal constant _MAX_PUBLIC_MINT_AMOUNT_PER_TRANSACTION = 5;
}

File 9 of 18 : ColorfulArt.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

import { Utils } from "./Utils.sol";
import { Trigonometry } from "./Trigonometry.sol";
import { svg } from "./SVG.sol";
import { LibPRNG } from "./LibPRNG.sol";
import { LibString } from "./LibString.sol";

/// @title ColorfulArt
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice ColorfulArt provides utility functions for creating colorful polygon art
contract ColorfulArt {
  using LibPRNG for LibPRNG.PRNG;
  using LibString for uint256;

  struct Point {
    // Scale by 1e18
    uint256 x;
    uint256 y;
  }

  uint16 internal constant MAX_POINTS_TOTAL = 1000;
  uint24[MAX_POINTS_TOTAL + 1] internal tokenToColor;
  uint24[MAX_POINTS_TOTAL + 1] internal tokenToBackgroundColor;

  uint8 internal constant MAX_POINTS_PER_POLYGON = 100;
  uint8 private constant LINE_WIDTH = 10;
  uint8 private constant MAX_RADIUS = 120;
  bool internal commenced;

  function polarToCartesian(uint256 radius, uint256 angleInDegrees) internal pure returns (Point memory) {
    int256 angleInRadians = ((int256(angleInDegrees)) * int256(Trigonometry.PI)) /
      (180.0 * 1e18) +
      // Add 2 PI to ensure radians is always positive
      int256(
        Trigonometry.TWO_PI -
          // Rotate 90 degrees counterclockwise
          Trigonometry.PI /
          2.0
      );

    return
      // Point has both x and y scaled by 1e18
      Point({
        x: uint256(
          int256(
            // Scale by 1e18
            200 * 1e18
          ) + ((int256(radius) * Trigonometry.cos(uint256(angleInRadians))))
        ),
        y: uint256(
          int256(
            // Scale by 1e18
            200 * 1e18
          ) + ((int256(radius) * Trigonometry.sin(uint256(angleInRadians))))
        )
      });
  }

  function getDecimalStringFrom1e18ScaledUint256(uint256 scaled) internal pure returns (string memory decimal) {
    uint256 partBeforeDecimal = scaled / 1e18;
    uint256 partAfterDecimal = (scaled % 1e18);
    if (partAfterDecimal > 1e17) {
      // Throw out last 12 digits, as that much precision is unnecessary and bloats the string size
      partAfterDecimal = partAfterDecimal / 1e12;
    }
    return string.concat(partBeforeDecimal.toString(), ".", partAfterDecimal.toString());
  }

  function polygon(uint256 radius, uint16 numPoints) internal pure returns (Point[] memory points) {
    points = new Point[](numPoints);

    // Degrees scaled by 1e18 for precision
    unchecked {
      uint256 degreeIncrement = (360 * 1e18) / numPoints;
      for (uint32 i; i < numPoints; ++i) {
        uint256 angleInDegrees = degreeIncrement * i;
        points[i] = polarToCartesian(radius, angleInDegrees);
      }
    }
  }

  function linesSvgFromPoints(
    Point[] memory points,
    uint16 startColorIdx,
    // This is the token id
    uint16 pointWithIdIdx
  ) internal view returns (string memory linesSvg) {
    if (points.length == 1) {
      // Return a dot in the center
      return
        svg.line(
          string.concat(
            svg.prop("x1", "200"),
            svg.prop("y1", "200"),
            svg.prop("x2", "200"),
            svg.prop("y2", "200"),
            svg.prop("stroke-linecap", "round"),
            svg.prop("stroke", Utils.getRGBStr(tokenToColor[startColorIdx])),
            svg.prop("id", "m")
          )
        );
    }

    unchecked {
      for (uint16 i; i < points.length; ++i) {
        bool isEnd = i + 1 == points.length;
        uint32 colorIdx = i + startColorIdx;
        linesSvg = string.concat(
          linesSvg,
          svg.line(
            string.concat(
              svg.prop("x1", getDecimalStringFrom1e18ScaledUint256(points[i].x)),
              svg.prop("y1", getDecimalStringFrom1e18ScaledUint256(points[i].y)),
              svg.prop("x2", getDecimalStringFrom1e18ScaledUint256(isEnd ? points[0].x : points[i + 1].x)),
              svg.prop("y2", getDecimalStringFrom1e18ScaledUint256(isEnd ? points[0].y : points[i + 1].y)),
              svg.prop("stroke", Utils.getRGBStr(tokenToColor[colorIdx])),
              pointWithIdIdx == colorIdx ? svg.prop("id", "m") : ""
            )
          )
        );
      }
    }
  }

  function art(uint16 numberOfPoints) public view returns (string memory) {
    if (!commenced) {
      return "";
    }

    string memory lines;

    unchecked {
      uint256 fullPolygonsCount = numberOfPoints / MAX_POINTS_PER_POLYGON;

      for (uint8 i; i < fullPolygonsCount; ++i) {
        Point[] memory polyPoints = polygon(MAX_RADIUS - i * LINE_WIDTH, MAX_POINTS_PER_POLYGON);
        lines = string.concat(
          lines,
          linesSvgFromPoints(polyPoints, uint16(i * MAX_POINTS_PER_POLYGON + 1), numberOfPoints)
        );
      }

      uint16 remainingPoints = numberOfPoints % MAX_POINTS_PER_POLYGON;

      if (remainingPoints > 0) {
        lines = string.concat(
          lines,
          linesSvgFromPoints(
            polygon(uint16(MAX_RADIUS - fullPolygonsCount * LINE_WIDTH), remainingPoints),
            uint16(fullPolygonsCount * MAX_POINTS_PER_POLYGON + 1),
            numberOfPoints
          )
        );
      }
    }

    return
      string.concat(
        '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 400 400" style="background:',
        Utils.getRGBStr(tokenToBackgroundColor[numberOfPoints]),
        ';display:block;margin:auto"><style>line{stroke-width:10;}</style>',
        lines,
        "</svg>"
      );
  }
}

File 10 of 18 : 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)
library LibString {
  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                        CUSTOM ERRORS                       */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                         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));
    }
    /// @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 {
        // Store the function selector of `HexLengthInsufficient()`.
        mstore(0x00, 0x2194895a)
        // Revert with (offset, size).
        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 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 toHexStringChecksumed(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, and(add(o, 31), not(31))) // 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
          }
        }
      }
    }
  }

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

  // For performance and bytecode compactness, all indices of the following operations
  // are byte (ASCII) offsets, not UTF character offsets.

  /// @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, 32)) {
          h := keccak256(search, searchLength)
        }
        let m := shl(3, sub(32, and(searchLength, 31)))
        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)
      // Zeroize the slot after the string.
      let last := add(add(result, 0x20), k)
      mstore(last, 0)
      // Allocate memory for the length and the bytes,
      // rounded up to a multiple of 32.
      mstore(0x40, and(add(last, 31), not(31)))
      // Store the length of the result.
      mstore(result, k)
    }
  }

  /// @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(32, and(searchLength, 31)))
        let s := mload(add(search, 0x20))

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

        if iszero(lt(searchLength, 32)) {
          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 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
          }
        }
        // Zeroize the slot after the string.
        mstore(output, 0)
        // Store the length.
        let resultLength := sub(output, add(result, 0x20))
        mstore(result, resultLength)
        // Allocate memory for the length and the bytes,
        // rounded up to a multiple of 32.
        mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
      }
    }
  }

  /// @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(31)
        // Copy the `subject` one word at a time, backwards.
        for {
          let o := and(add(resultLength, 31), 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, 63), 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, 32)) {
          h := keccak256(search, searchLength)
        }
        let m := shl(3, sub(32, and(searchLength, 31)))
        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(31)
      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, 31), 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, 63), 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(31)
      result := mload(0x40)
      let aLength := mload(a)
      // Copy `a` one word at a time, backwards.
      for {
        let o := and(add(mload(a), 32), 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, mload(a))
      // Copy `b` one word at a time, backwards.
      for {
        let o := and(add(bLength, 32), 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, 31), w))
    }
  }

  /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
  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)), 67108863)
        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
          }
        }
        // Restore the result.
        result := mload(0x40)
        // Stores the string length.
        mstore(result, length)
        // Zeroize the slot after the string.
        let last := add(add(result, 0x20), length)
        mstore(last, 0)
        // Allocate memory for the length and the bytes,
        // rounded up to a multiple of 32.
        mstore(0x40, and(add(last, 31), not(31)))
      }
    }
  }

  /// @dev Returns a lowercased copy of the string.
  function lower(string memory subject) internal pure returns (string memory result) {
    result = toCase(subject, false);
  }

  /// @dev Returns an UPPERCASED copy of the string.
  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 {
      for {
        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))
      } 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, 31)))
        result := add(result, shr(5, t))
      }
      let last := result
      // Zeroize the slot after the string.
      mstore(last, 0)
      // Restore the result to the start of the free memory.
      result := mload(0x40)
      // Store the length of the result.
      mstore(result, sub(last, add(result, 0x20)))
      // Allocate memory for the length and the bytes,
      // rounded up to a multiple of 32.
      mstore(0x40, and(add(last, 31), not(31)))
    }
  }

  /// @dev Escapes the string to be used within double-quotes in a JSON.
  function escapeJSON(string memory s) internal pure returns (string memory result) {
    /// @solidity memory-safe-assembly
    assembly {
      for {
        let end := add(s, mload(s))
        result := add(mload(0x40), 0x20)
        // 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))
      } 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)
      }
      let last := result
      // Zeroize the slot after the string.
      mstore(last, 0)
      // Restore the result to the start of the free memory.
      result := mload(0x40)
      // Store the length of the result.
      mstore(result, sub(last, add(result, 0x20)))
      // Allocate memory for the length and the bytes,
      // rounded up to a multiple of 32.
      mstore(0x40, and(add(last, 31), not(31)))
    }
  }

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

  /// @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 behaviour 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 behaviour 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 11 of 18 : AllowList.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17.0;

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

/**
 * @notice Smart contract that verifies and tracks allow list redemptions against a configurable Merkle root, up to a
 * max number configured at deploy
 */
contract AllowList is TwoStepOwnable {
    bytes32 public merkleRoot;

    error NotAllowListed();

    ///@notice Checks if msg.sender is included in AllowList, revert otherwise
    ///@param proof Merkle proof
    modifier onlyAllowListed(bytes32[] calldata proof) {
        if (!isAllowListed(proof, msg.sender)) {
            revert NotAllowListed();
        }
        _;
    }

    constructor(bytes32 _merkleRoot) {
        merkleRoot = _merkleRoot;
    }

    ///@notice set the Merkle root in the contract. OnlyOwner.
    ///@param _merkleRoot the new Merkle root
    function setMerkleRoot(bytes32 _merkleRoot) public onlyOwner {
        merkleRoot = _merkleRoot;
    }

    ///@notice Given a Merkle proof, check if an address is AllowListed against the root
    ///@param proof Merkle proof
    ///@param data abi-encoded data to be checked against the root
    ///@return boolean isAllowListed
    function isAllowListed(bytes32[] calldata proof, bytes memory data) public view returns (bool) {
        return verifyCalldata(proof, merkleRoot, keccak256(data));
    }

    ///@notice Given a Merkle proof, check if an address is AllowListed against the root
    ///@param proof Merkle proof
    ///@param addr address to check against allow list
    ///@return boolean isAllowListed
    function isAllowListed(bytes32[] calldata proof, address addr) public view returns (bool) {
        return verifyCalldata(proof, merkleRoot, keccak256(abi.encodePacked(addr)));
    }

    /**
     * @dev Calldata version of {verify}
     * Copied from OpenZeppelin's MerkleProof.sol
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {processProof}
     * Copied from OpenZeppelin's MerkleProof.sol
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length;) {
            computedHash = _hashPair(computedHash, proof[i]);
            unchecked {
                ++i;
            }
        }
        return computedHash;
    }

    /// @dev Copied from OpenZeppelin's MerkleProof.sol
    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    /// @dev Copied from OpenZeppelin's MerkleProof.sol
    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 12 of 18 : IERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

/**
 * @dev Interface of ERC721A.
 */
interface IERC721A {
    /**
     * The caller must own the token or be an approved operator.
     */
    error ApprovalCallerNotOwnerNorApproved();

    /**
     * The token does not exist.
     */
    error ApprovalQueryForNonexistentToken();

    /**
     * Cannot query the balance for the zero address.
     */
    error BalanceQueryForZeroAddress();

    /**
     * Cannot mint to the zero address.
     */
    error MintToZeroAddress();

    /**
     * The quantity of tokens minted must be more than zero.
     */
    error MintZeroQuantity();

    /**
     * The token does not exist.
     */
    error OwnerQueryForNonexistentToken();

    /**
     * The caller must own the token or be an approved operator.
     */
    error TransferCallerNotOwnerNorApproved();

    /**
     * The token must be owned by `from`.
     */
    error TransferFromIncorrectOwner();

    /**
     * Cannot safely transfer to a contract that does not implement the
     * ERC721Receiver interface.
     */
    error TransferToNonERC721ReceiverImplementer();

    /**
     * Cannot transfer to the zero address.
     */
    error TransferToZeroAddress();

    /**
     * The token does not exist.
     */
    error URIQueryForNonexistentToken();

    /**
     * The `quantity` minted with ERC2309 exceeds the safety limit.
     */
    error MintERC2309QuantityExceedsLimit();

    /**
     * The `extraData` cannot be set on an unintialized ownership slot.
     */
    error OwnershipNotInitializedForExtraData();

    // =============================================================
    //                            STRUCTS
    // =============================================================

    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Stores the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
        // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
        uint24 extraData;
    }

    // =============================================================
    //                         TOKEN COUNTERS
    // =============================================================

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() external view returns (uint256);

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);

    // =============================================================
    //                            IERC721
    // =============================================================

    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

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

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

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

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

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

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom}
     * whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token
     * by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

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

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

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

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

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);

    // =============================================================
    //                           IERC2309
    // =============================================================

    /**
     * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
     * (inclusive) is transferred from `from` to `to`, as defined in the
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
     *
     * See {_mintERC2309} for more details.
     */
    event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}

File 13 of 18 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

File 14 of 18 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

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

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

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

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

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

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

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

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

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

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

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

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

File 15 of 18 : LibPRNG.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for generating psuedorandom numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibPRNG.sol)
library LibPRNG {
  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                          STRUCTS                           */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

  /// @dev A psuedorandom number state in memory.
  struct PRNG {
    uint256 state;
  }

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

  /// @dev Seeds the `prng` with `state`.
  function seed(PRNG memory prng, bytes32 state) internal pure {
    /// @solidity memory-safe-assembly
    assembly {
      mstore(prng, state)
    }
  }

  /// @dev Returns a psuedorandom uint256, uniformly distributed
  /// between 0 (inclusive) and `upper` (exclusive).
  /// If your modulus is big, this method is recommended
  /// for uniform sampling to avoid modulo bias.
  /// For uniform sampling across all uint256 values,
  /// or for small enough moduli such that the bias is neligible,
  /// use {next} instead.
  function uniform(PRNG memory prng, uint256 upper) internal pure returns (uint256 result) {
    /// @solidity memory-safe-assembly
    assembly {
      // prettier-ignore
      for {} 1 {} {
                result := keccak256(prng, 0x20)
                mstore(prng, result)
                // prettier-ignore
                if iszero(lt(result, mod(sub(0, upper), upper))) { break }
            }
      result := mod(result, upper)
    }
  }
}

File 16 of 18 : Trigonometry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @notice Solidity library offering basic trigonometry functions where inputs and outputs are
 * integers. Inputs are specified in radians scaled by 1e18, and similarly outputs are scaled by 1e18.
 *
 * This implementation is based off the Solidity trigonometry library written by Lefteris Karapetsas
 * which can be found here: https://github.com/Sikorkaio/sikorka/blob/e75c91925c914beaedf4841c0336a806f2b5f66d/contracts/trigonometry.sol
 *
 * Compared to Lefteris' implementation, this version makes the following changes:
 *   - Uses a 32 bits instead of 16 bits for improved accuracy
 *   - Updated for Solidity 0.8.x
 *   - Various gas optimizations
 *   - Change inputs/outputs to standard trig format (scaled by 1e18) instead of requiring the
 *     integer format used by the algorithm
 *
 * Lefertis' implementation is based off Dave Dribin's trigint C library
 *     http://www.dribin.org/dave/trigint/
 *
 * Which in turn is based from a now deleted article which can be found in the Wayback Machine:
 *     http://web.archive.org/web/20120301144605/http://www.dattalo.com/technical/software/pic/picsine.html
 */
library Trigonometry {
  // Table index into the trigonometric table
  uint256 constant INDEX_WIDTH = 8;
  // Interpolation between successive entries in the table
  uint256 constant INTERP_WIDTH = 16;
  uint256 constant INDEX_OFFSET = 28 - INDEX_WIDTH;
  uint256 constant INTERP_OFFSET = INDEX_OFFSET - INTERP_WIDTH;
  uint32 constant ANGLES_IN_CYCLE = 1073741824;
  uint32 constant QUADRANT_HIGH_MASK = 536870912;
  uint32 constant QUADRANT_LOW_MASK = 268435456;
  uint256 constant SINE_TABLE_SIZE = 256;

  // Pi as an 18 decimal value, which is plenty of accuracy: "For JPL's highest accuracy calculations, which are for
  // interplanetary navigation, we use 3.141592653589793: https://www.jpl.nasa.gov/edu/news/2016/3/16/how-many-decimals-of-pi-do-we-really-need/
  uint256 constant PI = 3141592653589793238;
  uint256 constant TWO_PI = 2 * PI;
  uint256 constant PI_OVER_TWO = PI / 2;

  // The constant sine lookup table was generated by generate_trigonometry.py. We must use a constant
  // bytes array because constant arrays are not supported in Solidity. Each entry in the lookup
  // table is 4 bytes. Since we're using 32-bit parameters for the lookup table, we get a table size
  // of 2^(32/4) + 1 = 257, where the first and last entries are equivalent (hence the table size of
  // 256 defined above)
  uint8 constant entry_bytes = 4; // each entry in the lookup table is 4 bytes
  uint256 constant entry_mask = ((1 << (8 * entry_bytes)) - 1); // mask used to cast bytes32 -> lookup table entry
  bytes constant sin_table =
    hex"00_00_00_00_00_c9_0f_88_01_92_1d_20_02_5b_26_d7_03_24_2a_bf_03_ed_26_e6_04_b6_19_5d_05_7f_00_35_06_47_d9_7c_07_10_a3_45_07_d9_5b_9e_08_a2_00_9a_09_6a_90_49_0a_33_08_bc_0a_fb_68_05_0b_c3_ac_35_0c_8b_d3_5e_0d_53_db_92_0e_1b_c2_e4_0e_e3_87_66_0f_ab_27_2b_10_72_a0_48_11_39_f0_cf_12_01_16_d5_12_c8_10_6e_13_8e_db_b1_14_55_76_b1_15_1b_df_85_15_e2_14_44_16_a8_13_05_17_6d_d9_de_18_33_66_e8_18_f8_b8_3c_19_bd_cb_f3_1a_82_a0_25_1b_47_32_ef_1c_0b_82_6a_1c_cf_8c_b3_1d_93_4f_e5_1e_56_ca_1e_1f_19_f9_7b_1f_dc_dc_1b_20_9f_70_1c_21_61_b3_9f_22_23_a4_c5_22_e5_41_af_23_a6_88_7e_24_67_77_57_25_28_0c_5d_25_e8_45_b6_26_a8_21_85_27_67_9d_f4_28_26_b9_28_28_e5_71_4a_29_a3_c4_85_2a_61_b1_01_2b_1f_34_eb_2b_dc_4e_6f_2c_98_fb_ba_2d_55_3a_fb_2e_11_0a_62_2e_cc_68_1e_2f_87_52_62_30_41_c7_60_30_fb_c5_4d_31_b5_4a_5d_32_6e_54_c7_33_26_e2_c2_33_de_f2_87_34_96_82_4f_35_4d_90_56_36_04_1a_d9_36_ba_20_13_37_6f_9e_46_38_24_93_b0_38_d8_fe_93_39_8c_dd_32_3a_40_2d_d1_3a_f2_ee_b7_3b_a5_1e_29_3c_56_ba_70_3d_07_c1_d5_3d_b8_32_a5_3e_68_0b_2c_3f_17_49_b7_3f_c5_ec_97_40_73_f2_1d_41_21_58_9a_41_ce_1e_64_42_7a_41_d0_43_25_c1_35_43_d0_9a_ec_44_7a_cd_50_45_24_56_bc_45_cd_35_8f_46_75_68_27_47_1c_ec_e6_47_c3_c2_2e_48_69_e6_64_49_0f_57_ee_49_b4_15_33_4a_58_1c_9d_4a_fb_6c_97_4b_9e_03_8f_4c_3f_df_f3_4c_e1_00_34_4d_81_62_c3_4e_21_06_17_4e_bf_e8_a4_4f_5e_08_e2_4f_fb_65_4c_50_97_fc_5e_51_33_cc_94_51_ce_d4_6e_52_69_12_6e_53_02_85_17_53_9b_2a_ef_54_33_02_7d_54_ca_0a_4a_55_60_40_e2_55_f5_a4_d2_56_8a_34_a9_57_1d_ee_f9_57_b0_d2_55_58_42_dd_54_58_d4_0e_8c_59_64_64_97_59_f3_de_12_5a_82_79_99_5b_10_35_ce_5b_9d_11_53_5c_29_0a_cc_5c_b4_20_df_5d_3e_52_36_5d_c7_9d_7b_5e_50_01_5d_5e_d7_7c_89_5f_5e_0d_b2_5f_e3_b3_8d_60_68_6c_ce_60_ec_38_2f_61_6f_14_6b_61_f1_00_3e_62_71_fa_68_62_f2_01_ac_63_71_14_cc_63_ef_32_8f_64_6c_59_bf_64_e8_89_25_65_63_bf_91_65_dd_fb_d2_66_57_3c_bb_66_cf_81_1f_67_46_c7_d7_67_bd_0f_bc_68_32_57_aa_68_a6_9e_80_69_19_e3_1f_69_8c_24_6b_69_fd_61_4a_6a_6d_98_a3_6a_dc_c9_64_6b_4a_f2_78_6b_b8_12_d0_6c_24_29_5f_6c_8f_35_1b_6c_f9_34_fb_6d_62_27_f9_6d_ca_0d_14_6e_30_e3_49_6e_96_a9_9c_6e_fb_5f_11_6f_5f_02_b1_6f_c1_93_84_70_23_10_99_70_83_78_fe_70_e2_cb_c5_71_41_08_04_71_9e_2c_d1_71_fa_39_48_72_55_2c_84_72_af_05_a6_73_07_c3_cf_73_5f_66_25_73_b5_eb_d0_74_0b_53_fa_74_5f_9d_d0_74_b2_c8_83_75_04_d3_44_75_55_bd_4b_75_a5_85_ce_75_f4_2c_0a_76_41_af_3c_76_8e_0e_a5_76_d9_49_88_77_23_5f_2c_77_6c_4e_da_77_b4_17_df_77_fa_b9_88_78_40_33_28_78_84_84_13_78_c7_ab_a1_79_09_a9_2c_79_4a_7c_11_79_8a_23_b0_79_c8_9f_6d_7a_05_ee_ac_7a_42_10_d8_7a_7d_05_5a_7a_b6_cb_a3_7a_ef_63_23_7b_26_cb_4e_7b_5d_03_9d_7b_92_0b_88_7b_c5_e2_8f_7b_f8_88_2f_7c_29_fb_ed_7c_5a_3d_4f_7c_89_4b_dd_7c_b7_27_23_7c_e3_ce_b1_7d_0f_42_17_7d_39_80_eb_7d_62_8a_c5_7d_8a_5f_3f_7d_b0_fd_f7_7d_d6_66_8e_7d_fa_98_a7_7e_1d_93_e9_7e_3f_57_fe_7e_5f_e4_92_7e_7f_39_56_7e_9d_55_fb_7e_ba_3a_38_7e_d5_e5_c5_7e_f0_58_5f_7f_09_91_c3_7f_21_91_b3_7f_38_57_f5_7f_4d_e4_50_7f_62_36_8e_7f_75_4e_7f_7f_87_2b_f2_7f_97_ce_bc_7f_a7_36_b3_7f_b5_63_b2_7f_c2_55_95_7f_ce_0c_3d_7f_d8_87_8d_7f_e1_c7_6a_7f_e9_cb_bf_7f_f0_94_77_7f_f6_21_81_7f_fa_72_d0_7f_fd_88_59_7f_ff_62_15_7f_ff_ff_ff";

  /**
   * @notice Return the sine of a value, specified in radians scaled by 1e18
   * @dev This algorithm for converting sine only uses integer values, and it works by dividing the
   * circle into 30 bit angles, i.e. there are 1,073,741,824 (2^30) angle units, instead of the
   * standard 360 degrees (2pi radians). From there, we get an output in range -2,147,483,647 to
   * 2,147,483,647, (which is the max value of an int32) which is then converted back to the standard
   * range of -1 to 1, again scaled by 1e18
   * @param _angle Angle to convert
   * @return Result scaled by 1e18
   */
  function sin(uint256 _angle) internal pure returns (int256) {
    unchecked {
      // Convert angle from from arbitrary radian value (range of 0 to 2pi) to the algorithm's range
      // of 0 to 1,073,741,824
      _angle = (ANGLES_IN_CYCLE * (_angle % TWO_PI)) / TWO_PI;

      // Apply a mask on an integer to extract a certain number of bits, where angle is the integer
      // whose bits we want to get, the width is the width of the bits (in bits) we want to extract,
      // and the offset is the offset of the bits (in bits) we want to extract. The result is an
      // integer containing _width bits of _value starting at the offset bit
      uint256 interp = (_angle >> INTERP_OFFSET) & ((1 << INTERP_WIDTH) - 1);
      uint256 index = (_angle >> INDEX_OFFSET) & ((1 << INDEX_WIDTH) - 1);

      // The lookup table only contains data for one quadrant (since sin is symmetric around both
      // axes), so here we figure out which quadrant we're in, then we lookup the values in the
      // table then modify values accordingly
      bool is_odd_quadrant = (_angle & QUADRANT_LOW_MASK) == 0;
      bool is_negative_quadrant = (_angle & QUADRANT_HIGH_MASK) != 0;

      if (!is_odd_quadrant) {
        index = SINE_TABLE_SIZE - 1 - index;
      }

      bytes memory table = sin_table;
      // We are looking for two consecutive indices in our lookup table
      // Since EVM is left aligned, to read n bytes of data from idx i, we must read from `i * data_len` + `n`
      // therefore, to read two entries of size entry_bytes `index * entry_bytes` + `entry_bytes * 2`
      uint256 offset1_2 = (index + 2) * entry_bytes;

      // This following snippet will function for any entry_bytes <= 15
      uint256 x1_2;
      assembly {
        // mload will grab one word worth of bytes (32), as that is the minimum size in EVM
        x1_2 := mload(add(table, offset1_2))
      }

      // We now read the last two numbers of size entry_bytes from x1_2
      // in example: entry_bytes = 4; x1_2 = 0x00...12345678abcdefgh
      // therefore: entry_mask = 0xFFFFFFFF

      // 0x00...12345678abcdefgh >> 8*4 = 0x00...12345678
      // 0x00...12345678 & 0xFFFFFFFF = 0x12345678
      uint256 x1 = (x1_2 >> (8 * entry_bytes)) & entry_mask;
      // 0x00...12345678abcdefgh & 0xFFFFFFFF = 0xabcdefgh
      uint256 x2 = x1_2 & entry_mask;

      // Approximate angle by interpolating in the table, accounting for the quadrant
      uint256 approximation = ((x2 - x1) * interp) >> INTERP_WIDTH;
      int256 sine = is_odd_quadrant ? int256(x1) + int256(approximation) : int256(x2) - int256(approximation);
      if (is_negative_quadrant) {
        sine *= -1;
      }

      // Bring result from the range of -2,147,483,647 through 2,147,483,647 to -1e18 through 1e18.
      // This can never overflow because sine is bounded by the above values
      return (sine * 1e18) / 2_147_483_647;
    }
  }

  /**
   * @notice Return the cosine of a value, specified in radians scaled by 1e18
   * @dev This is identical to the sin() method, and just computes the value by delegating to the
   * sin() method using the identity cos(x) = sin(x + pi/2)
   * @dev Overflow when `angle + PI_OVER_TWO > type(uint256).max` is ok, results are still accurate
   * @param _angle Angle to convert
   * @return Result scaled by 1e18
   */
  function cos(uint256 _angle) internal pure returns (int256) {
    unchecked {
      return sin(_angle + PI_OVER_TWO);
    }
  }
}

File 17 of 18 : SVG.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

// Core SVG utility library which helps us construct
// onchain SVG's with a simple, web-like API.
// Credit to w1nt3r.eth for the base of this.
library svg {
  /* MAIN ELEMENTS */

  function line(string memory _props) internal pure returns (string memory) {
    return string.concat("<line ", _props, "/>");
  }

  /* COMMON */

  // an SVG attribute
  function prop(string memory _key, string memory _val) internal pure returns (string memory) {
    return string.concat(_key, "=", '"', _val, '" ');
  }
}

File 18 of 18 : TwoStepOwnable.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

/**
 * @notice A two-step extension of Ownable, where the new owner must claim ownership of the contract after owner
 * initiates transfer
 * Owner can cancel the transfer at any point before the new owner claims ownership.
 * Helpful in guarding against transferring ownership to an address that is unable to act as the Owner.
 */
abstract contract TwoStepOwnable {
    address private _owner;

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

    address internal potentialOwner;

    event PotentialOwnerUpdated(address newPotentialAdministrator);

    error NewOwnerIsZeroAddress();
    error NotNextOwner();
    error OnlyOwner();

    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    constructor() {
        _transferOwnership(msg.sender);
    }

    ///@notice Initiate ownership transfer to newPotentialOwner. Note: new owner will have to manually acceptOwnership
    ///@param newPotentialOwner address of potential new owner
    function transferOwnership(address newPotentialOwner) public virtual onlyOwner {
        if (newPotentialOwner == address(0)) {
            revert NewOwnerIsZeroAddress();
        }
        potentialOwner = newPotentialOwner;
        emit PotentialOwnerUpdated(newPotentialOwner);
    }

    ///@notice Claim ownership of smart contract, after the current owner has initiated the process with
    /// transferOwnership
    function acceptOwnership() public virtual {
        address _potentialOwner = potentialOwner;
        if (msg.sender != _potentialOwner) {
            revert NotNextOwner();
        }
        delete potentialOwner;
        emit PotentialOwnerUpdated(address(0));
        _transferOwnership(_potentialOwner);
    }

    ///@notice cancel ownership transfer
    function cancelOwnershipTransfer() public virtual onlyOwner {
        delete potentialOwner;
        emit PotentialOwnerUpdated(address(0));
    }

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

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

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

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

Settings
{
  "remappings": [
    "@prb/test/=lib/prb-test/src/",
    "forge-std/=lib/forge-std/src/",
    "src/=src/",
    "@erc721a/=lib/ERC721A/contracts/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@solady/=lib/solady/src/",
    "operator-filter-registry/=lib/operator-filter-registry/",
    "ERC721A/=lib/ERC721A/contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/operator-filter-registry/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/operator-filter-registry/lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "prb-math/=lib/prb-math/src/",
    "prb-test/=lib/prb-test/src/",
    "solady/=lib/solady/src/",
    "solidity-trigonometry/=lib/solidity-trigonometry/src/",
    "solmate/=lib/solady/lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 20
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"bytes32","name":"allowListMerkleRoot","type":"bytes32"},{"internalType":"uint16","name":"allowListMintMaxTotalVal","type":"uint16"},{"internalType":"uint8","name":"allowListMintMaxPerWalletVal","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AllowListMintCapExceeded","type":"error"},{"inputs":[],"name":"AllowListMintCapPerWalletExceeded","type":"error"},{"inputs":[],"name":"AlreadyCommenced","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"IncorrectPayment","type":"error"},{"inputs":[],"name":"MaxSupplyReached","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintNotStarted","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"MsgSenderDoesNotOwnXXYYZZToken","type":"error"},{"inputs":[],"name":"MsgSenderNotTokenOwner","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NotAllowListed","type":"error"},{"inputs":[],"name":"NotNextOwner","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"PublicMintMaxPerTransactionExceeded","type":"error"},{"inputs":[],"name":"PublicMintNotEnabled","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newPotentialAdministrator","type":"address"}],"name":"PotentialOwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"numberOfPoints","type":"uint16"}],"name":"art","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint24","name":"xxyyzzTokenId","type":"uint24"}],"name":"colorBackground","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"commence","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"isAllowListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"address","name":"addr","type":"address"}],"name":"isAllowListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"uint8","name":"amount","type":"uint8"}],"name":"mintAllowList","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8","name":"amount","type":"uint8"}],"name":"mintPublic","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicMintEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newPotentialOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_publicMintEnabled","type":"bool"}],"name":"updatePublicMintEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c06040523480156200001157600080fd5b5060405162003d1238038062003d12833981016040819052620000349162000110565b82604051806040016040528060088152602001672a37b3b2ba3432b960c11b815250604051806040016040528060038152602001622a23a960e91b8152508160cd908162000083919062000208565b5060ce62000092828262000208565b5050600160cb5550620000a533620000be565b60d55561ffff90911660805260ff1660a05250620002d4565b60d380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000806000606084860312156200012657600080fd5b83519250602084015161ffff811681146200014057600080fd5b604085015190925060ff811681146200015857600080fd5b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200018e57607f821691505b602082108103620001af57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200020357600081815260208120601f850160051c81016020861015620001de5750805b601f850160051c820191505b81811015620001ff57828155600101620001ea565b5050505b505050565b81516001600160401b0381111562000224576200022462000163565b6200023c8162000235845462000179565b84620001b5565b602080601f8311600181146200027457600084156200025b5750858301515b600019600386901b1c1916600185901b178555620001ff565b600085815260208120601f198616915b82811015620002a55788860151825594840194600190910190840162000284565b5085821015620002c45787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a051613a18620002fa6000396000610c9601526000610c2e0152613a186000f3fe60806040526004361061018e5760003560e01c806370a08231116100dd57806370a0823114610385578063715018a6146103a55780637449da48146103ba57806379ba5097146103cd5780637cb64759146103e257806383281cec146104025780638d859f3e146104225780638da5cb5b1461043d57806395d89b411461045b578063a22cb46514610470578063a6b4086814610490578063b88d4fde146104b0578063c87b56dd146104c3578063dbd76cd9146104e3578063e985e9c5146104f8578063f2fde38b14610518578063f3b674a41461053857600080fd5b806301ffc9a71461019357806306fdde03146101c8578063081812fc146101ea57806308fe4be614610217578063095ea7b3146102375780630f4161aa1461024c57806318160ddd1461026657806323452b9c1461028d57806323b872dd146102a25780632a55205a146102b55780632eb4a7ab146102f45780633ccfd60b1461030a57806342842e0e1461031f57806356ff1be8146103325780636352211e1461035257806367dce1ed14610372575b600080fd5b34801561019f57600080fd5b506101b36101ae36600461233e565b610558565b60405190151581526020015b60405180910390f35b3480156101d457600080fd5b506101dd610583565b6040516101bf91906123ab565b3480156101f657600080fd5b5061020a6102053660046123be565b610615565b6040516101bf91906123d7565b34801561022357600080fd5b506101dd6102323660046123eb565b610659565b61024a610245366004612424565b6107b8565b005b34801561025857600080fd5b5060d6546101b39060ff1681565b34801561027257600080fd5b5060cc5460cb5403600019015b6040519081526020016101bf565b34801561029957600080fd5b5061024a610858565b61024a6102b0366004612450565b610899565b3480156102c157600080fd5b506102d56102d0366004612491565b610a1f565b604080516001600160a01b0390931683526020830191909152016101bf565b34801561030057600080fd5b5061027f60d55481565b34801561031657600080fd5b5061024a610a44565b61024a61032d366004612450565b610ab0565b34801561033e57600080fd5b5061024a61034d3660046124c8565b610ad0565b34801561035e57600080fd5b5061020a61036d3660046123be565b610aeb565b61024a6103803660046124f4565b610af6565b34801561039157600080fd5b5061027f6103a036600461250f565b610b74565b3480156103b157600080fd5b5061024a610bc2565b61024a6103c8366004612570565b610bd6565b3480156103d957600080fd5b5061024a610d0f565b3480156103ee57600080fd5b5061024a6103fd3660046123be565b610d7b565b34801561040e57600080fd5b506101b361041d366004612665565b610d88565b34801561042e57600080fd5b5061027f662386f26fc1000081565b34801561044957600080fd5b5060d3546001600160a01b031661020a565b34801561046757600080fd5b506101dd610da9565b34801561047c57600080fd5b5061024a61048b3660046126cd565b610db8565b34801561049c57600080fd5b5061024a6104ab366004612702565b610e24565b61024a6104be36600461273a565b610f81565b3480156104cf57600080fd5b506101dd6104de3660046123be565b610fcb565b3480156104ef57600080fd5b5061024a611062565b34801561050457600080fd5b506101b36105133660046127a5565b6110a6565b34801561052457600080fd5b5061024a61053336600461250f565b6110d4565b34801561054457600080fd5b506101b36105533660046127d3565b611147565b60006001600160e01b0319821663152a902d60e11b148061057d575061057d8261118a565b92915050565b606060cd805461059290612829565b80601f01602080910402602001604051908101604052809291908181526020018280546105be90612829565b801561060b5780601f106105e05761010080835404028352916020019161060b565b820191906000526020600020905b8154815290600101906020018083116105ee57829003601f168201915b5050505050905090565b6000610620826111d8565b61063d576040516333d1c03960e21b815260040160405180910390fd5b50600090815260d160205260409020546001600160a01b031690565b60ca5460609060ff1661067a57505060408051602081019091526000815290565b60606000606461ffff85160461ffff16905060005b818160ff1610156106f35760006106b060ff600a840260780316606461120d565b9050836106c7826064850260010160ff16896112ed565b6040516020016106d8929190612895565b60408051601f1981840301815291905293505060010161068f565b506000606461ffff861606905061ffff811615610752578261072f61072261ffff600a8602607803168461120d565b60016064860201886112ed565b604051602001610740929190612895565b60405160208183030381529060405292505b505061078f60658461ffff166103e9811061076f5761076f6128c4565b600a91828204019190066003029054906101000a900462ffffff16611744565b816040516020016107a19291906128da565b604051602081830303815290604052915050919050565b60006107c382610aeb565b9050336001600160a01b038216146107fc576107df81336110a6565b6107fc576040516367d9dca160e11b815260040160405180910390fd5b600082815260d1602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b61086061177c565b60d480546001600160a01b03191690556040516000805160206139d88339815191529061088f906000906123d7565b60405180910390a1565b60006108a4826117a7565b9050836001600160a01b0316816001600160a01b0316146108d75760405162a1148160e81b815260040160405180910390fd5b600082815260d1602052604090208054338082146001600160a01b038816909114176109245761090786336110a6565b61092457604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b03851661094b57604051633a954ecd60e21b815260040160405180910390fd5b801561095657600082555b6001600160a01b03868116600090815260d0602052604080822080546000190190559187168152208054600101905561099385600160e11b611816565b600085815260cf6020526040812091909155600160e11b841690036109e85760018401600081815260cf602052604081205490036109e65760cb5481146109e657600081815260cf602052604090208490555b505b83856001600160a01b0316876001600160a01b03166000805160206139f883398151915260405160405180910390a4505050505050565b7339ab90066cec746a032d67e4fe3378f16294cf6b61271060fa8302045b9250929050565b6040516000907339ab90066cec746a032d67e4fe3378f16294cf6b9047908381818185875af1925050503d8060008114610a9a576040519150601f19603f3d011682016040523d82523d6000602084013e610a9f565b606091505b5050905080610aad57600080fd5b50565b610acb83838360405180602001604052806000815250610f81565b505050565b610ad861177c565b60d6805460ff1916911515919091179055565b600061057d826117a7565b60d65460ff16610b19576040516393e8222560e01b815260040160405180910390fd5b600560ff82161115610b3e576040516392e5b1c960e01b815260040160405180910390fd5b34662386f26fc100008260ff160214610b6a5760405163569e8c1160e01b815260040160405180910390fd5b610aad338261182b565b60006001600160a01b038216610b9d576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b0316600090815260d060205260409020546001600160401b031690565b610bca61177c565b610bd46000611918565b565b8282610be3828233611147565b610c0057604051630e5060e160e21b815260040160405180910390fd5b34662386f26fc100008460ff160214610c2c5760405163569e8c1160e01b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000061ffff168360ff16610c6260cb546000190190565b011115610c8257604051637c44f5af60e11b815260040160405180910390fd5b33600090815260d7602052604090205460ff7f000000000000000000000000000000000000000000000000000000000000000081169181168501161115610cdc57604051632ae6c11560e21b815260040160405180910390fd5b33600081815260d760205260409020805460ff80821687011660ff19909116179055610d08908461182b565b5050505050565b60d4546001600160a01b0316338114610d3b57604051636b7584e760e11b815260040160405180910390fd5b60d480546001600160a01b03191690556040516000805160206139d883398151915290610d6a906000906123d7565b60405180910390a1610aad81611918565b610d8361177c565b60d555565b6000610d9f848460d554858051906020012061196a565b90505b9392505050565b606060ce805461059290612829565b33600081815260d2602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b33610e2e83610aeb565b6001600160a01b031614610e5557604051633533b18360e11b815260040160405180910390fd5b62ffffff811615801590610eea57506040516331a9108f60e11b815262ffffff82166004820152339073ff6000a85baac9c4854faa7155e70ba850bf726b90636352211e90602401602060405180830381865afa158015610eba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ede91906129fe565b6001600160a01b031614155b15610f0857604051630140282360e71b815260040160405180910390fd5b806065836103e98110610f1d57610f1d6128c4565b600a91828204019190066003026101000a81548162ffffff021916908362ffffff1602179055507ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce782604051610f7591815260200190565b60405180910390a15050565b610f8c848484610899565b6001600160a01b0383163b15610fc557610fa884848484611984565b610fc5576040516368d2bf6b60e11b815260040160405180910390fd5b50505050565b6060610fd6826111d8565b610ff357604051630a14c4b560e41b815260040160405180910390fd5b6000610ffe83610659565b9050610da28361100d83611a6c565b611035846040516020016110219190612a1b565b604051602081830303815290604052611a87565b6040516020016110459190612e5e565b60405160208183030381529060405261105d87611bd9565b611d37565b61106a61177c565b60ca5460ff161561108e5760405163532c890b60e11b815260040160405180910390fd5b60ca805460ff19166001179055610bd433600561182b565b6001600160a01b03918216600090815260d26020908152604080832093909416825291909152205460ff1690565b6110dc61177c565b6001600160a01b03811661110357604051633a247dd760e11b815260040160405180910390fd5b60d480546001600160a01b0319166001600160a01b0383161790556040516000805160206139d88339815191529061113c9083906123d7565b60405180910390a150565b60d5546040516001600160601b0319606084901b166020820152600091610d9f91869186916034016040516020818303038152906040528051906020012061196a565b60006301ffc9a760e01b6001600160e01b0319831614806111bb57506380ac58cd60e01b6001600160e01b03198316145b8061057d5750506001600160e01b031916635b5e139f60e01b1490565b6000816001111580156111ec575060cb5482105b801561057d575050600090815260cf6020526040902054600160e01b161590565b60608161ffff166001600160401b0381111561122b5761122b6125c3565b60405190808252806020026020018201604052801561126457816020015b61125161230e565b8152602001906001900390816112495790505b50905060008261ffff1668138400eca364a000008161128557611285612863565b046001600160481b0316905060005b8361ffff168163ffffffff1610156112e55763ffffffff811682026112b98682611d84565b848363ffffffff16815181106112d1576112d16128c4565b602090810291909101015250600101611294565b505092915050565b606083516001036114f4576114ed61133a60405180604001604052806002815260200161783160f01b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b61137960405180604001604052806002815260200161793160f01b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6113b8604051806040016040528060028152602001613c1960f11b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6113f7604051806040016040528060028152602001613c9960f11b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6114446040518060400160405280600e81526020016d07374726f6b652d6c696e656361760941b815250604051806040016040528060058152602001641c9bdd5b9960da1b815250611e56565b611486604051806040016040528060068152602001657374726f6b6560d01b81525061148160008c61ffff166103e9811061076f5761076f6128c4565b611e56565b6114c3604051806040016040528060028152602001611a5960f21b815250604051806040016040528060018152602001606d60f81b815250611e56565b6040516020016114d99796959493929190612e9c565b604051602081830303815290604052611e82565b9050610da2565b60005b84518161ffff16101561173c57600085518260010161ffff16149050600085830161ffff1690508361170d61156a60405180604001604052806002815260200161783160f01b8152506114818b8861ffff1681518110611559576115596128c4565b602002602001015160000151611e95565b6115b260405180604001604052806002815260200161793160f01b8152506114818c8961ffff16815181106115a1576115a16128c4565b602002602001015160200151611e95565b611604604051806040016040528060028152602001613c1960f11b815250611481886115f1578d8a60010161ffff1681518110611559576115596128c4565b8d600081518110611559576115596128c4565b611656604051806040016040528060028152602001613c9960f11b81525061148189611643578e8b60010161ffff16815181106115a1576115a16128c4565b8e6000815181106115a1576115a16128c4565b611695604051806040016040528060068152602001657374726f6b6560d01b81525061148160008a63ffffffff166103e9811061076f5761076f6128c4565b8763ffffffff168c61ffff16146116bb57604051806020016040528060008152506116f8565b6116f8604051806040016040528060028152602001611a5960f21b815250604051806040016040528060018152602001606d60f81b815250611e56565b6040516020016114d996959493929190612f2e565b60405160200161171e929190612895565b604051602081830303815290604052935050508060010190506114f7565b509392505050565b606061175662ffffff83166003611f20565b6040516020016117669190612fad565b6040516020818303038152906040529050919050565b60d3546001600160a01b03163314610bd457604051635fc483c560e01b815260040160405180910390fd5b600081806001116117fd5760cb548110156117fd57600081815260cf602052604081205490600160e01b821690036117fb575b80600003610da2575060001901600081815260cf60205260409020546117da565b505b604051636f96cda160e11b815260040160405180910390fd5b4260a01b176001600160a01b03919091161790565b60ca5460ff1661184e57604051630314872760e11b815260040160405180910390fd5b600061185960cb5490565b905060ff821681016103e910156118835760405163d05cb60960e01b815260040160405180910390fd5b805b8260ff16820181101561190a57604080514460208201529081018290526118c49060600160405160208183030381529060405280519060200120611fa8565b6000826103e981106118d8576118d86128c4565b600a91828204019190066003026101000a81548162ffffff021916908362ffffff160217905550806001019050611885565b50610acb838360ff16612005565b60d380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000826119788686856120ed565b1490505b949350505050565b604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a02906119b9903390899088908890600401612fd6565b6020604051808303816000875af19250505080156119f4575060408051601f3d908101601f191682019092526119f191810190613013565b60015b611a52573d808015611a22576040519150601f19603f3d011682016040523d82523d6000602084013e611a27565b606091505b508051600003611a4a576040516368d2bf6b60e11b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061197c565b6060611a7782611a87565b6040516020016117669190613030565b60608151600003611aa657505060408051602081019091526000815290565b600060405180606001604052806040815260200161399860409139905060006003845160020181611ad957611ad9612863565b0460040290506000816020016001600160401b03811115611afc57611afc6125c3565b6040519080825280601f01601f191660200182016040528015611b26576020820181803683370190505b509050818152600183018586518101602084015b81831015611b945760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b93820193909352600401611b3a565b600389510660018114611bae5760028114611bbf57611bcb565b613d3d60f01b600119830152611bcb565b603d60f81b6000198301525b509398975050505050505050565b60606000606483069050611c1c6040518060400160405280600c81526020016b546f74616c20506f696e747360a01b815250611c148561212f565b600180612173565b604080518082019091526005815264088cae0e8d60db1b6020820152611c4890611c146064870461212f565b611c8e60405180604001604052806012815260200171496e6e657220536861706520506f696e747360701b815250611c1460008611611c8857606461212f565b8561212f565b611cd86040518060400160405280600e81526020016d2732bb902634b7329021b7b637b960911b815250611ccf6000896103e9811061076f5761076f6128c4565b60006001612173565b611d236040518060400160405280601081526020016f2130b1b5b3b937bab7321021b7b637b960811b815250611d1b60658a6103e9811061076f5761076f6128c4565b600080612173565b6040516020016107a1959493929190613072565b6060611d5b611d458661212f565b83868660405160200161102194939291906130fc565b604051602001611d6b91906132eb565b6040516020818303038152906040529050949350505050565b611d8c61230e565b6000611da16002672b992ddfa23249d6613346565b611db4672b992ddfa23249d6600261335a565b611dbe9190613371565b6809c2007651b2500000611dda672b992ddfa23249d686613384565b611de491906133b4565b611dee91906133e2565b90506040518060400160405280611e04836121e9565b611e0e9087613384565b611e2190680ad78ebc5ac62000006133e2565b8152602001611e2f836121fa565b611e399087613384565b611e4c90680ad78ebc5ac62000006133e2565b9052949350505050565b60608282604051602001611e6b929190613402565b604051602081830303815290604052905092915050565b6060816040516020016117669190613457565b60606000611eab670de0b6b3a764000084613346565b90506000611ec1670de0b6b3a764000085613491565b905067016345785d8a0000811115611ee557611ee264e8d4a5100082613346565b90505b611eee8261212f565b611ef78261212f565b604051602001611f089291906134a5565b60405160208183030381529060405292505050919050565b6060601f1960428360011b01166040510190506020810160405260008152806f30313233343536373839616263646566600f528283018203600119855b600f811651948201946001860153600f8160041c1651855360081c848303611f5d578015611f9357632194895a6000526004601cfd5b505050819003601f1990910190815292915050565b6000611fc06040518060200160405280600081525090565b828152611fcf816101006122c4565b60ff166008611fe0836101006122c4565b62ffffff16901b6010611ff5846101006122c4565b62ffffff16901b01019392505050565b60cb54600082900361202a5760405163b562e8dd60e01b815260040160405180910390fd5b6001600160a01b038316600090815260d06020526040902080546001600160401b018402019055612061836001841460e11b611816565b600082815260cf60205260408120919091556001600160a01b0384169083830190839083906000805160206139f88339815191528180a4600183015b8181146120c357808360006000805160206139f8833981519152600080a460010161209d565b50816000036120e457604051622e076360e81b815260040160405180910390fd5b60cb5550505050565b600081815b848110156121265761211c82878784818110612110576121106128c4565b905060200201356122e2565b91506001016120f2565b50949350505050565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a90048061214a575050819003601f19909101908152919050565b606084836121a0578460405160200161218c91906134e1565b6040516020818303038152906040526121a2565b845b836121bc57604051806020016040528060008152506121d7565b604051806040016040528060018152602001600b60fa1b8152505b604051602001611d6b9392919061350f565b600061057d6715cc96efd11924eb83015b6757325bbf446493ac9081900663400000000204600061ffff600483901c1660ff601484901c1663100000008416156320000000851615158161223f5760ff92909203915b60006040518061044001604052806104048152602001613594610404913960046002860102818101519192509063ffffffff602082901c8116908216818103890260101c60008861229257818303612296565b8184015b905087156122a2576000035b637fffffff670de0b6b3a76400008202059d9c50505050505050505050505050565b60005b60208320905080835281826000030681106122c75706919050565b60008183106122fe576000828152602084905260409020610da2565b5060009182526020526040902090565b604051806040016040528060008152602001600081525090565b6001600160e01b031981168114610aad57600080fd5b60006020828403121561235057600080fd5b8135610da281612328565b60005b8381101561237657818101518382015260200161235e565b50506000910152565b6000815180845261239781602086016020860161235b565b601f01601f19169290920160200192915050565b602081526000610da2602083018461237f565b6000602082840312156123d057600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6000602082840312156123fd57600080fd5b813561ffff81168114610da257600080fd5b6001600160a01b0381168114610aad57600080fd5b6000806040838503121561243757600080fd5b82356124428161240f565b946020939093013593505050565b60008060006060848603121561246557600080fd5b83356124708161240f565b925060208401356124808161240f565b929592945050506040919091013590565b600080604083850312156124a457600080fd5b50508035926020909101359150565b803580151581146124c357600080fd5b919050565b6000602082840312156124da57600080fd5b610da2826124b3565b803560ff811681146124c357600080fd5b60006020828403121561250657600080fd5b610da2826124e3565b60006020828403121561252157600080fd5b8135610da28161240f565b60008083601f84011261253e57600080fd5b5081356001600160401b0381111561255557600080fd5b6020830191508360208260051b8501011115610a3d57600080fd5b60008060006040848603121561258557600080fd5b83356001600160401b0381111561259b57600080fd5b6125a78682870161252c565b90945092506125ba9050602085016124e3565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126125ea57600080fd5b81356001600160401b0380821115612604576126046125c3565b604051601f8301601f19908116603f0116810190828211818310171561262c5761262c6125c3565b8160405283815286602085880101111561264557600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006040848603121561267a57600080fd5b83356001600160401b038082111561269157600080fd5b61269d8783880161252c565b909550935060208601359150808211156126b657600080fd5b506126c3868287016125d9565b9150509250925092565b600080604083850312156126e057600080fd5b82356126eb8161240f565b91506126f9602084016124b3565b90509250929050565b6000806040838503121561271557600080fd5b82359150602083013562ffffff8116811461272f57600080fd5b809150509250929050565b6000806000806080858703121561275057600080fd5b843561275b8161240f565b9350602085013561276b8161240f565b92506040850135915060608501356001600160401b0381111561278d57600080fd5b612799878288016125d9565b91505092959194509250565b600080604083850312156127b857600080fd5b82356127c38161240f565b9150602083013561272f8161240f565b6000806000604084860312156127e857600080fd5b83356001600160401b038111156127fe57600080fd5b61280a8682870161252c565b909450925050602084013561281e8161240f565b809150509250925092565b600181811c9082168061283d57607f821691505b60208210810361285d57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b6000815161288b81856020860161235b565b9290920192915050565b600083516128a781846020880161235b565b8351908301906128bb81836020880161235b565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d223130302522206865696768743d2231303060208201527f25222076696577426f783d22302030203430302034303022207374796c653d2260408201526a3130b1b5b3b937bab7321d60a91b60608201526000835161297281606b85016020880161235b565b7f3b646973706c61793a626c6f636b3b6d617267696e3a6175746f223e3c737479606b918401918201527f6c653e6c696e657b7374726f6b652d77696474683a31303b7d3c2f7374796c65608b820152601f60f91b60ab82015283516129df8160ac84016020880161235b565b651e17b9bb339f60d11b60ac929091019182015260b201949350505050565b600060208284031215612a1057600080fd5b8151610da28161240f565b7f3c68746d6c207374796c653d226f766572666c6f773a68696464656e223e3c6281527437b23c9039ba3cb6329e9136b0b933b4b71d18111f60591b602082015260008251612a7181603585016020870161235b565b7f3c7363726970743e6c657420613d21312c623d21312c6f3d41727261792e667260359390910192830152507f6f6d28646f63756d656e742e676574456c656d656e747342795461674e616d6560558201527f28226c696e652229292e6d617028653d3e652e7374796c652e7374726f6b652960758201527f2c733d653d3e6e65772050726f6d69736528743d3e73657454696d656f75742860958201527f742c6529292c633d28293d3e4d6174682e666c6f6f72283235362a4d6174682e60b58201527f72616e646f6d2829293b646f63756d656e742e626f64792e6164644576656e7460d58201527f4c697374656e65722822636c69636b222c6173796e6328293d3e7b696628216160f58201527f7c7c622972657475726e3b623d21303b6c657420653d646f63756d656e742e676101158201527f6574456c656d656e747342795461674e616d6528226c696e6522293b666f72286101358201527f3b613b297b666f72286c657420743d303b743c652e6c656e6774683b742b2b296101558201527f655b745d2e7374796c652e7374726f6b653d6072676228247b6328297d2c247b6101758201527f6328297d2c247b6328297d29603b61776169742073283530297d666f72286c656101958201527f74206c3d303b6c3c652e6c656e6774683b6c2b2b29655b6c5d2e7374796c652e6101b58201527f7374726f6b653d6f5b6c5d3b623d21317d2c2130292c646f63756d656e742e626101d58201527f6f64792e6164644576656e744c697374656e65722822636c69636b222c6173796101f58201527f6e6328293d3e7b696628612972657475726e3b613d21303b6c657420653d646f6102158201527f63756d656e742e676574456c656d656e747342795461674e616d6528226c696e6102358201527f6522293b666f72286c657420743d303b743c322a652e6c656e6774683b742b2b6102558201527f297b6c6574206c3d743c652e6c656e6774683f2230223a2231222c6e3d743c656102758201527f2e6c656e6774683f743a322a652e6c656e6774682d742d313b226d22213d3d656102958201527f5b6e5d2e6964262628655b6e5d2e7374796c652e7374726f6b654f70616369746102b58201527f793d6c293b6c657420673d652e6c656e677468253130303b61776169742073286102d58201527f28743c652e6c656e6774683f743e3d652e6c656e6774682d673a743e652e6c656102f58201527f6e6774682626742d652e6c656e6774683c3d67293f32302b283130302d67292f6103158201527f3130302a37353a3130297d613d21317d2c2130293b3c2f7363726970743e3c2f6103358201526b3137b23c9f1e17b43a36b61f60a11b61035582015261036101919050565b7519185d184e9d195e1d0bda1d1b5b0ed8985cd94d8d0b60521b815260008251612e8f81601685016020870161235b565b9190910160160192915050565b600088516020612eaf8285838e0161235b565b895191840191612ec28184848e0161235b565b8951920191612ed48184848d0161235b565b8851920191612ee68184848c0161235b565b8751920191612ef88184848b0161235b565b8651920191612f0a8184848a0161235b565b8551920191612f1c818484890161235b565b919091019a9950505050505050505050565b600087516020612f418285838d0161235b565b885191840191612f548184848d0161235b565b8851920191612f668184848c0161235b565b8751920191612f788184848b0161235b565b8651920191612f8a8184848a0161235b565b8551920191612f9c818484890161235b565b919091019998505050505050505050565b602360f81b815260008251612fc981600185016020870161235b565b9190910160010192915050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906130099083018461237f565b9695505050505050565b60006020828403121561302557600080fd5b8151610da281612328565b7919185d184e9a5b5859d94bdcdd99cade1b5b0ed8985cd94d8d0b60321b81526000825161306581601a85016020870161235b565b91909101601a0192915050565b605b60f81b815260006001875161308f8183860160208c0161235b565b8751908401906130a58184840160208c0161235b565b87519101906130ba8184840160208b0161235b565b86519101906130cf8184840160208a0161235b565b85519101906130e4818484016020890161235b565b605d60f81b9101820190815201979650505050505050565b727b226e616d65223a22546f676574686572202360681b8152845160009061312b816013850160208a0161235b565b7f222c226465736372697074696f6e223a22546f676574686572206973206120676013918401918201527f656e65726174697665206f6e636861696e204e4654207769746820636f6c6f7260338201527f66756c2061727420666f726d656420627920697473206d696e746572732e205460538201527f686520617274206f66206561636820746f6b656e206275696c6473206f6e207460738201527f686520617274206f6620616c6c2070726576696f757320746f6b656e7320706c60938201527f75732061206e6577206c696e652067656e65726174656420617420746865207460b38201527f696d65206f66206d696e742e20546170206f6e636520746f20756e77696e642060d38201527f796f7572206172742e2054617020747769636520746f20756e77696e6420796f60f3820152763ab91036b4b7321711161130ba3a3934b13aba32b9911d60491b61011382015261328e61012a820187612879565b69161134b6b0b3b2911d1160b11b815290506132ad600a820186612879565b7211161130b734b6b0ba34b7b72fbab936111d1160691b815290506132d56013820185612879565b61227d60f01b8152600201979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161332381601d85016020870161235b565b91909101601d0192915050565b634e487b7160e01b600052601160045260246000fd5b60008261335557613355612863565b500490565b808202811582820484141761057d5761057d613330565b8181038181111561057d5761057d613330565b80820260008212600160ff1b841416156133a0576133a0613330565b818105831482151761057d5761057d613330565b6000826133c3576133c3612863565b600160ff1b8214600019841416156133dd576133dd613330565b500590565b80820182811260008312801582168215821617156112e5576112e5613330565b6000835161341481846020880161235b565b603d60f81b908301908152601160f91b6001820152835161343c81600284016020880161235b565b61011160f51b60029290910191820152600401949350505050565b6501e3634b732960d51b81526000825161347881600685016020870161235b565b61179f60f11b6006939091019283015250600801919050565b6000826134a0576134a0612863565b500690565b600083516134b781846020880161235b565b601760f91b90830190815283516134d581600184016020880161235b565b01600101949350505050565b6000601160f91b80835283516134fe81600186016020880161235b565b600193019283015250600201919050565b6e3d913a3930b4ba2fba3cb832911d1160891b8152835160009061353a81600f85016020890161235b565b691116113b30b63ab2911d60b11b600f91840191820152845161356481601984016020890161235b565b607d60f81b60199290910191820152835161358681601a84016020880161235b565b01601a019594505050505056fe0000000000c90f8801921d20025b26d703242abf03ed26e604b6195d057f00350647d97c0710a34507d95b9e08a2009a096a90490a3308bc0afb68050bc3ac350c8bd35e0d53db920e1bc2e40ee387660fab272b1072a0481139f0cf120116d512c8106e138edbb1145576b1151bdf8515e2144416a81305176dd9de183366e818f8b83c19bdcbf31a82a0251b4732ef1c0b826a1ccf8cb31d934fe51e56ca1e1f19f97b1fdcdc1b209f701c2161b39f2223a4c522e541af23a6887e2467775725280c5d25e845b626a8218527679df42826b92828e5714a29a3c4852a61b1012b1f34eb2bdc4e6f2c98fbba2d553afb2e110a622ecc681e2f8752623041c76030fbc54d31b54a5d326e54c73326e2c233def2873496824f354d905636041ad936ba2013376f9e46382493b038d8fe93398cdd323a402dd13af2eeb73ba51e293c56ba703d07c1d53db832a53e680b2c3f1749b73fc5ec974073f21d4121589a41ce1e64427a41d04325c13543d09aec447acd50452456bc45cd358f46756827471cece647c3c22e4869e664490f57ee49b415334a581c9d4afb6c974b9e038f4c3fdff34ce100344d8162c34e2106174ebfe8a44f5e08e24ffb654c5097fc5e5133cc9451ced46e5269126e53028517539b2aef5433027d54ca0a4a556040e255f5a4d2568a34a9571deef957b0d2555842dd5458d40e8c5964649759f3de125a8279995b1035ce5b9d11535c290acc5cb420df5d3e52365dc79d7b5e50015d5ed77c895f5e0db25fe3b38d60686cce60ec382f616f146b61f1003e6271fa6862f201ac637114cc63ef328f646c59bf64e889256563bf9165ddfbd266573cbb66cf811f6746c7d767bd0fbc683257aa68a69e806919e31f698c246b69fd614a6a6d98a36adcc9646b4af2786bb812d06c24295f6c8f351b6cf934fb6d6227f96dca0d146e30e3496e96a99c6efb5f116f5f02b16fc1938470231099708378fe70e2cbc571410804719e2cd171fa394872552c8472af05a67307c3cf735f662573b5ebd0740b53fa745f9dd074b2c8837504d3447555bd4b75a585ce75f42c0a7641af3c768e0ea576d9498877235f2c776c4eda77b417df77fab988784033287884841378c7aba17909a92c794a7c11798a23b079c89f6d7a05eeac7a4210d87a7d055a7ab6cba37aef63237b26cb4e7b5d039d7b920b887bc5e28f7bf8882f7c29fbed7c5a3d4f7c894bdd7cb727237ce3ceb17d0f42177d3980eb7d628ac57d8a5f3f7db0fdf77dd6668e7dfa98a77e1d93e97e3f57fe7e5fe4927e7f39567e9d55fb7eba3a387ed5e5c57ef0585f7f0991c37f2191b37f3857f57f4de4507f62368e7f754e7f7f872bf27f97cebc7fa736b37fb563b27fc255957fce0c3d7fd8878d7fe1c76a7fe9cbbf7ff094777ff621817ffa72d07ffd88597fff62157fffffff4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974daddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efd1413436ac25d03aca50f1065e5c13af57f2d6b99ba49602405b80ae9f2a6c9a00000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000002

Deployed Bytecode

0x60806040526004361061018e5760003560e01c806370a08231116100dd57806370a0823114610385578063715018a6146103a55780637449da48146103ba57806379ba5097146103cd5780637cb64759146103e257806383281cec146104025780638d859f3e146104225780638da5cb5b1461043d57806395d89b411461045b578063a22cb46514610470578063a6b4086814610490578063b88d4fde146104b0578063c87b56dd146104c3578063dbd76cd9146104e3578063e985e9c5146104f8578063f2fde38b14610518578063f3b674a41461053857600080fd5b806301ffc9a71461019357806306fdde03146101c8578063081812fc146101ea57806308fe4be614610217578063095ea7b3146102375780630f4161aa1461024c57806318160ddd1461026657806323452b9c1461028d57806323b872dd146102a25780632a55205a146102b55780632eb4a7ab146102f45780633ccfd60b1461030a57806342842e0e1461031f57806356ff1be8146103325780636352211e1461035257806367dce1ed14610372575b600080fd5b34801561019f57600080fd5b506101b36101ae36600461233e565b610558565b60405190151581526020015b60405180910390f35b3480156101d457600080fd5b506101dd610583565b6040516101bf91906123ab565b3480156101f657600080fd5b5061020a6102053660046123be565b610615565b6040516101bf91906123d7565b34801561022357600080fd5b506101dd6102323660046123eb565b610659565b61024a610245366004612424565b6107b8565b005b34801561025857600080fd5b5060d6546101b39060ff1681565b34801561027257600080fd5b5060cc5460cb5403600019015b6040519081526020016101bf565b34801561029957600080fd5b5061024a610858565b61024a6102b0366004612450565b610899565b3480156102c157600080fd5b506102d56102d0366004612491565b610a1f565b604080516001600160a01b0390931683526020830191909152016101bf565b34801561030057600080fd5b5061027f60d55481565b34801561031657600080fd5b5061024a610a44565b61024a61032d366004612450565b610ab0565b34801561033e57600080fd5b5061024a61034d3660046124c8565b610ad0565b34801561035e57600080fd5b5061020a61036d3660046123be565b610aeb565b61024a6103803660046124f4565b610af6565b34801561039157600080fd5b5061027f6103a036600461250f565b610b74565b3480156103b157600080fd5b5061024a610bc2565b61024a6103c8366004612570565b610bd6565b3480156103d957600080fd5b5061024a610d0f565b3480156103ee57600080fd5b5061024a6103fd3660046123be565b610d7b565b34801561040e57600080fd5b506101b361041d366004612665565b610d88565b34801561042e57600080fd5b5061027f662386f26fc1000081565b34801561044957600080fd5b5060d3546001600160a01b031661020a565b34801561046757600080fd5b506101dd610da9565b34801561047c57600080fd5b5061024a61048b3660046126cd565b610db8565b34801561049c57600080fd5b5061024a6104ab366004612702565b610e24565b61024a6104be36600461273a565b610f81565b3480156104cf57600080fd5b506101dd6104de3660046123be565b610fcb565b3480156104ef57600080fd5b5061024a611062565b34801561050457600080fd5b506101b36105133660046127a5565b6110a6565b34801561052457600080fd5b5061024a61053336600461250f565b6110d4565b34801561054457600080fd5b506101b36105533660046127d3565b611147565b60006001600160e01b0319821663152a902d60e11b148061057d575061057d8261118a565b92915050565b606060cd805461059290612829565b80601f01602080910402602001604051908101604052809291908181526020018280546105be90612829565b801561060b5780601f106105e05761010080835404028352916020019161060b565b820191906000526020600020905b8154815290600101906020018083116105ee57829003601f168201915b5050505050905090565b6000610620826111d8565b61063d576040516333d1c03960e21b815260040160405180910390fd5b50600090815260d160205260409020546001600160a01b031690565b60ca5460609060ff1661067a57505060408051602081019091526000815290565b60606000606461ffff85160461ffff16905060005b818160ff1610156106f35760006106b060ff600a840260780316606461120d565b9050836106c7826064850260010160ff16896112ed565b6040516020016106d8929190612895565b60408051601f1981840301815291905293505060010161068f565b506000606461ffff861606905061ffff811615610752578261072f61072261ffff600a8602607803168461120d565b60016064860201886112ed565b604051602001610740929190612895565b60405160208183030381529060405292505b505061078f60658461ffff166103e9811061076f5761076f6128c4565b600a91828204019190066003029054906101000a900462ffffff16611744565b816040516020016107a19291906128da565b604051602081830303815290604052915050919050565b60006107c382610aeb565b9050336001600160a01b038216146107fc576107df81336110a6565b6107fc576040516367d9dca160e11b815260040160405180910390fd5b600082815260d1602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b61086061177c565b60d480546001600160a01b03191690556040516000805160206139d88339815191529061088f906000906123d7565b60405180910390a1565b60006108a4826117a7565b9050836001600160a01b0316816001600160a01b0316146108d75760405162a1148160e81b815260040160405180910390fd5b600082815260d1602052604090208054338082146001600160a01b038816909114176109245761090786336110a6565b61092457604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b03851661094b57604051633a954ecd60e21b815260040160405180910390fd5b801561095657600082555b6001600160a01b03868116600090815260d0602052604080822080546000190190559187168152208054600101905561099385600160e11b611816565b600085815260cf6020526040812091909155600160e11b841690036109e85760018401600081815260cf602052604081205490036109e65760cb5481146109e657600081815260cf602052604090208490555b505b83856001600160a01b0316876001600160a01b03166000805160206139f883398151915260405160405180910390a4505050505050565b7339ab90066cec746a032d67e4fe3378f16294cf6b61271060fa8302045b9250929050565b6040516000907339ab90066cec746a032d67e4fe3378f16294cf6b9047908381818185875af1925050503d8060008114610a9a576040519150601f19603f3d011682016040523d82523d6000602084013e610a9f565b606091505b5050905080610aad57600080fd5b50565b610acb83838360405180602001604052806000815250610f81565b505050565b610ad861177c565b60d6805460ff1916911515919091179055565b600061057d826117a7565b60d65460ff16610b19576040516393e8222560e01b815260040160405180910390fd5b600560ff82161115610b3e576040516392e5b1c960e01b815260040160405180910390fd5b34662386f26fc100008260ff160214610b6a5760405163569e8c1160e01b815260040160405180910390fd5b610aad338261182b565b60006001600160a01b038216610b9d576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b0316600090815260d060205260409020546001600160401b031690565b610bca61177c565b610bd46000611918565b565b8282610be3828233611147565b610c0057604051630e5060e160e21b815260040160405180910390fd5b34662386f26fc100008460ff160214610c2c5760405163569e8c1160e01b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000032061ffff168360ff16610c6260cb546000190190565b011115610c8257604051637c44f5af60e11b815260040160405180910390fd5b33600090815260d7602052604090205460ff7f000000000000000000000000000000000000000000000000000000000000000281169181168501161115610cdc57604051632ae6c11560e21b815260040160405180910390fd5b33600081815260d760205260409020805460ff80821687011660ff19909116179055610d08908461182b565b5050505050565b60d4546001600160a01b0316338114610d3b57604051636b7584e760e11b815260040160405180910390fd5b60d480546001600160a01b03191690556040516000805160206139d883398151915290610d6a906000906123d7565b60405180910390a1610aad81611918565b610d8361177c565b60d555565b6000610d9f848460d554858051906020012061196a565b90505b9392505050565b606060ce805461059290612829565b33600081815260d2602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b33610e2e83610aeb565b6001600160a01b031614610e5557604051633533b18360e11b815260040160405180910390fd5b62ffffff811615801590610eea57506040516331a9108f60e11b815262ffffff82166004820152339073ff6000a85baac9c4854faa7155e70ba850bf726b90636352211e90602401602060405180830381865afa158015610eba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ede91906129fe565b6001600160a01b031614155b15610f0857604051630140282360e71b815260040160405180910390fd5b806065836103e98110610f1d57610f1d6128c4565b600a91828204019190066003026101000a81548162ffffff021916908362ffffff1602179055507ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce782604051610f7591815260200190565b60405180910390a15050565b610f8c848484610899565b6001600160a01b0383163b15610fc557610fa884848484611984565b610fc5576040516368d2bf6b60e11b815260040160405180910390fd5b50505050565b6060610fd6826111d8565b610ff357604051630a14c4b560e41b815260040160405180910390fd5b6000610ffe83610659565b9050610da28361100d83611a6c565b611035846040516020016110219190612a1b565b604051602081830303815290604052611a87565b6040516020016110459190612e5e565b60405160208183030381529060405261105d87611bd9565b611d37565b61106a61177c565b60ca5460ff161561108e5760405163532c890b60e11b815260040160405180910390fd5b60ca805460ff19166001179055610bd433600561182b565b6001600160a01b03918216600090815260d26020908152604080832093909416825291909152205460ff1690565b6110dc61177c565b6001600160a01b03811661110357604051633a247dd760e11b815260040160405180910390fd5b60d480546001600160a01b0319166001600160a01b0383161790556040516000805160206139d88339815191529061113c9083906123d7565b60405180910390a150565b60d5546040516001600160601b0319606084901b166020820152600091610d9f91869186916034016040516020818303038152906040528051906020012061196a565b60006301ffc9a760e01b6001600160e01b0319831614806111bb57506380ac58cd60e01b6001600160e01b03198316145b8061057d5750506001600160e01b031916635b5e139f60e01b1490565b6000816001111580156111ec575060cb5482105b801561057d575050600090815260cf6020526040902054600160e01b161590565b60608161ffff166001600160401b0381111561122b5761122b6125c3565b60405190808252806020026020018201604052801561126457816020015b61125161230e565b8152602001906001900390816112495790505b50905060008261ffff1668138400eca364a000008161128557611285612863565b046001600160481b0316905060005b8361ffff168163ffffffff1610156112e55763ffffffff811682026112b98682611d84565b848363ffffffff16815181106112d1576112d16128c4565b602090810291909101015250600101611294565b505092915050565b606083516001036114f4576114ed61133a60405180604001604052806002815260200161783160f01b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b61137960405180604001604052806002815260200161793160f01b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6113b8604051806040016040528060028152602001613c1960f11b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6113f7604051806040016040528060028152602001613c9960f11b8152506040518060400160405280600381526020016203230360ec1b815250611e56565b6114446040518060400160405280600e81526020016d07374726f6b652d6c696e656361760941b815250604051806040016040528060058152602001641c9bdd5b9960da1b815250611e56565b611486604051806040016040528060068152602001657374726f6b6560d01b81525061148160008c61ffff166103e9811061076f5761076f6128c4565b611e56565b6114c3604051806040016040528060028152602001611a5960f21b815250604051806040016040528060018152602001606d60f81b815250611e56565b6040516020016114d99796959493929190612e9c565b604051602081830303815290604052611e82565b9050610da2565b60005b84518161ffff16101561173c57600085518260010161ffff16149050600085830161ffff1690508361170d61156a60405180604001604052806002815260200161783160f01b8152506114818b8861ffff1681518110611559576115596128c4565b602002602001015160000151611e95565b6115b260405180604001604052806002815260200161793160f01b8152506114818c8961ffff16815181106115a1576115a16128c4565b602002602001015160200151611e95565b611604604051806040016040528060028152602001613c1960f11b815250611481886115f1578d8a60010161ffff1681518110611559576115596128c4565b8d600081518110611559576115596128c4565b611656604051806040016040528060028152602001613c9960f11b81525061148189611643578e8b60010161ffff16815181106115a1576115a16128c4565b8e6000815181106115a1576115a16128c4565b611695604051806040016040528060068152602001657374726f6b6560d01b81525061148160008a63ffffffff166103e9811061076f5761076f6128c4565b8763ffffffff168c61ffff16146116bb57604051806020016040528060008152506116f8565b6116f8604051806040016040528060028152602001611a5960f21b815250604051806040016040528060018152602001606d60f81b815250611e56565b6040516020016114d996959493929190612f2e565b60405160200161171e929190612895565b604051602081830303815290604052935050508060010190506114f7565b509392505050565b606061175662ffffff83166003611f20565b6040516020016117669190612fad565b6040516020818303038152906040529050919050565b60d3546001600160a01b03163314610bd457604051635fc483c560e01b815260040160405180910390fd5b600081806001116117fd5760cb548110156117fd57600081815260cf602052604081205490600160e01b821690036117fb575b80600003610da2575060001901600081815260cf60205260409020546117da565b505b604051636f96cda160e11b815260040160405180910390fd5b4260a01b176001600160a01b03919091161790565b60ca5460ff1661184e57604051630314872760e11b815260040160405180910390fd5b600061185960cb5490565b905060ff821681016103e910156118835760405163d05cb60960e01b815260040160405180910390fd5b805b8260ff16820181101561190a57604080514460208201529081018290526118c49060600160405160208183030381529060405280519060200120611fa8565b6000826103e981106118d8576118d86128c4565b600a91828204019190066003026101000a81548162ffffff021916908362ffffff160217905550806001019050611885565b50610acb838360ff16612005565b60d380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000826119788686856120ed565b1490505b949350505050565b604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a02906119b9903390899088908890600401612fd6565b6020604051808303816000875af19250505080156119f4575060408051601f3d908101601f191682019092526119f191810190613013565b60015b611a52573d808015611a22576040519150601f19603f3d011682016040523d82523d6000602084013e611a27565b606091505b508051600003611a4a576040516368d2bf6b60e11b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061197c565b6060611a7782611a87565b6040516020016117669190613030565b60608151600003611aa657505060408051602081019091526000815290565b600060405180606001604052806040815260200161399860409139905060006003845160020181611ad957611ad9612863565b0460040290506000816020016001600160401b03811115611afc57611afc6125c3565b6040519080825280601f01601f191660200182016040528015611b26576020820181803683370190505b509050818152600183018586518101602084015b81831015611b945760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b93820193909352600401611b3a565b600389510660018114611bae5760028114611bbf57611bcb565b613d3d60f01b600119830152611bcb565b603d60f81b6000198301525b509398975050505050505050565b60606000606483069050611c1c6040518060400160405280600c81526020016b546f74616c20506f696e747360a01b815250611c148561212f565b600180612173565b604080518082019091526005815264088cae0e8d60db1b6020820152611c4890611c146064870461212f565b611c8e60405180604001604052806012815260200171496e6e657220536861706520506f696e747360701b815250611c1460008611611c8857606461212f565b8561212f565b611cd86040518060400160405280600e81526020016d2732bb902634b7329021b7b637b960911b815250611ccf6000896103e9811061076f5761076f6128c4565b60006001612173565b611d236040518060400160405280601081526020016f2130b1b5b3b937bab7321021b7b637b960811b815250611d1b60658a6103e9811061076f5761076f6128c4565b600080612173565b6040516020016107a1959493929190613072565b6060611d5b611d458661212f565b83868660405160200161102194939291906130fc565b604051602001611d6b91906132eb565b6040516020818303038152906040529050949350505050565b611d8c61230e565b6000611da16002672b992ddfa23249d6613346565b611db4672b992ddfa23249d6600261335a565b611dbe9190613371565b6809c2007651b2500000611dda672b992ddfa23249d686613384565b611de491906133b4565b611dee91906133e2565b90506040518060400160405280611e04836121e9565b611e0e9087613384565b611e2190680ad78ebc5ac62000006133e2565b8152602001611e2f836121fa565b611e399087613384565b611e4c90680ad78ebc5ac62000006133e2565b9052949350505050565b60608282604051602001611e6b929190613402565b604051602081830303815290604052905092915050565b6060816040516020016117669190613457565b60606000611eab670de0b6b3a764000084613346565b90506000611ec1670de0b6b3a764000085613491565b905067016345785d8a0000811115611ee557611ee264e8d4a5100082613346565b90505b611eee8261212f565b611ef78261212f565b604051602001611f089291906134a5565b60405160208183030381529060405292505050919050565b6060601f1960428360011b01166040510190506020810160405260008152806f30313233343536373839616263646566600f528283018203600119855b600f811651948201946001860153600f8160041c1651855360081c848303611f5d578015611f9357632194895a6000526004601cfd5b505050819003601f1990910190815292915050565b6000611fc06040518060200160405280600081525090565b828152611fcf816101006122c4565b60ff166008611fe0836101006122c4565b62ffffff16901b6010611ff5846101006122c4565b62ffffff16901b01019392505050565b60cb54600082900361202a5760405163b562e8dd60e01b815260040160405180910390fd5b6001600160a01b038316600090815260d06020526040902080546001600160401b018402019055612061836001841460e11b611816565b600082815260cf60205260408120919091556001600160a01b0384169083830190839083906000805160206139f88339815191528180a4600183015b8181146120c357808360006000805160206139f8833981519152600080a460010161209d565b50816000036120e457604051622e076360e81b815260040160405180910390fd5b60cb5550505050565b600081815b848110156121265761211c82878784818110612110576121106128c4565b905060200201356122e2565b91506001016120f2565b50949350505050565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a90048061214a575050819003601f19909101908152919050565b606084836121a0578460405160200161218c91906134e1565b6040516020818303038152906040526121a2565b845b836121bc57604051806020016040528060008152506121d7565b604051806040016040528060018152602001600b60fa1b8152505b604051602001611d6b9392919061350f565b600061057d6715cc96efd11924eb83015b6757325bbf446493ac9081900663400000000204600061ffff600483901c1660ff601484901c1663100000008416156320000000851615158161223f5760ff92909203915b60006040518061044001604052806104048152602001613594610404913960046002860102818101519192509063ffffffff602082901c8116908216818103890260101c60008861229257818303612296565b8184015b905087156122a2576000035b637fffffff670de0b6b3a76400008202059d9c50505050505050505050505050565b60005b60208320905080835281826000030681106122c75706919050565b60008183106122fe576000828152602084905260409020610da2565b5060009182526020526040902090565b604051806040016040528060008152602001600081525090565b6001600160e01b031981168114610aad57600080fd5b60006020828403121561235057600080fd5b8135610da281612328565b60005b8381101561237657818101518382015260200161235e565b50506000910152565b6000815180845261239781602086016020860161235b565b601f01601f19169290920160200192915050565b602081526000610da2602083018461237f565b6000602082840312156123d057600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6000602082840312156123fd57600080fd5b813561ffff81168114610da257600080fd5b6001600160a01b0381168114610aad57600080fd5b6000806040838503121561243757600080fd5b82356124428161240f565b946020939093013593505050565b60008060006060848603121561246557600080fd5b83356124708161240f565b925060208401356124808161240f565b929592945050506040919091013590565b600080604083850312156124a457600080fd5b50508035926020909101359150565b803580151581146124c357600080fd5b919050565b6000602082840312156124da57600080fd5b610da2826124b3565b803560ff811681146124c357600080fd5b60006020828403121561250657600080fd5b610da2826124e3565b60006020828403121561252157600080fd5b8135610da28161240f565b60008083601f84011261253e57600080fd5b5081356001600160401b0381111561255557600080fd5b6020830191508360208260051b8501011115610a3d57600080fd5b60008060006040848603121561258557600080fd5b83356001600160401b0381111561259b57600080fd5b6125a78682870161252c565b90945092506125ba9050602085016124e3565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126125ea57600080fd5b81356001600160401b0380821115612604576126046125c3565b604051601f8301601f19908116603f0116810190828211818310171561262c5761262c6125c3565b8160405283815286602085880101111561264557600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006040848603121561267a57600080fd5b83356001600160401b038082111561269157600080fd5b61269d8783880161252c565b909550935060208601359150808211156126b657600080fd5b506126c3868287016125d9565b9150509250925092565b600080604083850312156126e057600080fd5b82356126eb8161240f565b91506126f9602084016124b3565b90509250929050565b6000806040838503121561271557600080fd5b82359150602083013562ffffff8116811461272f57600080fd5b809150509250929050565b6000806000806080858703121561275057600080fd5b843561275b8161240f565b9350602085013561276b8161240f565b92506040850135915060608501356001600160401b0381111561278d57600080fd5b612799878288016125d9565b91505092959194509250565b600080604083850312156127b857600080fd5b82356127c38161240f565b9150602083013561272f8161240f565b6000806000604084860312156127e857600080fd5b83356001600160401b038111156127fe57600080fd5b61280a8682870161252c565b909450925050602084013561281e8161240f565b809150509250925092565b600181811c9082168061283d57607f821691505b60208210810361285d57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b6000815161288b81856020860161235b565b9290920192915050565b600083516128a781846020880161235b565b8351908301906128bb81836020880161235b565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d223130302522206865696768743d2231303060208201527f25222076696577426f783d22302030203430302034303022207374796c653d2260408201526a3130b1b5b3b937bab7321d60a91b60608201526000835161297281606b85016020880161235b565b7f3b646973706c61793a626c6f636b3b6d617267696e3a6175746f223e3c737479606b918401918201527f6c653e6c696e657b7374726f6b652d77696474683a31303b7d3c2f7374796c65608b820152601f60f91b60ab82015283516129df8160ac84016020880161235b565b651e17b9bb339f60d11b60ac929091019182015260b201949350505050565b600060208284031215612a1057600080fd5b8151610da28161240f565b7f3c68746d6c207374796c653d226f766572666c6f773a68696464656e223e3c6281527437b23c9039ba3cb6329e9136b0b933b4b71d18111f60591b602082015260008251612a7181603585016020870161235b565b7f3c7363726970743e6c657420613d21312c623d21312c6f3d41727261792e667260359390910192830152507f6f6d28646f63756d656e742e676574456c656d656e747342795461674e616d6560558201527f28226c696e652229292e6d617028653d3e652e7374796c652e7374726f6b652960758201527f2c733d653d3e6e65772050726f6d69736528743d3e73657454696d656f75742860958201527f742c6529292c633d28293d3e4d6174682e666c6f6f72283235362a4d6174682e60b58201527f72616e646f6d2829293b646f63756d656e742e626f64792e6164644576656e7460d58201527f4c697374656e65722822636c69636b222c6173796e6328293d3e7b696628216160f58201527f7c7c622972657475726e3b623d21303b6c657420653d646f63756d656e742e676101158201527f6574456c656d656e747342795461674e616d6528226c696e6522293b666f72286101358201527f3b613b297b666f72286c657420743d303b743c652e6c656e6774683b742b2b296101558201527f655b745d2e7374796c652e7374726f6b653d6072676228247b6328297d2c247b6101758201527f6328297d2c247b6328297d29603b61776169742073283530297d666f72286c656101958201527f74206c3d303b6c3c652e6c656e6774683b6c2b2b29655b6c5d2e7374796c652e6101b58201527f7374726f6b653d6f5b6c5d3b623d21317d2c2130292c646f63756d656e742e626101d58201527f6f64792e6164644576656e744c697374656e65722822636c69636b222c6173796101f58201527f6e6328293d3e7b696628612972657475726e3b613d21303b6c657420653d646f6102158201527f63756d656e742e676574456c656d656e747342795461674e616d6528226c696e6102358201527f6522293b666f72286c657420743d303b743c322a652e6c656e6774683b742b2b6102558201527f297b6c6574206c3d743c652e6c656e6774683f2230223a2231222c6e3d743c656102758201527f2e6c656e6774683f743a322a652e6c656e6774682d742d313b226d22213d3d656102958201527f5b6e5d2e6964262628655b6e5d2e7374796c652e7374726f6b654f70616369746102b58201527f793d6c293b6c657420673d652e6c656e677468253130303b61776169742073286102d58201527f28743c652e6c656e6774683f743e3d652e6c656e6774682d673a743e652e6c656102f58201527f6e6774682626742d652e6c656e6774683c3d67293f32302b283130302d67292f6103158201527f3130302a37353a3130297d613d21317d2c2130293b3c2f7363726970743e3c2f6103358201526b3137b23c9f1e17b43a36b61f60a11b61035582015261036101919050565b7519185d184e9d195e1d0bda1d1b5b0ed8985cd94d8d0b60521b815260008251612e8f81601685016020870161235b565b9190910160160192915050565b600088516020612eaf8285838e0161235b565b895191840191612ec28184848e0161235b565b8951920191612ed48184848d0161235b565b8851920191612ee68184848c0161235b565b8751920191612ef88184848b0161235b565b8651920191612f0a8184848a0161235b565b8551920191612f1c818484890161235b565b919091019a9950505050505050505050565b600087516020612f418285838d0161235b565b885191840191612f548184848d0161235b565b8851920191612f668184848c0161235b565b8751920191612f788184848b0161235b565b8651920191612f8a8184848a0161235b565b8551920191612f9c818484890161235b565b919091019998505050505050505050565b602360f81b815260008251612fc981600185016020870161235b565b9190910160010192915050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906130099083018461237f565b9695505050505050565b60006020828403121561302557600080fd5b8151610da281612328565b7919185d184e9a5b5859d94bdcdd99cade1b5b0ed8985cd94d8d0b60321b81526000825161306581601a85016020870161235b565b91909101601a0192915050565b605b60f81b815260006001875161308f8183860160208c0161235b565b8751908401906130a58184840160208c0161235b565b87519101906130ba8184840160208b0161235b565b86519101906130cf8184840160208a0161235b565b85519101906130e4818484016020890161235b565b605d60f81b9101820190815201979650505050505050565b727b226e616d65223a22546f676574686572202360681b8152845160009061312b816013850160208a0161235b565b7f222c226465736372697074696f6e223a22546f676574686572206973206120676013918401918201527f656e65726174697665206f6e636861696e204e4654207769746820636f6c6f7260338201527f66756c2061727420666f726d656420627920697473206d696e746572732e205460538201527f686520617274206f66206561636820746f6b656e206275696c6473206f6e207460738201527f686520617274206f6620616c6c2070726576696f757320746f6b656e7320706c60938201527f75732061206e6577206c696e652067656e65726174656420617420746865207460b38201527f696d65206f66206d696e742e20546170206f6e636520746f20756e77696e642060d38201527f796f7572206172742e2054617020747769636520746f20756e77696e6420796f60f3820152763ab91036b4b7321711161130ba3a3934b13aba32b9911d60491b61011382015261328e61012a820187612879565b69161134b6b0b3b2911d1160b11b815290506132ad600a820186612879565b7211161130b734b6b0ba34b7b72fbab936111d1160691b815290506132d56013820185612879565b61227d60f01b8152600201979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161332381601d85016020870161235b565b91909101601d0192915050565b634e487b7160e01b600052601160045260246000fd5b60008261335557613355612863565b500490565b808202811582820484141761057d5761057d613330565b8181038181111561057d5761057d613330565b80820260008212600160ff1b841416156133a0576133a0613330565b818105831482151761057d5761057d613330565b6000826133c3576133c3612863565b600160ff1b8214600019841416156133dd576133dd613330565b500590565b80820182811260008312801582168215821617156112e5576112e5613330565b6000835161341481846020880161235b565b603d60f81b908301908152601160f91b6001820152835161343c81600284016020880161235b565b61011160f51b60029290910191820152600401949350505050565b6501e3634b732960d51b81526000825161347881600685016020870161235b565b61179f60f11b6006939091019283015250600801919050565b6000826134a0576134a0612863565b500690565b600083516134b781846020880161235b565b601760f91b90830190815283516134d581600184016020880161235b565b01600101949350505050565b6000601160f91b80835283516134fe81600186016020880161235b565b600193019283015250600201919050565b6e3d913a3930b4ba2fba3cb832911d1160891b8152835160009061353a81600f85016020890161235b565b691116113b30b63ab2911d60b11b600f91840191820152845161356481601984016020890161235b565b607d60f81b60199290910191820152835161358681601a84016020880161235b565b01601a019594505050505056fe0000000000c90f8801921d20025b26d703242abf03ed26e604b6195d057f00350647d97c0710a34507d95b9e08a2009a096a90490a3308bc0afb68050bc3ac350c8bd35e0d53db920e1bc2e40ee387660fab272b1072a0481139f0cf120116d512c8106e138edbb1145576b1151bdf8515e2144416a81305176dd9de183366e818f8b83c19bdcbf31a82a0251b4732ef1c0b826a1ccf8cb31d934fe51e56ca1e1f19f97b1fdcdc1b209f701c2161b39f2223a4c522e541af23a6887e2467775725280c5d25e845b626a8218527679df42826b92828e5714a29a3c4852a61b1012b1f34eb2bdc4e6f2c98fbba2d553afb2e110a622ecc681e2f8752623041c76030fbc54d31b54a5d326e54c73326e2c233def2873496824f354d905636041ad936ba2013376f9e46382493b038d8fe93398cdd323a402dd13af2eeb73ba51e293c56ba703d07c1d53db832a53e680b2c3f1749b73fc5ec974073f21d4121589a41ce1e64427a41d04325c13543d09aec447acd50452456bc45cd358f46756827471cece647c3c22e4869e664490f57ee49b415334a581c9d4afb6c974b9e038f4c3fdff34ce100344d8162c34e2106174ebfe8a44f5e08e24ffb654c5097fc5e5133cc9451ced46e5269126e53028517539b2aef5433027d54ca0a4a556040e255f5a4d2568a34a9571deef957b0d2555842dd5458d40e8c5964649759f3de125a8279995b1035ce5b9d11535c290acc5cb420df5d3e52365dc79d7b5e50015d5ed77c895f5e0db25fe3b38d60686cce60ec382f616f146b61f1003e6271fa6862f201ac637114cc63ef328f646c59bf64e889256563bf9165ddfbd266573cbb66cf811f6746c7d767bd0fbc683257aa68a69e806919e31f698c246b69fd614a6a6d98a36adcc9646b4af2786bb812d06c24295f6c8f351b6cf934fb6d6227f96dca0d146e30e3496e96a99c6efb5f116f5f02b16fc1938470231099708378fe70e2cbc571410804719e2cd171fa394872552c8472af05a67307c3cf735f662573b5ebd0740b53fa745f9dd074b2c8837504d3447555bd4b75a585ce75f42c0a7641af3c768e0ea576d9498877235f2c776c4eda77b417df77fab988784033287884841378c7aba17909a92c794a7c11798a23b079c89f6d7a05eeac7a4210d87a7d055a7ab6cba37aef63237b26cb4e7b5d039d7b920b887bc5e28f7bf8882f7c29fbed7c5a3d4f7c894bdd7cb727237ce3ceb17d0f42177d3980eb7d628ac57d8a5f3f7db0fdf77dd6668e7dfa98a77e1d93e97e3f57fe7e5fe4927e7f39567e9d55fb7eba3a387ed5e5c57ef0585f7f0991c37f2191b37f3857f57f4de4507f62368e7f754e7f7f872bf27f97cebc7fa736b37fb563b27fc255957fce0c3d7fd8878d7fe1c76a7fe9cbbf7ff094777ff621817ffa72d07ffd88597fff62157fffffff4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974daddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

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

d1413436ac25d03aca50f1065e5c13af57f2d6b99ba49602405b80ae9f2a6c9a00000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000002

-----Decoded View---------------
Arg [0] : allowListMerkleRoot (bytes32): 0xd1413436ac25d03aca50f1065e5c13af57f2d6b99ba49602405b80ae9f2a6c9a
Arg [1] : allowListMintMaxTotalVal (uint16): 800
Arg [2] : allowListMintMaxPerWalletVal (uint8): 2

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : d1413436ac25d03aca50f1065e5c13af57f2d6b99ba49602405b80ae9f2a6c9a
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000320
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000002


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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