ETH Price: $3,912.10 (+1.37%)

Contract

0x0Ab72EB204001E24c2714f14cdDFB8755ad9bc84
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ArchetypeBurgers404

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 1 runs

Other Settings:
paris EvmVersion
File 1 of 16 : ArchetypeBurgers404.sol
// SPDX-License-Identifier: MIT
// Archetype v0.8.0 - BURGERS404
//
//        d8888                 888               888
//       d88888                 888               888
//      d88P888                 888               888
//     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
//    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
//   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
//  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
// d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
//                                                            888 888
//                                                       Y8b d88P 888
//                                                        "Y88P"  888

pragma solidity ^0.8.20;

import "./ArchetypeLogicBurgers404.sol";
import "dn404/src/DN420.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "solady/src/utils/LibString.sol";
import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";

contract ArchetypeBurgers404 is DN420, Initializable, OwnableUpgradeable, ERC2981Upgradeable {
  //
  // EVENTS
  //
  event Invited(bytes32 indexed key, bytes32 indexed cid);
  event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints);
  event Withdrawal(address indexed src, address token, uint128 wad);

  //
  // VARIABLES
  //
  mapping(bytes32 => AdvancedInvite) public invites;
  mapping(bytes32 => uint256) public packedBonusDiscounts;
  mapping(address => mapping(bytes32 => uint256)) private _minted;
  mapping(bytes32 => uint256) private _listSupply;
  mapping(address => uint128) private _ownerBalance;
  mapping(address => mapping(address => uint128)) private _affiliateBalance;
  mapping(bytes32 => bytes32) public pairedListKeys; 

  string private _name;
  string private _symbol;
  uint256 private totalErc20Mints;
  Config public config;
  PayoutConfig public payoutConfig;
  uint256 public flags;
  // bit 0: uriLocked
  // bit 1: maxSupplyLocked 
  // bit 2: ownerAltPayoutLocked

  //
  // METHODS
  //
  function initialize(
    string memory name_,
    string memory symbol_,
    Config calldata config_,
    PayoutConfig calldata payoutConfig_,
    address _receiver
  ) external initializer {
    _name = name_;
    _symbol = symbol_;
    config = config_;

    _initializeDN420(0, address(0));

    // check max bps not reached and min platform fee.
    if (
      config_.affiliateFee > MAXBPS ||
      config_.affiliateDiscount > MAXBPS ||
      config_.affiliateSigner == address(0) ||
      config_.maxBatchSize == 0
    ) {
      revert InvalidConfig();
    }
    __Ownable_init();

    uint256 totalShares = payoutConfig_.ownerBps +
      payoutConfig_.platformBps +
      payoutConfig_.partnerBps +
      payoutConfig_.superAffiliateBps;

    if (payoutConfig_.platformBps < 250 || totalShares != 10000) {
      revert InvalidSplitShares();
    }
    payoutConfig = payoutConfig_;
    setDefaultRoyalty(_receiver, config.defaultRoyalty);
  }

  //
  // PUBLIC
  //
  function mint(
    Auth calldata auth,
    uint256 quantity,
    address affiliate,
    bytes calldata signature
  ) external payable {
    mintTo(auth, quantity, _msgSender(), affiliate, signature);
  }

  function batchMintTo(
    Auth calldata auth,
    address[] calldata toList,
    uint256[] calldata quantityList,
    address affiliate,
    bytes calldata signature
  ) external payable {
    if (quantityList.length != toList.length) {
      revert InvalidConfig();
    }

    AdvancedInvite storage invite = invites[auth.key];
    uint256 packedDiscount = packedBonusDiscounts[auth.key];

    uint256 totalQuantity;
    uint256 totalBonusMints;

    for (uint256 i; i < toList.length; ) {
      uint256 quantityToAdd;
      if (invite.unitSize > 1) {
        quantityToAdd = quantityList[i] * invite.unitSize;
      } else {
        quantityToAdd = quantityList[i];
      }

      uint256 numBonusMints = ArchetypeLogicBurgers404.bonusMintsAwarded(quantityToAdd / config.erc20Ratio, packedDiscount) * config.erc20Ratio;
      _mintNext(toList[i], (quantityToAdd + numBonusMints) * ERC20_UNIT, "");

      totalQuantity += quantityToAdd;
      totalBonusMints += numBonusMints;

      unchecked {
        ++i;
      }
    }

    validateAndCreditMint(invite, auth, totalQuantity, totalBonusMints, totalErc20Mints, affiliate, signature);
  }

  function mintTo(
    Auth calldata auth,
    uint256 quantity,
    address to,
    address affiliate,
    bytes calldata signature
  ) public payable {
    AdvancedInvite storage invite = invites[auth.key];
    uint256 packedDiscount = packedBonusDiscounts[auth.key];

    if (invite.unitSize > 1) {
      quantity = quantity * invite.unitSize;
    }

    uint256 numBonusMints = ArchetypeLogicBurgers404.bonusMintsAwarded(quantity / config.erc20Ratio, packedDiscount) * config.erc20Ratio;
    _mintNext(to, (quantity + numBonusMints) * ERC20_UNIT, "");

    validateAndCreditMint(invite, auth, quantity, numBonusMints, totalErc20Mints, affiliate, signature);
  }

  function validateAndCreditMint(
    AdvancedInvite storage invite,
    Auth calldata auth,
    uint256 quantity,
    uint256 numBonusMints,
    uint256 curSupply,
    address affiliate,
    bytes calldata signature
  ) internal {
    uint256 totalQuantity = quantity + numBonusMints;
    ValidationArgs memory args;
    {
      bytes32 pairedKey = pairedListKeys[auth.key];
      uint256 pairedSupply = pairedKey != 0 ? _listSupply[bytes32(uint256(pairedKey) - 1)]: 0;
      args = ValidationArgs({
        owner: owner(),
        affiliate: affiliate,
        quantity: totalQuantity,
        curSupply: curSupply,
        listSupply: _listSupply[auth.key],
        pairedSupply: pairedSupply
      });
    }

    uint128 cost = uint128(
      ArchetypeLogicBurgers404.computePrice(
        invite,
        config.affiliateDiscount,
        quantity,
        args.listSupply,
        args.affiliate != address(0)
      )
    );

    ArchetypeLogicBurgers404.validateMint(invite, config, auth, _minted, signature, args, cost);

    if (invite.limit < invite.maxSupply) {
      _minted[_msgSender()][auth.key] += totalQuantity;
    }
    if (invite.maxSupply < UINT32_MAX) {
      _listSupply[auth.key] += totalQuantity;
    }
    totalErc20Mints += totalQuantity;

    ArchetypeLogicBurgers404.updateBalances(
      invite,
      config,
      _ownerBalance,
      _affiliateBalance,
      affiliate,
      quantity,
      cost
    );

    if (msg.value > cost) {
      _refund(_msgSender(), msg.value - cost);
    }
  }

  function burnToRemint(uint256[] calldata tokenIds) public {
    if(config.remintPremium == 0) {
      revert burnToRemintDisabled();
    }

    if(tokenIds.length < 1) {
      revert invalidTokenIdLength();
    }

    address msgSender = _msgSender();
    uint256 mintQuantity = 1 * _unit();
    uint256 burnQuantity =  mintQuantity * config.remintPremium / 10000;
    uint256 msgSenderBalance = balanceOf(msgSender);
    uint256 change = 0;

    // transfer nft 1
    safeTransferNFT(msgSender, 0x000000000000000000000000000000000000dEaD, tokenIds[0], "");

    // if premium will make minter lose an nft, transfer nft 2 and give back change, otherwise just transfer erc20
    if(msgSenderBalance % _unit() < burnQuantity) {
      if(tokenIds.length < 2) {
        revert invalidTokenIdLength();
      }
      _safeTransferNFT(msgSender, msgSender, 0x000000000000000000000000000000000000dEaD, tokenIds[1], "");
      change += _unit() - burnQuantity;
    } else {
      _transfer(msgSender, 0x000000000000000000000000000000000000dEaD, burnQuantity, "");
    }

    // remint
    _mintNext(msgSender, mintQuantity + change, "");
  }

  function withdraw() external {
    address[] memory tokens = new address[](1);
    tokens[0] = address(0);
    withdrawTokens(tokens);
  }

  function withdrawTokens(address[] memory tokens) public {
    ArchetypeLogicBurgers404.withdrawTokens(payoutConfig, _ownerBalance, owner(), tokens);
  }

  function withdrawAffiliate() external {
    address[] memory tokens = new address[](1);
    tokens[0] = address(0);
    withdrawTokensAffiliate(tokens);
  }

  function withdrawTokensAffiliate(address[] memory tokens) public {
    ArchetypeLogicBurgers404.withdrawTokensAffiliate(_affiliateBalance, tokens);
  }

  function ownerBalance() external view returns (uint128) {
    return _ownerBalance[address(0)];
  }

  function ownerBalanceToken(address token) external view returns (uint128) {
    return _ownerBalance[token];
  }

  function affiliateBalance(address affiliate) external view returns (uint128) {
    return _affiliateBalance[affiliate][address(0)];
  }

  function affiliateBalanceToken(address affiliate, address token) external view returns (uint128) {
    return _affiliateBalance[affiliate][token];
  }

  function minted(address minter, bytes32 key) external view returns (uint256) {
    return _minted[minter][key];
  }

  function listSupply(bytes32 key) external view returns (uint256) {
    return _listSupply[key];
  }

  function numErc20Minted() public view returns (uint256) {
    return totalErc20Mints;
  }

  function numNftsMinted() public view returns (uint256) {
    return totalErc20Mints / config.erc20Ratio;
  }

  function balanceOfNFT(address owner) public view returns (uint256) {
    return _balanceOfNFT(owner);
  }

  function exists(uint256 id) external view returns (bool) {
    return _exists(id);
  }

  function platform() external pure returns (address) {
    return PLATFORM;
  }

  function computePrice(
    bytes32 key,
    uint256 quantity,
    bool affiliateUsed
  ) external view returns (uint256) {
    AdvancedInvite storage i = invites[key];
    uint256 listSupply_ = _listSupply[key];
    return ArchetypeLogicBurgers404.computePrice(i, config.affiliateDiscount, quantity, listSupply_, affiliateUsed);
  }

  //
  // Overides
  //

  function name() public view override returns (string memory) {
    return _name;
  }

  function symbol() public view override returns (string memory) {
    return _symbol;
  }

  function uri(uint256 tokenId) public view override returns (string memory) {
    if (!_exists(tokenId)) revert URIQueryForNonexistentToken();

    return
      bytes(config.baseUri).length != 0
        ? string(abi.encodePacked(config.baseUri, LibString.toString(tokenId)))
        : "";
  }

  //
  // OWNER ONLY
  //

  function setBaseURI(string memory baseUri) external _onlyOwner {
    if (_getFlag(0)) {
      revert LockedForever();
    }

    config.baseUri = baseUri;
  }

  /// @notice the password is "forever"
  function lockURI(string calldata password) external _onlyOwner {
    _checkPassword(password);
    _setFlag(0);
  }

  // max supply cannot subceed total supply. Be careful changing.
  function setMaxSupply(uint32 maxSupply) external _onlyOwner {
    if (_getFlag(1)) {
      revert LockedForever();
    }

    if (maxSupply < numErc20Minted()) {
      revert MaxSupplyExceeded();
    }

    config.maxSupply = maxSupply;
  }

  /// @notice the password is "forever"
  function lockMaxSupply(string calldata password) external _onlyOwner {
    _checkPassword(password);
    _setFlag(1);
  }

  function setAffiliateFee(uint16 affiliateFee) external _onlyOwner {
    if (affiliateFee > MAXBPS) {
      revert InvalidConfig();
    }

    config.affiliateFee = affiliateFee;
  }

  function setAffiliateDiscount(uint16 affiliateDiscount) external _onlyOwner {
    if (affiliateDiscount > MAXBPS) {
      revert InvalidConfig();
    }

    config.affiliateDiscount = affiliateDiscount;
  }

  function setOwnerAltPayout(address ownerAltPayout) external _onlyOwner {
    if (_getFlag(2)) {
      revert LockedForever();
    }

    payoutConfig.ownerAltPayout = ownerAltPayout;
  }

  /// @notice the password is "forever"
  function lockOwnerAltPayout(string calldata password) external _onlyOwner {
    _checkPassword(password);
    _setFlag(2);
  }

  function setMaxBatchSize(uint32 maxBatchSize) external _onlyOwner {
    config.maxBatchSize = maxBatchSize;
  }

  function setRemintPremium(uint16 remintPremium) external _onlyOwner {
    config.remintPremium = remintPremium;
  }

  // Up to 8 discount tiers: [discount7][discount6][discount5][discount4][discount3][discount2][discount1][discount0]
  function setBonusDiscounts(bytes32 _key, BonusDiscount[] calldata _bonusDiscounts) public onlyOwner {
      if(_bonusDiscounts.length > 8) {
        revert InvalidConfig();
      }
      
      uint256 packed;
      for (uint8 i = 0; i < _bonusDiscounts.length; i++) {
          if (i > 0 && _bonusDiscounts[i].numMints >= _bonusDiscounts[i - 1].numMints) {
              revert InvalidConfig();
          }
          uint32 discount = (uint32(_bonusDiscounts[i].numMints) << 16) | uint32(_bonusDiscounts[i].numBonusMints);
          packed |= uint256(discount) << (32 * i);
      }
      packedBonusDiscounts[_key] = packed;
  }

  function setBonusInvite(
    bytes32 _key,
    bytes32 _cid,
    AdvancedInvite calldata _advancedInvite,
    BonusDiscount[] calldata _bonusDiscount
  ) external _onlyOwner {
    setBonusDiscounts(_key, _bonusDiscount);
    setAdvancedInvite(_key, _cid, _advancedInvite);
  }

  function setInvite(
    bytes32 _key,
    bytes32 _cid,
    Invite calldata _invite
  ) external _onlyOwner {
    setAdvancedInvite(_key, _cid, AdvancedInvite({
      price: _invite.price,
      reservePrice: _invite.price,
      delta: 0,
      start: _invite.start,
      end: _invite.end,
      limit: _invite.limit,
      maxSupply: _invite.maxSupply,
      interval: 0,
      unitSize: _invite.unitSize,
      tokenAddress: _invite.tokenAddress,
      isBlacklist: _invite.isBlacklist
    }));
  }

  function setAdvancedInvite(
    bytes32 _key,
    bytes32 _cid,
    AdvancedInvite memory _AdvancedInvite
  ) public _onlyOwner {
    // approve token for withdrawals if erc20 list
    if (_AdvancedInvite.tokenAddress != address(0)) {
      bool success = IERC20(_AdvancedInvite.tokenAddress).approve(PAYOUTS, 2**256 - 1);
      if (!success) {
        revert NotApprovedToTransfer();
      }
    }
    if (_AdvancedInvite.start < block.timestamp) {
      _AdvancedInvite.start = uint32(block.timestamp);
    }
    invites[_key] = _AdvancedInvite;
    emit Invited(_key, _cid);
  }

  // method will pair the supplies of two invite lists
  function setPairedInvite(bytes32 key1, bytes32 key2) external _onlyOwner {
    if(invites[key1].maxSupply != invites[key2].maxSupply) {
      revert InvalidConfig();
    }
    pairedListKeys[key1] = bytes32(uint256(key2) + 1);
    pairedListKeys[key2] = bytes32(uint256(key1) + 1);
  }

  //
  // INTERNAL
  //

  function _unit() internal view override returns (uint256) {
    return ERC20_UNIT * uint256(config.erc20Ratio);
  }

  function _msgSender() internal view override returns (address) {
    return msg.sender == BATCH ? tx.origin : msg.sender;
  }

  modifier _onlyOwner() {
    if (_msgSender() != owner()) {
      revert NotOwner();
    }
    _;
  }

  function _refund(address to, uint256 refund) internal {
    (bool success, ) = payable(to).call{ value: refund }("");
    if (!success) {
      revert TransferFailed();
    }
  }

  function _checkPassword(string calldata password) internal pure {
    if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
      revert WrongPassword();
    }
  }

  function _setFlag(uint256 flag) internal {
    flags |= 1 << flag;
  }

  function _getFlag(uint256 flag) internal view returns (bool) {
    return (flags & (1 << flag)) != 0;
  }

  //ERC2981 ROYALTY
  function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    override(DN420, ERC2981Upgradeable)
    returns (bool)
  {
    // Supports the following `interfaceId`s:
    // - IERC165: 0x01ffc9a7
    // - ERC1155: 0xd9b67a26
    // - ERC1155MetadataURI: 0x0e89341c
    // - IERC2981: 0x2a55205a
    return
      DN420.supportsInterface(interfaceId) || ERC2981Upgradeable.supportsInterface(interfaceId);
  }

  function setDefaultRoyalty(address receiver, uint16 feeNumerator) public _onlyOwner {
    config.defaultRoyalty = feeNumerator;
    _setDefaultRoyalty(receiver, feeNumerator);
  }
}

File 2 of 16 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 3 of 16 : IERC2981Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165Upgradeable.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 IERC2981Upgradeable is IERC165Upgradeable {
    /**
     * @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 4 of 16 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 5 of 16 : ERC2981Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/common/ERC2981.sol)

pragma solidity ^0.8.0;

import "../../interfaces/IERC2981Upgradeable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
 *
 * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
 * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
 *
 * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
 * fee is specified in basis points by default.
 *
 * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
 * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
 * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
 *
 * _Available since v4.5._
 */
abstract contract ERC2981Upgradeable is Initializable, IERC2981Upgradeable, ERC165Upgradeable {
    struct RoyaltyInfo {
        address receiver;
        uint96 royaltyFraction;
    }

    RoyaltyInfo private _defaultRoyaltyInfo;
    mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;

    function __ERC2981_init() internal onlyInitializing {
    }

    function __ERC2981_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC165Upgradeable) returns (bool) {
        return interfaceId == type(IERC2981Upgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @inheritdoc IERC2981Upgradeable
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual override returns (address, uint256) {
        RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];

        if (royalty.receiver == address(0)) {
            royalty = _defaultRoyaltyInfo;
        }

        uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();

        return (royalty.receiver, royaltyAmount);
    }

    /**
     * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
     * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
     * override.
     */
    function _feeDenominator() internal pure virtual returns (uint96) {
        return 10000;
    }

    /**
     * @dev Sets the royalty information that all ids in this contract will default to.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
        require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
        require(receiver != address(0), "ERC2981: invalid receiver");

        _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Removes default royalty information.
     */
    function _deleteDefaultRoyalty() internal virtual {
        delete _defaultRoyaltyInfo;
    }

    /**
     * @dev Sets the royalty information for a specific token id, overriding the global default.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
        require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
        require(receiver != address(0), "ERC2981: Invalid parameters");

        _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Resets royalty information for the token id back to the global default.
     */
    function _resetTokenRoyalty(uint256 tokenId) internal virtual {
        delete _tokenRoyaltyInfo[tokenId];
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[48] private __gap;
}

File 6 of 16 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 7 of 16 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";

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

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 8 of 16 : ERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 9 of 16 : IERC165Upgradeable.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 IERC165Upgradeable {
    /**
     * @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 10 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 11 of 16 : ArchetypePayouts.sol
// SPDX-License-Identifier: MIT
// ArchetypePayouts v0.7.0
//
//        d8888                 888               888
//       d88888                 888               888
//      d88P888                 888               888
//     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
//    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
//   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
//  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
// d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
//                                                            888 888
//                                                       Y8b d88P 888
//

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

error InvalidLength();
error InvalidSplitShares();
error TransferFailed();
error BalanceEmpty();
error NotApprovedToWithdraw();

contract ArchetypePayouts {
  event Withdrawal(address indexed src, address token, uint256 wad);
  event FundsAdded(address indexed recipient, address token, uint256 amount);

  mapping(address => mapping(address => uint256)) private _balance;
  mapping(address => mapping(address => bool)) private _approvals;

  function updateBalances(
    uint256 totalAmount,
    address token,
    address[] calldata recipients,
    uint16[] calldata splits
  ) public payable {
    if (recipients.length != splits.length) {
      revert InvalidLength();
    }

    uint256 totalShares = 0;
    for (uint256 i = 0; i < splits.length; i++) {
      totalShares += splits[i];
    }
    if (totalShares != 10000) {
      revert InvalidSplitShares();
    }

    if (token == address(0)) {
      // ETH payments
      uint256 totalReceived = msg.value;
      for (uint256 i = 0; i < recipients.length; i++) {
        if (splits[i] > 0) {
          uint256 amountToAdd = (totalReceived * splits[i]) / 10000;
          _balance[recipients[i]][token] += amountToAdd;
          emit FundsAdded(recipients[i], token, amountToAdd);
        }
      }
    } else {
      // ERC20 payments
      IERC20 paymentToken = IERC20(token);
      bool success = paymentToken.transferFrom(msg.sender, address(this), totalAmount);
      if (!success) {
        revert TransferFailed();
      }

      for (uint256 i = 0; i < recipients.length; i++) {
        if (splits[i] > 0) {
          uint256 amountToAdd = (totalAmount * splits[i]) / 10000;
          _balance[recipients[i]][token] += amountToAdd;
          emit FundsAdded(recipients[i], token, amountToAdd);
        }
      }
    }
  }

  function withdraw() external {
    address msgSender = msg.sender;
    _withdraw(msgSender, msgSender, address(0));
  }

  function withdrawTokens(address[] memory tokens) external {
    address msgSender = msg.sender;

    for (uint256 i = 0; i < tokens.length; i++) {
      _withdraw(msgSender, msgSender, tokens[i]);
    }
  }

  function withdrawFrom(address from, address to) public {
    if (from != msg.sender && !_approvals[from][to]) {
      revert NotApprovedToWithdraw();
    }
    _withdraw(from, to, address(0));
  }

  function withdrawTokensFrom(
    address from,
    address to,
    address[] memory tokens
  ) public {
    if (from != msg.sender && !_approvals[from][to]) {
      revert NotApprovedToWithdraw();
    }
    for (uint256 i = 0; i < tokens.length; i++) {
      _withdraw(from, to, tokens[i]);
    }
  }

  function _withdraw(
    address from,
    address to,
    address token
  ) internal {
    uint256 wad;

    wad = _balance[from][token];
    _balance[from][token] = 0;

    if (wad == 0) {
      revert BalanceEmpty();
    }

    if (token == address(0)) {
      bool success = false;
      (success, ) = to.call{ value: wad }("");
      if (!success) {
        revert TransferFailed();
      }
    } else {
      IERC20 erc20Token = IERC20(token);
      bool success = erc20Token.transfer(to, wad);
      if (!success) {
        revert TransferFailed();
      }
    }
    emit Withdrawal(from, token, wad);
  }

  function approveWithdrawal(address delegate, bool approved) external {
    _approvals[msg.sender][delegate] = approved;
  }

  function isApproved(address from, address delegate) external view returns (bool) {
    return _approvals[from][delegate];
  }

  function balance(address recipient) external view returns (uint256) {
    return _balance[recipient][address(0)];
  }

  function balanceToken(address recipient, address token) external view returns (uint256) {
    return _balance[recipient][token];
  }
}

File 12 of 16 : ArchetypeLogicBurgers404.sol
// SPDX-License-Identifier: MIT
// ArchetypeLogic v0.8.0 - BURGERS404
//
//        d8888                 888               888
//       d88888                 888               888
//      d88P888                 888               888
//     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
//    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
//   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
//  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
// d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
//                                                            888 888
//                                                       Y8b d88P 888
//                                                        "Y88P"  888

pragma solidity ^0.8.20;

import "../ArchetypePayouts.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "solady/src/utils/MerkleProofLib.sol";
import "solady/src/utils/ECDSA.sol";

error InvalidConfig();
error MintNotYetStarted();
error MintEnded();
error WalletUnauthorizedToMint();
error InsufficientEthSent();
error ExcessiveEthSent();
error Erc20BalanceTooLow();
error MaxSupplyExceeded();
error ListMaxSupplyExceeded();
error NumberOfMintsExceeded();
error MintingPaused();
error InvalidReferral();
error InvalidSignature();
error MaxBatchSizeExceeded();
error NotTokenOwner();
error NotPlatform();
error NotOwner();
error NotShareholder();
error NotApprovedToTransfer();
error InvalidAmountOfTokens();
error WrongPassword();
error LockedForever();
error Blacklisted();
error URIQueryForNonexistentToken();
error invalidTokenIdLength();
error burnToRemintDisabled();

//
// STRUCTS
//
struct Auth {
  bytes32 key;
  bytes32[] proof;
}

struct BonusDiscount {
  uint16 numMints;
  uint16 numBonusMints;
}

struct Config {
  string baseUri;
  address affiliateSigner;
  uint32 maxSupply; // in erc20
  uint32 maxBatchSize; // in erc20
  uint16 affiliateFee; //BPS
  uint16 affiliateDiscount; // BPS
  uint16 defaultRoyalty; //BPS
  uint16 remintPremium; //BPS premium for burning and reminting a new token
  uint16 erc20Ratio; // number of erc20 (10**18) equal to one nft
}

struct PayoutConfig {
  uint16 ownerBps;
  uint16 platformBps;
  uint16 partnerBps;
  uint16 superAffiliateBps;
  address partner;
  address superAffiliate;
  address ownerAltPayout;
}

struct AdvancedInvite {
  uint128 price; // in erc20
  uint128 reservePrice; // in erc20
  uint128 delta; // in erc20
  uint32 maxSupply; // in erc20
  uint32 limit; // in erc20
  uint32 start;
  uint32 end;
  uint32 interval;
  uint32 unitSize; // mint 1 get x
  address tokenAddress;
  bool isBlacklist;
}

struct Invite {
  uint128 price; 
  uint32 maxSupply; // in erc20
  uint32 limit; // in erc20
  uint32 start;
  uint32 end;
  uint32 unitSize; // mint 1 get x
  address tokenAddress;
  bool isBlacklist;
}

struct ValidationArgs {
  address owner;
  address affiliate;
  uint256 quantity;
  uint256 curSupply;
  uint256 listSupply;
  uint256 pairedSupply;
}

// UPDATE CONSTANTS BEFORE DEPLOY
address constant PLATFORM = 0x86B82972282Dd22348374bC63fd21620F7ED847B;
address constant BATCH = 0x6Bc558A6DC48dEfa0e7022713c23D65Ab26e4Fa7;
address constant PAYOUTS = 0xaAfdfA4a935d8511bF285af11A0544ce7e4a1199;
uint16 constant MAXBPS = 5000; // max fee or discount is 50%
uint32 constant UINT32_MAX = 2**32 - 1;
uint256 constant ERC20_UNIT = 10 ** 18;

library ArchetypeLogicBurgers404 {
  //
  // EVENTS
  //
  event Invited(bytes32 indexed key, bytes32 indexed cid);
  event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints);
  event Withdrawal(address indexed src, address token, uint128 wad);

  // calculate price based on affiliate usage and mint discounts
  function computePrice(
    AdvancedInvite storage invite,
    uint16 affiliateDiscount,
    uint256 numTokens,
    uint256 listSupply,
    bool affiliateUsed
  ) public view returns (uint256) {
    uint256 price = invite.price;
    uint256 cost;
    if (invite.interval > 0 && invite.delta > 0) {
      // Apply dutch pricing
      uint256 diff = (((block.timestamp - invite.start) / invite.interval) * invite.delta);
      if (price > invite.reservePrice) {
        if (diff > price - invite.reservePrice) {
          price = invite.reservePrice;
        } else {
          price = price - diff;
        }
      } else if (price < invite.reservePrice) {
        if (diff > invite.reservePrice - price) {
          price = invite.reservePrice;
        } else {
          price = price + diff;
        }
      }
      cost = price * numTokens;
    } else if (invite.interval == 0 && invite.delta > 0) {
      // Apply linear curve
      uint256 lastPrice = price + invite.delta * listSupply;
      cost = lastPrice * numTokens + (invite.delta * numTokens * (numTokens - 1)) / 2;
    } else {
      cost = price * numTokens;
    }

    if (affiliateUsed) {
      cost = cost - ((cost * affiliateDiscount) / 10000);
    }

    return cost;
  }

  function bonusMintsAwarded(uint256 numNfts, uint256 packedDiscount) internal pure returns (uint256) {
    for (uint8 i = 0; i < 8; i++) {
        uint32 discount = uint32((packedDiscount >> (32 * i)) & 0xFFFFFFFF);
        uint16 tierNumMints = uint16(discount >> 16);
        uint16 tierBonusMints = uint16(discount);
        
        if (tierNumMints == 0) {
            break; // End of valid discounts
        }
        
        if (numNfts >= tierNumMints) {
            return (numNfts / tierNumMints) * tierBonusMints;
        }
    }
    return 0;
  }

  function validateMint(
    AdvancedInvite storage i,
    Config storage config,
    Auth calldata auth,
    mapping(address => mapping(bytes32 => uint256)) storage minted,
    bytes calldata signature,
    ValidationArgs memory args,
    uint128 cost
  ) public view {
    address msgSender = _msgSender();
    if (args.affiliate != address(0)) {
      if (
        args.affiliate == PLATFORM || args.affiliate == args.owner || args.affiliate == msgSender
      ) {
        revert InvalidReferral();
      }
      validateAffiliate(args.affiliate, signature, config.affiliateSigner);
    }

    if (i.limit == 0) {
      revert MintingPaused();
    }

    if (!i.isBlacklist) {
      if (!verify(auth, i.tokenAddress, msgSender)) {
        revert WalletUnauthorizedToMint();
      }
    } else {
      if (verify(auth, i.tokenAddress, msgSender)) {
        revert Blacklisted();
      }
    }

    if (block.timestamp < i.start) {
      revert MintNotYetStarted();
    }

    if (i.end > i.start && block.timestamp > i.end) {
      revert MintEnded();
    }

    if (i.limit < i.maxSupply) {
      uint256 totalAfterMint = minted[msgSender][auth.key] + args.quantity;

      if (totalAfterMint > i.limit) {
        revert NumberOfMintsExceeded();
      }
    }

    if (i.maxSupply < config.maxSupply) {
      uint256 totalAfterMint = args.listSupply + args.pairedSupply + args.quantity;
        if (totalAfterMint > i.maxSupply) {
            revert ListMaxSupplyExceeded();
        }
    }

    if (args.quantity > config.maxBatchSize) {
      revert MaxBatchSizeExceeded();
    }

    if ((args.curSupply + args.quantity) > config.maxSupply) {
      revert MaxSupplyExceeded();
    }

    if (i.tokenAddress != address(0)) {
      IERC20 erc20Token = IERC20(i.tokenAddress);
      if (erc20Token.allowance(msgSender, address(this)) < cost) {
        revert NotApprovedToTransfer();
      }

      if (erc20Token.balanceOf(msgSender) < cost) {
        revert Erc20BalanceTooLow();
      }

      if (msg.value != 0) {
        revert ExcessiveEthSent();
      }
    } else {
      if (msg.value < cost) {
        revert InsufficientEthSent();
      }
    }
  }

  function updateBalances(
    AdvancedInvite storage i,
    Config storage config,
    mapping(address => uint128) storage _ownerBalance,
    mapping(address => mapping(address => uint128)) storage _affiliateBalance,
    address affiliate,
    uint256 quantity,
    uint128 value
  ) public {
    address tokenAddress = i.tokenAddress;

    uint128 affiliateWad;
    if (affiliate != address(0)) {
      affiliateWad = (value * config.affiliateFee) / 10000;
      _affiliateBalance[affiliate][tokenAddress] += affiliateWad;
      emit Referral(affiliate, tokenAddress, affiliateWad, quantity);
    }

    uint128 balance = _ownerBalance[tokenAddress];
    uint128 ownerWad = value - affiliateWad;
    _ownerBalance[tokenAddress] = balance + ownerWad;

    if (tokenAddress != address(0)) {
      IERC20 erc20Token = IERC20(tokenAddress);
      bool success = erc20Token.transferFrom(_msgSender(), address(this), value);
      if (!success) {
        revert TransferFailed();
      }
    }
  }

  function withdrawTokensAffiliate(
    mapping(address => mapping(address => uint128)) storage _affiliateBalance,
    address[] calldata tokens
  ) public {
    address msgSender = _msgSender();

    for (uint256 i; i < tokens.length; i++) {
      address tokenAddress = tokens[i];
      uint128 wad = _affiliateBalance[msgSender][tokenAddress];
      _affiliateBalance[msgSender][tokenAddress] = 0;

      if (wad == 0) {
        revert BalanceEmpty();
      }

      if (tokenAddress == address(0)) {
        bool success = false;
        (success, ) = msgSender.call{ value: wad }("");
        if (!success) {
          revert TransferFailed();
        }
      } else {
        IERC20 erc20Token = IERC20(tokenAddress);
        bool success = erc20Token.transfer(msgSender, wad);
        if (!success) {
          revert TransferFailed();
        }
      }

      emit Withdrawal(msgSender, tokenAddress, wad);
    }
  }

  function withdrawTokens(
    PayoutConfig storage payoutConfig,
    mapping(address => uint128) storage _ownerBalance,
    address owner,
    address[] calldata tokens
  ) public {
    address msgSender = _msgSender();
    for (uint256 i; i < tokens.length; i++) {
      address tokenAddress = tokens[i];
      uint128 wad;

      if (
        msgSender == owner ||
        msgSender == PLATFORM ||
        msgSender == payoutConfig.partner ||
        msgSender == payoutConfig.superAffiliate ||
        msgSender == payoutConfig.ownerAltPayout
      ) {
        wad = _ownerBalance[tokenAddress];
        _ownerBalance[tokenAddress] = 0;
      } else {
        revert NotShareholder();
      }

      if (wad == 0) {
        revert BalanceEmpty();
      }

      if (payoutConfig.ownerAltPayout == address(0)) {
        address[] memory recipients = new address[](4);
        recipients[0] = owner;
        recipients[1] = PLATFORM;
        recipients[2] = payoutConfig.partner;
        recipients[3] = payoutConfig.superAffiliate;

        uint16[] memory splits = new uint16[](4);
        splits[0] = payoutConfig.ownerBps;
        splits[1] = payoutConfig.platformBps;
        splits[2] = payoutConfig.partnerBps;
        splits[3] = payoutConfig.superAffiliateBps;

        if (tokenAddress == address(0)) {
          ArchetypePayouts(PAYOUTS).updateBalances{ value: wad }(
            wad,
            tokenAddress,
            recipients,
            splits
          );
        } else {
          ArchetypePayouts(PAYOUTS).updateBalances(wad, tokenAddress, recipients, splits);
        }
      } else {
        uint256 ownerShare = (uint256(wad) * payoutConfig.ownerBps) / 10000;
        uint256 remainingShare = wad - ownerShare;

        if (tokenAddress == address(0)) {
          (bool success, ) = payable(payoutConfig.ownerAltPayout).call{ value: ownerShare }("");
          if (!success) revert TransferFailed();
        } else {
          IERC20(tokenAddress).transfer(payoutConfig.ownerAltPayout, ownerShare);
        }

        address[] memory recipients = new address[](3);
        recipients[0] = PLATFORM;
        recipients[1] = payoutConfig.partner;
        recipients[2] = payoutConfig.superAffiliate;

        uint16[] memory splits = new uint16[](3);
        uint16 remainingBps = 10000 - payoutConfig.ownerBps;
        splits[1] = uint16((uint256(payoutConfig.partnerBps) * 10000) / remainingBps);
        splits[2] = uint16((uint256(payoutConfig.superAffiliateBps) * 10000) / remainingBps);
        splits[0] = 10000 - splits[1] - splits[2];

        if (tokenAddress == address(0)) {
          ArchetypePayouts(PAYOUTS).updateBalances{ value: remainingShare }(
            remainingShare,
            tokenAddress,
            recipients,
            splits
          );
        } else {
          ArchetypePayouts(PAYOUTS).updateBalances(
            remainingShare,
            tokenAddress,
            recipients,
            splits
          );
        }
      }
      emit Withdrawal(msgSender, tokenAddress, wad);
    }
  }


  function validateAffiliate(
    address affiliate,
    bytes calldata signature,
    address affiliateSigner
  ) public view {
    bytes32 signedMessagehash = ECDSA.toEthSignedMessageHash(
      keccak256(abi.encodePacked(affiliate))
    );
    address signer = ECDSA.recover(signedMessagehash, signature);

    if (signer != affiliateSigner) {
      revert InvalidSignature();
    }
  }

  function verify(
    Auth calldata auth,
    address tokenAddress,
    address account
  ) public pure returns (bool) {
    // keys 0-255 and tokenAddress are public
    if (uint256(auth.key) <= 0xff || auth.key == keccak256(abi.encodePacked(tokenAddress))) {
      return true;
    }

    return MerkleProofLib.verify(auth.proof, auth.key, keccak256(abi.encodePacked(account)));
  }

  function _msgSender() internal view returns (address) {
    return msg.sender == BATCH ? tx.origin : msg.sender;
  }
}

File 13 of 16 : DN420.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @title DN420
/// @notice DN420 is a fully standard compliant, single-contract,
/// ERC20 and ERC1155 chimera implementation that mints
/// and burns NFTs based on an account's ERC20 token balance.
///
/// This contract has not yet been audited. USE AT YOUR OWN RISK!
///
/// @author vectorized.eth (@optimizoor)
/// @author Quit (@0xQuit)
/// @author Michael Amadi (@AmadiMichaels)
/// @author cygaar (@0xCygaar)
/// @author Thomas (@0xjustadev)
/// @author Harrison (@PopPunkOnChain)
///
/// @dev Note:
/// - On-transfer token ID burning scheme:
///     * DN420: Largest token ID up to owned checkpoint (inclusive) first.
///     * DN404: Most recently acquired token ID first.
/// - This implementation uses bitmap scans to find ERC1155 token IDs
///   to transfer / burn upon ERC20 transfers.
/// - For long-term gas efficiency, please ensure that the maximum
///   supply of NFTs is bounded and not too big.
///   10k is fine; it will cost less than 100k gas to bitmap scan 10k bits.
///   Otherwise, users can still always call `setOwnedCheckpoint` to unblock.
/// - A unit worth of ERC20 tokens equates to a deed to one NFT token.
///   The skip NFT status determines if this deed is automatically exercised.
///   An account can configure their skip NFT status.
///     * If `getSkipNFT(owner) == true`, ERC20 mints / transfers to `owner`
///       will NOT trigger NFT mints / transfers to `owner` (i.e. deeds are left unexercised).
///     * If `getSkipNFT(owner) == false`, ERC20 mints / transfers to `owner`
///       will trigger NFT mints / transfers to `owner`, until the NFT balance of `owner`
///       is equal to its ERC20 balance divided by the unit (rounded down).
/// - Invariant: `_balanceOfNFT(owner) <= balanceOf(owner) / _unit()`.
/// - The gas costs for automatic minting / transferring / burning of NFTs is O(n).
///   This can exceed the block gas limit.
///   Applications and users may need to break up large transfers into a few transactions.
/// - This implementation uses safe transfers for automatic NFT transfers,
///   as all transfers require the recipient check by the ERC1155 spec.
/// - The ERC20 token allowances and ERC1155 token / operator approvals are separate.
/// - For MEV safety, users should NOT have concurrently open orders for the ERC20 and ERC1155.
abstract contract DN420 {
    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                           EVENTS                           */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

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

    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @dev Emitted when `owner` sets their skipNFT flag to `status`.
    event SkipNFTSet(address indexed owner, bool status);

    /// @dev Emitted when `owner` sets their owned checkpoint to `id`.
    event OwnedCheckpointSet(address indexed owner, uint256 id);

    /// @dev Emitted when `amount` of token `id` is transferred
    /// from `from` to `to` by `operator`.
    event TransferSingle(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 id,
        uint256 amount
    );

    /// @dev Emitted when `amounts` of token `ids` are transferred
    /// from `from` to `to` by `operator`.
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] amounts
    );

    /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
    event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);

    /// @dev Emitted when the Uniform Resource Identifier (URI) for token `id`
    /// is updated to `value`. This event is not used in the base contract.
    /// You may need to emit this event depending on your URI logic.
    ///
    /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
    event URI(string value, uint256 indexed id);

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

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

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

    /// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.
    uint256 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE =
        0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;

    /// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.
    uint256 private constant _TRANSFER_BATCH_EVENT_SIGNATURE =
        0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;

    /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
    uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
        0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;

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

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

    /// @dev The contract has not been initialized.
    error DNNotInitialized();

    /// @dev Thrown when attempting to transfer or burn more tokens than sender's balance.
    error InsufficientBalance();

    /// @dev Thrown when a spender attempts to transfer tokens with an insufficient allowance.
    error InsufficientAllowance();

    /// @dev Thrown when minting an amount of tokens that would overflow the max tokens.
    error TotalSupplyOverflow();

    /// @dev The lengths of the input arrays are not the same.
    error ArrayLengthsMismatch();

    /// @dev The unit must be greater than zero and less than `2**96`.
    error InvalidUnit();

    /// @dev Thrown when attempting to transfer tokens to the zero address.
    error TransferToZeroAddress();

    /// @dev Thrown when transferring an NFT
    /// and the caller is not the owner or an approved operator.
    error NotOwnerNorApproved();

    /// @dev Thrown when transferring an NFT and the from address is not the current owner.
    error TransferFromIncorrectOwner();

    /// @dev The amount of ERC1155 NFT transferred per token must be 1.
    error InvalidNFTAmount();

    /// @dev The function selector is not recognized.
    error FnSelectorNotRecognized();

    /// @dev Cannot safely transfer to a contract that does not implement
    /// the ERC1155Receiver interface.
    error TransferToNonERC1155ReceiverImplementer();

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                         CONSTANTS                          */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev The flag to denote that the skip NFT flag is initialized.
    uint8 internal constant _ADDRESS_DATA_SKIP_NFT_INITIALIZED_FLAG = 1 << 0;

    /// @dev The flag to denote that the address should skip NFTs.
    uint8 internal constant _ADDRESS_DATA_SKIP_NFT_FLAG = 1 << 1;

    /// @dev The flag to denote that the address has overridden the default Permit2 allowance.
    uint8 internal constant _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG = 1 << 2;

    /// @dev The canonical Permit2 address.
    /// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
    /// To enable, override `_givePermit2DefaultInfiniteAllowance()`.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

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

    /// @dev Struct containing an address's token data and settings.
    struct AddressData {
        // Auxiliary data.
        uint88 aux;
        // Flags for `initialized` and `skipNFT`.
        uint8 flags;
        // The index which to start scanning backwards for burnable / transferable token IDs.
        uint32 ownedCheckpoint;
        // The number of NFT tokens.
        uint32 ownedCount;
        // The token balance in wei.
        uint96 balance;
    }

    /// @dev A bitmap in storage.
    struct Bitmap {
        uint256 spacer;
    }

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

    /// @dev A mapping of an address pair to a Uint256Ref.
    struct AddressPairToUint256RefMap {
        uint256 spacer;
    }

    /// @dev Struct containing the base token contract storage.
    struct DN420Storage {
        // Next NFT ID to assign for a mint.
        uint32 nextTokenId;
        // This is greater than or equal to the largest NFT ID minted thus far.
        // A non-zero value is used to denote that the contract has been initialized.
        uint32 tokenIdUpTo;
        // Total supply of tokens.
        uint96 totalSupply;
        // Mapping of user operator approvals for NFTs.
        AddressPairToUint256RefMap operatorApprovals;
        // Bitmap of whether a NFT ID exists.
        Bitmap exists;
        // Mapping of user allowances for ERC20 spenders.
        AddressPairToUint256RefMap allowance;
        // Bitmap of NFT IDs owned by an address.
        mapping(address => Bitmap) owned;
        // Mapping of user account AddressData.
        mapping(address => AddressData) addressData;
    }

    /// @dev Returns a storage pointer for DN420Storage.
    function _getDN420Storage() internal pure virtual returns (DN420Storage storage $) {
        /// @solidity memory-safe-assembly
        assembly {
            // `uint72(bytes9(keccak256("DN420_STORAGE")))`.
            $.slot := 0xb6dffd38a260769cb2 // Truncate to 9 bytes to reduce bytecode size.
        }
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                         INITIALIZER                        */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Initializes the DN420 contract with an
    /// `initialTokenSupply` and `initialTokenOwner`.
    ///
    /// Note: The `initialSupplyOwner` will have their skip NFT status set to true.
    function _initializeDN420(uint256 initialTokenSupply, address initialSupplyOwner)
        internal
        virtual
    {
        DN420Storage storage $ = _getDN420Storage();

        if ($.tokenIdUpTo != 0) revert DNAlreadyInitialized();
        unchecked {
            $.tokenIdUpTo = uint32((initialTokenSupply / _unit()) | 1);
            if (_unit() - 1 >= 2 ** 96 - 1) revert InvalidUnit();
        }
        $.nextTokenId = 1;

        if (initialTokenSupply != 0) {
            if (initialSupplyOwner == address(0)) revert TransferToZeroAddress();
            if (_totalSupplyOverflows(initialTokenSupply)) revert TotalSupplyOverflow();

            $.totalSupply = uint96(initialTokenSupply);

            AddressData storage initialOwnerAddressData = $.addressData[initialSupplyOwner];
            initialOwnerAddressData.balance = uint96(initialTokenSupply);

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

            _setSkipNFT(initialSupplyOwner, true);
        }
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*               BASE UNIT FUNCTION TO OVERRIDE               */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Amount of token balance that is equal to one NFT.
    ///
    /// Note: The return value MUST be kept constant after `_initializeDN420` is called.
    function _unit() internal view virtual returns (uint256) {
        return 10 ** 18;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*               METADATA FUNCTIONS TO OVERRIDE               */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns the name of the token.
    function name() public view virtual returns (string memory);

    /// @dev Returns the symbol of the token.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the URI for token `id`.
    ///
    /// You can either return the same templated URI for all token IDs,
    /// (e.g. "https://example.com/api/{id}.json"),
    /// or return a unique URI for each `id`.
    ///
    /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
    function uri(uint256 id) public view virtual returns (string memory);

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                       CONFIGURABLES                        */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns if direct NFT transfers should be used during ERC20 transfers
    /// whenever possible, instead of burning and re-minting.
    function _useDirectTransfersIfPossible() internal view virtual returns (bool) {
        return true;
    }

    /// @dev Hook that is called after a batch of NFT transfers.
    /// The lengths of `from`, `to`, and `ids` are guaranteed to be the same.
    function _afterNFTTransfers(address[] memory from, address[] memory to, uint256[] memory ids)
        internal
        virtual
    {}

    /// @dev Override this function to return true if `_afterNFTTransfers` is used.
    /// This is to help the compiler avoid producing dead bytecode.
    function _useAfterNFTTransfers() internal virtual returns (bool) {}

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                      ERC20 OPERATIONS                      */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns the decimals places of the ERC20 token. Always 18.
    function decimals() public pure returns (uint8) {
        return 18;
    }

    /// @dev Returns the amount of ERC20 tokens in existence.
    function totalSupply() public view virtual returns (uint256) {
        return uint256(_getDN420Storage().totalSupply);
    }

    /// @dev Returns the amount of ERC20 tokens owned by `owner`.
    function balanceOf(address owner) public view virtual returns (uint256) {
        return _getDN420Storage().addressData[owner].balance;
    }

    /// @dev Returns the amount of ERC20 tokens that `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender) public view returns (uint256) {
        if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) {
            uint8 flags = _getDN420Storage().addressData[owner].flags;
            if ((flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG) == uint256(0)) {
                return type(uint256).max;
            }
        }
        return _ref(_getDN420Storage().allowance, owner, spender).value;
    }

    /// @dev Sets `amount` as the allowance of `spender` over the caller's ERC20 tokens.
    ///
    /// Emits an ERC20 {Approval} event.
    function approve(address spender, uint256 amount) public virtual returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    /// @dev Transfer `amount` ERC20 tokens from the caller to `to`.
    ///
    /// Will burn sender's ERC1155 NFTs if balance after transfer is less than
    /// the amount required to support the current NFT balance.
    ///
    /// Will mint ERC1155 NFTs to `to` if the recipient's new balance supports
    /// additional ERC1155 NFTs ***AND*** the `to` address's skipNFT flag is
    /// set to false.
    ///
    /// Requirements:
    /// - `from` must at least have `amount` ERC20 tokens.
    ///
    /// Emits an ERC1155 {TransferBatch} event for direct transfers (if any).
    /// Emits an ERC1155 {TransferBatch} event for mints (if any).
    /// Emits an ERC1155 {TransferBatch} event for burns (if any).
    /// Emits an ERC20 {Transfer} event.
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        _transfer(msg.sender, to, amount, "");
        return true;
    }

    /// @dev Transfers `amount` ERC20 tokens from `from` to `to`.
    ///
    /// Note: Does not update the ERC20 allowance if it is the maximum uint256 value.
    ///
    /// Will burn sender ERC1155 NFTs if balance after transfer is less than
    /// the amount required to support the current ERC1155 NFT balance.
    ///
    /// Will mint ERC1155 NFTs to `to` if the recipient's new balance supports
    /// additional ERC1155 NFTs ***AND*** the `to` address's skipNFT flag is
    /// set to false.
    ///
    /// Requirements:
    /// - `from` must at least have `amount` ERC20 tokens.
    /// - The caller must have at least `amount` of ERC20 allowance to transfer the tokens of `from`.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
        Uint256Ref storage a = _ref(_getDN420Storage().allowance, from, msg.sender);

        uint256 allowed = _givePermit2DefaultInfiniteAllowance() && msg.sender == _PERMIT2
            && (_getDN420Storage().addressData[from].flags & _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG)
                == uint256(0) ? type(uint256).max : a.value;

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

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                          PERMIT2                           */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Whether Permit2 has infinite ERC20 allowances by default for all owners.
    /// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
    /// To enable, override this function to return true.
    function _givePermit2DefaultInfiniteAllowance() internal view virtual returns (bool) {
        return false;
    }

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

    /// @dev Mints `amount` ERC20 tokens to `to`, increasing the total supply.
    ///
    /// Will mint ERC1155 NFTs to `to` if the recipient's new balance supports
    /// additional ERC1155 NFTs ***AND*** the `to` address's skipNFT flag is set to false.
    ///
    /// Emits an ERC1155 {TransferBatch} event for mints (if any).
    /// Emits an ERC20 {Transfer} event.
    function _mint(address to, uint256 amount, bytes memory data) internal virtual {
        if (to == address(0)) revert TransferToZeroAddress();

        DN420Storage storage $ = _getDN420Storage();
        if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized();
        AddressData storage toAddressData = $.addressData[to];

        _DNMintTemps memory t;
        unchecked {
            {
                uint256 toBalance = uint256(toAddressData.balance) + amount;
                toAddressData.balance = uint96(toBalance);
                t.toEnd = toBalance / _unit();
            }
            uint256 maxId;
            {
                uint256 totalSupply_ = uint256($.totalSupply) + amount;
                $.totalSupply = uint96(totalSupply_);
                uint256 overflows = _toUint(_totalSupplyOverflows(totalSupply_));
                if (overflows | _toUint(totalSupply_ < amount) != 0) revert TotalSupplyOverflow();
                maxId = totalSupply_ / _unit();
                $.tokenIdUpTo = uint32(_max($.tokenIdUpTo, maxId));
            }
            if (!getSkipNFT(to)) {
                t.mintIds = _idsMalloc(_zeroFloorSub(t.toEnd, toAddressData.ownedCount));
                if (t.mintIds.length != 0) {
                    Bitmap storage toOwned = $.owned[to];
                    uint256 ownedCheckpoint = toAddressData.ownedCheckpoint;
                    uint256 id = _wrapNFTId($.nextTokenId, maxId);
                    // Mint loop.
                    for (uint256 n = t.mintIds.length;;) {
                        while (_get($.exists, id)) {
                            id = _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId);
                        }
                        _set($.exists, id, true);
                        _set(toOwned, id, true);
                        ownedCheckpoint = _max(ownedCheckpoint, id);
                        _idsAppend(t.mintIds, id);
                        id = _wrapNFTId(id + 1, maxId);
                        if (--n == uint256(0)) break;
                    }
                    toAddressData.ownedCheckpoint = uint32(ownedCheckpoint);
                    toAddressData.ownedCount = uint32(t.toEnd);
                    $.nextTokenId = uint32(id);
                    _batchTransferEmit(address(0), to, t.mintIds);
                }
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Emit the ERC20 {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
        }
        if (_useAfterNFTTransfers()) {
            _afterNFTTransfers(
                _zeroAddresses(t.mintIds.length), _filled(t.mintIds.length, to), t.mintIds
            );
        }
        if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, t.mintIds, data);
    }

    /// @dev Mints `amount` tokens to `to`, increasing the total supply.
    /// This variant mints NFT tokens starting from ID `preTotalSupply / _unit() + 1`.
    /// The `nextTokenId` will not be changed.
    ///
    /// Will mint NFTs to `to` if the recipient's new balance supports
    /// additional NFTs ***AND*** the `to` address's skipNFT flag is set to false.
    ///
    /// Note:
    /// - May mint more NFTs than `amount / _unit()`.
    ///   The number of NFTs minted is what is needed to make `to`'s NFT balance whole.
    /// - Token IDs may wrap around `totalSupply / _unit()` back to 1.
    ///
    /// Emits an ERC1155 {TransferBatch} event for mints (if any).
    /// Emits an ERC20 {Transfer} event.
    function _mintNext(address to, uint256 amount, bytes memory data) internal virtual {
        if (to == address(0)) revert TransferToZeroAddress();

        DN420Storage storage $ = _getDN420Storage();
        if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized();
        AddressData storage toAddressData = $.addressData[to];

        _DNMintTemps memory t;
        unchecked {
            {
                uint256 toBalance = uint256(toAddressData.balance) + amount;
                toAddressData.balance = uint96(toBalance);
                t.toEnd = toBalance / _unit();
            }
            uint256 id;
            uint256 maxId;
            {
                uint256 preTotalSupply = uint256($.totalSupply);
                uint256 newTotalSupply = uint256(preTotalSupply) + amount;
                $.totalSupply = uint96(newTotalSupply);
                uint256 overflows = _toUint(_totalSupplyOverflows(newTotalSupply));
                if (overflows | _toUint(newTotalSupply < amount) != 0) revert TotalSupplyOverflow();
                maxId = newTotalSupply / _unit();
                id = _wrapNFTId(preTotalSupply / _unit() + 1, maxId);
                $.tokenIdUpTo = uint32(_max($.tokenIdUpTo, maxId));
            }
            if (!getSkipNFT(to)) {
                t.mintIds = _idsMalloc(_zeroFloorSub(t.toEnd, toAddressData.ownedCount));
                if (t.mintIds.length != 0) {
                    Bitmap storage toOwned = $.owned[to];
                    uint256 ownedCheckpoint = toAddressData.ownedCheckpoint;
                    // Mint loop.
                    for (uint256 n = t.mintIds.length;;) {
                        while (_get($.exists, id)) {
                            id = _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId);
                        }
                        _set($.exists, id, true);
                        _set(toOwned, id, true);
                        ownedCheckpoint = _max(ownedCheckpoint, id);
                        _idsAppend(t.mintIds, id);
                        id = _wrapNFTId(id + 1, maxId);
                        if (--n == uint256(0)) break;
                    }
                    toAddressData.ownedCheckpoint = uint32(ownedCheckpoint);
                    toAddressData.ownedCount = uint32(t.toEnd);
                    _batchTransferEmit(address(0), to, t.mintIds);
                }
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Emit the ERC20 {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)))
        }
        if (_useAfterNFTTransfers()) {
            _afterNFTTransfers(
                _zeroAddresses(t.mintIds.length), _filled(t.mintIds.length, to), t.mintIds
            );
        }
        if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, t.mintIds, data);
    }

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

    /// @dev Burns `amount` ERC20 tokens from `from`, reducing the total supply.
    ///
    /// Will burn sender's ERC1155 NFTs if balance after transfer is less than
    /// the amount required to support the current ERC1155 NFT balance.
    ///
    /// Emits an ERC1155 {TransferBatch} event for burns (if any).
    /// Emits an ERC20 {Transfer} event.
    function _burn(address from, uint256 amount) internal virtual {
        DN420Storage storage $ = _getDN420Storage();
        if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized();
        AddressData storage fromAddressData = $.addressData[from];

        uint256[] memory ids;
        unchecked {
            uint256 fromBalance = fromAddressData.balance;
            if (amount > fromBalance) revert InsufficientBalance();

            fromAddressData.balance = uint96(fromBalance -= amount);
            $.totalSupply -= uint96(amount);

            Bitmap storage fromOwned = $.owned[from];
            uint256 fromIndex = fromAddressData.ownedCount;
            uint256 numNFTBurns = _zeroFloorSub(fromIndex, fromBalance / _unit());

            if (numNFTBurns != 0) {
                ids = _idsMalloc(numNFTBurns);
                fromAddressData.ownedCount = uint32(fromIndex - numNFTBurns);
                uint256 id = fromAddressData.ownedCheckpoint;
                // Burn loop.
                while (true) {
                    id = _findLastSet(fromOwned, id);
                    if (id == uint256(0)) id = _findLastSet(fromOwned, $.tokenIdUpTo);
                    _set(fromOwned, id, false);
                    _set($.exists, id, false);
                    _idsAppend(ids, id);
                    if (--numNFTBurns == uint256(0)) break;
                }
                fromAddressData.ownedCheckpoint = uint32(id);
                _batchTransferEmit(from, address(0), ids);
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Emit the ERC20 {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
        if (_useAfterNFTTransfers()) {
            _afterNFTTransfers(_filled(ids.length, from), _zeroAddresses(ids.length), ids);
        }
    }

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

    /// @dev Moves `amount` of ERC20 tokens from `from` to `to`.
    ///
    /// Will burn sender ERC1155 NFTs if balance after transfer is less than
    /// the amount required to support the current ERC1155 NFT balance.
    ///
    /// Will mint ERC1155 NFTs to `to` if the recipient's new balance supports
    /// additional ERC1155 NFTs ***AND*** the `to` address's skipNFT flag is
    /// set to false.
    ///.
    /// Emits an ERC1155 {TransferBatch} event for direct transfers (if any).
    /// Emits an ERC1155 {TransferBatch} event for mints (if any).
    /// Emits an ERC1155 {TransferBatch} event for burns (if any).
    /// Emits an ERC20 {Transfer} event
    function _transfer(address from, address to, uint256 amount, bytes memory data)
        internal
        virtual
    {
        if (to == address(0)) revert TransferToZeroAddress();

        DN420Storage storage $ = _getDN420Storage();
        if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized();
        AddressData storage fromAddressData = $.addressData[from];
        AddressData storage toAddressData = $.addressData[to];

        _DNTransferTemps memory t;
        t.fromOwnedCount = fromAddressData.ownedCount;
        t.toOwnedCount = toAddressData.ownedCount;

        unchecked {
            uint256 toBalance;
            uint256 fromBalance = fromAddressData.balance;
            if (amount > fromBalance) revert InsufficientBalance();

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

            t.numNFTBurns = _zeroFloorSub(t.fromOwnedCount, fromBalance / _unit());

            if (!getSkipNFT(to)) {
                if (from == to) t.toOwnedCount = t.fromOwnedCount - t.numNFTBurns;
                t.numNFTMints = _zeroFloorSub(toBalance / _unit(), t.toOwnedCount);
            }
        }

        unchecked {
            while (_useDirectTransfersIfPossible()) {
                uint256 n = _min(t.fromOwnedCount, _min(t.numNFTBurns, t.numNFTMints));
                if (n == uint256(0)) break;
                t.numNFTBurns -= n;
                t.numNFTMints -= n;
                if (from == to) {
                    t.toOwnedCount += n;
                    break;
                }
                t.directIds = _idsMalloc(n);
                Bitmap storage fromOwned = $.owned[from];
                Bitmap storage toOwned = $.owned[to];

                uint256 id = fromAddressData.ownedCheckpoint;
                fromAddressData.ownedCount = uint32(t.fromOwnedCount -= n);
                toAddressData.ownedCheckpoint = uint32(_max(toAddressData.ownedCheckpoint, id));
                toAddressData.ownedCount = uint32(t.toOwnedCount += n);
                // Direct transfer loop.
                while (true) {
                    id = _findLastSet(fromOwned, id);
                    if (id == uint256(0)) id = _findLastSet(fromOwned, $.tokenIdUpTo);
                    _set(fromOwned, id, false);
                    _set(toOwned, id, true);
                    _idsAppend(t.directIds, id);
                    if (--n == uint256(0)) break;
                }
                fromAddressData.ownedCheckpoint = uint32(id);
                _batchTransferEmit(from, to, t.directIds);
                break;
            }

            if (t.numNFTBurns != 0) {
                uint256 n = t.numNFTBurns;
                t.burnIds = _idsMalloc(n);
                Bitmap storage fromOwned = $.owned[from];
                fromAddressData.ownedCount = uint32(t.fromOwnedCount - n);
                uint256 id = fromAddressData.ownedCheckpoint;
                // Burn loop.
                while (true) {
                    id = _findLastSet(fromOwned, id);
                    if (id == uint256(0)) id = _findLastSet(fromOwned, $.tokenIdUpTo);
                    _set(fromOwned, id, false);
                    _set($.exists, id, false);
                    _idsAppend(t.burnIds, id);
                    if (--n == uint256(0)) break;
                }
                fromAddressData.ownedCheckpoint = uint32(id);
                _batchTransferEmit(from, address(0), t.burnIds);
            }

            if (t.numNFTMints != 0) {
                uint256 n = t.numNFTMints;
                t.mintIds = _idsMalloc(n);
                Bitmap storage toOwned = $.owned[to];
                toAddressData.ownedCount = uint32(t.toOwnedCount + n);
                uint256 maxId = $.totalSupply / _unit();
                uint256 id = _wrapNFTId($.nextTokenId, maxId);
                uint256 ownedCheckpoint = toAddressData.ownedCheckpoint;
                // Mint loop.
                while (true) {
                    while (_get($.exists, id)) {
                        id = _wrapNFTId(_findFirstUnset($.exists, id + 1, maxId), maxId);
                    }
                    _set($.exists, id, true);
                    _set(toOwned, id, true);
                    ownedCheckpoint = _max(ownedCheckpoint, id);
                    _idsAppend(t.mintIds, id);
                    id = _wrapNFTId(id + 1, maxId);
                    if (--n == uint256(0)) break;
                }
                toAddressData.ownedCheckpoint = uint32(ownedCheckpoint);
                $.nextTokenId = uint32(id);
                _batchTransferEmit(address(0), to, t.mintIds);
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Emit the ERC20 {Transfer} event.
            mstore(0x00, amount)
            // forgefmt: disable-next-item
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)))
        }
        if (_useAfterNFTTransfers()) {
            uint256[] memory ids = t.directIds;
            unchecked {
                _afterNFTTransfers(
                    _concat(
                        _filled(ids.length + t.numNFTBurns, from), _zeroAddresses(t.numNFTMints)
                    ),
                    _concat(
                        _concat(_filled(ids.length, to), _zeroAddresses(t.numNFTBurns)),
                        _filled(t.numNFTMints, to)
                    ),
                    _concat(_concat(ids, t.burnIds), t.mintIds)
                );
            }
        }
        if (_hasCode(to)) {
            _checkOnERC1155BatchReceived(from, to, t.directIds, data);
            _checkOnERC1155BatchReceived(address(0), to, t.mintIds, data);
        }
    }

    /// @dev Transfers ERC1155 `id` from `from` to `to`.
    ///
    /// Requirements:
    /// - `to` cannot be the zero address.
    /// - `from` must have `id`.
    /// - If `by` is not the zero address, it must be either `from`,
    ///   or approved to manage the ERC1155 tokens of `from`.
    /// - If `to` refers to a smart contract, it must implement
    ///   {ERC1155-onERC1155Reveived}, which is called upon a batch transfer.
    ///
    /// Emits an ERC1155 {TransferSingle} event.
    /// Emits an ERC20 {Transfer} event.
    function _safeTransferNFT(address by, address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        if (to == address(0)) revert TransferToZeroAddress();

        DN420Storage storage $ = _getDN420Storage();
        if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized();

        if (_toUint(by == address(0)) | _toUint(by == from) == uint256(0)) {
            if (!isApprovedForAll(from, by)) revert NotOwnerNorApproved();
        }

        Bitmap storage fromOwned = $.owned[from];
        if (!_owns(fromOwned, id)) revert TransferFromIncorrectOwner();
        _set(fromOwned, id, false);
        _set($.owned[to], id, true);

        uint256 unit = _unit();
        AddressData storage fromAddressData = $.addressData[from];
        AddressData storage toAddressData = $.addressData[to];
        /// @solidity memory-safe-assembly
        assembly {
            let diff := shl(128, or(shl(32, unit), 1))
            sstore(fromAddressData.slot, sub(sload(fromAddressData.slot), diff))
            let toPacked := sload(toAddressData.slot)
            let toCheckpoint := and(0xffffffff, shr(96, toPacked))
            // forgefmt: disable-next-item
            sstore(toAddressData.slot, add(diff,
                xor(toPacked, shl(96, mul(gt(id, toCheckpoint), xor(id, toCheckpoint))))))
        }
        /// @solidity memory-safe-assembly
        assembly {
            from := shr(96, shl(96, from))
            to := shr(96, shl(96, to))
            // Emit the ERC1155 {TransferSingle} event.
            mstore(0x00, id)
            mstore(0x20, 1)
            log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), from, to)
            // Emit the ERC20 {Transfer} event.
            mstore(0x00, unit)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, from, to)
        }
        if (_useAfterNFTTransfers()) {
            _afterNFTTransfers(_filled(1, from), _filled(1, to), _filled(1, id));
        }
        if (_hasCode(to)) _checkOnERC1155Received(from, to, id, data);
    }

    /// @dev Transfers `id` from `from` to `to`.
    ///
    /// Requirements:
    /// - `to` cannot be the zero address.
    /// - `from` must have `ids`.
    /// - If `by` is not the zero address, it must be either `from`,
    ///   or approved to manage the ERC1155 tokens of `from`.
    /// - If `to` refers to a smart contract, it must implement
    ///   {ERC1155-onERC1155Reveived}, which is called upon a batch transfer.
    ///
    /// Emits an ERC1155 {TransferBatch} event.
    /// Emits an ERC20 {Transfer} event.
    function _safeBatchTransferNFTs(
        address by,
        address from,
        address to,
        uint256[] memory ids,
        bytes memory data
    ) internal virtual {
        if (to == address(0)) revert TransferToZeroAddress();

        DN420Storage storage $ = _getDN420Storage();
        if ($.tokenIdUpTo == uint256(0)) revert DNNotInitialized();

        if (_toUint(by == address(0)) | _toUint(by == from) == uint256(0)) {
            if (!isApprovedForAll(from, by)) revert NotOwnerNorApproved();
        }

        uint256 amount;
        uint256 upTo;
        AddressData storage fromAddressData = $.addressData[from];
        AddressData storage toAddressData = $.addressData[to];
        unchecked {
            uint256 n = ids.length;
            amount = n * _unit();
            Bitmap storage fromOwned = $.owned[from];
            Bitmap storage toOwned = $.owned[to];
            while (n != 0) {
                uint256 id = _get(ids, --n);
                if (!_owns(fromOwned, id)) revert TransferFromIncorrectOwner();
                _set(fromOwned, id, false);
                _set(toOwned, id, true);
                upTo = _max(upTo, id);
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            let diff := shl(128, or(shl(32, amount), mload(ids)))
            sstore(fromAddressData.slot, sub(sload(fromAddressData.slot), diff))
            let toPacked := sload(toAddressData.slot)
            let toCheckpoint := and(0xffffffff, shr(96, toPacked))
            // forgefmt: disable-next-item
            sstore(toAddressData.slot, add(diff,
                xor(toPacked, shl(96, mul(gt(upTo, toCheckpoint), xor(upTo, toCheckpoint))))))
        }
        _batchTransferEmit(from, to, ids);
        /// @solidity memory-safe-assembly
        assembly {
            // Emit the ERC20 {Transfer} event.
            mstore(0x00, amount)
            // forgefmt: disable-next-item
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)))
        }
        if (_useAfterNFTTransfers()) {
            _afterNFTTransfers(_filled(ids.length, from), _filled(ids.length, to), ids);
        }
        if (_hasCode(to)) _checkOnERC1155BatchReceived(from, to, ids, data);
    }

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

    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
    ///
    /// Emits a {Approval} event.
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        if (_givePermit2DefaultInfiniteAllowance() && spender == _PERMIT2) {
            _getDN420Storage().addressData[owner].flags |= _ADDRESS_DATA_OVERRIDE_PERMIT2_FLAG;
        }
        _ref(_getDN420Storage().allowance, owner, spender).value = amount;
        /// @solidity memory-safe-assembly
        assembly {
            // Emit the {Approval} event.
            mstore(0x00, amount)
            // forgefmt: disable-next-item
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, shl(96, owner)), shr(96, shl(96, spender)))
        }
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                 DATA HITCHHIKING FUNCTIONS                 */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns the auxiliary data for `owner`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _getAux(address owner) internal view virtual returns (uint88) {
        return _getDN420Storage().addressData[owner].aux;
    }

    /// @dev Set the auxiliary data for `owner` to `value`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _setAux(address owner, uint88 value) internal virtual {
        _getDN420Storage().addressData[owner].aux = value;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                     SKIP NFT FUNCTIONS                     */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns true if minting and transferring ERC20s to `owner` will skip minting NFTs.
    /// Returns false otherwise.
    function getSkipNFT(address owner) public view virtual returns (bool result) {
        uint8 flags = _getDN420Storage().addressData[owner].flags;
        /// @solidity memory-safe-assembly
        assembly {
            result := iszero(iszero(and(flags, _ADDRESS_DATA_SKIP_NFT_FLAG)))
            if iszero(and(flags, _ADDRESS_DATA_SKIP_NFT_INITIALIZED_FLAG)) {
                result := iszero(iszero(extcodesize(owner)))
            }
        }
    }

    /// @dev Sets the caller's skipNFT flag to `skipNFT`. Returns true.
    ///
    /// Emits a {SkipNFTSet} event.
    function setSkipNFT(bool skipNFT) public virtual returns (bool) {
        _setSkipNFT(msg.sender, skipNFT);
        return true;
    }

    /// @dev Internal function to set account `owner` skipNFT flag to `state`
    ///
    /// Initializes account `owner` AddressData if it is not currently initialized.
    ///
    /// Emits a {SkipNFTSet} event.
    function _setSkipNFT(address owner, bool state) internal virtual {
        AddressData storage d = _getDN420Storage().addressData[owner];
        uint8 flags = d.flags;
        /// @solidity memory-safe-assembly
        assembly {
            let s := xor(iszero(and(flags, _ADDRESS_DATA_SKIP_NFT_FLAG)), iszero(state))
            flags := xor(mul(_ADDRESS_DATA_SKIP_NFT_FLAG, s), flags)
            flags := or(_ADDRESS_DATA_SKIP_NFT_INITIALIZED_FLAG, flags)
            mstore(0x00, iszero(iszero(state)))
            log2(0x00, 0x20, _SKIP_NFT_SET_EVENT_SIGNATURE, shr(96, shl(96, owner)))
        }
        d.flags = flags;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                 OWNED CHECKPOINT FUNCTIONS                 */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns the owned checkpoint of `owner`.
    function getOwnedCheckpoint(address owner) public view virtual returns (uint256) {
        return _getDN420Storage().addressData[owner].ownedCheckpoint;
    }

    /// @dev Just in case the collection gets too large and the caller needs
    /// to set their owned checkpoint manually to skip large bitmap scans
    /// for automatic ERC1155 NFT burns upon ERC20 transfers.
    function setOwnedCheckpoint(uint256 id) public virtual {
        _setOwnedCheckpoint(msg.sender, id);
    }

    /// @dev Sets the owned checkpoint of `owner` to `id`.
    /// `id` will be clamped to `[1..tokenIdUpTo]`.
    function _setOwnedCheckpoint(address owner, uint256 id) internal virtual {
        DN420Storage storage $ = _getDN420Storage();
        id = _min(_max(1, id), $.tokenIdUpTo);
        $.addressData[owner].ownedCheckpoint = uint32(id);
        emit OwnedCheckpointSet(owner, id);
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                     ERC1155 OPERATIONS                     */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns if `owner` owns ERC1155 `id`.
    function owns(address owner, uint256 id) public view virtual returns (bool) {
        return _owns(_getDN420Storage().owned[owner], id);
    }

    /// @dev Returns if the ERC1155 `id` is set in `owned`.
    function _owns(Bitmap storage owned, uint256 id) internal view virtual returns (bool) {
        return _get(owned, _restrictNFTId(id));
    }

    /// @dev Returns whether `operator` is approved to manage the ERC1155 tokens of `owner`.
    function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
        return _ref(_getDN420Storage().operatorApprovals, owner, operator).value != 0;
    }

    /// @dev Sets whether `operator` is approved to manage the ERC1155 tokens of the caller.
    ///
    /// Emits a {ApprovalForAll} event.
    function setApprovalForAll(address operator, bool isApproved) public virtual {
        _setApprovalForAll(msg.sender, operator, isApproved);
    }

    /// @dev Sets whether `operator` is approved to manage the ERC1155 tokens of the caller.
    ///
    /// Emits a {ApprovalForAll} event.
    function _setApprovalForAll(address owner, address operator, bool isApproved)
        internal
        virtual
    {
        _ref(_getDN420Storage().operatorApprovals, owner, operator).value = _toUint(isApproved);
        /// @solidity memory-safe-assembly
        assembly {
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            // forgefmt: disable-next-item
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE,
                shr(96, shl(96, owner)), shr(96, shl(96, operator)))
        }
    }

    /// @dev Transfers the ERC1155 NFT at `id` from `from` to `to`.
    function safeTransferNFT(address from, address to, uint256 id, bytes memory data)
        public
        virtual
    {
        _safeTransferNFT(msg.sender, from, to, id, data);
    }

    /// @dev Transfers the ERC1155 NFTs at `ids` from `from` to `to`.
    function safeBatchTransferNFTs(
        address from,
        address to,
        uint256[] memory ids,
        bytes memory data
    ) public virtual {
        _safeBatchTransferNFTs(msg.sender, from, to, ids, data);
    }

    /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
    /// See: https://eips.ethereum.org/EIPS/eip-165
    /// This function call must use less than 30000 gas.
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let s := shr(224, interfaceId)
            // ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c.
            result := or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c))
        }
    }

    /// @dev Returns `owner`'s ERC1155 NFT balance.
    function _balanceOfNFT(address owner) internal view virtual returns (uint256) {
        return _getDN420Storage().addressData[owner].ownedCount;
    }

    /// @dev Returns if the ERC1155 token `id` exists.
    function _exists(uint256 id) internal view virtual returns (bool) {
        return _get(_getDN420Storage().exists, _restrictNFTId(id));
    }

    /// @dev Returns the ERC1155 NFT IDs of `owner` in range `[lower, upper)`.
    /// Optimized for smaller bytecode size, as this function is intended for off-chain calling.
    function _findOwnedIds(address owner, uint256 lower, uint256 upper)
        internal
        view
        virtual
        returns (uint256[] memory ids)
    {
        unchecked {
            DN420Storage storage $ = _getDN420Storage();
            Bitmap storage owned = $.owned[owner];
            upper = _min(uint256($.tokenIdUpTo) + 1, upper);
            /// @solidity memory-safe-assembly
            assembly {
                ids := mload(0x40)
                let n := 0
                let s := shl(96, owned.slot)
                for { let id := lower } lt(id, upper) { id := add(1, id) } {
                    if and(1, shr(and(0xff, id), sload(add(s, shr(8, id))))) {
                        mstore(add(add(ids, 0x20), shl(5, n)), id)
                        n := add(1, n)
                    }
                }
                mstore(ids, n)
                mstore(0x40, add(shl(5, n), add(0x20, ids)))
            }
        }
    }

    /// @dev Fallback modifier for the regular ERC1155 functions and other functions.
    modifier dn420Fallback() virtual {
        uint256 fnSelector = _calldataload(0x00) >> 224;

        // We hide the regular ERC1155 functions that has variable amounts
        // in the fallback for ABI aesthetic purposes.

        // `safeTransferFrom(address,address,uint256,uint256,bytes)`.
        if (fnSelector == 0xf242432a) {
            if (_calldataload(0x64) != 1) revert InvalidNFTAmount();
            _safeTransferNFT(
                msg.sender, // `by`.
                address(uint160(_calldataload(0x04))), // `from`.
                address(uint160(_calldataload(0x24))), // `to`.
                _calldataload(0x44), // `id`.
                _calldataBytes(0x84) // `data`.
            );
            _return(1);
        }
        // `safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)`.
        if (fnSelector == 0x2eb2c2d6) {
            uint256[] memory ids = _calldataUint256Array(0x44);
            unchecked {
                uint256[] memory amounts = _calldataUint256Array(0x64);
                uint256 n = ids.length;
                if (n != amounts.length) revert ArrayLengthsMismatch();
                while (n-- != 0) if (_get(amounts, n) != 1) revert InvalidNFTAmount();
            }
            _safeBatchTransferNFTs(
                msg.sender,
                address(uint160(_calldataload(0x04))), // `from`.
                address(uint160(_calldataload(0x24))), // `to`.
                ids,
                _calldataBytes(0x84) // `data.
            );
            _return(1);
        }
        // `balanceOfBatch(address[],uint256[])`.
        if (fnSelector == 0x4e1273f4) {
            uint256[] memory owners = _calldataUint256Array(0x04);
            uint256[] memory ids = _calldataUint256Array(0x24);
            unchecked {
                uint256 n = ids.length;
                if (owners.length != n) revert ArrayLengthsMismatch();
                uint256[] memory result = _idsMalloc(n);
                while (n-- != 0) {
                    address owner = address(uint160(_get(owners, n)));
                    _set(result, n, _toUint(owns(owner, _get(ids, n))));
                }
                /// @solidity memory-safe-assembly
                assembly {
                    mstore(sub(result, 0x20), 0x20)
                    return(sub(result, 0x20), add(0x40, shl(5, mload(result))))
                }
            }
        }
        // `balanceOf(address,uint256)`.
        if (fnSelector == 0x00fdd58e) {
            bool result = owns(
                address(uint160(_calldataload(0x04))), // `owner`.
                _calldataload(0x24) // `id`.
            );
            _return(_toUint(result));
        }
        // `implementsDN420()`.
        if (fnSelector == 0x0e0b0984) {
            _return(1);
        }
        _;
    }

    /// @dev Fallback function for regular ERC1155 functions and other functions.
    /// Override this if you need to implement your custom
    /// fallback with utilities like Solady's `LibZip.cdFallback()`.
    /// And always remember to always wrap the fallback with `dn420Fallback`.
    fallback() external payable virtual dn420Fallback {
        revert FnSelectorNotRecognized(); // Not mandatory. Just for quality of life.
    }

    /// @dev This is to silence the compiler warning.
    /// Override and remove the revert if you want your contract to receive ETH via receive.
    receive() external payable virtual {
        if (msg.value != 0) revert();
    }

    /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155Received} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC1155Received(address from, address to, uint256 id, bytes memory data)
        private
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            // `onERC1155Received(address,address,uint256,uint256,bytes)`.
            mstore(m, 0xf23a6e61)
            mstore(add(m, 0x20), caller())
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            mstore(add(m, 0x60), id)
            mstore(add(m, 0x80), 1)
            mstore(add(m, 0xa0), 0xa0)
            let n := mload(data)
            mstore(add(m, 0xc0), n)
            if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xe0), n)) }
            // Revert if the call reverts.
            if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, n), m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(m, 0x00, returndatasize())
                    revert(m, returndatasize())
                }
            }
            // Load the returndata and compare it with the function selector.
            if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155BatchReceived} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC1155BatchReceived(
        address from,
        address to,
        uint256[] memory ids,
        bytes memory data
    ) private {
        if (ids.length == uint256(0)) return;
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
            mstore(m, 0xbc197c81)
            mstore(add(m, 0x20), caller())
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            // Copy the `ids`.
            mstore(add(m, 0x60), 0xa0)
            let o := add(m, 0xc0)
            {
                let n := add(0x20, shl(5, mload(ids)))
                pop(staticcall(gas(), 4, ids, n, o, n))
            }
            // Copy the `amounts`.
            mstore(add(m, 0x80), add(0xa0, returndatasize()))
            mstore(add(m, 0xa0), add(returndatasize(), add(0xa0, returndatasize())))
            o := add(o, returndatasize())
            mstore(o, mload(ids))
            let end := add(o, returndatasize())
            for { o := add(o, 0x20) } iszero(eq(o, end)) { o := add(0x20, o) } { mstore(o, 1) }
            // Copy the `data`.
            {
                let n := add(0x20, mload(data))
                pop(staticcall(gas(), 4, data, n, end, n))
            }
            // Revert if the call reverts.
            // forgefmt: disable-next-item
            if iszero(call(gas(), to, 0,
                add(m, 0x1c), sub(add(end, returndatasize()), add(m, 0x1c)), m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(m, 0x00, returndatasize())
                    revert(m, returndatasize())
                }
            }
            // Load the returndata and compare it with the function selector.
            if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }

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

    /// @dev Returns the boolean value of the bit at `index` in `bitmap`.
    function _get(Bitmap storage bitmap, uint256 index) internal view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot.
            result := and(1, shr(and(0xff, index), sload(s)))
        }
    }

    /// @dev Updates the bit at `index` in `bitmap` to `value`.
    function _set(Bitmap storage bitmap, uint256 index, bool value) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let s := add(shl(96, bitmap.slot), shr(8, index)) // Storage slot.
            let o := and(0xff, index) // Storage slot offset (bits).
            sstore(s, or(and(sload(s), not(shl(o, 1))), shl(o, iszero(iszero(value)))))
        }
    }

    /// @dev Returns the index of the least significant unset bit in `[begin..upTo]`.
    /// If no unset bit is found, returns `type(uint256).max`.
    function _findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 upTo)
        internal
        view
        returns (uint256 unsetBitIndex)
    {
        /// @solidity memory-safe-assembly
        assembly {
            unsetBitIndex := not(0) // Initialize to `type(uint256).max`.
            let s := shl(96, bitmap.slot) // Storage offset of the bitmap.
            let bucket := add(s, shr(8, begin))
            let negBits := shl(and(0xff, begin), shr(and(0xff, begin), not(sload(bucket))))
            if iszero(negBits) {
                let lastBucket := add(s, shr(8, upTo))
                for {} 1 {} {
                    bucket := add(bucket, 1)
                    negBits := not(sload(bucket))
                    if or(negBits, gt(bucket, lastBucket)) { break }
                }
                if gt(bucket, lastBucket) {
                    negBits := shr(and(0xff, not(upTo)), shl(and(0xff, not(upTo)), negBits))
                }
            }
            if negBits {
                // Find-first-set routine.
                // From: https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol
                let b := and(negBits, add(not(negBits), 1)) // Isolate the least significant bit.
                // For the upper 3 bits of the result, use a De Bruijn-like lookup.
                // Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
                // forgefmt: disable-next-item
                let r := shl(5, shr(252, shl(shl(2, shr(250, mul(b,
                    0x2aaaaaaaba69a69a6db6db6db2cb2cb2ce739ce73def7bdeffffffff))),
                    0x1412563212c14164235266736f7425221143267a45243675267677)))
                // For the lower 5 bits of the result, use a De Bruijn lookup.
                // forgefmt: disable-next-item
                r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
                    0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
                r := or(shl(8, sub(bucket, s)), r)
                unsetBitIndex := or(r, sub(0, or(gt(r, upTo), lt(r, begin))))
            }
        }
    }

    /// @dev Returns the index of the most significant set bit in `[0..upTo]`.
    /// If no set bit is found, returns zero.
    function _findLastSet(Bitmap storage bitmap, uint256 upTo)
        internal
        view
        returns (uint256 setBitIndex)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let s := shl(96, bitmap.slot) // Storage offset of the bitmap.
            let bucket := add(s, shr(8, upTo))
            let bits := shr(and(0xff, not(upTo)), shl(and(0xff, not(upTo)), sload(bucket)))
            if iszero(or(bits, eq(bucket, s))) {
                for {} 1 {} {
                    bucket := sub(bucket, 1)
                    mstore(0x00, bucket)
                    bits := sload(bucket)
                    if or(bits, eq(bucket, s)) { break }
                }
            }
            if bits {
                // Find-last-set routine.
                let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, bits))
                r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, bits))))
                r := or(r, shl(5, lt(0xffffffff, shr(r, bits))))
                r := or(r, shl(4, lt(0xffff, shr(r, bits))))
                r := or(r, shl(3, lt(0xff, shr(r, bits))))
                // forgefmt: disable-next-item
                r := or(r, byte(and(0x1f, shr(shr(r, bits), 0x8421084210842108cc6318c6db6d54be)),
                    0x0706060506020504060203020504030106050205030304010505030400000000))
                r := or(shl(8, sub(bucket, s)), r)
                setBitIndex := mul(r, iszero(gt(r, upTo)))
            }
        }
    }

    /// @dev Returns a storage reference to the value at (`a0`, `a1`) in `map`.
    function _ref(AddressPairToUint256RefMap storage map, address a0, address a1)
        internal
        pure
        returns (Uint256Ref storage ref)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x28, a1)
            mstore(0x14, a0)
            mstore(0x00, map.slot)
            ref.slot := keccak256(0x00, 0x48)
            // Clear the part of the free memory pointer that was overwritten.
            mstore(0x28, 0x00)
        }
    }

    /// @dev Wraps the NFT ID.
    function _wrapNFTId(uint256 id, uint256 maxId) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := or(mul(iszero(gt(id, maxId)), id), gt(id, maxId))
        }
    }

    /// @dev Returns `id > type(uint32).max ? 0 : id`.
    function _restrictNFTId(uint256 id) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mul(id, lt(id, 0x100000000))
        }
    }

    /// @dev Returns whether `amount` is a valid `totalSupply`.
    function _totalSupplyOverflows(uint256 amount) internal view returns (bool result) {
        uint256 unit = _unit();
        /// @solidity memory-safe-assembly
        assembly {
            result := iszero(iszero(or(shr(96, amount), lt(0xfffffffe, div(amount, unit)))))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function _zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `x < y ? x : y`.
    function _min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns `x > y ? x : y`.
    function _max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns `b ? 1 : 0`.
    function _toUint(bool b) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := iszero(iszero(b))
        }
    }

    /// @dev Creates an array with length `n` that is suitable for `_idsAppend`.
    function _idsMalloc(uint256 n) private pure returns (uint256[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := add(0x20, mload(0x40))
            let offset := add(result, 0x20)
            mstore(sub(result, 0x20), offset)
            mstore(result, n)
            mstore(0x40, add(offset, shl(5, n)))
        }
    }

    /// @dev Appends `id` to `a`. `a` must be created via `_idsMalloc`.
    function _idsAppend(uint256[] memory a, uint256 id) private pure {
        /// @solidity memory-safe-assembly
        assembly {
            let offset := mload(sub(a, 0x20))
            mstore(offset, id)
            mstore(sub(a, 0x20), add(offset, 0x20))
        }
    }

    /// @dev Emits the ERC1155 {TransferBatch} event with `from`, `to`, and `ids`.
    function _batchTransferEmit(address from, address to, uint256[] memory ids) private {
        if (ids.length == uint256(0)) return;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x40)
            let o := add(m, 0x40)
            // We have to copy the `ids`, as it might not be from `_idsMalloc`.
            // See: `_safeBatchTransferNFTs`.
            {
                let n := add(0x20, shl(5, mload(ids)))
                pop(staticcall(gas(), 4, ids, n, o, n))
            }
            mstore(add(m, 0x20), add(0x40, returndatasize()))
            o := add(o, returndatasize())
            // Store the length of `amounts`.
            mstore(o, mload(ids))
            let end := add(o, returndatasize())
            for { o := add(o, 0x20) } iszero(eq(o, end)) { o := add(0x20, o) } { mstore(o, 1) }
            // Emit a {TransferBatch} event.
            // forgefmt: disable-next-item
            log4(m, sub(o, m), _TRANSFER_BATCH_EVENT_SIGNATURE, caller(),
                shr(96, shl(96, from)), shr(96, shl(96, to)))
        }
    }

    /// @dev Returns an array of zero addresses.
    function _zeroAddresses(uint256 n) private pure returns (address[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(0x40, add(add(result, 0x20), shl(5, n)))
            mstore(result, n)
            codecopy(add(result, 0x20), codesize(), shl(5, n))
        }
    }

    /// @dev Returns an array each set to `value`.
    function _filled(uint256 n, uint256 value) private pure returns (uint256[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let o := add(result, 0x20)
            let end := add(o, shl(5, n))
            mstore(0x40, end)
            mstore(result, n)
            for {} iszero(eq(o, end)) { o := add(o, 0x20) } { mstore(o, value) }
        }
    }

    /// @dev Returns an array each set to `value`.
    function _filled(uint256 n, address value) private pure returns (address[] memory result) {
        result = _toAddresses(_filled(n, uint160(value)));
    }

    /// @dev Concatenates the arrays.
    function _concat(uint256[] memory a, uint256[] memory b)
        private
        view
        returns (uint256[] memory result)
    {
        uint256 aN = a.length;
        uint256 bN = b.length;
        if (aN == uint256(0)) return b;
        if (bN == uint256(0)) return a;
        /// @solidity memory-safe-assembly
        assembly {
            let n := add(aN, bN)
            if n {
                result := mload(0x40)
                mstore(result, n)
                let o := add(result, 0x20)
                mstore(0x40, add(o, shl(5, n)))
                let aL := shl(5, aN)
                pop(staticcall(gas(), 4, add(a, 0x20), aL, o, aL))
                pop(staticcall(gas(), 4, add(b, 0x20), shl(5, bN), add(o, aL), shl(5, bN)))
            }
        }
    }

    /// @dev Concatenates the arrays.
    function _concat(address[] memory a, address[] memory b)
        private
        view
        returns (address[] memory result)
    {
        result = _toAddresses(_concat(_toUints(a), _toUints(b)));
    }

    /// @dev Reinterpret cast to an uint array.
    function _toUints(address[] memory a) private pure returns (uint256[] memory casted) {
        /// @solidity memory-safe-assembly
        assembly {
            casted := a
        }
    }

    /// @dev Reinterpret cast to an address array.
    function _toAddresses(uint256[] memory a) private pure returns (address[] memory casted) {
        /// @solidity memory-safe-assembly
        assembly {
            casted := a
        }
    }

    /// @dev Struct of temporary variables for mints.
    struct _DNMintTemps {
        uint256 toEnd;
        uint256[] mintIds;
    }

    /// @dev Struct of temporary variables for transfers.
    struct _DNTransferTemps {
        uint256 numNFTBurns;
        uint256 numNFTMints;
        uint256 fromOwnedCount;
        uint256 toOwnedCount;
        uint256 ownedCheckpoint;
        uint256[] directIds;
        uint256[] burnIds;
        uint256[] mintIds;
    }

    /// @dev Returns if `a` has bytecode of non-zero length.
    function _hasCode(address a) private view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := extcodesize(a) // Can handle dirty upper bits.
        }
    }

    /// @dev Returns a `uint256[] calldata` at `offset` in calldata as `uint256[] memory`.
    function _calldataUint256Array(uint256 offset) private pure returns (uint256[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let o := add(0x04, calldataload(offset))
            let n := calldataload(o)
            mstore(result, n)
            calldatacopy(add(0x20, result), add(o, 0x20), shl(5, n))
            mstore(0x40, add(add(0x20, result), shl(5, n)))
        }
    }

    /// @dev Returns a `bytes calldata` at `offset` in calldata as `bytes memory`.
    function _calldataBytes(uint256 offset) private pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let o := add(0x04, calldataload(offset))
            let n := calldataload(o)
            mstore(result, n)
            calldatacopy(add(0x20, result), add(o, 0x20), n)
            o := add(add(0x20, result), n)
            mstore(o, 0) // Zeroize the slot after the last word.
            mstore(0x40, add(0x20, o))
        }
    }

    /// @dev Returns `a[i]` without bounds check.
    function _get(uint256[] memory a, uint256 i) private pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(add(add(0x20, a), shl(5, i)))
        }
    }

    /// @dev Sets `a[i]` to `value`, without bounds check.
    function _set(uint256[] memory a, uint256 i, uint256 value) private pure {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(add(add(0x20, a), shl(5, i)), value)
        }
    }

    /// @dev Returns the calldata value at `offset`.
    function _calldataload(uint256 offset) private pure returns (uint256 value) {
        /// @solidity memory-safe-assembly
        assembly {
            value := calldataload(offset)
        }
    }

    /// @dev Executes a return opcode to return `x` and end the current call frame.
    function _return(uint256 x) private pure {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, x)
            return(0x00, 0x20)
        }
    }
}

File 14 of 16 : ECDSA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
///
/// @dev Note:
/// - The recovery functions use the ecrecover precompile (0x1).
/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
///   This is for more safety by default.
///   Use the `tryRecover` variants if you need to get the zero address back
///   upon recovery failure instead.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
///   See: https://eips.ethereum.org/EIPS/eip-2098
///   This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT directly use signatures as unique identifiers:
/// - The recovery operations do NOT check if a signature is non-malleable.
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
///   EIP-712 also enables readable signing of typed data for better user safety.
/// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
library ECDSA {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The order of the secp256k1 elliptic curve.
    uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;

    /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
    uint256 private constant _HALF_N_PLUS_1 =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The signature is invalid.
    error InvalidSignature();

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

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let m := mload(0x40) // Cache the free memory pointer.
            for {} 1 {} {
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                if eq(mload(signature), 64) {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    break
                }
                if eq(mload(signature), 65) {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    break
                }
                result := 0
                break
            }
            result :=
                mload(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        result, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                )
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            for {} 1 {} {
                if eq(signature.length, 64) {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    break
                }
                if eq(signature.length, 65) {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                    break
                }
                result := 0
                break
            }
            result :=
                mload(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        result, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                )
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            result :=
                mload(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                )
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            result :=
                mload(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                )
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   TRY-RECOVER OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // WARNING!
    // These functions will NOT revert upon recovery failure.
    // Instead, they will return the zero address upon recovery failure.
    // It is critical that the returned address is NEVER compared against
    // a zero address (e.g. an uninitialized address variable).

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecover(bytes32 hash, bytes memory signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let m := mload(0x40) // Cache the free memory pointer.
            for {} 1 {} {
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                if eq(mload(signature), 64) {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    break
                }
                if eq(mload(signature), 65) {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    break
                }
                result := 0
                break
            }
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    result, // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x40, // Start of output.
                    0x20 // Size of output.
                )
            )
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            for {} 1 {} {
                if eq(signature.length, 64) {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    break
                }
                if eq(signature.length, 65) {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                    break
                }
                result := 0
                break
            }
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    result, // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x40, // Start of output.
                    0x20 // Size of output.
                )
            )
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    1, // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x40, // Start of output.
                    0x20 // Size of output.
                )
            )
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    1, // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x40, // Start of output.
                    0x20 // Size of output.
                )
            )
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

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

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, hash) // Store into scratch space for keccak256.
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
            result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    /// Note: Supports lengths of `s` up to 999999 bytes.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let sLength := mload(s)
            let o := 0x20
            mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
            mstore(0x00, 0x00)
            // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
            for { let temp := sLength } 1 {} {
                o := sub(o, 1)
                mstore8(o, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
            // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
            mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
            result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
            mstore(s, sLength) // Restore the length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  CANONICAL HASH FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // The following functions returns the hash of the signature in it's canonicalized format,
    // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
    // If `s` is greater than `N / 2` then it will be converted to `N - s`
    // and the `v` value will be flipped.
    // If the signature has an invalid length, or if `v` is invalid,
    // a uniquely corrupt hash will be returned.
    // These functions are useful for "poor-mans-VRF".

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            let l := mload(signature)
            for {} 1 {} {
                mstore(0x00, mload(add(signature, 0x20))) // `r`.
                let s := mload(add(signature, 0x40))
                let v := mload(add(signature, 0x41))
                if eq(l, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }

            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(l, 64), 2)) {
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHashCalldata(bytes calldata signature)
        internal
        pure
        returns (bytes32 result)
    {
        // @solidity memory-safe-assembly
        assembly {
            let l := signature.length
            for {} 1 {} {
                mstore(0x00, calldataload(signature.offset)) // `r`.
                let s := calldataload(add(signature.offset, 0x20))
                let v := calldataload(add(signature.offset, 0x21))
                if eq(l, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }
            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(l, 64), 2)) {
                calldatacopy(mload(0x40), signature.offset, l)
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(mload(0x40), l), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            let v := add(shr(255, vs), 27)
            let s := shr(1, shl(1, vs))
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            if iszero(lt(s, _HALF_N_PLUS_1)) {
                v := xor(v, 7)
                s := sub(N, s)
            }
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}

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

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

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

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

    /// @dev The input string must be a 7-bit ASCII.
    error StringNot7BitASCII();

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

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

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
    uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;

    /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;

    /// @dev Lookup for '0123456789'.
    uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;

    /// @dev Lookup for '0123456789abcdefABCDEF'.
    uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;

    /// @dev Lookup for '01234567'.
    uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
    uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;

    /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
    uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;

    /// @dev Lookup for ' \t\n\r\x0b\x0c'.
    uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;

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

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory result) {
        /// @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.
            result := add(mload(0x40), 0x80)
            mstore(0x40, add(result, 0x20)) // Allocate memory.
            mstore(result, 0) // Zeroize the slot after the string.

            let end := result // Cache the end of the memory to calculate the length later.
            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 {} {
                result := add(result, w) // `sub(result, 1)`.
                // Store the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(result, add(48, mod(temp, 10)))
                temp := div(temp, 10) // Keep dividing `temp` until zero.
                if iszero(temp) { break }
            }
            let n := sub(end, result)
            result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
            mstore(result, n) // Store the length.
        }
    }

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

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is not prefixed with "0x" and is 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 result)
    {
        /// @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.
            result := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
            mstore(0x40, add(result, 0x20)) // Allocate memory.
            mstore(result, 0) // Zeroize the slot after the string.

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

            let start := sub(result, 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 {} {
                result := add(result, w) // `sub(result, 2)`.
                mstore8(add(result, 1), mload(and(temp, 15)))
                mstore8(result, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(result, start)) { break }
            }
            if temp {
                mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                revert(0x1c, 0x04)
            }
            let n := sub(end, result)
            result := sub(result, 0x20)
            mstore(result, n) // Store the length.
        }
    }

    /// @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 result) {
        result = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let n := add(mload(result), 2) // Compute the length.
            mstore(result, 0x3078) // Store the "0x" prefix.
            result := sub(result, 2) // Move the pointer.
            mstore(result, n) // Store the length.
        }
    }

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

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

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2` bytes.
    function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) {
        /// @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.
            result := add(mload(0x40), 0x80)
            mstore(0x40, add(result, 0x20)) // Allocate memory.
            mstore(result, 0) // Zeroize the slot after the string.

            let end := result // Cache the end to calculate the length later.
            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.

            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 {} {
                result := add(result, w) // `sub(result, 2)`.
                mstore8(add(result, 1), mload(and(temp, 15)))
                mstore8(result, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(temp) { break }
            }
            let n := sub(end, result)
            result := sub(result, 0x20)
            mstore(result, n) // Store the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksummed(address value) internal pure returns (string memory result) {
        result = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(result, 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 result) {
        result = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let n := add(mload(result), 2) // Compute the length.
            mstore(result, 0x3078) // Store the "0x" prefix.
            result := sub(result, 2) // Move the pointer.
            mstore(result, n) // Store 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 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            // Allocate 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(result, 0x80))
            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.

            result := add(result, 2)
            mstore(result, 40) // Store the length.
            let o := add(result, 0x20)
            mstore(add(o, 40), 0) // Zeroize the slot after the string.
            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 result) {
        result = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let n := add(mload(result), 2) // Compute the length.
            mstore(result, 0x3078) // Store the "0x" prefix.
            result := sub(result, 2) // Move the pointer.
            mstore(result, n) // Store 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 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(raw)
            result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(result, add(n, n)) // Store the length of the output.

            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
            let o := add(result, 0x20)
            let end := add(raw, n)
            for {} iszero(eq(raw, end)) {} {
                raw := add(raw, 1)
                mstore8(add(o, 1), mload(and(mload(raw), 15)))
                mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                o := add(o, 2)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate memory.
        }
    }

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

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

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

    /// @dev Returns if this string is a 7-bit ASCII string,
    /// AND all characters are in the `allowed` lookup.
    /// Note: If `s` is empty, returns true regardless of `allowed`.
    function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            if mload(s) {
                let allowed_ := shr(128, shl(128, allowed))
                let o := add(s, 0x20)
                for { let end := add(o, mload(s)) } 1 {} {
                    result := and(result, shr(byte(0, mload(o)), allowed_))
                    o := add(o, 1)
                    if iszero(and(result, lt(o, end))) { break }
                }
            }
        }
    }

    /// @dev Converts the bytes in the 7-bit ASCII string `s` to
    /// an allowed lookup for use in `is7BitASCII(s, allowed)`.
    /// To save runtime gas, you can cache the result in an immutable variable.
    function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                let o := add(s, 0x20)
                for { let end := add(o, mload(s)) } 1 {} {
                    result := or(result, shl(byte(0, mload(o)), 1))
                    o := add(o, 1)
                    if iszero(lt(o, end)) { break }
                }
                if shr(128, result) {
                    mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

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

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

    /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
    function replace(string memory subject, string memory needle, string memory replacement)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let needleLen := mload(needle)
            let replacementLen := mload(replacement)
            let d := sub(result, subject) // Memory difference.
            let i := add(subject, 0x20) // Subject bytes pointer.
            let end := add(i, mload(subject))
            if iszero(gt(needleLen, mload(subject))) {
                let subjectSearchEnd := add(sub(end, needleLen), 1)
                let h := 0 // The hash of `needle`.
                if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
                let s := mload(add(needle, 0x20))
                for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
                    let t := mload(i)
                    // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(i, needleLen), h)) {
                                mstore(add(i, d), t)
                                i := add(i, 1)
                                if iszero(lt(i, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        for { let j := 0 } 1 {} {
                            mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
                            j := add(j, 0x20)
                            if iszero(lt(j, replacementLen)) { break }
                        }
                        d := sub(add(d, replacementLen), needleLen)
                        if needleLen {
                            i := add(i, needleLen)
                            if iszero(lt(i, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(add(i, d), t)
                    i := add(i, 1)
                    if iszero(lt(i, subjectSearchEnd)) { break }
                }
            }
            let n := add(sub(d, add(result, 0x20)), end)
            // Copy the rest of the string one word at a time.
            for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
            let o := add(i, d)
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate memory.
            mstore(result, n) // Store the length.
        }
    }

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

                subject := add(subjectStart, from)
                let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
                let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
                let s := mload(add(needle, 0x20))

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

                if iszero(lt(needleLen, 0x20)) {
                    for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, needleLen), 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 `needle` in `subject`,
    /// needleing from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function indexOf(string memory subject, string memory needle)
        internal
        pure
        returns (uint256 result)
    {
        result = indexOf(subject, needle, 0);
    }

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

                let fromMax := sub(mload(subject), needleLen)
                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(needle, 0x20), needleLen) } 1 {} {
                    if eq(keccak256(subject, needleLen), 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 `needle` in `subject`,
    /// needleing from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function lastIndexOf(string memory subject, string memory needle)
        internal
        pure
        returns (uint256 result)
    {
        result = lastIndexOf(subject, needle, type(uint256).max);
    }

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

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

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

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

    /// @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 subjectLen := mload(subject)
            if iszero(gt(subjectLen, end)) { end := subjectLen }
            if iszero(gt(subjectLen, start)) { start := subjectLen }
            if lt(start, end) {
                result := mload(0x40)
                let n := sub(end, start)
                let i := add(subject, start)
                let w := not(0x1f)
                // Copy the `subject` one word at a time, backwards.
                for { let j := and(add(n, 0x1f), w) } 1 {} {
                    mstore(add(result, j), mload(add(i, j)))
                    j := add(j, w) // `sub(j, 0x20)`.
                    if iszero(j) { break }
                }
                let o := add(add(result, 0x20), n)
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
                mstore(result, n) // Store the length.
            }
        }
    }

    /// @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, type(uint256).max);
    }

    /// @dev Returns all the indices of `needle` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory needle)
        internal
        pure
        returns (uint256[] memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLen := mload(needle)
            if iszero(gt(searchLen, mload(subject))) {
                result := mload(0x40)
                let i := add(subject, 0x20)
                let o := add(result, 0x20)
                let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
                let h := 0 // The hash of `needle`.
                if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
                let s := mload(add(needle, 0x20))
                for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
                    let t := mload(i)
                    // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(i, searchLen), h)) {
                                i := add(i, 1)
                                if iszero(lt(i, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
                        o := add(o, 0x20)
                        i := add(i, searchLen) // Advance `i` by `searchLen`.
                        if searchLen {
                            if iszero(lt(i, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    i := add(i, 1)
                    if iszero(lt(i, subjectSearchEnd)) { break }
                }
                mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(o, 0x20))
            }
        }
    }

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

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

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

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

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

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

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

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

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

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

    /// @dev Encodes `s` so that it can be safely used in a URI,
    /// just like `encodeURIComponent` in JavaScript.
    /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
    /// See: https://datatracker.ietf.org/doc/html/rfc2396
    /// See: https://datatracker.ietf.org/doc/html/rfc3986
    function encodeURIComponent(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            // Store "0123456789ABCDEF" in scratch space.
            // Uppercased to be consistent with JavaScript's implementation.
            mstore(0x0f, 0x30313233343536373839414243444546)
            let o := add(result, 0x20)
            for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // If not in `[0-9A-Z-a-z-_.!~*'()]`.
                if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) {
                    mstore8(o, 0x25) // '%'.
                    mstore8(add(o, 1), mload(and(shr(4, c), 15)))
                    mstore8(add(o, 2), mload(and(c, 15)))
                    o := add(o, 3)
                    continue
                }
                mstore8(o, c)
                o := add(o, 1)
            }
            mstore(result, sub(o, add(result, 0x20))) // Store the length.
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate memory.
        }
    }

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

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

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

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

    /// @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 aLen := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    or( // Load the length and the bytes of `a` and `b`.
                    shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))),
                    // `totalLen != 0 && totalLen < 31`. Abuses underflow.
                    // Assumes that the lengths are valid and within the block gas limit.
                    lt(sub(add(aLen, mload(b)), 1), 0x1e)
                )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behavior is undefined.
    function unpackTwo(bytes32 packed)
        internal
        pure
        returns (string memory resultA, string memory resultB)
    {
        /// @solidity memory-safe-assembly
        assembly {
            resultA := mload(0x40) // Grab the free memory pointer.
            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 retUnpaddedSize := 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, retUnpaddedSize), 0)
            mstore(retStart, 0x20) // Store the return offset.
            // End the transaction, returning the string.
            return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
        }
    }
}

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

/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            MERKLE PROOF VERIFICATION OPERATIONS            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(proof) {
                // Initialize `offset` to the offset of `proof` elements in memory.
                let offset := add(proof, 0x20)
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(offset, shl(5, mload(proof)))
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, mload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), mload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if proof.length {
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(proof.offset, shl(5, proof.length))
                // Initialize `offset` to the offset of `proof` in the calldata.
                let offset := proof.offset
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, calldataload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), calldataload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - The sum of the lengths of `proof` and `leaves` must never overflow.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The memory offset of `proof` must be non-zero
    ///   (i.e. `proof` is not pointing to the scratch space).
    function verifyMultiProof(
        bytes32[] memory proof,
        bytes32 root,
        bytes32[] memory leaves,
        bool[] memory flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // Cache the lengths of the arrays.
            let leavesLength := mload(leaves)
            let proofLength := mload(proof)
            let flagsLength := mload(flags)

            // Advance the pointers of the arrays to point to the data.
            leaves := add(0x20, leaves)
            proof := add(0x20, proof)
            flags := add(0x20, flags)

            // If the number of flags is correct.
            for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flagsLength) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof, shl(5, proofLength))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                leavesLength := shl(5, leavesLength)
                for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                    mstore(add(hashesFront, i), mload(add(leaves, i)))
                }
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, leavesLength)
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flagsLength := add(hashesBack, shl(5, flagsLength))

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(mload(flags)) {
                        // Loads the next proof.
                        b := mload(proof)
                        proof := add(proof, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag.
                    flags := add(flags, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flagsLength)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof)
                    )
                break
            }
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The calldata offset of `proof` must be non-zero
    ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
    function verifyMultiProofCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32[] calldata leaves,
        bool[] calldata flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // If the number of flags is correct.
            for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flags.length) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    // forgefmt: disable-next-item
                    isValid := eq(
                        calldataload(
                            xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                        ),
                        root
                    )
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof.offset, shl(5, proof.length))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, shl(5, leaves.length))
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flags.length := add(hashesBack, shl(5, flags.length))

                // We don't need to make a copy of `proof.offset` or `flags.offset`,
                // as they are pass-by-value (this trick may not always save gas).

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(calldataload(flags.offset)) {
                        // Loads the next proof.
                        b := calldataload(proof.offset)
                        proof.offset := add(proof.offset, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag offset.
                    flags.offset := add(flags.offset, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flags.length)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof.offset)
                    )
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes32 array.
    function emptyProof() internal pure returns (bytes32[] calldata proof) {
        /// @solidity memory-safe-assembly
        assembly {
            proof.length := 0
        }
    }

    /// @dev Returns an empty calldata bytes32 array.
    function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
        /// @solidity memory-safe-assembly
        assembly {
            leaves.length := 0
        }
    }

    /// @dev Returns an empty calldata bool array.
    function emptyFlags() internal pure returns (bool[] calldata flags) {
        /// @solidity memory-safe-assembly
        assembly {
            flags.length := 0
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/BURGERS404/ArchetypeLogicBurgers404.sol": {
      "ArchetypeLogicBurgers404": "0xd4e182124131fe5f3bde4cdef00975fb97f5b3d8"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"ArrayLengthsMismatch","type":"error"},{"inputs":[],"name":"DNAlreadyInitialized","type":"error"},{"inputs":[],"name":"DNNotInitialized","type":"error"},{"inputs":[],"name":"FnSelectorNotRecognized","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidConfig","type":"error"},{"inputs":[],"name":"InvalidNFTAmount","type":"error"},{"inputs":[],"name":"InvalidSplitShares","type":"error"},{"inputs":[],"name":"InvalidUnit","type":"error"},{"inputs":[],"name":"LockedForever","type":"error"},{"inputs":[],"name":"MaxSupplyExceeded","type":"error"},{"inputs":[],"name":"NotApprovedToTransfer","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC1155ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"WrongPassword","type":"error"},{"inputs":[],"name":"burnToRemintDisabled","type":"error"},{"inputs":[],"name":"invalidTokenIdLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"cid","type":"bytes32"}],"name":"Invited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"OwnedCheckpointSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"affiliate","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint128","name":"wad","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"numMints","type":"uint256"}],"name":"Referral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"SkipNFTSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint128","name":"wad","type":"uint128"}],"name":"Withdrawal","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"affiliate","type":"address"}],"name":"affiliateBalance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"affiliateBalanceToken","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct Auth","name":"auth","type":"tuple"},{"internalType":"address[]","name":"toList","type":"address[]"},{"internalType":"uint256[]","name":"quantityList","type":"uint256[]"},{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"batchMintTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"burnToRemint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bool","name":"affiliateUsed","type":"bool"}],"name":"computePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"config","outputs":[{"internalType":"string","name":"baseUri","type":"string"},{"internalType":"address","name":"affiliateSigner","type":"address"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"maxBatchSize","type":"uint32"},{"internalType":"uint16","name":"affiliateFee","type":"uint16"},{"internalType":"uint16","name":"affiliateDiscount","type":"uint16"},{"internalType":"uint16","name":"defaultRoyalty","type":"uint16"},{"internalType":"uint16","name":"remintPremium","type":"uint16"},{"internalType":"uint16","name":"erc20Ratio","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flags","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getOwnedCheckpoint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"getSkipNFT","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"components":[{"internalType":"string","name":"baseUri","type":"string"},{"internalType":"address","name":"affiliateSigner","type":"address"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"maxBatchSize","type":"uint32"},{"internalType":"uint16","name":"affiliateFee","type":"uint16"},{"internalType":"uint16","name":"affiliateDiscount","type":"uint16"},{"internalType":"uint16","name":"defaultRoyalty","type":"uint16"},{"internalType":"uint16","name":"remintPremium","type":"uint16"},{"internalType":"uint16","name":"erc20Ratio","type":"uint16"}],"internalType":"struct Config","name":"config_","type":"tuple"},{"components":[{"internalType":"uint16","name":"ownerBps","type":"uint16"},{"internalType":"uint16","name":"platformBps","type":"uint16"},{"internalType":"uint16","name":"partnerBps","type":"uint16"},{"internalType":"uint16","name":"superAffiliateBps","type":"uint16"},{"internalType":"address","name":"partner","type":"address"},{"internalType":"address","name":"superAffiliate","type":"address"},{"internalType":"address","name":"ownerAltPayout","type":"address"}],"internalType":"struct PayoutConfig","name":"payoutConfig_","type":"tuple"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"invites","outputs":[{"internalType":"uint128","name":"price","type":"uint128"},{"internalType":"uint128","name":"reservePrice","type":"uint128"},{"internalType":"uint128","name":"delta","type":"uint128"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"uint32","name":"unitSize","type":"uint32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"isBlacklist","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":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"listSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"password","type":"string"}],"name":"lockMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"password","type":"string"}],"name":"lockOwnerAltPayout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"password","type":"string"}],"name":"lockURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct Auth","name":"auth","type":"tuple"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct Auth","name":"auth","type":"tuple"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mintTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"},{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"minted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numErc20Minted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numNftsMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerBalance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"ownerBalanceToken","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"owns","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"packedBonusDiscounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"pairedListKeys","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payoutConfig","outputs":[{"internalType":"uint16","name":"ownerBps","type":"uint16"},{"internalType":"uint16","name":"platformBps","type":"uint16"},{"internalType":"uint16","name":"partnerBps","type":"uint16"},{"internalType":"uint16","name":"superAffiliateBps","type":"uint16"},{"internalType":"address","name":"partner","type":"address"},{"internalType":"address","name":"superAffiliate","type":"address"},{"internalType":"address","name":"ownerAltPayout","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platform","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"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":"ids","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"bytes32","name":"_cid","type":"bytes32"},{"components":[{"internalType":"uint128","name":"price","type":"uint128"},{"internalType":"uint128","name":"reservePrice","type":"uint128"},{"internalType":"uint128","name":"delta","type":"uint128"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"uint32","name":"unitSize","type":"uint32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"isBlacklist","type":"bool"}],"internalType":"struct AdvancedInvite","name":"_AdvancedInvite","type":"tuple"}],"name":"setAdvancedInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"affiliateDiscount","type":"uint16"}],"name":"setAffiliateDiscount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"affiliateFee","type":"uint16"}],"name":"setAffiliateFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseUri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"components":[{"internalType":"uint16","name":"numMints","type":"uint16"},{"internalType":"uint16","name":"numBonusMints","type":"uint16"}],"internalType":"struct BonusDiscount[]","name":"_bonusDiscounts","type":"tuple[]"}],"name":"setBonusDiscounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"bytes32","name":"_cid","type":"bytes32"},{"components":[{"internalType":"uint128","name":"price","type":"uint128"},{"internalType":"uint128","name":"reservePrice","type":"uint128"},{"internalType":"uint128","name":"delta","type":"uint128"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"interval","type":"uint32"},{"internalType":"uint32","name":"unitSize","type":"uint32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"isBlacklist","type":"bool"}],"internalType":"struct AdvancedInvite","name":"_advancedInvite","type":"tuple"},{"components":[{"internalType":"uint16","name":"numMints","type":"uint16"},{"internalType":"uint16","name":"numBonusMints","type":"uint16"}],"internalType":"struct BonusDiscount[]","name":"_bonusDiscount","type":"tuple[]"}],"name":"setBonusInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint16","name":"feeNumerator","type":"uint16"}],"name":"setDefaultRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_key","type":"bytes32"},{"internalType":"bytes32","name":"_cid","type":"bytes32"},{"components":[{"internalType":"uint128","name":"price","type":"uint128"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"unitSize","type":"uint32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"isBlacklist","type":"bool"}],"internalType":"struct Invite","name":"_invite","type":"tuple"}],"name":"setInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"maxBatchSize","type":"uint32"}],"name":"setMaxBatchSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"maxSupply","type":"uint32"}],"name":"setMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"setOwnedCheckpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ownerAltPayout","type":"address"}],"name":"setOwnerAltPayout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key1","type":"bytes32"},{"internalType":"bytes32","name":"key2","type":"bytes32"}],"name":"setPairedInvite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"remintPremium","type":"uint16"}],"name":"setRemintPremium","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"skipNFT","type":"bool"}],"name":"setSkipNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"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":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAffiliate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"withdrawTokensAffiliate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b50615f9980620000216000396000f3fe6080604052600436106102f85760003560e01c806301d88f5d1461053f57806301ffc9a71461055f57806306fdde0314610594578063095ea7b3146105b65780630c6f910b146105d65780630e89341c146105f657806315ec67201461061657806318160ddd1461062b57806321d5bf241461064e57806323b872dd146106b5578063274e430b146106d557806327a59437146106f557806329a96532146107345780632a55205a146107545780632a6a935d14610782578063313ce567146107a257806332f7c6d4146107be5780633ccfd60b146107de5780633e100f6e146107f35780634331f639146108135780634a21a2df146108335780634bde38c8146108465780634f558e791461088157806351305a82146108a157806355f804b3146108ce5780635ecb16cd146108ee5780635fb8ecfb1461090e57806363dbc71f1461092e57806364cc4aa51461094e5780636e884900146109645780636f33659f1461098457806370a08231146109a4578063715018a6146109c457806379502c55146109d95780637c5d0a0814610a03578063818d4b5d14610a235780638b4795d614610a3e5780638da5cb5b14610a5e5780639564e8e414610a7357806395d89b4114610a8657806396a0924e14610a9b578063978a450914610abb5780639a7a973c14610b055780639c8770b714610b32578063a22cb46514610b52578063a3edb86a14610b72578063a5aa4aa414610bb5578063a9059cbb14610cd1578063aab6a17b14610cf1578063ac17811c14610d11578063b68836fa14610d26578063bcb40d5414610d53578063bcc1ed0714610d68578063bedcf00314610d7b578063caf3e53214610dc1578063d404844114610de1578063db8a560014610e89578063db8f6ec414610ea9578063dd62ed3e14610ec9578063de6cd0db14610ee9578063e2f2379a14610f09578063e985e9c514610f29578063ead0055314610f49578063f2fde38b14610f69578063f588eb5014610f89578063f5b100ea14610fa9578063f9da322414610fc95761030a565b3661030a57341561030857600080fd5b005b60003560e01c63f242432a8190036103655760643560011461033f57604051631ec9b93b60e21b815260040160405180910390fd5b61035b336004356024356044356103566084610fe9565b61101e565b6103656001611245565b80632eb2c2d60361041f57600061037c604461124f565b9050600061038a606461124f565b825181519192509081146103b157604051631dc0052360e11b815260040160405180910390fd5b600019810190156103f1576103cc828260051b016020015190565b6001146103ec57604051631ec9b93b60e21b815260040160405180910390fd5b6103b1565b50506104133361040060043590565b6024358461040e6084610fe9565b61127e565b61041d6001611245565b505b80634e1273f4036104e8576000610436600461124f565b90506000610444602461124f565b8051835191925090811461046b57604051631dc0052360e11b815260040160405180910390fd5b600061047682611467565b90505b600019820191156104d4576000610496858460051b016020015190565b90506104ce82846104be6104b9856104b48a8a60051b016020015190565b611485565b151590565b808260051b846020010152505050565b50610479565b602080820352805160051b60400160208203f35b8062fdd58e03610511576000610502600435602435611485565b905061050f811515611245565b505b80630e0b098403610526576105266001611245565b604051631e085ca760e11b815260040160405180910390fd5b34801561054b57600080fd5b5061030861055a366004614b07565b6114bd565b34801561056b57600080fd5b5061057f61057a366004614b3e565b61173f565b60405190151581526020015b60405180910390f35b3480156105a057600080fd5b506105a9611776565b60405161058b9190614bbf565b3480156105c257600080fd5b5061057f6105d1366004614bd2565b611808565b3480156105e257600080fd5b506103086105f1366004614c0e565b61181e565b34801561060257600080fd5b506105a9610611366004614c2b565b6118a7565b34801561062257600080fd5b5061030861192b565b34801561063757600080fd5b5061064061198e565b60405190815260200161058b565b34801561065a57600080fd5b5061069d610669366004614c44565b6001600160a01b03918216600090815260ce602090815260408083209390941682529190915220546001600160801b031690565b6040516001600160801b03909116815260200161058b565b3480156106c157600080fd5b5061057f6106d0366004614c7d565b6119ae565b3480156106e157600080fd5b5061057f6106f0366004614cbe565b611a22565b34801561070157600080fd5b5061069d610710366004614cbe565b6001600160a01b0316600090815260cd60205260409020546001600160801b031690565b34801561074057600080fd5b5061030861074f366004614c2b565b611a73565b34801561076057600080fd5b5061077461076f366004614cdb565b611a7d565b60405161058b929190614cfd565b34801561078e57600080fd5b5061057f61079d366004614d16565b611b2b565b3480156107ae57600080fd5b506040516012815260200161058b565b3480156107ca57600080fd5b506103086107d9366004614d74565b611b3f565b3480156107ea57600080fd5b50610308611b93565b3480156107ff57600080fd5b5061064061080e366004614cbe565b611bf3565b34801561081f57600080fd5b5061030861082e366004614db5565b611c2c565b610308610841366004614df5565b611c8b565b34801561085257600080fd5b507386b82972282dd22348374bc63fd21620f7ed847b5b6040516001600160a01b03909116815260200161058b565b34801561088d57600080fd5b5061057f61089c366004614c2b565b611ca7565b3480156108ad57600080fd5b506106406108bc366004614c2b565b60cf6020526000908152604090205481565b3480156108da57600080fd5b506103086108e9366004614eec565b611cb2565b3480156108fa57600080fd5b50610308610909366004614f4b565b611d22565b34801561091a57600080fd5b5061030861092936600461502d565b611d94565b34801561093a57600080fd5b50610308610949366004614cdb565b611df3565b34801561095a57600080fd5b5061064060d95481565b34801561097057600080fd5b5061030861097f366004615092565b611ebf565b34801561099057600080fd5b5061030861099f3660046150d5565b612027565b3480156109b057600080fd5b506106406109bf366004614cbe565b61208d565b3480156109d057600080fd5b506103086120c9565b3480156109e557600080fd5b506109ee6120dd565b60405161058b999897969594939291906150f2565b348015610a0f57600080fd5b50610308610a1e366004614d74565b6121c9565b348015610a2f57600080fd5b5061057f6104b4366004614bd2565b348015610a4a57600080fd5b50610308610a59366004614c0e565b61221d565b348015610a6a57600080fd5b506108696122a7565b610308610a8136600461515f565b6122b6565b348015610a9257600080fd5b506105a961238f565b348015610aa757600080fd5b50610308610ab6366004614c0e565b61239e565b348015610ac757600080fd5b5061069d610ad6366004614cbe565b6001600160a01b0316600090815260ce602090815260408083208380529091529020546001600160801b031690565b348015610b1157600080fd5b50610640610b20366004614c2b565b600090815260cc602052604090205490565b348015610b3e57600080fd5b50610308610b4d36600461523f565b6123fe565b348015610b5e57600080fd5b50610308610b6d366004615274565b6125aa565b348015610b7e57600080fd5b50610640610b8d366004614bd2565b6001600160a01b0391909116600090815260cb60209081526040808320938352929052205490565b348015610bc157600080fd5b50610c5a610bd0366004614c2b565b60c9602052600090815260409020805460018201546002909201546001600160801b0380831693600160801b938490048216939181169263ffffffff928204831692600160a01b8304811692600160c01b8104821692600160e01b9182900483169281811692600160201b8304909116916001600160a01b03600160401b8204169160ff9104168b565b604080516001600160801b039c8d1681529a8c1660208c015298909a169789019790975263ffffffff9586166060890152938516608088015291841660a0870152831660c0860152821660e0850152166101008301526001600160a01b03166101208201529015156101408201526101600161058b565b348015610cdd57600080fd5b5061057f610cec366004614bd2565b6125b5565b348015610cfd57600080fd5b50610308610d0c3660046152a2565b6125d2565b348015610d1d57600080fd5b506106406125e5565b348015610d3257600080fd5b50610640610d41366004614c2b565b60ca6020526000908152604090205481565b348015610d5f57600080fd5b5060d254610640565b610308610d7636600461530d565b612609565b348015610d8757600080fd5b506000805260cd6020527fcd565b10a72538d86f6d352f37ebc5dff31587960b12c0afe00fd03947a6932a546001600160801b031661069d565b348015610dcd57600080fd5b50610640610ddc3660046153df565b61277a565b348015610ded57600080fd5b5060d65460d75460d854610e3a9261ffff80821693620100008304821693600160201b8404831693600160301b8104909316926001600160a01b03600160401b9091048116928116911687565b6040805161ffff98891681529688166020880152948716948601949094529190941660608401526001600160a01b039384166080840152831660a08301529190911660c082015260e00161058b565b348015610e9557600080fd5b50610308610ea4366004615418565b61282c565b348015610eb557600080fd5b50610308610ec43660046154eb565b612839565b348015610ed557600080fd5b50610640610ee4366004614c44565b6129b9565b348015610ef557600080fd5b50610308610f04366004614d74565b6129d8565b348015610f1557600080fd5b50610308610f24366004615536565b612a2c565b348015610f3557600080fd5b5061057f610f44366004614c44565b612cd9565b348015610f5557600080fd5b50610308610f64366004614cbe565b612cfa565b348015610f7557600080fd5b50610308610f84366004614cbe565b612d80565b348015610f9557600080fd5b50610308610fa4366004614f4b565b612df6565b348015610fb557600080fd5b50610640610fc4366004614cbe565b612e30565b348015610fd557600080fd5b50610308610fe43660046150d5565b612e3b565b606060405190508135600401803580835280602083018460200137808360200101915050600081528060200160405250919050565b6001600160a01b03831661104557604051633a954ecd60e21b815260040160405180910390fd5b600061104f612eee565b8054909150600160201b900463ffffffff1661107e5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b03868116801591871614176110bb5761109e8587612cd9565b6110bb5760405163096dcfe360e31b815260040160405180910390fd5b6001600160a01b038516600090815260048201602052604090206110df8185612efb565b6110fb5760405162a1148160e81b815260040160405180910390fd5b61110781856000612f0f565b6001600160a01b0385166000908152600483016020526040902061112d90856001612f0f565b6000611137612f33565b90506000836005016000896001600160a01b03166001600160a01b0316815260200190815260200160002090506000846005016000896001600160a01b03166001600160a01b03168152602001908152602001600020905060018360201b1760801b80835403835581548060601c63ffffffff16808a18818b110260601b8218830184555050508860601b60601c98508760601b60601c97508660005260016020528789337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260406000a4826000528789600080516020615f4483398151915260206000a3611226565b505050565b873b156112395761123989898989612f73565b50505050505050505050565b8060005260206000f35b60606040519050813560040180358083528060051b60208301846020013760051b820160200160405250919050565b6001600160a01b0383166112a557604051633a954ecd60e21b815260040160405180910390fd5b60006112af612eee565b8054909150600160201b900463ffffffff166112de5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b038681168015918716141761131b576112fe8587612cd9565b61131b5760405163096dcfe360e31b815260040160405180910390fd5b6001600160a01b0380861660009081526005830160205260408082209287168252812085519192839290919061134f612f33565b6001600160a01b03808c166000908152600489016020526040808220928d16825290209183029650905b82156113e8576000199092019160006113988b8560051b016020015190565b90506113a48382612efb565b6113c05760405162a1148160e81b815260040160405180910390fd5b6113cc83826000612f0f565b6113d882826001612f0f565b8681189087100290951894611379565b50505086518460201b1760801b80835403835581548060601c63ffffffff168086188187110260601b821883018455505050611425898989613001565b836000528760601b60601c8960601b60601c600080516020615f4483398151915260206000a3611454565b5050565b873b156112395761123989898989613098565b6040805180820181526020810183815260059390931b018101905290565b60006114b4611492612eee565b6001600160a01b03851660009081526004919091016020526040902083612efb565b90505b92915050565b6114c56122a7565b6001600160a01b03166114d661316b565b6001600160a01b0316146114fd576040516330cd747160e01b815260040160405180910390fd5b6101208101516001600160a01b0316156115c25761012081015160405163095ea7b360e01b81526000916001600160a01b03169063095ea7b39061155d9073aafdfa4a935d8511bf285af11a0544ce7e4a11999060001990600401614cfd565b6020604051808303816000875af115801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a091906155f3565b9050806115c0576040516302df483560e21b815260040160405180910390fd5b505b428160a0015163ffffffff1610156115e15763ffffffff421660a08201525b600083815260c9602090815260408083208451928501516001600160801b03938416600160801b918516820217825585830151600183018054606089015160808a015160a08b015160c08c0151959099166001600160a01b03199093169290921763ffffffff91821690950294909417600160a01b600160e01b031916600160a01b9185169190910263ffffffff60c01b191617600160c01b96841696909602959095176001600160e01b0316600160e01b91831682021790945560e0860151600290920180546101008801516101208901516101408a01519585166001600160401b031990931692909217600160201b919094160292909217600160401b600160e81b031916600160401b6001600160a01b039093169290920260ff60e01b1916919091179115159093021790915551839185917fe9a0c17645ed78ccc9996259f00297ffc75e6b9d22cd605ccc9992cc8ca3f4c19190a3505050565b6000611767826301ffc9a760e09190911c90811463d9b67a26821417630e89341c9091141790565b806114b757506114b782613192565b606060d0805461178590615610565b80601f01602080910402602001604051908101604052809291908181526020018280546117b190615610565b80156117fe5780601f106117d3576101008083540402835291602001916117fe565b820191906000526020600020905b8154815290600101906020018083116117e157829003601f168201915b5050505050905090565b60006118153384846131c7565b50600192915050565b6118266122a7565b6001600160a01b031661183761316b565b6001600160a01b03161461185e576040516330cd747160e01b815260040160405180910390fd5b61138861ffff82161115611885576040516306b7c75960e31b815260040160405180910390fd5b60d4805461ffff909216600160e01b0261ffff60e01b19909216919091179055565b60606118b28261321e565b6118cf57604051630a14c4b560e41b815260040160405180910390fd5b60d380546118dc90615610565b90506000036118fa57604051806020016040528060008152506114b7565b60d36119058361323c565b604051602001611916929190615644565b60405160208183030381529060405292915050565b60408051600180825281830190925260009160208083019080368337019050509050600081600081518110611962576119626156cb565b60200260200101906001600160a01b031690816001600160a01b03168152505061198b81612df6565b50565b6000611998612eee565b54600160401b90046001600160601b0316919050565b6000806119c66119bc612eee565b6003018633613280565b805490915060001981146119fb57808411156119f5576040516313be252b60e01b815260040160405180910390fd5b83810382555b611a168686866040518060200160405280600081525061329a565b50600195945050505050565b600080611a2d612eee565b6001600160a01b03841660009081526005919091016020526040902054600160581b9004600281161515925060ff81169150600116611a6d57823b151591505b50919050565b61198b3382613912565b60008281526098602090815260408083208151808301909252546001600160a01b038116808352600160a01b9091046001600160601b0316928201929092528291611af25750604080518082019091526097546001600160a01b0381168252600160a01b90046001600160601b031660208201525b602081015160009061271090611b11906001600160601b0316876156f7565b611b1b9190615724565b91519350909150505b9250929050565b6000611b3733836139b9565b506001919050565b611b476122a7565b6001600160a01b0316611b5861316b565b6001600160a01b031614611b7f576040516330cd747160e01b815260040160405180910390fd5b611b898282613a49565b6114506002613abc565b60408051600180825281830190925260009160208083019080368337019050509050600081600081518110611bca57611bca6156cb565b60200260200101906001600160a01b031690816001600160a01b03168152505061198b81611d22565b6000611bfd612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160601b900463ffffffff1690565b611c346122a7565b6001600160a01b0316611c4561316b565b6001600160a01b031614611c6c576040516330cd747160e01b815260040160405180910390fd5b60d5805461ffff191661ffff8316908117909155611450908390613acc565b611ca08585611c9861316b565b8686866122b6565b5050505050565b60006114b78261321e565b611cba6122a7565b6001600160a01b0316611ccb61316b565b6001600160a01b031614611cf2576040516330cd747160e01b815260040160405180910390fd5b60d95460011615611d165760405163249fab5d60e01b815260040160405180910390fd5b60d36114508282615793565b73d4e182124131fe5f3bde4cdef00975fb97f5b3d8638e1ab86660d660cd611d486122a7565b856040518563ffffffff1660e01b8152600401611d689493929190615890565b60006040518083038186803b158015611d8057600080fd5b505af4158015611ca0573d6000803e3d6000fd5b611d9c6122a7565b6001600160a01b0316611dad61316b565b6001600160a01b031614611dd4576040516330cd747160e01b815260040160405180910390fd5b611ddf858383612839565b611ca0858561055a368790038701876158bd565b611dfb6122a7565b6001600160a01b0316611e0c61316b565b6001600160a01b031614611e33576040516330cd747160e01b815260040160405180910390fd5b600081815260c960205260408082206001908101548584529190922090910154600160801b9182900463ffffffff908116929091041614611e87576040516306b7c75960e31b815260040160405180910390fd5b611e928160016158da565b600083815260cf6020526040902055611eac8260016158da565b600091825260cf60205260409091205550565b611ec76122a7565b6001600160a01b0316611ed861316b565b6001600160a01b031614611eff576040516330cd747160e01b815260040160405180910390fd5b6112218383604051806101600160405280856000016020810190611f2391906158ed565b6001600160801b03168152602090810190611f40908701876158ed565b6001600160801b0316815260200160006001600160801b03168152602001856020016020810190611f7191906150d5565b63ffffffff168152602001611f8c60608701604088016150d5565b63ffffffff168152602001611fa760808701606088016150d5565b63ffffffff168152602001611fc260a08701608088016150d5565b63ffffffff16815260006020820152604001611fe460c0870160a088016150d5565b63ffffffff168152602001611fff60e0870160c08801614cbe565b6001600160a01b0316815260200161201e610100870160e08801614d16565b151590526114bd565b61202f6122a7565b6001600160a01b031661204061316b565b6001600160a01b031614612067576040516330cd747160e01b815260040160405180910390fd5b60d4805463ffffffff909216600160c01b0263ffffffff60c01b19909216919091179055565b6000612097612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160a01b90046001600160601b031690565b6120d1613bc5565b6120db6000613c34565b565b60d3805481906120ec90615610565b80601f016020809104026020016040519081016040528092919081815260200182805461211890615610565b80156121655780601f1061213a57610100808354040283529160200191612165565b820191906000526020600020905b81548152906001019060200180831161214857829003601f168201915b505050600184015460029094015492936001600160a01b0381169363ffffffff600160a01b830481169450600160c01b830416925061ffff600160e01b8304811692600160f01b900481169180821691620100008204811691600160201b90041689565b6121d16122a7565b6001600160a01b03166121e261316b565b6001600160a01b031614612209576040516330cd747160e01b815260040160405180910390fd5b6122138282613a49565b6114506001613abc565b6122256122a7565b6001600160a01b031661223661316b565b6001600160a01b03161461225d576040516330cd747160e01b815260040160405180910390fd5b61138861ffff82161115612284576040516306b7c75960e31b815260040160405180910390fd5b60d4805461ffff909216600160f01b026001600160f01b03909216919091179055565b6033546001600160a01b031690565b8535600090815260c96020908152604080832060ca9092529091205460028201546001600160201b90910463ffffffff16111561230c57600282015461230990600160201b900463ffffffff16886156f7565b96505b60d554600090600160201b900461ffff1661233061232a828b615724565b84613c86565b61233a91906156f7565b905061237287670de0b6b3a7640000612353848c6158da565b61235d91906156f7565b60405180602001604052806000815250613d20565b612384838a8a8460d2548b8b8b61405b565b505050505050505050565b606060d1805461178590615610565b6123a66122a7565b6001600160a01b03166123b761316b565b6001600160a01b0316146123de576040516330cd747160e01b815260040160405180910390fd5b60d5805461ffff909216620100000263ffff000019909216919091179055565b60d55462010000900461ffff1660000361242b5760405163017dfd0760e71b815260040160405180910390fd5b600181101561244d57604051633ccf184f60e11b815260040160405180910390fd5b600061245761316b565b90506000612463612f33565b61246e9060016156f7565b60d5549091506000906127109061248f9062010000900461ffff16846156f7565b6124999190615724565b905060006124a68461208d565b905060006124e08561dead898960008181106124c4576124c46156cb565b90506020020135604051806020016040528060008152506125d2565b826124e9612f33565b6124f39084615908565b101561257657600286101561251b57604051633ccf184f60e11b815260040160405180910390fd5b612552858661dead8a8a6001818110612536576125366156cb565b905060200201356040518060200160405280600081525061101e565b8261255b612f33565b612565919061591c565b61256f90826158da565b9050612593565b6125938561dead856040518060200160405280600081525061329a565b6125a18561235d83876158da565b50505050505050565b611450338383614435565b60006118153384846040518060200160405280600081525061329a565b6125df338585858561101e565b50505050565b60d55460d25460009161260491600160201b90910461ffff1690615724565b905090565b838614612629576040516306b7c75960e31b815260040160405180910390fd5b8735600090815260c96020908152604080832060ca909252822054909180805b8a8110156127595760028501546000906001600160201b90910463ffffffff1611156126ab576002860154600160201b900463ffffffff168b8b84818110612693576126936156cb565b905060200201356126a491906156f7565b90506126c7565b8a8a838181106126bd576126bd6156cb565b9050602002013590505b60d554600090600160201b900461ffff166126eb6126e58285615724565b88613c86565b6126f591906156f7565b90506127348e8e8581811061270c5761270c6156cb565b90506020020160208101906127219190614cbe565b670de0b6b3a764000061235384866158da565b61273e82866158da565b945061274a81856158da565b93508260010192505050612649565b5061276c848d848460d2548c8c8c61405b565b505050505050505050505050565b600083815260c96020908152604080832060cc9092528083205460d4549151631c130e3f60e21b8152909173d4e182124131fe5f3bde4cdef00975fb97f5b3d89163704c38fc916127e1918691600160f01b900461ffff16908a9087908b9060040161592f565b602060405180830381865af41580156127fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128229190615958565b9695505050505050565b6125df338585858561127e565b612841613bc5565b6008811115612863576040516306b7c75960e31b815260040160405180910390fd5b6000805b60ff81168311156129a15760008160ff161180156128eb5750838361288d600184615971565b60ff1681811061289f5761289f6156cb565b6128b59260206040909202019081019150614c0e565b61ffff1684848360ff168181106128ce576128ce6156cb565b6128e49260206040909202019081019150614c0e565b61ffff1610155b15612909576040516306b7c75960e31b815260040160405180910390fd5b600084848360ff16818110612920576129206156cb565b90506040020160200160208101906129389190614c0e565b61ffff16601086868560ff16818110612953576129536156cb565b6129699260206040909202019081019150614c0e565b61ffff16901b17905061297d82602061598a565b60ff168163ffffffff16901b83179250508080612999906159ad565b915050612867565b50600093845260ca6020526040909320929092555050565b60006129d06129c6612eee565b6003018484613280565b549392505050565b6129e06122a7565b6001600160a01b03166129f161316b565b6001600160a01b031614612a18576040516330cd747160e01b815260040160405180910390fd5b612a228282613a49565b6114506000613abc565b600054610100900460ff1615808015612a4c5750600054600160ff909116105b80612a665750303b158015612a66575060005460ff166001145b612ace5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff191660011790558015612af1576000805461ff0019166101001790555b60d0612afd8782615793565b5060d1612b0a8682615793565b508360d3612b188282615b07565b905050612b2660008061448e565b611388612b3960a0860160808701614c0e565b61ffff161180612b5d5750611388612b5760c0860160a08701614c0e565b61ffff16115b80612b8057506000612b756040860160208701614cbe565b6001600160a01b0316145b80612b9e5750612b9660808501606086016150d5565b63ffffffff16155b15612bbc576040516306b7c75960e31b815260040160405180910390fd5b612bc4614613565b6000612bd66080850160608601614c0e565b612be66060860160408701614c0e565b612bf66040870160208801614c0e565b612c036020880188614c0e565b612c0d9190615c88565b612c179190615c88565b612c219190615c88565b61ffff16905060fa612c396040860160208701614c0e565b61ffff161080612c4b57508061271014155b15612c6957604051632429608560e11b815260040160405180910390fd5b8360d6612c768282615ca3565b505060d554612c8a90849061ffff16611c2c565b508015612cd1576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b6000612cf0612ce6612eee565b6001018484613280565b5415159392505050565b612d026122a7565b6001600160a01b0316612d1361316b565b6001600160a01b031614612d3a576040516330cd747160e01b815260040160405180910390fd5b60d95460041615612d5e5760405163249fab5d60e01b815260040160405180910390fd5b60d880546001600160a01b0319166001600160a01b0392909216919091179055565b612d88613bc5565b6001600160a01b038116612ded5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401612ac5565b61198b81613c34565b60405163140fbc8560e01b815273d4e182124131fe5f3bde4cdef00975fb97f5b3d89063140fbc8590611d689060ce908590600401615d81565b60006114b782614642565b612e436122a7565b6001600160a01b0316612e5461316b565b6001600160a01b031614612e7b576040516330cd747160e01b815260040160405180910390fd5b60d95460021615612e9f5760405163249fab5d60e01b815260040160405180910390fd5b60d2548163ffffffff161015612ec857604051638a164f6360e01b815260040160405180910390fd5b60d4805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b68b6dffd38a260769cb290565b60006114b483600160201b8410840261467b565b8160081c8360601b018260ff16821515811b6001821b198354161782555050505050565b60d55460009061260490600160201b900461ffff16670de0b6b3a76400006156f7565b808214612f6b57838252602082019150612f56565b505092915050565b60405163f23a6e6181523360208201528460601b60601c60408201528260608201526001608082015260a08082015281518060c08301528015612fc0578060e08301826020860160045afa505b6020828260c401601c85016000895af1612fe3573d15612fe3573d6000833e3d82fd5b50805163f23a6e6160e01b14611ca057639c05499b6000526004601cfd5b805161300c57505050565b6040516040815260408101825160051b6020018082828660045afa50503d60400160208301523d81019050825181523d81016020820191505b80821461305b5760018252816020019150613045565b506001600160a01b03808516908616337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb85850386a45050505050565b8151156125df5760405163bc197c8181523360208201528460601b60601c604082015260a0606082015260c08101835160051b6020018082828760045afa50503d60a00160808301523d60a0013d0160a08301523d81019050835181523d81016020820191505b80821461311557600182528160200191506130ff565b835160200191508181838660045afa50602083601c85013d840103601c860160008a5af161314c573d1561314c573d6000843e3d83fd5b5050805163bc197c8160e01b14611ca057639c05499b6000526004601cfd5b600033736bc558a6dc48defa0e7022713c23d65ab26e4fa71461318d57503390565b503290565b60006001600160e01b0319821663152a902d60e11b14806114b757506301ffc9a760e01b6001600160e01b03198316146114b7565b806131dd6131d3612eee565b6003018585613280565b5560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b60006114b761322b612eee565b600201600160201b8410840261467b565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480613257575050819003601f19909101908152919050565b602890815260149190915260009182526048822091905290565b6001600160a01b0383166132c157604051633a954ecd60e21b815260040160405180910390fd5b60006132cb612eee565b8054909150600160201b900463ffffffff166132fa5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b0385811660009081526005830160209081526040808320938816835280832081516101008101835284815292830184905260808301849052606060a0840181905260c0840181905260e084018190528554600160801b80820463ffffffff90811695870195909552835404909316908401529290600160a01b90046001600160601b0316808811156133a657604051631e9acf1760e31b815260040160405180910390fd5b84546001600160601b0391899003828116600160a01b9081026001600160a01b03938416178855865481810485168c019485169091029216919091178555604084015191925090613411906133f9612f33565b83816134075761340761570e565b0480821191030290565b835261341c89611a22565b61347557886001600160a01b03168a6001600160a01b03160361344757825160408401510360608401525b61346f613452612f33565b83816134605761346061570e565b04846060015180821191030290565b60208401525b505061347f600190565b156136445760006134b182604001516134a684600001518560200151808218908211021890565b808218908211021890565b9050806134be5750613644565b8151819003825260208201805182900390526001600160a01b03808916908a16036134f3576060820180519091019052613644565b6134fc81611467565b60a08301526001600160a01b038981166000908152600487016020526040808220928b16825290819020865491850180518590039081905263ffffffff60801b198316600160801b63ffffffff9283160217885586549192600160601b9081900482169204168181108183180218865460608701805187019081905263ffffffff908116600160801b0263ffffffff60801b1993909116600160601b0292909216600160601b600160a01b0319909116171786555b6135bb8382614694565b9050806135df5787546135dc908490600160201b900463ffffffff16614694565b90505b6135eb83826000612f0f565b6135f782826001612f0f565b6136058560a001518261476c565b60001990930192836135b157865463ffffffff60601b1916600160601b63ffffffff83160217875560a085015161363f908d908d90613001565b505050505b80511561373d57805161365681611467565b60c08301526001600160a01b03891660009081526004860160205260409081902090830151855463ffffffff918490038216600160801b0263ffffffff60801b1990911617808755600160601b9004165b6136b18282614694565b9050806136d55786546136d2908390600160201b900463ffffffff16614694565b90505b6136e182826000612f0f565b6136f087600201826000612f0f565b6136fe8460c001518261476c565b60001990920191826136a757855463ffffffff60601b1916600160601b63ffffffff83160217865560c0840151613739908c90600090613001565b5050505b6020810151156138ba57602081015161375581611467565b60e08301526001600160a01b038816600090815260048601602052604081206060840151855463ffffffff60801b1916600160801b91850163ffffffff1691909102178555906137a3612f33565b8754600160401b90046001600160601b0316816137c2576137c261570e565b8854875492909104925063ffffffff90811683811180159091021791600160601b9004165b6137f4896002018361467b565b156138205761381961380d8a6002018460010186614781565b84811180159091021790565b91506137e7565b61382f89600201836001612f0f565b61383b84836001612f0f565b81811082821802811890506138548660e001518361476c565b60018201838111801590910217600019909501949150846137e757865463ffffffff60601b1916600160601b63ffffffff83811691909102919091178855895463ffffffff191690831617895560e08601516138b4906000908e90613001565b50505050505b856000528660601b60601c8860601b60601c600080516020615f4483398151915260206000a3863b15613908576138f788888360a0015188613098565b6139086000888360e0015188613098565b5050505050505050565b600061391c612eee565b9050613945600183811084821802188254600160201b900463ffffffff16808211908218021890565b6001600160a01b0384166000818152600584016020908152604091829020805463ffffffff60601b1916600160601b63ffffffff871602179055905183815292945090917ff2e9b90ef293d49024cb7f7f9fc4b87a50aca7b77895c750060efc5067161d61910160405180910390a2505050565b60006139c3612eee565b6001600160a01b0384166000818152600592909201602090815260408320805486158015865291955060ff600160581b9091049081166002918216159092180218600117927f0b5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d6693939190a2815460ff909116600160581b0260ff60581b199091161790555050565b604051663337b932bb32b960c91b6020820152602701604051602081830303815290604052805190602001208282604051602001613a88929190615d9a565b604051602081830303815290604052805190602001201461145057604051635ee88f9760e01b815260040160405180910390fd5b60d98054600190921b9091179055565b6127106001600160601b0382161115613b3a5760405162461bcd60e51b815260206004820152602a60248201527f455243323938313a20726f79616c7479206665652077696c6c206578636565646044820152692073616c65507269636560b01b6064820152608401612ac5565b6001600160a01b038216613b8c5760405162461bcd60e51b815260206004820152601960248201527822a921991c9c189d1034b73b30b634b2103932b1b2b4bb32b960391b6044820152606401612ac5565b604080518082019091526001600160a01b039092168083526001600160601b039091166020909201829052600160a01b90910217609755565b613bcd61316b565b6001600160a01b0316613bde6122a7565b6001600160a01b0316146120db5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401612ac5565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000805b60088160ff161015613d16576000613ca382602061598a565b60ff1684901c63ffffffff8116915060101c61ffff16816000829003613ccb57505050613d16565b8161ffff168710613d00578061ffff168261ffff1688613ceb9190615724565b613cf591906156f7565b9450505050506114b7565b5050508080613d0e906159ad565b915050613c8a565b5060009392505050565b6001600160a01b038316613d4757604051633a954ecd60e21b815260040160405180910390fd5b6000613d51612eee565b8054909150600160201b900463ffffffff16613d805760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b03841660009081526005820160209081526040808320815180830190925292815260609181019190915281546001600160601b03600160a01b80830482168801918216026001600160a01b03909216919091178355613de4612f33565b8181613df257613df261570e565b0482525082546001600160601b03600160401b8083048216888101928316909102600160401b600160a01b0319909316929092178555600091829182613e3a6104b98361487c565b9050898210811715613e5f5760405163e5cfe95760e01b815260040160405180910390fd5b613e67612f33565b8281613e7557613e7561570e565b049350613ea1613e83612f33565b8481613e9157613e9161570e565b0460010185811180159091021790565b8854600160201b80820463ffffffff9081168881108982180218160263ffffffff60201b199091161789559450613edd92508a9150611a229050565b61401e5782518454613f0291600160801b90910463ffffffff16808203911102611467565b60208401819052511561401e576001600160a01b03881660009081526004860160209081526040909120855491850151519091600160601b900463ffffffff16905b613f51886002018661467b565b15613f7d57613f76613f6a896002018760010187614781565b85811180159091021790565b9450613f44565b613f8c88600201866001612f0f565b613f9883866001612f0f565b8482108583180282189150613fb186602001518661476c565b6001850184811180159091021794506000190180613f4457508554855163ffffffff908116600160801b0263ffffffff60801b19918416600160601b0291909116600160601b600160a01b031990921691909117178655602085015161401b906000908c90613001565b50505b505060008581526001600160a01b03871690600080516020615f44833981519152602082a3853b15612cd157612cd1600087836020015187613098565b600061406786886158da565b90506140b46040518060c0016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b8835600090815260cf6020526040812054908181036140d45760006140f6565b60cc60006140e360018561591c565b60001b8152602001908152602001600020545b90506040518060c0016040528061410b6122a7565b6001600160a01b03168152602001886001600160a01b0316815260200185815260200189815260200160cc60008e6000013581526020019081526020016000205481526020018281525092505050600073d4e182124131fe5f3bde4cdef00975fb97f5b3d863704c38fc8c60d3600101601e9054906101000a900461ffff168c866080015160006001600160a01b031688602001516001600160a01b031614156040518663ffffffff1660e01b81526004016141cb95949392919061592f565b602060405180830381865af41580156141e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061420c9190615958565b604051631306dbef60e21b815290915073d4e182124131fe5f3bde4cdef00975fb97f5b3d890634c1b6fbc90614256908e9060d3908f9060cb908c908c908b908b90600401615dd3565b60006040518083038186803b15801561426e57600080fd5b505af4158015614282573d6000803e3d6000fd5b50505060018c015463ffffffff600160801b82048116600160a01b9092041610159050614300578260cb60006142b661316b565b6001600160a01b03166001600160a01b0316815260200190815260200160002060008c60000135815260200190815260200160002060008282546142fa91906158da565b90915550505b60018b015463ffffffff600160801b90910481161015614340578935600090815260cc60205260408120805485929061433a9084906158da565b90915550505b8260d2600082825461435291906158da565b909155505060405163032cef8f60e41b8152600481018c905260d3602482015260cd604482015260ce60648201526001600160a01b038716608482015260a481018a90526001600160801b03821660c482015273d4e182124131fe5f3bde4cdef00975fb97f5b3d8906332cef8f09060e40160006040518083038186803b1580156143dc57600080fd5b505af41580156143f0573d6000803e3d6000fd5b50505050806001600160801b03163411156144285761442861441061316b565b6144236001600160801b0384163461591c565b6148a0565b5050505050505050505050565b80151561444d614443612eee565b6001018585613280565b5560008181526001600160a01b0380841691908516907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3190602090a3505050565b6000614498612eee565b8054909150600160201b900463ffffffff16156144c857604051633ab534b960e21b815260040160405180910390fd5b6144d0612f33565b83816144de576144de61570e565b825463ffffffff60201b1916600160201b92909104600190811763ffffffff16929092021782556001600160601b0390614516612f33565b03106145355760405163265f13bd60e21b815260040160405180910390fd5b805463ffffffff191660011781558215611221576001600160a01b03821661457057604051633a954ecd60e21b815260040160405180910390fd5b6145798361487c565b156145975760405163e5cfe95760e01b815260040160405180910390fd5b8054600160401b600160a01b031916600160401b6001600160601b0385169081029190911782556001600160a01b038381166000818152600585016020908152604082208054909416600160a01b9095029490941783558681529192909190600080516020615f448339815191529082a36125df8360016139b9565b600054610100900460ff1661463a5760405162461bcd60e51b8152600401612ac590615ef8565b6120db614914565b600061464c612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160801b900463ffffffff1690565b60609190911b600882901c0154600160ff9092161c1690565b60008260601b8260081c81018054841960ff161b841960ff161c82821481176146cf575b5060001901600081905280548282148117156146b8575b8015614763577b01c1818141808141018080c0814100c04181408140c0c100414140c160221b6f8421084210842108cc6318c6db6d54be6001600160801b03831160071b83811c6001600160401b031060061b1783811c63ffffffff1060051b1783811c61ffff1060041b1783811c60ff1060031b1783811c9190911c601f169190911a1783830360081b17858111150293505b50505092915050565b601f1990910180519182526020919091019052565b6000801990508360601b8360081c81018054198560ff161c8560ff161b806147d6578460081c83015b60018301925082541991508083118217156147aa57808311156147d45760ff86191691821b90911c905b505b80156148725782820360081b7e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405821960010183166101e07a1412563212c14164235266736f7425221143267a4524367526767760fc7b2aaaaaaaba69a69a6db6db6db2cb2cb2ce739ce73def7bdeffffffff840260f81c161b60f71c1690811c63d76453e004601f169190911a1717858111878210176000031793505b5050509392505050565b600080614887612f33565b606084901c930463fffffffe1092909217151592915050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146148ed576040519150601f19603f3d011682016040523d82523d6000602084013e6148f2565b606091505b5050905080611221576040516312171d8360e31b815260040160405180910390fd5b600054610100900460ff1661493b5760405162461bcd60e51b8152600401612ac590615ef8565b6120db61494661316b565b613c34565b634e487b7160e01b600052604160045260246000fd5b60405161016081016001600160401b03811182821017156149845761498461494b565b60405290565b604051601f8201601f191681016001600160401b03811182821017156149b2576149b261494b565b604052919050565b80356001600160801b03811681146149d157600080fd5b919050565b63ffffffff8116811461198b57600080fd5b80356149d1816149d6565b6001600160a01b038116811461198b57600080fd5b80356149d1816149f3565b801515811461198b57600080fd5b80356149d181614a13565b60006101608284031215614a3f57600080fd5b614a47614961565b9050614a52826149ba565b8152614a60602083016149ba565b6020820152614a71604083016149ba565b6040820152614a82606083016149e8565b6060820152614a93608083016149e8565b6080820152614aa460a083016149e8565b60a0820152614ab560c083016149e8565b60c0820152614ac660e083016149e8565b60e0820152610100614ad98184016149e8565b90820152610120614aeb838201614a08565b90820152610140614afd838201614a21565b9082015292915050565b60008060006101a08486031215614b1d57600080fd5b8335925060208401359150614b358560408601614a2c565b90509250925092565b600060208284031215614b5057600080fd5b81356001600160e01b031981168114614b6857600080fd5b9392505050565b60005b83811015614b8a578181015183820152602001614b72565b50506000910152565b60008151808452614bab816020860160208601614b6f565b601f01601f19169290920160200192915050565b6020815260006114b46020830184614b93565b60008060408385031215614be557600080fd5b8235614bf0816149f3565b946020939093013593505050565b61ffff8116811461198b57600080fd5b600060208284031215614c2057600080fd5b8135614b6881614bfe565b600060208284031215614c3d57600080fd5b5035919050565b60008060408385031215614c5757600080fd5b8235614c62816149f3565b91506020830135614c72816149f3565b809150509250929050565b600080600060608486031215614c9257600080fd5b8335614c9d816149f3565b92506020840135614cad816149f3565b929592945050506040919091013590565b600060208284031215614cd057600080fd5b8135614b68816149f3565b60008060408385031215614cee57600080fd5b50508035926020909101359150565b6001600160a01b03929092168252602082015260400190565b600060208284031215614d2857600080fd5b8135614b6881614a13565b60008083601f840112614d4557600080fd5b5081356001600160401b03811115614d5c57600080fd5b602083019150836020828501011115611b2457600080fd5b60008060208385031215614d8757600080fd5b82356001600160401b03811115614d9d57600080fd5b614da985828601614d33565b90969095509350505050565b60008060408385031215614dc857600080fd5b8235614dd3816149f3565b91506020830135614c7281614bfe565b600060408284031215611a6d57600080fd5b600080600080600060808688031215614e0d57600080fd5b85356001600160401b0380821115614e2457600080fd5b614e3089838a01614de3565b96506020880135955060408801359150614e49826149f3565b90935060608701359080821115614e5f57600080fd5b50614e6c88828901614d33565b969995985093965092949392505050565b600082601f830112614e8e57600080fd5b81356001600160401b03811115614ea757614ea761494b565b614eba601f8201601f191660200161498a565b818152846020838601011115614ecf57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215614efe57600080fd5b81356001600160401b03811115614f1457600080fd5b614f2084828501614e7d565b949350505050565b60006001600160401b03821115614f4157614f4161494b565b5060051b60200190565b60006020808385031215614f5e57600080fd5b82356001600160401b03811115614f7457600080fd5b8301601f81018513614f8557600080fd5b8035614f98614f9382614f28565b61498a565b81815260059190911b82018301908381019087831115614fb757600080fd5b928401925b82841015614fde578335614fcf816149f3565b82529284019290840190614fbc565b979650505050505050565b60008083601f840112614ffb57600080fd5b5081356001600160401b0381111561501257600080fd5b6020830191508360208260061b8501011115611b2457600080fd5b60008060008060008587036101c081121561504757600080fd5b8635955060208701359450610160603f198201121561506557600080fd5b506040860192506101a08601356001600160401b0381111561508657600080fd5b614e6c88828901614fe9565b60008060008385036101408112156150a957600080fd5b8435935060208501359250610100603f19820112156150c757600080fd5b506040840190509250925092565b6000602082840312156150e757600080fd5b8135614b68816149d6565b60006101208083526151068184018d614b93565b6001600160a01b039b909b166020840152505063ffffffff978816604082015295909616606086015261ffff938416608086015291831660a0850152821660c0840152811660e083015290911661010090910152919050565b60008060008060008060a0878903121561517857600080fd5b86356001600160401b038082111561518f57600080fd5b61519b8a838b01614de3565b975060208901359650604089013591506151b4826149f3565b9094506060880135906151c6826149f3565b909350608088013590808211156151dc57600080fd5b506151e989828a01614d33565b979a9699509497509295939492505050565b60008083601f84011261520d57600080fd5b5081356001600160401b0381111561522457600080fd5b6020830191508360208260051b8501011115611b2457600080fd5b6000806020838503121561525257600080fd5b82356001600160401b0381111561526857600080fd5b614da9858286016151fb565b6000806040838503121561528757600080fd5b8235615292816149f3565b91506020830135614c7281614a13565b600080600080608085870312156152b857600080fd5b84356152c3816149f3565b935060208501356152d3816149f3565b92506040850135915060608501356001600160401b038111156152f557600080fd5b61530187828801614e7d565b91505092959194509250565b60008060008060008060008060a0898b03121561532957600080fd5b88356001600160401b038082111561534057600080fd5b61534c8c838d01614de3565b995060208b013591508082111561536257600080fd5b61536e8c838d016151fb565b909950975060408b013591508082111561538757600080fd5b6153938c838d016151fb565b909750955060608b013591506153a8826149f3565b90935060808a013590808211156153be57600080fd5b506153cb8b828c01614d33565b999c989b5096995094979396929594505050565b6000806000606084860312156153f457600080fd5b8335925060208401359150604084013561540d81614a13565b809150509250925092565b6000806000806080858703121561542e57600080fd5b8435615439816149f3565b935060208581013561544a816149f3565b935060408601356001600160401b038082111561546657600080fd5b818801915088601f83011261547a57600080fd5b8135615488614f9382614f28565b81815260059190911b8301840190848101908b8311156154a757600080fd5b938501935b828510156154c5578435825293850193908501906154ac565b9650505060608801359250808311156154dd57600080fd5b505061530187828801614e7d565b60008060006040848603121561550057600080fd5b8335925060208401356001600160401b0381111561551d57600080fd5b61552986828701614fe9565b9497909650939450505050565b600080600080600085870361016081121561555057600080fd5b86356001600160401b038082111561556757600080fd5b6155738a838b01614e7d565b9750602089013591508082111561558957600080fd5b6155958a838b01614e7d565b965060408901359150808211156155ab57600080fd5b508701610120818a0312156155bf57600080fd5b935060e0605f19820112156155d357600080fd5b506060860191506155e76101408701614a08565b90509295509295909350565b60006020828403121561560557600080fd5b8151614b6881614a13565b600181811c9082168061562457607f821691505b602082108103611a6d57634e487b7160e01b600052602260045260246000fd5b600080845461565281615610565b6001828116801561566a576001811461567f576156ae565b60ff19841687528215158302870194506156ae565b8860005260208060002060005b858110156156a55781548a82015290840190820161568c565b50505082870194505b5050505083516156c2818360208801614b6f565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176114b7576114b76156e1565b634e487b7160e01b600052601260045260246000fd5b6000826157335761573361570e565b500490565b601f82111561122157600081815260208120601f850160051c8101602086101561575f5750805b601f850160051c820191505b81811015612cd15782815560010161576b565b600019600383901b1c191660019190911b1790565b81516001600160401b038111156157ac576157ac61494b565b6157c0816157ba8454615610565b84615738565b602080601f8311600181146157ef57600084156157dd5750858301515b6157e7858261577e565b865550612cd1565b600085815260208120601f198616915b8281101561581e578886015182559484019460019091019084016157ff565b508582101561583c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600081518084526020808501945080840160005b838110156158855781516001600160a01b031687529582019590820190600101615860565b509495945050505050565b84815283602082015260018060a01b0383166040820152608060608201526000612822608083018461584c565b600061016082840312156158d057600080fd5b6114b48383614a2c565b808201808211156114b7576114b76156e1565b6000602082840312156158ff57600080fd5b6114b4826149ba565b6000826159175761591761570e565b500690565b818103818111156114b7576114b76156e1565b94855261ffff939093166020850152604084019190915260608301521515608082015260a00190565b60006020828403121561596a57600080fd5b5051919050565b60ff82811682821603908111156114b7576114b76156e1565b60ff81811683821602908116908181146159a6576159a66156e1565b5092915050565b600060ff821660ff81036159c3576159c36156e1565b60010192915050565b6001600160401b038311156159e3576159e361494b565b6159f7836159f18354615610565b83615738565b6000601f841160018114615a255760008515615a135750838201355b615a1d868261577e565b845550611ca0565b600083815260209020601f19861690835b82811015615a565786850135825560209485019460019092019101615a36565b5086821015615a735760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b600081356114b7816149f3565b80546001600160a01b0319166001600160a01b0392909216919091179055565b600081356114b7816149d6565b600081356114b781614bfe565b805463ffff00008360101b1663ffff0000198216178255505050565b805461ffff60201b191660209290921b61ffff60201b16919091179055565b8135601e19833603018112615b1b57600080fd5b820180356001600160401b03811115615b3357600080fd5b602082019150803603821315615b4857600080fd5b615b538183856159cc565b505060018101615b6e615b6860208501615a85565b82615a92565b615ba1615b7d60408501615ab2565b82805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b615bd4615bb060608501615ab2565b82805463ffffffff60c01b191660c09290921b63ffffffff60c01b16919091179055565b615c03615be360808501615abf565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b615c36615c1260a08501615abf565b8280546001600160f01b031660f09290921b6001600160f01b031916919091179055565b5060028101615c5d615c4a60c08501615abf565b825461ffff191661ffff91909116178255565b615c72615c6c60e08501615abf565b82615acc565b611221615c826101008501615abf565b82615ae8565b61ffff8181168382160190808211156159a6576159a66156e1565b8135615cae81614bfe565b815461ffff191661ffff8216178255506020820135615ccc81614bfe565b615cd68183615acc565b506040820135615ce581614bfe565b615cef8183615ae8565b506060820135615cfe81614bfe565b815461ffff60301b19811660309290921b61ffff60301b1691821783556080840135615d29816149f3565b600160301b600160e01b03199190911690911760409190911b600160401b600160e01b0316178155615d69615d6060a08401615a85565b60018301615a92565b611450615d7860c08401615a85565b60028301615a92565b828152604060208201526000614f20604083018461584c565b8183823760009101908152919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006101808a8352896020840152806040840152883581840152506020880135601e19893603018112615e0557600080fd5b88016020810190356001600160401b03811115615e2157600080fd5b8060051b803603831315615e3457600080fd5b60406101a08601526101c085018290526101e06001600160fb1b03831115615e5b57600080fd5b81848288013781860193508a606087015280868503016080870152615e838185018a8c615daa565b88516001600160a01b0390811660a08981019190915260208b015190911660c089015260408a015160e089015260608a015161010089015260808a01516101208901528901516101408801529450615edb9350505050565b6001600160801b0383166101608301529998505050505050505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122021f93e85066bebf74ccd8699a0d7668cf79e75e8b6518bcf716582c3f584590664736f6c63430008140033

Deployed Bytecode

0x6080604052600436106102f85760003560e01c806301d88f5d1461053f57806301ffc9a71461055f57806306fdde0314610594578063095ea7b3146105b65780630c6f910b146105d65780630e89341c146105f657806315ec67201461061657806318160ddd1461062b57806321d5bf241461064e57806323b872dd146106b5578063274e430b146106d557806327a59437146106f557806329a96532146107345780632a55205a146107545780632a6a935d14610782578063313ce567146107a257806332f7c6d4146107be5780633ccfd60b146107de5780633e100f6e146107f35780634331f639146108135780634a21a2df146108335780634bde38c8146108465780634f558e791461088157806351305a82146108a157806355f804b3146108ce5780635ecb16cd146108ee5780635fb8ecfb1461090e57806363dbc71f1461092e57806364cc4aa51461094e5780636e884900146109645780636f33659f1461098457806370a08231146109a4578063715018a6146109c457806379502c55146109d95780637c5d0a0814610a03578063818d4b5d14610a235780638b4795d614610a3e5780638da5cb5b14610a5e5780639564e8e414610a7357806395d89b4114610a8657806396a0924e14610a9b578063978a450914610abb5780639a7a973c14610b055780639c8770b714610b32578063a22cb46514610b52578063a3edb86a14610b72578063a5aa4aa414610bb5578063a9059cbb14610cd1578063aab6a17b14610cf1578063ac17811c14610d11578063b68836fa14610d26578063bcb40d5414610d53578063bcc1ed0714610d68578063bedcf00314610d7b578063caf3e53214610dc1578063d404844114610de1578063db8a560014610e89578063db8f6ec414610ea9578063dd62ed3e14610ec9578063de6cd0db14610ee9578063e2f2379a14610f09578063e985e9c514610f29578063ead0055314610f49578063f2fde38b14610f69578063f588eb5014610f89578063f5b100ea14610fa9578063f9da322414610fc95761030a565b3661030a57341561030857600080fd5b005b60003560e01c63f242432a8190036103655760643560011461033f57604051631ec9b93b60e21b815260040160405180910390fd5b61035b336004356024356044356103566084610fe9565b61101e565b6103656001611245565b80632eb2c2d60361041f57600061037c604461124f565b9050600061038a606461124f565b825181519192509081146103b157604051631dc0052360e11b815260040160405180910390fd5b600019810190156103f1576103cc828260051b016020015190565b6001146103ec57604051631ec9b93b60e21b815260040160405180910390fd5b6103b1565b50506104133361040060043590565b6024358461040e6084610fe9565b61127e565b61041d6001611245565b505b80634e1273f4036104e8576000610436600461124f565b90506000610444602461124f565b8051835191925090811461046b57604051631dc0052360e11b815260040160405180910390fd5b600061047682611467565b90505b600019820191156104d4576000610496858460051b016020015190565b90506104ce82846104be6104b9856104b48a8a60051b016020015190565b611485565b151590565b808260051b846020010152505050565b50610479565b602080820352805160051b60400160208203f35b8062fdd58e03610511576000610502600435602435611485565b905061050f811515611245565b505b80630e0b098403610526576105266001611245565b604051631e085ca760e11b815260040160405180910390fd5b34801561054b57600080fd5b5061030861055a366004614b07565b6114bd565b34801561056b57600080fd5b5061057f61057a366004614b3e565b61173f565b60405190151581526020015b60405180910390f35b3480156105a057600080fd5b506105a9611776565b60405161058b9190614bbf565b3480156105c257600080fd5b5061057f6105d1366004614bd2565b611808565b3480156105e257600080fd5b506103086105f1366004614c0e565b61181e565b34801561060257600080fd5b506105a9610611366004614c2b565b6118a7565b34801561062257600080fd5b5061030861192b565b34801561063757600080fd5b5061064061198e565b60405190815260200161058b565b34801561065a57600080fd5b5061069d610669366004614c44565b6001600160a01b03918216600090815260ce602090815260408083209390941682529190915220546001600160801b031690565b6040516001600160801b03909116815260200161058b565b3480156106c157600080fd5b5061057f6106d0366004614c7d565b6119ae565b3480156106e157600080fd5b5061057f6106f0366004614cbe565b611a22565b34801561070157600080fd5b5061069d610710366004614cbe565b6001600160a01b0316600090815260cd60205260409020546001600160801b031690565b34801561074057600080fd5b5061030861074f366004614c2b565b611a73565b34801561076057600080fd5b5061077461076f366004614cdb565b611a7d565b60405161058b929190614cfd565b34801561078e57600080fd5b5061057f61079d366004614d16565b611b2b565b3480156107ae57600080fd5b506040516012815260200161058b565b3480156107ca57600080fd5b506103086107d9366004614d74565b611b3f565b3480156107ea57600080fd5b50610308611b93565b3480156107ff57600080fd5b5061064061080e366004614cbe565b611bf3565b34801561081f57600080fd5b5061030861082e366004614db5565b611c2c565b610308610841366004614df5565b611c8b565b34801561085257600080fd5b507386b82972282dd22348374bc63fd21620f7ed847b5b6040516001600160a01b03909116815260200161058b565b34801561088d57600080fd5b5061057f61089c366004614c2b565b611ca7565b3480156108ad57600080fd5b506106406108bc366004614c2b565b60cf6020526000908152604090205481565b3480156108da57600080fd5b506103086108e9366004614eec565b611cb2565b3480156108fa57600080fd5b50610308610909366004614f4b565b611d22565b34801561091a57600080fd5b5061030861092936600461502d565b611d94565b34801561093a57600080fd5b50610308610949366004614cdb565b611df3565b34801561095a57600080fd5b5061064060d95481565b34801561097057600080fd5b5061030861097f366004615092565b611ebf565b34801561099057600080fd5b5061030861099f3660046150d5565b612027565b3480156109b057600080fd5b506106406109bf366004614cbe565b61208d565b3480156109d057600080fd5b506103086120c9565b3480156109e557600080fd5b506109ee6120dd565b60405161058b999897969594939291906150f2565b348015610a0f57600080fd5b50610308610a1e366004614d74565b6121c9565b348015610a2f57600080fd5b5061057f6104b4366004614bd2565b348015610a4a57600080fd5b50610308610a59366004614c0e565b61221d565b348015610a6a57600080fd5b506108696122a7565b610308610a8136600461515f565b6122b6565b348015610a9257600080fd5b506105a961238f565b348015610aa757600080fd5b50610308610ab6366004614c0e565b61239e565b348015610ac757600080fd5b5061069d610ad6366004614cbe565b6001600160a01b0316600090815260ce602090815260408083208380529091529020546001600160801b031690565b348015610b1157600080fd5b50610640610b20366004614c2b565b600090815260cc602052604090205490565b348015610b3e57600080fd5b50610308610b4d36600461523f565b6123fe565b348015610b5e57600080fd5b50610308610b6d366004615274565b6125aa565b348015610b7e57600080fd5b50610640610b8d366004614bd2565b6001600160a01b0391909116600090815260cb60209081526040808320938352929052205490565b348015610bc157600080fd5b50610c5a610bd0366004614c2b565b60c9602052600090815260409020805460018201546002909201546001600160801b0380831693600160801b938490048216939181169263ffffffff928204831692600160a01b8304811692600160c01b8104821692600160e01b9182900483169281811692600160201b8304909116916001600160a01b03600160401b8204169160ff9104168b565b604080516001600160801b039c8d1681529a8c1660208c015298909a169789019790975263ffffffff9586166060890152938516608088015291841660a0870152831660c0860152821660e0850152166101008301526001600160a01b03166101208201529015156101408201526101600161058b565b348015610cdd57600080fd5b5061057f610cec366004614bd2565b6125b5565b348015610cfd57600080fd5b50610308610d0c3660046152a2565b6125d2565b348015610d1d57600080fd5b506106406125e5565b348015610d3257600080fd5b50610640610d41366004614c2b565b60ca6020526000908152604090205481565b348015610d5f57600080fd5b5060d254610640565b610308610d7636600461530d565b612609565b348015610d8757600080fd5b506000805260cd6020527fcd565b10a72538d86f6d352f37ebc5dff31587960b12c0afe00fd03947a6932a546001600160801b031661069d565b348015610dcd57600080fd5b50610640610ddc3660046153df565b61277a565b348015610ded57600080fd5b5060d65460d75460d854610e3a9261ffff80821693620100008304821693600160201b8404831693600160301b8104909316926001600160a01b03600160401b9091048116928116911687565b6040805161ffff98891681529688166020880152948716948601949094529190941660608401526001600160a01b039384166080840152831660a08301529190911660c082015260e00161058b565b348015610e9557600080fd5b50610308610ea4366004615418565b61282c565b348015610eb557600080fd5b50610308610ec43660046154eb565b612839565b348015610ed557600080fd5b50610640610ee4366004614c44565b6129b9565b348015610ef557600080fd5b50610308610f04366004614d74565b6129d8565b348015610f1557600080fd5b50610308610f24366004615536565b612a2c565b348015610f3557600080fd5b5061057f610f44366004614c44565b612cd9565b348015610f5557600080fd5b50610308610f64366004614cbe565b612cfa565b348015610f7557600080fd5b50610308610f84366004614cbe565b612d80565b348015610f9557600080fd5b50610308610fa4366004614f4b565b612df6565b348015610fb557600080fd5b50610640610fc4366004614cbe565b612e30565b348015610fd557600080fd5b50610308610fe43660046150d5565b612e3b565b606060405190508135600401803580835280602083018460200137808360200101915050600081528060200160405250919050565b6001600160a01b03831661104557604051633a954ecd60e21b815260040160405180910390fd5b600061104f612eee565b8054909150600160201b900463ffffffff1661107e5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b03868116801591871614176110bb5761109e8587612cd9565b6110bb5760405163096dcfe360e31b815260040160405180910390fd5b6001600160a01b038516600090815260048201602052604090206110df8185612efb565b6110fb5760405162a1148160e81b815260040160405180910390fd5b61110781856000612f0f565b6001600160a01b0385166000908152600483016020526040902061112d90856001612f0f565b6000611137612f33565b90506000836005016000896001600160a01b03166001600160a01b0316815260200190815260200160002090506000846005016000896001600160a01b03166001600160a01b03168152602001908152602001600020905060018360201b1760801b80835403835581548060601c63ffffffff16808a18818b110260601b8218830184555050508860601b60601c98508760601b60601c97508660005260016020528789337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260406000a4826000528789600080516020615f4483398151915260206000a3611226565b505050565b873b156112395761123989898989612f73565b50505050505050505050565b8060005260206000f35b60606040519050813560040180358083528060051b60208301846020013760051b820160200160405250919050565b6001600160a01b0383166112a557604051633a954ecd60e21b815260040160405180910390fd5b60006112af612eee565b8054909150600160201b900463ffffffff166112de5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b038681168015918716141761131b576112fe8587612cd9565b61131b5760405163096dcfe360e31b815260040160405180910390fd5b6001600160a01b0380861660009081526005830160205260408082209287168252812085519192839290919061134f612f33565b6001600160a01b03808c166000908152600489016020526040808220928d16825290209183029650905b82156113e8576000199092019160006113988b8560051b016020015190565b90506113a48382612efb565b6113c05760405162a1148160e81b815260040160405180910390fd5b6113cc83826000612f0f565b6113d882826001612f0f565b8681189087100290951894611379565b50505086518460201b1760801b80835403835581548060601c63ffffffff168086188187110260601b821883018455505050611425898989613001565b836000528760601b60601c8960601b60601c600080516020615f4483398151915260206000a3611454565b5050565b873b156112395761123989898989613098565b6040805180820181526020810183815260059390931b018101905290565b60006114b4611492612eee565b6001600160a01b03851660009081526004919091016020526040902083612efb565b90505b92915050565b6114c56122a7565b6001600160a01b03166114d661316b565b6001600160a01b0316146114fd576040516330cd747160e01b815260040160405180910390fd5b6101208101516001600160a01b0316156115c25761012081015160405163095ea7b360e01b81526000916001600160a01b03169063095ea7b39061155d9073aafdfa4a935d8511bf285af11a0544ce7e4a11999060001990600401614cfd565b6020604051808303816000875af115801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a091906155f3565b9050806115c0576040516302df483560e21b815260040160405180910390fd5b505b428160a0015163ffffffff1610156115e15763ffffffff421660a08201525b600083815260c9602090815260408083208451928501516001600160801b03938416600160801b918516820217825585830151600183018054606089015160808a015160a08b015160c08c0151959099166001600160a01b03199093169290921763ffffffff91821690950294909417600160a01b600160e01b031916600160a01b9185169190910263ffffffff60c01b191617600160c01b96841696909602959095176001600160e01b0316600160e01b91831682021790945560e0860151600290920180546101008801516101208901516101408a01519585166001600160401b031990931692909217600160201b919094160292909217600160401b600160e81b031916600160401b6001600160a01b039093169290920260ff60e01b1916919091179115159093021790915551839185917fe9a0c17645ed78ccc9996259f00297ffc75e6b9d22cd605ccc9992cc8ca3f4c19190a3505050565b6000611767826301ffc9a760e09190911c90811463d9b67a26821417630e89341c9091141790565b806114b757506114b782613192565b606060d0805461178590615610565b80601f01602080910402602001604051908101604052809291908181526020018280546117b190615610565b80156117fe5780601f106117d3576101008083540402835291602001916117fe565b820191906000526020600020905b8154815290600101906020018083116117e157829003601f168201915b5050505050905090565b60006118153384846131c7565b50600192915050565b6118266122a7565b6001600160a01b031661183761316b565b6001600160a01b03161461185e576040516330cd747160e01b815260040160405180910390fd5b61138861ffff82161115611885576040516306b7c75960e31b815260040160405180910390fd5b60d4805461ffff909216600160e01b0261ffff60e01b19909216919091179055565b60606118b28261321e565b6118cf57604051630a14c4b560e41b815260040160405180910390fd5b60d380546118dc90615610565b90506000036118fa57604051806020016040528060008152506114b7565b60d36119058361323c565b604051602001611916929190615644565b60405160208183030381529060405292915050565b60408051600180825281830190925260009160208083019080368337019050509050600081600081518110611962576119626156cb565b60200260200101906001600160a01b031690816001600160a01b03168152505061198b81612df6565b50565b6000611998612eee565b54600160401b90046001600160601b0316919050565b6000806119c66119bc612eee565b6003018633613280565b805490915060001981146119fb57808411156119f5576040516313be252b60e01b815260040160405180910390fd5b83810382555b611a168686866040518060200160405280600081525061329a565b50600195945050505050565b600080611a2d612eee565b6001600160a01b03841660009081526005919091016020526040902054600160581b9004600281161515925060ff81169150600116611a6d57823b151591505b50919050565b61198b3382613912565b60008281526098602090815260408083208151808301909252546001600160a01b038116808352600160a01b9091046001600160601b0316928201929092528291611af25750604080518082019091526097546001600160a01b0381168252600160a01b90046001600160601b031660208201525b602081015160009061271090611b11906001600160601b0316876156f7565b611b1b9190615724565b91519350909150505b9250929050565b6000611b3733836139b9565b506001919050565b611b476122a7565b6001600160a01b0316611b5861316b565b6001600160a01b031614611b7f576040516330cd747160e01b815260040160405180910390fd5b611b898282613a49565b6114506002613abc565b60408051600180825281830190925260009160208083019080368337019050509050600081600081518110611bca57611bca6156cb565b60200260200101906001600160a01b031690816001600160a01b03168152505061198b81611d22565b6000611bfd612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160601b900463ffffffff1690565b611c346122a7565b6001600160a01b0316611c4561316b565b6001600160a01b031614611c6c576040516330cd747160e01b815260040160405180910390fd5b60d5805461ffff191661ffff8316908117909155611450908390613acc565b611ca08585611c9861316b565b8686866122b6565b5050505050565b60006114b78261321e565b611cba6122a7565b6001600160a01b0316611ccb61316b565b6001600160a01b031614611cf2576040516330cd747160e01b815260040160405180910390fd5b60d95460011615611d165760405163249fab5d60e01b815260040160405180910390fd5b60d36114508282615793565b73d4e182124131fe5f3bde4cdef00975fb97f5b3d8638e1ab86660d660cd611d486122a7565b856040518563ffffffff1660e01b8152600401611d689493929190615890565b60006040518083038186803b158015611d8057600080fd5b505af4158015611ca0573d6000803e3d6000fd5b611d9c6122a7565b6001600160a01b0316611dad61316b565b6001600160a01b031614611dd4576040516330cd747160e01b815260040160405180910390fd5b611ddf858383612839565b611ca0858561055a368790038701876158bd565b611dfb6122a7565b6001600160a01b0316611e0c61316b565b6001600160a01b031614611e33576040516330cd747160e01b815260040160405180910390fd5b600081815260c960205260408082206001908101548584529190922090910154600160801b9182900463ffffffff908116929091041614611e87576040516306b7c75960e31b815260040160405180910390fd5b611e928160016158da565b600083815260cf6020526040902055611eac8260016158da565b600091825260cf60205260409091205550565b611ec76122a7565b6001600160a01b0316611ed861316b565b6001600160a01b031614611eff576040516330cd747160e01b815260040160405180910390fd5b6112218383604051806101600160405280856000016020810190611f2391906158ed565b6001600160801b03168152602090810190611f40908701876158ed565b6001600160801b0316815260200160006001600160801b03168152602001856020016020810190611f7191906150d5565b63ffffffff168152602001611f8c60608701604088016150d5565b63ffffffff168152602001611fa760808701606088016150d5565b63ffffffff168152602001611fc260a08701608088016150d5565b63ffffffff16815260006020820152604001611fe460c0870160a088016150d5565b63ffffffff168152602001611fff60e0870160c08801614cbe565b6001600160a01b0316815260200161201e610100870160e08801614d16565b151590526114bd565b61202f6122a7565b6001600160a01b031661204061316b565b6001600160a01b031614612067576040516330cd747160e01b815260040160405180910390fd5b60d4805463ffffffff909216600160c01b0263ffffffff60c01b19909216919091179055565b6000612097612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160a01b90046001600160601b031690565b6120d1613bc5565b6120db6000613c34565b565b60d3805481906120ec90615610565b80601f016020809104026020016040519081016040528092919081815260200182805461211890615610565b80156121655780601f1061213a57610100808354040283529160200191612165565b820191906000526020600020905b81548152906001019060200180831161214857829003601f168201915b505050600184015460029094015492936001600160a01b0381169363ffffffff600160a01b830481169450600160c01b830416925061ffff600160e01b8304811692600160f01b900481169180821691620100008204811691600160201b90041689565b6121d16122a7565b6001600160a01b03166121e261316b565b6001600160a01b031614612209576040516330cd747160e01b815260040160405180910390fd5b6122138282613a49565b6114506001613abc565b6122256122a7565b6001600160a01b031661223661316b565b6001600160a01b03161461225d576040516330cd747160e01b815260040160405180910390fd5b61138861ffff82161115612284576040516306b7c75960e31b815260040160405180910390fd5b60d4805461ffff909216600160f01b026001600160f01b03909216919091179055565b6033546001600160a01b031690565b8535600090815260c96020908152604080832060ca9092529091205460028201546001600160201b90910463ffffffff16111561230c57600282015461230990600160201b900463ffffffff16886156f7565b96505b60d554600090600160201b900461ffff1661233061232a828b615724565b84613c86565b61233a91906156f7565b905061237287670de0b6b3a7640000612353848c6158da565b61235d91906156f7565b60405180602001604052806000815250613d20565b612384838a8a8460d2548b8b8b61405b565b505050505050505050565b606060d1805461178590615610565b6123a66122a7565b6001600160a01b03166123b761316b565b6001600160a01b0316146123de576040516330cd747160e01b815260040160405180910390fd5b60d5805461ffff909216620100000263ffff000019909216919091179055565b60d55462010000900461ffff1660000361242b5760405163017dfd0760e71b815260040160405180910390fd5b600181101561244d57604051633ccf184f60e11b815260040160405180910390fd5b600061245761316b565b90506000612463612f33565b61246e9060016156f7565b60d5549091506000906127109061248f9062010000900461ffff16846156f7565b6124999190615724565b905060006124a68461208d565b905060006124e08561dead898960008181106124c4576124c46156cb565b90506020020135604051806020016040528060008152506125d2565b826124e9612f33565b6124f39084615908565b101561257657600286101561251b57604051633ccf184f60e11b815260040160405180910390fd5b612552858661dead8a8a6001818110612536576125366156cb565b905060200201356040518060200160405280600081525061101e565b8261255b612f33565b612565919061591c565b61256f90826158da565b9050612593565b6125938561dead856040518060200160405280600081525061329a565b6125a18561235d83876158da565b50505050505050565b611450338383614435565b60006118153384846040518060200160405280600081525061329a565b6125df338585858561101e565b50505050565b60d55460d25460009161260491600160201b90910461ffff1690615724565b905090565b838614612629576040516306b7c75960e31b815260040160405180910390fd5b8735600090815260c96020908152604080832060ca909252822054909180805b8a8110156127595760028501546000906001600160201b90910463ffffffff1611156126ab576002860154600160201b900463ffffffff168b8b84818110612693576126936156cb565b905060200201356126a491906156f7565b90506126c7565b8a8a838181106126bd576126bd6156cb565b9050602002013590505b60d554600090600160201b900461ffff166126eb6126e58285615724565b88613c86565b6126f591906156f7565b90506127348e8e8581811061270c5761270c6156cb565b90506020020160208101906127219190614cbe565b670de0b6b3a764000061235384866158da565b61273e82866158da565b945061274a81856158da565b93508260010192505050612649565b5061276c848d848460d2548c8c8c61405b565b505050505050505050505050565b600083815260c96020908152604080832060cc9092528083205460d4549151631c130e3f60e21b8152909173d4e182124131fe5f3bde4cdef00975fb97f5b3d89163704c38fc916127e1918691600160f01b900461ffff16908a9087908b9060040161592f565b602060405180830381865af41580156127fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128229190615958565b9695505050505050565b6125df338585858561127e565b612841613bc5565b6008811115612863576040516306b7c75960e31b815260040160405180910390fd5b6000805b60ff81168311156129a15760008160ff161180156128eb5750838361288d600184615971565b60ff1681811061289f5761289f6156cb565b6128b59260206040909202019081019150614c0e565b61ffff1684848360ff168181106128ce576128ce6156cb565b6128e49260206040909202019081019150614c0e565b61ffff1610155b15612909576040516306b7c75960e31b815260040160405180910390fd5b600084848360ff16818110612920576129206156cb565b90506040020160200160208101906129389190614c0e565b61ffff16601086868560ff16818110612953576129536156cb565b6129699260206040909202019081019150614c0e565b61ffff16901b17905061297d82602061598a565b60ff168163ffffffff16901b83179250508080612999906159ad565b915050612867565b50600093845260ca6020526040909320929092555050565b60006129d06129c6612eee565b6003018484613280565b549392505050565b6129e06122a7565b6001600160a01b03166129f161316b565b6001600160a01b031614612a18576040516330cd747160e01b815260040160405180910390fd5b612a228282613a49565b6114506000613abc565b600054610100900460ff1615808015612a4c5750600054600160ff909116105b80612a665750303b158015612a66575060005460ff166001145b612ace5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff191660011790558015612af1576000805461ff0019166101001790555b60d0612afd8782615793565b5060d1612b0a8682615793565b508360d3612b188282615b07565b905050612b2660008061448e565b611388612b3960a0860160808701614c0e565b61ffff161180612b5d5750611388612b5760c0860160a08701614c0e565b61ffff16115b80612b8057506000612b756040860160208701614cbe565b6001600160a01b0316145b80612b9e5750612b9660808501606086016150d5565b63ffffffff16155b15612bbc576040516306b7c75960e31b815260040160405180910390fd5b612bc4614613565b6000612bd66080850160608601614c0e565b612be66060860160408701614c0e565b612bf66040870160208801614c0e565b612c036020880188614c0e565b612c0d9190615c88565b612c179190615c88565b612c219190615c88565b61ffff16905060fa612c396040860160208701614c0e565b61ffff161080612c4b57508061271014155b15612c6957604051632429608560e11b815260040160405180910390fd5b8360d6612c768282615ca3565b505060d554612c8a90849061ffff16611c2c565b508015612cd1576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b6000612cf0612ce6612eee565b6001018484613280565b5415159392505050565b612d026122a7565b6001600160a01b0316612d1361316b565b6001600160a01b031614612d3a576040516330cd747160e01b815260040160405180910390fd5b60d95460041615612d5e5760405163249fab5d60e01b815260040160405180910390fd5b60d880546001600160a01b0319166001600160a01b0392909216919091179055565b612d88613bc5565b6001600160a01b038116612ded5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401612ac5565b61198b81613c34565b60405163140fbc8560e01b815273d4e182124131fe5f3bde4cdef00975fb97f5b3d89063140fbc8590611d689060ce908590600401615d81565b60006114b782614642565b612e436122a7565b6001600160a01b0316612e5461316b565b6001600160a01b031614612e7b576040516330cd747160e01b815260040160405180910390fd5b60d95460021615612e9f5760405163249fab5d60e01b815260040160405180910390fd5b60d2548163ffffffff161015612ec857604051638a164f6360e01b815260040160405180910390fd5b60d4805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b68b6dffd38a260769cb290565b60006114b483600160201b8410840261467b565b8160081c8360601b018260ff16821515811b6001821b198354161782555050505050565b60d55460009061260490600160201b900461ffff16670de0b6b3a76400006156f7565b808214612f6b57838252602082019150612f56565b505092915050565b60405163f23a6e6181523360208201528460601b60601c60408201528260608201526001608082015260a08082015281518060c08301528015612fc0578060e08301826020860160045afa505b6020828260c401601c85016000895af1612fe3573d15612fe3573d6000833e3d82fd5b50805163f23a6e6160e01b14611ca057639c05499b6000526004601cfd5b805161300c57505050565b6040516040815260408101825160051b6020018082828660045afa50503d60400160208301523d81019050825181523d81016020820191505b80821461305b5760018252816020019150613045565b506001600160a01b03808516908616337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb85850386a45050505050565b8151156125df5760405163bc197c8181523360208201528460601b60601c604082015260a0606082015260c08101835160051b6020018082828760045afa50503d60a00160808301523d60a0013d0160a08301523d81019050835181523d81016020820191505b80821461311557600182528160200191506130ff565b835160200191508181838660045afa50602083601c85013d840103601c860160008a5af161314c573d1561314c573d6000843e3d83fd5b5050805163bc197c8160e01b14611ca057639c05499b6000526004601cfd5b600033736bc558a6dc48defa0e7022713c23d65ab26e4fa71461318d57503390565b503290565b60006001600160e01b0319821663152a902d60e11b14806114b757506301ffc9a760e01b6001600160e01b03198316146114b7565b806131dd6131d3612eee565b6003018585613280565b5560008181526001600160a01b0380841691908516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3505050565b60006114b761322b612eee565b600201600160201b8410840261467b565b60606080604051019050602081016040526000815280600019835b928101926030600a8206018453600a900480613257575050819003601f19909101908152919050565b602890815260149190915260009182526048822091905290565b6001600160a01b0383166132c157604051633a954ecd60e21b815260040160405180910390fd5b60006132cb612eee565b8054909150600160201b900463ffffffff166132fa5760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b0385811660009081526005830160209081526040808320938816835280832081516101008101835284815292830184905260808301849052606060a0840181905260c0840181905260e084018190528554600160801b80820463ffffffff90811695870195909552835404909316908401529290600160a01b90046001600160601b0316808811156133a657604051631e9acf1760e31b815260040160405180910390fd5b84546001600160601b0391899003828116600160a01b9081026001600160a01b03938416178855865481810485168c019485169091029216919091178555604084015191925090613411906133f9612f33565b83816134075761340761570e565b0480821191030290565b835261341c89611a22565b61347557886001600160a01b03168a6001600160a01b03160361344757825160408401510360608401525b61346f613452612f33565b83816134605761346061570e565b04846060015180821191030290565b60208401525b505061347f600190565b156136445760006134b182604001516134a684600001518560200151808218908211021890565b808218908211021890565b9050806134be5750613644565b8151819003825260208201805182900390526001600160a01b03808916908a16036134f3576060820180519091019052613644565b6134fc81611467565b60a08301526001600160a01b038981166000908152600487016020526040808220928b16825290819020865491850180518590039081905263ffffffff60801b198316600160801b63ffffffff9283160217885586549192600160601b9081900482169204168181108183180218865460608701805187019081905263ffffffff908116600160801b0263ffffffff60801b1993909116600160601b0292909216600160601b600160a01b0319909116171786555b6135bb8382614694565b9050806135df5787546135dc908490600160201b900463ffffffff16614694565b90505b6135eb83826000612f0f565b6135f782826001612f0f565b6136058560a001518261476c565b60001990930192836135b157865463ffffffff60601b1916600160601b63ffffffff83160217875560a085015161363f908d908d90613001565b505050505b80511561373d57805161365681611467565b60c08301526001600160a01b03891660009081526004860160205260409081902090830151855463ffffffff918490038216600160801b0263ffffffff60801b1990911617808755600160601b9004165b6136b18282614694565b9050806136d55786546136d2908390600160201b900463ffffffff16614694565b90505b6136e182826000612f0f565b6136f087600201826000612f0f565b6136fe8460c001518261476c565b60001990920191826136a757855463ffffffff60601b1916600160601b63ffffffff83160217865560c0840151613739908c90600090613001565b5050505b6020810151156138ba57602081015161375581611467565b60e08301526001600160a01b038816600090815260048601602052604081206060840151855463ffffffff60801b1916600160801b91850163ffffffff1691909102178555906137a3612f33565b8754600160401b90046001600160601b0316816137c2576137c261570e565b8854875492909104925063ffffffff90811683811180159091021791600160601b9004165b6137f4896002018361467b565b156138205761381961380d8a6002018460010186614781565b84811180159091021790565b91506137e7565b61382f89600201836001612f0f565b61383b84836001612f0f565b81811082821802811890506138548660e001518361476c565b60018201838111801590910217600019909501949150846137e757865463ffffffff60601b1916600160601b63ffffffff83811691909102919091178855895463ffffffff191690831617895560e08601516138b4906000908e90613001565b50505050505b856000528660601b60601c8860601b60601c600080516020615f4483398151915260206000a3863b15613908576138f788888360a0015188613098565b6139086000888360e0015188613098565b5050505050505050565b600061391c612eee565b9050613945600183811084821802188254600160201b900463ffffffff16808211908218021890565b6001600160a01b0384166000818152600584016020908152604091829020805463ffffffff60601b1916600160601b63ffffffff871602179055905183815292945090917ff2e9b90ef293d49024cb7f7f9fc4b87a50aca7b77895c750060efc5067161d61910160405180910390a2505050565b60006139c3612eee565b6001600160a01b0384166000818152600592909201602090815260408320805486158015865291955060ff600160581b9091049081166002918216159092180218600117927f0b5a1de456fff688115a4f75380060c23c8532d14ff85f687cc871456d6693939190a2815460ff909116600160581b0260ff60581b199091161790555050565b604051663337b932bb32b960c91b6020820152602701604051602081830303815290604052805190602001208282604051602001613a88929190615d9a565b604051602081830303815290604052805190602001201461145057604051635ee88f9760e01b815260040160405180910390fd5b60d98054600190921b9091179055565b6127106001600160601b0382161115613b3a5760405162461bcd60e51b815260206004820152602a60248201527f455243323938313a20726f79616c7479206665652077696c6c206578636565646044820152692073616c65507269636560b01b6064820152608401612ac5565b6001600160a01b038216613b8c5760405162461bcd60e51b815260206004820152601960248201527822a921991c9c189d1034b73b30b634b2103932b1b2b4bb32b960391b6044820152606401612ac5565b604080518082019091526001600160a01b039092168083526001600160601b039091166020909201829052600160a01b90910217609755565b613bcd61316b565b6001600160a01b0316613bde6122a7565b6001600160a01b0316146120db5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401612ac5565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000805b60088160ff161015613d16576000613ca382602061598a565b60ff1684901c63ffffffff8116915060101c61ffff16816000829003613ccb57505050613d16565b8161ffff168710613d00578061ffff168261ffff1688613ceb9190615724565b613cf591906156f7565b9450505050506114b7565b5050508080613d0e906159ad565b915050613c8a565b5060009392505050565b6001600160a01b038316613d4757604051633a954ecd60e21b815260040160405180910390fd5b6000613d51612eee565b8054909150600160201b900463ffffffff16613d805760405163040739bf60e41b815260040160405180910390fd5b6001600160a01b03841660009081526005820160209081526040808320815180830190925292815260609181019190915281546001600160601b03600160a01b80830482168801918216026001600160a01b03909216919091178355613de4612f33565b8181613df257613df261570e565b0482525082546001600160601b03600160401b8083048216888101928316909102600160401b600160a01b0319909316929092178555600091829182613e3a6104b98361487c565b9050898210811715613e5f5760405163e5cfe95760e01b815260040160405180910390fd5b613e67612f33565b8281613e7557613e7561570e565b049350613ea1613e83612f33565b8481613e9157613e9161570e565b0460010185811180159091021790565b8854600160201b80820463ffffffff9081168881108982180218160263ffffffff60201b199091161789559450613edd92508a9150611a229050565b61401e5782518454613f0291600160801b90910463ffffffff16808203911102611467565b60208401819052511561401e576001600160a01b03881660009081526004860160209081526040909120855491850151519091600160601b900463ffffffff16905b613f51886002018661467b565b15613f7d57613f76613f6a896002018760010187614781565b85811180159091021790565b9450613f44565b613f8c88600201866001612f0f565b613f9883866001612f0f565b8482108583180282189150613fb186602001518661476c565b6001850184811180159091021794506000190180613f4457508554855163ffffffff908116600160801b0263ffffffff60801b19918416600160601b0291909116600160601b600160a01b031990921691909117178655602085015161401b906000908c90613001565b50505b505060008581526001600160a01b03871690600080516020615f44833981519152602082a3853b15612cd157612cd1600087836020015187613098565b600061406786886158da565b90506140b46040518060c0016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b8835600090815260cf6020526040812054908181036140d45760006140f6565b60cc60006140e360018561591c565b60001b8152602001908152602001600020545b90506040518060c0016040528061410b6122a7565b6001600160a01b03168152602001886001600160a01b0316815260200185815260200189815260200160cc60008e6000013581526020019081526020016000205481526020018281525092505050600073d4e182124131fe5f3bde4cdef00975fb97f5b3d863704c38fc8c60d3600101601e9054906101000a900461ffff168c866080015160006001600160a01b031688602001516001600160a01b031614156040518663ffffffff1660e01b81526004016141cb95949392919061592f565b602060405180830381865af41580156141e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061420c9190615958565b604051631306dbef60e21b815290915073d4e182124131fe5f3bde4cdef00975fb97f5b3d890634c1b6fbc90614256908e9060d3908f9060cb908c908c908b908b90600401615dd3565b60006040518083038186803b15801561426e57600080fd5b505af4158015614282573d6000803e3d6000fd5b50505060018c015463ffffffff600160801b82048116600160a01b9092041610159050614300578260cb60006142b661316b565b6001600160a01b03166001600160a01b0316815260200190815260200160002060008c60000135815260200190815260200160002060008282546142fa91906158da565b90915550505b60018b015463ffffffff600160801b90910481161015614340578935600090815260cc60205260408120805485929061433a9084906158da565b90915550505b8260d2600082825461435291906158da565b909155505060405163032cef8f60e41b8152600481018c905260d3602482015260cd604482015260ce60648201526001600160a01b038716608482015260a481018a90526001600160801b03821660c482015273d4e182124131fe5f3bde4cdef00975fb97f5b3d8906332cef8f09060e40160006040518083038186803b1580156143dc57600080fd5b505af41580156143f0573d6000803e3d6000fd5b50505050806001600160801b03163411156144285761442861441061316b565b6144236001600160801b0384163461591c565b6148a0565b5050505050505050505050565b80151561444d614443612eee565b6001018585613280565b5560008181526001600160a01b0380841691908516907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3190602090a3505050565b6000614498612eee565b8054909150600160201b900463ffffffff16156144c857604051633ab534b960e21b815260040160405180910390fd5b6144d0612f33565b83816144de576144de61570e565b825463ffffffff60201b1916600160201b92909104600190811763ffffffff16929092021782556001600160601b0390614516612f33565b03106145355760405163265f13bd60e21b815260040160405180910390fd5b805463ffffffff191660011781558215611221576001600160a01b03821661457057604051633a954ecd60e21b815260040160405180910390fd5b6145798361487c565b156145975760405163e5cfe95760e01b815260040160405180910390fd5b8054600160401b600160a01b031916600160401b6001600160601b0385169081029190911782556001600160a01b038381166000818152600585016020908152604082208054909416600160a01b9095029490941783558681529192909190600080516020615f448339815191529082a36125df8360016139b9565b600054610100900460ff1661463a5760405162461bcd60e51b8152600401612ac590615ef8565b6120db614914565b600061464c612eee565b6001600160a01b039290921660009081526005909201602052506040902054600160801b900463ffffffff1690565b60609190911b600882901c0154600160ff9092161c1690565b60008260601b8260081c81018054841960ff161b841960ff161c82821481176146cf575b5060001901600081905280548282148117156146b8575b8015614763577b01c1818141808141018080c0814100c04181408140c0c100414140c160221b6f8421084210842108cc6318c6db6d54be6001600160801b03831160071b83811c6001600160401b031060061b1783811c63ffffffff1060051b1783811c61ffff1060041b1783811c60ff1060031b1783811c9190911c601f169190911a1783830360081b17858111150293505b50505092915050565b601f1990910180519182526020919091019052565b6000801990508360601b8360081c81018054198560ff161c8560ff161b806147d6578460081c83015b60018301925082541991508083118217156147aa57808311156147d45760ff86191691821b90911c905b505b80156148725782820360081b7e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405821960010183166101e07a1412563212c14164235266736f7425221143267a4524367526767760fc7b2aaaaaaaba69a69a6db6db6db2cb2cb2ce739ce73def7bdeffffffff840260f81c161b60f71c1690811c63d76453e004601f169190911a1717858111878210176000031793505b5050509392505050565b600080614887612f33565b606084901c930463fffffffe1092909217151592915050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146148ed576040519150601f19603f3d011682016040523d82523d6000602084013e6148f2565b606091505b5050905080611221576040516312171d8360e31b815260040160405180910390fd5b600054610100900460ff1661493b5760405162461bcd60e51b8152600401612ac590615ef8565b6120db61494661316b565b613c34565b634e487b7160e01b600052604160045260246000fd5b60405161016081016001600160401b03811182821017156149845761498461494b565b60405290565b604051601f8201601f191681016001600160401b03811182821017156149b2576149b261494b565b604052919050565b80356001600160801b03811681146149d157600080fd5b919050565b63ffffffff8116811461198b57600080fd5b80356149d1816149d6565b6001600160a01b038116811461198b57600080fd5b80356149d1816149f3565b801515811461198b57600080fd5b80356149d181614a13565b60006101608284031215614a3f57600080fd5b614a47614961565b9050614a52826149ba565b8152614a60602083016149ba565b6020820152614a71604083016149ba565b6040820152614a82606083016149e8565b6060820152614a93608083016149e8565b6080820152614aa460a083016149e8565b60a0820152614ab560c083016149e8565b60c0820152614ac660e083016149e8565b60e0820152610100614ad98184016149e8565b90820152610120614aeb838201614a08565b90820152610140614afd838201614a21565b9082015292915050565b60008060006101a08486031215614b1d57600080fd5b8335925060208401359150614b358560408601614a2c565b90509250925092565b600060208284031215614b5057600080fd5b81356001600160e01b031981168114614b6857600080fd5b9392505050565b60005b83811015614b8a578181015183820152602001614b72565b50506000910152565b60008151808452614bab816020860160208601614b6f565b601f01601f19169290920160200192915050565b6020815260006114b46020830184614b93565b60008060408385031215614be557600080fd5b8235614bf0816149f3565b946020939093013593505050565b61ffff8116811461198b57600080fd5b600060208284031215614c2057600080fd5b8135614b6881614bfe565b600060208284031215614c3d57600080fd5b5035919050565b60008060408385031215614c5757600080fd5b8235614c62816149f3565b91506020830135614c72816149f3565b809150509250929050565b600080600060608486031215614c9257600080fd5b8335614c9d816149f3565b92506020840135614cad816149f3565b929592945050506040919091013590565b600060208284031215614cd057600080fd5b8135614b68816149f3565b60008060408385031215614cee57600080fd5b50508035926020909101359150565b6001600160a01b03929092168252602082015260400190565b600060208284031215614d2857600080fd5b8135614b6881614a13565b60008083601f840112614d4557600080fd5b5081356001600160401b03811115614d5c57600080fd5b602083019150836020828501011115611b2457600080fd5b60008060208385031215614d8757600080fd5b82356001600160401b03811115614d9d57600080fd5b614da985828601614d33565b90969095509350505050565b60008060408385031215614dc857600080fd5b8235614dd3816149f3565b91506020830135614c7281614bfe565b600060408284031215611a6d57600080fd5b600080600080600060808688031215614e0d57600080fd5b85356001600160401b0380821115614e2457600080fd5b614e3089838a01614de3565b96506020880135955060408801359150614e49826149f3565b90935060608701359080821115614e5f57600080fd5b50614e6c88828901614d33565b969995985093965092949392505050565b600082601f830112614e8e57600080fd5b81356001600160401b03811115614ea757614ea761494b565b614eba601f8201601f191660200161498a565b818152846020838601011115614ecf57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215614efe57600080fd5b81356001600160401b03811115614f1457600080fd5b614f2084828501614e7d565b949350505050565b60006001600160401b03821115614f4157614f4161494b565b5060051b60200190565b60006020808385031215614f5e57600080fd5b82356001600160401b03811115614f7457600080fd5b8301601f81018513614f8557600080fd5b8035614f98614f9382614f28565b61498a565b81815260059190911b82018301908381019087831115614fb757600080fd5b928401925b82841015614fde578335614fcf816149f3565b82529284019290840190614fbc565b979650505050505050565b60008083601f840112614ffb57600080fd5b5081356001600160401b0381111561501257600080fd5b6020830191508360208260061b8501011115611b2457600080fd5b60008060008060008587036101c081121561504757600080fd5b8635955060208701359450610160603f198201121561506557600080fd5b506040860192506101a08601356001600160401b0381111561508657600080fd5b614e6c88828901614fe9565b60008060008385036101408112156150a957600080fd5b8435935060208501359250610100603f19820112156150c757600080fd5b506040840190509250925092565b6000602082840312156150e757600080fd5b8135614b68816149d6565b60006101208083526151068184018d614b93565b6001600160a01b039b909b166020840152505063ffffffff978816604082015295909616606086015261ffff938416608086015291831660a0850152821660c0840152811660e083015290911661010090910152919050565b60008060008060008060a0878903121561517857600080fd5b86356001600160401b038082111561518f57600080fd5b61519b8a838b01614de3565b975060208901359650604089013591506151b4826149f3565b9094506060880135906151c6826149f3565b909350608088013590808211156151dc57600080fd5b506151e989828a01614d33565b979a9699509497509295939492505050565b60008083601f84011261520d57600080fd5b5081356001600160401b0381111561522457600080fd5b6020830191508360208260051b8501011115611b2457600080fd5b6000806020838503121561525257600080fd5b82356001600160401b0381111561526857600080fd5b614da9858286016151fb565b6000806040838503121561528757600080fd5b8235615292816149f3565b91506020830135614c7281614a13565b600080600080608085870312156152b857600080fd5b84356152c3816149f3565b935060208501356152d3816149f3565b92506040850135915060608501356001600160401b038111156152f557600080fd5b61530187828801614e7d565b91505092959194509250565b60008060008060008060008060a0898b03121561532957600080fd5b88356001600160401b038082111561534057600080fd5b61534c8c838d01614de3565b995060208b013591508082111561536257600080fd5b61536e8c838d016151fb565b909950975060408b013591508082111561538757600080fd5b6153938c838d016151fb565b909750955060608b013591506153a8826149f3565b90935060808a013590808211156153be57600080fd5b506153cb8b828c01614d33565b999c989b5096995094979396929594505050565b6000806000606084860312156153f457600080fd5b8335925060208401359150604084013561540d81614a13565b809150509250925092565b6000806000806080858703121561542e57600080fd5b8435615439816149f3565b935060208581013561544a816149f3565b935060408601356001600160401b038082111561546657600080fd5b818801915088601f83011261547a57600080fd5b8135615488614f9382614f28565b81815260059190911b8301840190848101908b8311156154a757600080fd5b938501935b828510156154c5578435825293850193908501906154ac565b9650505060608801359250808311156154dd57600080fd5b505061530187828801614e7d565b60008060006040848603121561550057600080fd5b8335925060208401356001600160401b0381111561551d57600080fd5b61552986828701614fe9565b9497909650939450505050565b600080600080600085870361016081121561555057600080fd5b86356001600160401b038082111561556757600080fd5b6155738a838b01614e7d565b9750602089013591508082111561558957600080fd5b6155958a838b01614e7d565b965060408901359150808211156155ab57600080fd5b508701610120818a0312156155bf57600080fd5b935060e0605f19820112156155d357600080fd5b506060860191506155e76101408701614a08565b90509295509295909350565b60006020828403121561560557600080fd5b8151614b6881614a13565b600181811c9082168061562457607f821691505b602082108103611a6d57634e487b7160e01b600052602260045260246000fd5b600080845461565281615610565b6001828116801561566a576001811461567f576156ae565b60ff19841687528215158302870194506156ae565b8860005260208060002060005b858110156156a55781548a82015290840190820161568c565b50505082870194505b5050505083516156c2818360208801614b6f565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176114b7576114b76156e1565b634e487b7160e01b600052601260045260246000fd5b6000826157335761573361570e565b500490565b601f82111561122157600081815260208120601f850160051c8101602086101561575f5750805b601f850160051c820191505b81811015612cd15782815560010161576b565b600019600383901b1c191660019190911b1790565b81516001600160401b038111156157ac576157ac61494b565b6157c0816157ba8454615610565b84615738565b602080601f8311600181146157ef57600084156157dd5750858301515b6157e7858261577e565b865550612cd1565b600085815260208120601f198616915b8281101561581e578886015182559484019460019091019084016157ff565b508582101561583c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600081518084526020808501945080840160005b838110156158855781516001600160a01b031687529582019590820190600101615860565b509495945050505050565b84815283602082015260018060a01b0383166040820152608060608201526000612822608083018461584c565b600061016082840312156158d057600080fd5b6114b48383614a2c565b808201808211156114b7576114b76156e1565b6000602082840312156158ff57600080fd5b6114b4826149ba565b6000826159175761591761570e565b500690565b818103818111156114b7576114b76156e1565b94855261ffff939093166020850152604084019190915260608301521515608082015260a00190565b60006020828403121561596a57600080fd5b5051919050565b60ff82811682821603908111156114b7576114b76156e1565b60ff81811683821602908116908181146159a6576159a66156e1565b5092915050565b600060ff821660ff81036159c3576159c36156e1565b60010192915050565b6001600160401b038311156159e3576159e361494b565b6159f7836159f18354615610565b83615738565b6000601f841160018114615a255760008515615a135750838201355b615a1d868261577e565b845550611ca0565b600083815260209020601f19861690835b82811015615a565786850135825560209485019460019092019101615a36565b5086821015615a735760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b600081356114b7816149f3565b80546001600160a01b0319166001600160a01b0392909216919091179055565b600081356114b7816149d6565b600081356114b781614bfe565b805463ffff00008360101b1663ffff0000198216178255505050565b805461ffff60201b191660209290921b61ffff60201b16919091179055565b8135601e19833603018112615b1b57600080fd5b820180356001600160401b03811115615b3357600080fd5b602082019150803603821315615b4857600080fd5b615b538183856159cc565b505060018101615b6e615b6860208501615a85565b82615a92565b615ba1615b7d60408501615ab2565b82805463ffffffff60a01b191660a09290921b63ffffffff60a01b16919091179055565b615bd4615bb060608501615ab2565b82805463ffffffff60c01b191660c09290921b63ffffffff60c01b16919091179055565b615c03615be360808501615abf565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b615c36615c1260a08501615abf565b8280546001600160f01b031660f09290921b6001600160f01b031916919091179055565b5060028101615c5d615c4a60c08501615abf565b825461ffff191661ffff91909116178255565b615c72615c6c60e08501615abf565b82615acc565b611221615c826101008501615abf565b82615ae8565b61ffff8181168382160190808211156159a6576159a66156e1565b8135615cae81614bfe565b815461ffff191661ffff8216178255506020820135615ccc81614bfe565b615cd68183615acc565b506040820135615ce581614bfe565b615cef8183615ae8565b506060820135615cfe81614bfe565b815461ffff60301b19811660309290921b61ffff60301b1691821783556080840135615d29816149f3565b600160301b600160e01b03199190911690911760409190911b600160401b600160e01b0316178155615d69615d6060a08401615a85565b60018301615a92565b611450615d7860c08401615a85565b60028301615a92565b828152604060208201526000614f20604083018461584c565b8183823760009101908152919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006101808a8352896020840152806040840152883581840152506020880135601e19893603018112615e0557600080fd5b88016020810190356001600160401b03811115615e2157600080fd5b8060051b803603831315615e3457600080fd5b60406101a08601526101c085018290526101e06001600160fb1b03831115615e5b57600080fd5b81848288013781860193508a606087015280868503016080870152615e838185018a8c615daa565b88516001600160a01b0390811660a08981019190915260208b015190911660c089015260408a015160e089015260608a015161010089015260808a01516101208901528901516101408801529450615edb9350505050565b6001600160801b0383166101608301529998505050505050505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122021f93e85066bebf74ccd8699a0d7668cf79e75e8b6518bcf716582c3f584590664736f6c63430008140033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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