ETH Price: $3,388.43 (+1.21%)
Gas: 5.17 Gwei

Token

XXYYZZ (XXYYZZ)
 

Overview

Max Total Supply

1,670 XXYYZZ

Holders

349

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A

Other Info

Filtered by Token Holder
bushwhacker.eth
Balance
7 XXYYZZ
0xAA58479f8a653fE21DDcB73eB5dD7861deD41d4b
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
XXYYZZ

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion, MIT license
File 1 of 16 : XXYYZZ.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {XXYYZZMetadata} from "./XXYYZZMetadata.sol";
import {XXYYZZBurn} from "./XXYYZZBurn.sol";
import {XXYYZZSeaDrop} from "./XXYYZZSeaDrop.sol";
import {XXYYZZCore} from "./XXYYZZCore.sol";
import {XXYYZZRerollFinalize} from "./XXYYZZRerollFinalize.sol";
import {XXYYZZCore} from "./XXYYZZCore.sol";
import {LibString} from "solady/utils/LibString.sol";
import {Base64} from "solady/utils/Base64.sol";

/**
 * @title XXYYZZ
 * @author emo.eth
 * @notice XXYYZZ is a collection of fully onchain, collectible colors. Each token has a unique hex value.
 *         Tokens may be "rerolled" to new hex values, unless they are "finalized," in which case, they are immutable.
 *
 *         Finalizing tokens also adds the finalizer's wallet address to the token's metadata.
 *         Tokens may be burned, which removes it from the token supply, but unless the token was finalized, its
 *         particular hex value may be minted or rerolled again.
 *
 *         Mints and rerolls are pseudorandom by default, unless one of the "Specific" methods is called.
 *         To prevent front-running "specific" mint transactions, the XXYYZZ contract uses a commit-reveal scheme.
 *         Users must commit a hash of their desired hex value with a secret salt, wait at least one minute, and then
 *         submit their mint or reroll transaction with the original hex value(s) and salt.
 *         Multiple IDs may be minted or rerolled in a single transaction by committixng the result of hash of all IDs in order
 *         with a single secret salt.
 *         In batch methods, unavailable IDs are skipped, and excess payment is refunded to the caller.
 */
contract XXYYZZ is XXYYZZMetadata, XXYYZZBurn, XXYYZZSeaDrop, XXYYZZRerollFinalize {
    using LibString for uint256;
    using LibString for address;
    using Base64 for bytes;

    constructor(
        address initialOwner,
        address creatorPayout,
        uint256 maxBatchSize,
        uint24[] memory preMintIds,
        address seaDrop
    ) XXYYZZSeaDrop(seaDrop, creatorPayout, initialOwner, maxBatchSize) {
        for (uint256 i; i < preMintIds.length;) {
            _mint(initialOwner, preMintIds[i]);
            _finalizeToken(preMintIds[i], initialOwner);
            unchecked {
                ++i;
            }
        }
        _numMinted = uint32(preMintIds.length);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        pure
        virtual
        override(XXYYZZSeaDrop, XXYYZZCore)
        returns (bool)
    {
        return XXYYZZSeaDrop.supportsInterface(interfaceId);
    }
}

File 2 of 16 : XXYYZZMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {XXYYZZCore} from "./XXYYZZCore.sol";
import {LibString} from "solady/utils/LibString.sol";
import {Base64} from "solady/utils/Base64.sol";

/**
 * @title XXYYZZMetadata
 * @author emo.eth
 * @notice XXYYZZMetadata implements the onchain metadata for XXYYZZ tokens.
 */
abstract contract XXYYZZMetadata is XXYYZZCore {
    using LibString for uint256;
    using LibString for address;
    using Base64 for bytes;

    /**
     * @notice Return the base64-encoded token metadata. Won't revert if the token doesn't exist.
     *         Will revert if the id is not a valid six-hex-digit ID.
     */
    function tokenURI(uint256 id) public view virtual override returns (string memory) {
        _validateId(id);
        return string.concat("data:application/json;base64,", bytes(_stringURI(id)).encode());
    }

    ///@notice Return the base64-encoded contract-level metadata
    function contractURI() public pure returns (string memory) {
        return string.concat("data:application/json;base64,", bytes(_stringContractURI()).encode());
    }

    ///@dev Return a token-level JSON string
    function _stringURI(uint256 id) internal view virtual returns (string memory) {
        return string.concat(
            "{",
            _kv("name", _name(id)),
            ",",
            _kv("external_link", "https://xxyyzz.art"),
            ",",
            _kv(
                "description",
                "Proof of color. XXYYZZ is a collection of fully onchain, unique, composable, and collectable colors."
            ),
            ",",
            _kv("image", _imageURI(id)),
            ",",
            _kRawV("attributes", _traits(id)),
            "}"
        );
    }

    ///@dev Return a contract-level JSON string
    function _stringContractURI() internal pure returns (string memory) {
        return
        '{"name":"XXYYZZ","description":"Collectible, composable, and unique onchain colors.","external_link":"https://xxyyzz.art"}';
    }

    ///@dev Return a name like "#aabbcc"
    function _name(uint256 id) internal pure returns (string memory) {
        return string.concat("#", id.toHexStringNoPrefix({length: 3}));
    }

    ///@dev Return an svg as a base64-encoded data uri string
    function _imageURI(uint256 id) internal pure returns (string memory) {
        return string.concat("data:image/svg+xml;base64,", bytes(_svg(id)).encode());
    }

    ///@dev Return a 690x690 SVG with a single rect of the token's color
    function _svg(uint256 id) internal pure returns (string memory) {
        return string.concat(
            '<svg xmlns="http://www.w3.org/2000/svg" width="690" height="690"><rect width="690" height="690" fill="#',
            id.toHexStringNoPrefix({length: 3}),
            '" /></svg>'
        );
    }

    ///@dev Return a JSON array of {"trait_type":"key","value":"value"} pairs
    function _traits(uint256 id) internal view returns (string memory) {
        string memory color = _trait("Color", _name(id));
        if (isFinalized(id)) {
            string memory finalizedProp = _trait("Finalized", "Yes");
            return string.concat(
                "[", color, ",", finalizedProp, ",", _trait("Finalizer", finalizers[id].toHexString()), "]"
            );
        } else {
            return string.concat("[", color, ",", _trait("Finalized", "No"), "]");
        }
    }

    ///@dev return a {"trait_type":"key","value":"value"} pair
    function _trait(string memory key, string memory value) internal pure returns (string memory) {
        return string.concat('{"trait_type":"', key, '","value":"', value, '"}');
    }

    ///@dev return a "key":"value" pair
    function _kv(string memory key, string memory value) internal pure returns (string memory) {
        return string.concat('"', key, '":"', value, '"');
    }

    ///@dev Return a "key":value pair without quoting value
    function _kRawV(string memory key, string memory value) internal pure returns (string memory) {
        return string.concat('"', key, '":', value);
    }
}

File 3 of 16 : XXYYZZBurn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

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

abstract contract XXYYZZBurn is XXYYZZCore {
    //////////
    // BURN //
    //////////

    /**
     * @notice Permanently burn a token that the caller owns or is approved for.
     * @param xxyyzz The token to burn.
     * @param onlyFinalized If true, only tokens that have been finalized can be burned. Useful if an approved operator
     *                      is burning tokens on behalf of a user.
     */
    function burn(uint256 xxyyzz, bool onlyFinalized) public {
        // cannot overflow as there are at most 2^24 tokens, and _numBurned is a uint32
        unchecked {
            _numBurned += 1;
        }
        if (onlyFinalized) {
            if (!_isFinalized(xxyyzz)) {
                revert OnlyFinalized();
            }
        }
        _burn(msg.sender, xxyyzz);
    }

    /**
     * @notice Permanently burn multiple tokens. All must be owned by the same address.
     * @param ids The tokens to burn.
     * @param onlyFinalized If true, only tokens that have been finalized can be burned. Useful if an approved operator
     *                      is burning tokens on behalf of a user.
     */
    function batchBurn(uint256[] calldata ids, bool onlyFinalized) public {
        if (ids.length == 0) {
            revert NoIdsProvided();
        }
        uint256 packedOwnerFinalizedSlot = _packedOwnershipSlot(ids[0]);
        address initialTokenOwner = address(uint160(packedOwnerFinalizedSlot));
        if (onlyFinalized) {
            if (packedOwnerFinalizedSlot < type(uint160).max) {
                revert OnlyFinalized();
            }
        }
        // validate that msg.sender has approval to burn all tokens
        if (initialTokenOwner != msg.sender) {
            if (!isApprovedForAll(initialTokenOwner, msg.sender)) {
                revert BatchBurnerNotApprovedForAll();
            }
        }
        // safe because there are at most 2^24 tokens, and ownerships are checked
        unchecked {
            _numBurned += uint32(ids.length);
        }
        _burn(ids[0]);
        for (uint256 i = 1; i < ids.length;) {
            uint256 id = ids[i];
            packedOwnerFinalizedSlot = _packedOwnershipSlot(id);
            address owner = address(uint160(packedOwnerFinalizedSlot));
            // ensure that all tokens are owned by the same address
            if (owner != initialTokenOwner) {
                revert OwnerMismatch();
            }
            if (onlyFinalized) {
                if (packedOwnerFinalizedSlot < type(uint160).max) {
                    revert OnlyFinalized();
                }
            }
            // no need to specify msg.sender since caller is approved for all tokens
            // this also checks token exists
            _burn(id);
            unchecked {
                ++i;
            }
        }
    }
}

File 4 of 16 : XXYYZZSeaDrop.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {XXYYZZMint} from "./XXYYZZMint.sol";
import {ISeaDrop, PublicDrop} from "./lib/SeaDropSpecific.sol";

abstract contract XXYYZZSeaDrop is XXYYZZMint {
    error OnlySeadrop();

    address immutable SEADROP;
    address immutable CREATOR_PAYOUT;
    uint256 immutable DEPLOYED_TIME;

    uint256 private constant SEADROP_TOKEN_CREATED_EVENT_TOPIC =
        0xd7aca75208b9be5ffc04c6a01922020ffd62b55e68e502e317f5344960279af8;
    address private constant SEADROP_FEE_RECIPIENT = 0x0000a26b00c1F0DF003000390027140000fAa719;
    address private constant SEADROP_ALLOWED_PAYER_1 = 0xf408Bee3443D0397e2c1cdE588Fb060AC657006F;
    address private constant SEADROP_ALLOWED_PAYER_2 = 0xE3d3D0eD702504e19825f44BC6542Ff2ec45cB9A;
    uint256 private constant INONFUNGIBLESEADROP_INTERFACE_ID = 0x1890fe8e;

    constructor(address seadrop, address creatorPayout, address initialOwner, uint256 maxBatchSize)
        XXYYZZMint(initialOwner, maxBatchSize)
    {
        SEADROP = seadrop;
        DEPLOYED_TIME = block.timestamp;
        CREATOR_PAYOUT = creatorPayout;

        // log without adding event to abi
        assembly {
            log1(0, 0, SEADROP_TOKEN_CREATED_EVENT_TOPIC)
        }
    }

    /**
     * @notice Configure the SeaDrop contract. onlyOwner.
     * @dev SeaDrop calls supportsInterface, so this unfortunately can't live in the constructor.
     */
    function configureSeaDrop() external onlyOwner {
        ISeaDrop seadrop = ISeaDrop(SEADROP);
        seadrop.updatePublicDrop(
            PublicDrop({
                mintPrice: uint80(0.005 ether),
                startTime: uint48(DEPLOYED_TIME),
                endTime: uint48(MAX_MINT_CLOSE_TIMESTAMP),
                maxTotalMintableByWallet: type(uint16).max,
                feeBps: uint16(1000),
                restrictFeeRecipients: true
            })
        );
        seadrop.updateCreatorPayoutAddress(CREATOR_PAYOUT);
        seadrop.updateAllowedFeeRecipient(SEADROP_FEE_RECIPIENT, true);
        seadrop.updatePayer(SEADROP_ALLOWED_PAYER_1, true);
        seadrop.updatePayer(SEADROP_ALLOWED_PAYER_2, true);
    }

    function mintSeaDrop(address recipient, uint256 quantity) external {
        if (msg.sender != SEADROP) {
            revert OnlySeadrop();
        }
        // increment supply before minting
        uint128 newAmount;
        // this can be unchecked because an ID can only be minted once, and all IDs are later validated to be uint24s
        unchecked {
            newAmount = _numMinted + uint128(quantity);
        }
        _numMinted = newAmount;
        _mintTo(recipient, quantity, newAmount);
    }

    /**
     * @dev See {IERC165-supportsInterface}. Overridden to support SeaDrop.
     */
    function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool result) {
        assembly {
            let s := shr(224, interfaceId)
            // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f. ERC4906: 0x49064906
            result :=
                or(
                    or(or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f)), eq(s, 0x49064906)),
                    eq(s, INONFUNGIBLESEADROP_INTERFACE_ID)
                )
        }
    }

    /**
     * @dev Hard-coded for SeaDrop support
     */
    function getMintStats(address) external view returns (uint256, uint256, uint256) {
        return (0, _numMinted, 16777216);
    }

    /**
     * @dev Hard-coded for SeaDrop support
     */
    function maxSupply() external pure returns (uint256) {
        return 16777216;
    }
}

File 5 of 16 : XXYYZZCore.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {ERC721} from "solady/tokens/ERC721.sol";
import {CommitReveal} from "./lib/CommitReveal.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {IERC4906, IERC165} from "./interfaces/IERC4906.sol";

/**
 * @title XXYYZZCore
 * @author emo.eth
 * @notice Core contract for XXYYZZ NFTs. Contains errors, constants, core token information, and helper functions.
 */
abstract contract XXYYZZCore is ERC721, IERC4906, CommitReveal, Ownable {
    error InvalidPayment();
    error InvalidHex();
    error MaximumSupplyExceeded();
    error AlreadyFinalized();
    error OnlyTokenOwner();
    error NoIdsProvided();
    error OwnerMismatch();
    error BatchBurnerNotApprovedForAll();
    error ArrayLengthMismatch();
    error MintClosed();
    error InvalidTimestamp();
    error OnlyFinalized();
    error Unavailable();
    error NoneAvailable();
    error MaxBatchSizeExceeded();

    uint256 public constant MINT_PRICE = 0.005 ether;
    uint256 public constant REROLL_PRICE = 0.00025 ether;
    uint256 public constant FINALIZE_PRICE = 0.005 ether;
    uint256 public constant REROLL_AND_FINALIZE_PRICE = 0.00525 ether;
    uint256 public immutable MAX_SPECIFIC_BATCH_SIZE;

    uint256 constant BYTES3_UINT_SHIFT = 232;
    uint256 constant MAX_UINT24 = 0xFFFFFF;
    uint96 constant FINALIZED = 1;
    uint96 constant NOT_FINALIZED = 0;

    // re-declared from solady ERC721 for custom gas optimizations
    uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192;

    mapping(uint256 tokenId => address finalizer) public finalizers;
    uint128 _numBurned;
    uint128 _numMinted;

    constructor(address initialOwner, uint256 maxBatchSize)
        // lifespan
        CommitReveal(
            1 days,
            // delay – MM/RPC will report a tx will revert until first eligible block is validated,
            // so 48 seconds will result in 60 seconds of delay before the frontend will report
            // that a tx will succeed
            48 seconds
        )
    {
        _initializeOwner(initialOwner);
        MAX_SPECIFIC_BATCH_SIZE = maxBatchSize;
    }

    receive() external payable {
        // send ether – see what happens! :)
    }

    ///////////////////
    // OWNER METHODS //
    ///////////////////

    /**
     * @notice Withdraws all funds from the contract to the current owner. onlyOwner.
     */
    function withdraw() public onlyOwner {
        assembly ("memory-safe") {
            let succ := call(gas(), caller(), selfbalance(), 0, 0, 0, 0)
            // revert with returndata if call failed
            if iszero(succ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
    }

    ///////////////////
    // INFORMATIONAL //
    ///////////////////

    /**
     * @notice Get the total number of tokens in circulation
     */
    function totalSupply() public view returns (uint256) {
        return _numMinted - _numBurned;
    }

    /**
     * @notice Get the total number of tokens minted
     */
    function numMinted() external view returns (uint256) {
        return _numMinted;
    }

    /**
     * @notice Get the total number of tokens burned
     */
    function numBurned() external view returns (uint256) {
        return _numBurned;
    }

    /**
     * @notice Get the name of the token
     */
    function name() public pure override returns (string memory) {
        // note that this is unsafe to call internally, as it abi-encodes the name and
        // performs a low-level return
        assembly {
            mstore(0x20, 0x20)
            mstore(0x46, 0x06585859595a5a)
            return(0x20, 0x80)
        }
    }

    /**
     * @notice Get the symbol of the token
     */
    function symbol() public pure override returns (string memory) {
        // note that this is unsafe to call internally, as it abi-encodes the symbol and
        // performs a low-level return
        assembly {
            mstore(0x20, 0x20)
            mstore(0x46, 0x06585859595a5a)
            return(0x20, 0x80)
        }
    }

    /**
     * @notice Check if a specific token ID has been finalized. Will return true for tokens that were finalized and
     *         then burned. Will not revert if the tokenID does not currently exist. Will revert on invalid tokenIds.
     * @param id The token ID to check
     * @return True if the token ID has been finalized, false otherwise
     */
    function isFinalized(uint256 id) public view returns (bool) {
        _validateId(id);
        return _isFinalized(id);
    }

    ///@inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId)
        public
        pure
        virtual
        override(ERC721, IERC165)
        returns (bool result)
    {
        assembly {
            let s := shr(224, interfaceId)
            // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f. ERC4906: 0x49064906
            result := or(or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f)), eq(s, 0x49064906))
        }
    }

    /////////////////
    // COMMITMENTS //
    /////////////////

    /**
     * @notice Get a commitment hash for a given sender, tokenId, and salt. Note that this could expose your desired
     *         ID to the RPC provider. Won't revert if the ID is invalid, but will return an invalid hash.
     * @param sender The address of the account that will mint or reroll the token ID
     * @param id The 6-hex-digit token ID to mint or reroll
     * @param salt The salt to use for the commitment
     */
    function computeCommitment(address sender, uint256 id, bytes32 salt)
        public
        pure
        returns (bytes32 committmentHash)
    {
        assembly ("memory-safe") {
            // shift sender left by 24 bits; id stays in bottom 24
            mstore(0, or(shl(24, sender), and(id, MAX_UINT24)))
            mstore(0x20, salt)
            // start hashing at 0x09 to skip 9 empty bytes (32 - (20 + 3))
            committmentHash := keccak256(0x09, 0x40)
        }
    }

    /**
     * @notice Get a commitment hash for a given sender, array of tokenIds, and salt. This allows for a single
     *         commitment for a batch of IDs, but note that order and length of IDs matters.
     *         If 5 IDs are passed, all 5 must be passed to either batchMintSpecific or batchRerollSpecific, in the
     *         same order. Note that this could expose your desired IDs to the RPC provider.
     *         Won't revert if any IDs are invalid or duplicated.
     * @param sender The address of the account that will mint or reroll the token IDs
     * @param ids The 6-hex-digit token IDs to mint or reroll
     * @param salt The salt to use for the batch commitment
     */
    function computeBatchCommitment(address sender, uint256[] calldata ids, bytes32 salt)
        public
        pure
        returns (bytes32 commitmentHash)
    {
        assembly ("memory-safe") {
            // cache free mem pointer
            let freeMemPtr := mload(0x40)
            // multiply length of elements by 32 bytes for each element
            let numBytes := shl(5, ids.length)
            // copy contents of array to unallocated free memory
            calldatacopy(freeMemPtr, ids.offset, numBytes)
            // hash contents of array, without length
            let arrayHash :=
                keccak256(
                    // start of array contents
                    freeMemPtr,
                    //length of array contents
                    numBytes
                )

            // store sender in first memory slot
            mstore(0, sender)
            // store array hash in second memory slot
            mstore(0x20, arrayHash)
            // clobber free memory pointer with salt
            mstore(0x40, salt)
            // compute commitment hash
            // start hashing at 12 bytes since addresses are 20 bytes
            commitmentHash := keccak256(0x0c, 0x60)
            // restore free memory pointer
            mstore(0x40, freeMemPtr)
        }
    }

    /////////////
    // HELPERS //
    /////////////

    /**
     * @dev Mint a token with a specific hex value and validate it was committed to
     * @param id The 6-hex-digit token ID to mint
     * @param salt The salt to use for the commitment
     */
    function _mintSpecific(uint256 id, bytes32 salt) internal {
        bytes32 computedCommitment = computeCommitment(msg.sender, id, salt);

        // validate ID is valid 6-hex-digit number
        _validateId(id);
        // validate commitment to prevent front-running
        _assertCommittedReveal(computedCommitment);

        // don't allow minting of tokens that were finalized and then burned
        if (_isFinalized(id)) {
            revert AlreadyFinalized();
        }
        _mint(msg.sender, id);
    }

    /**
     * @dev Mint a token with a specific hex value and validate it was committed to
     * @param id The 6-hex-digit token ID to mint
     * @param computedCommitment The commitment hash to validate
     */
    function _mintSpecificWithCommitment(uint256 id, bytes32 computedCommitment) internal {
        // validate ID is valid 6-hex-digit number
        _validateId(id);
        // validate commitment to prevent front-running
        _assertCommittedReveal(computedCommitment);

        // don't allow minting of tokens that were finalized and then burned
        if (_packedOwnershipSlot(id) != 0) {
            revert AlreadyFinalized();
        }
        _mint(msg.sender, id);
    }

    /**
     * @dev Mint a token with a specific hex value without validating it was committed to
     * @param id The 6-hex-digit token ID to mint
     * @return True if the token was minted, false otherwise
     */
    function _mintSpecificUnprotected(uint256 id) internal returns (bool) {
        // validate ID is valid 6-hex-digit number
        _validateId(id);
        // don't allow minting of tokens that exist or were finalized and then burned
        if (_packedOwnershipSlot(id) != 0) {
            // return false indicating a no-op
            return false;
        }
        // otherwise mint the token
        _mint(msg.sender, id);
        return true;
    }

    /**
     * @dev Find the first unminted token ID based on the current number minted and PREVRANDAO
     * @param seed The seed to use for the random number generation – when minting, should be _numMinted, when
     *             re-rolling, should be a function of the caller. In the case of re-rolling, this means that if a single caller makes
     *             multiple re-rolls in the same block, there will be collisions. This is fine, as the extra gas  cost
     *             discourages batch re-rolling with bots or scripts (at least from the same address).
     */
    function _findAvailableHex(uint256 seed) internal view returns (uint256) {
        uint256 tokenId;
        assembly {
            mstore(0, seed)
            mstore(0x20, prevrandao())
            // hash the two values together and then mask to a uint24
            // seed is max an address, so start hashing at 0x0c
            tokenId := and(keccak256(0x0c, 0x40), MAX_UINT24)
        }
        // check for the small chance that the token ID is already minted or finalized – if so, increment until we
        // find one that isn't
        while (_packedOwnershipSlot(tokenId) != 0) {
            // safe to do unchecked math here as it is modulo 2^24
            unchecked {
                tokenId = (tokenId + 1) & MAX_UINT24;
            }
        }
        return tokenId;
    }

    ///@dev Check if an ID is a valid six-hex-digit number
    function _validateId(uint256 xxyyzz) internal pure {
        if (xxyyzz > MAX_UINT24) {
            revert InvalidHex();
        }
    }

    ///@dev Validate msg value is equal to total price
    function _validatePayment(uint256 unitPrice, uint256 quantity) internal view {
        // can't overflow because there are at most uint24 tokens, and existence is checked for each token down the line
        unchecked {
            if (msg.value != (unitPrice * quantity)) {
                revert InvalidPayment();
            }
        }
    }

    /**
     * @dev Refund any overpayment
     * @param unitPrice The price per action (mint, reroll, reroll+finalize)
     * @param availableQuantity The number of tokens (mints, rerolls) that were actually available for purchase
     */
    function _refundOverpayment(uint256 unitPrice, uint256 availableQuantity) internal {
        unchecked {
            // can't underflow because payment was already validated; even if it did, value would be larger than ether
            // supply
            uint256 overpayment = msg.value - (unitPrice * availableQuantity);
            if (overpayment != 0) {
                SafeTransferLib.safeTransferETH(msg.sender, overpayment);
            }
        }
    }

    /**
     * @dev Check if a specific token has been finalized. Does not check if token exists.
     * @param id The 6-hex-digit token ID to check
     */
    function _isFinalized(uint256 id) internal view returns (bool) {
        return _getExtraData(id) == FINALIZED;
    }

    /**
     * @dev Load the raw ownership slot for a given token ID, which contains both the owner and the extra data
     *      (finalization status). This allows for succint checking of whether or not a token is mintable,
     *      i.e., whether it does not currently exist and has not been finalized. It also allows for avoiding
     *      an extra SLOAD in cases when checking both owner/existence and finalization status.
     * @param id The 6-hex-digit token ID to check
     */
    function _packedOwnershipSlot(uint256 id) internal view returns (uint256 result) {
        assembly {
            // since all ids are < uint24, this basically just clears the 0-slot before writing 4 bytes of slot seed
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := sload(add(id, add(id, keccak256(0x00, 0x20))))
        }
    }

    function _checkCallerIsOwnerAndNotFinalized(uint256 id) internal view {
        uint256 packedSlot = _packedOwnershipSlot(id);
        // clean and cast to address
        address owner = address(uint160(packedSlot));
        if ((packedSlot) > type(uint160).max) {
            revert AlreadyFinalized();
        }
        // check that caller is owner
        if (owner != msg.sender) {
            revert OnlyTokenOwner();
        }
    }

    /**
     * @dev Check that array lengths match, the batch size is not too large, and that the payment is correct
     * @param a The first array to check
     * @param b The second array to check
     * @param unitPrice The price per action (mint, reroll, reroll+finalize)
     */
    function _validateRerollBatchAndPayment(uint256[] calldata a, uint256[] calldata b, uint256 unitPrice)
        internal
        view
    {
        if (a.length != b.length) {
            revert ArrayLengthMismatch();
        }
        if (a.length > MAX_SPECIFIC_BATCH_SIZE) {
            revert MaxBatchSizeExceeded();
        }
        _validatePayment(a.length, unitPrice);
    }
}

File 6 of 16 : XXYYZZRerollFinalize.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

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

/**
 * @title XXYYZZRerollFinalize
 * @author emo.eth
 * @notice This contract handles "rerolling" and "finalizing" tokens.
 *         Rerolling allows users to burn a token they own in exchange for a new one. The new token may be either
 *         pseudorandom, or a specific color when using one of the "Specific" methods.
 *         Finalizing allows users to prevent a token from being rerolled again, in addition to adding their
 *         wallet address to the token's metadata as the "Finalizer" trait.
 */
abstract contract XXYYZZRerollFinalize is XXYYZZCore {
    ////////////
    // REROLL //
    ////////////
    /**
     * @notice Burn a token you own and mint a new one with a pseudorandom hex value.
     * @param oldId The 6-hex-digit token ID to burn
     * @return The new token ID
     */
    function reroll(uint256 oldId) public payable returns (uint256) {
        _validatePayment(REROLL_PRICE, 1);
        // use the caller as the seed to derive the new token ID
        // this means multiple calls in the same block will be gas-inefficient
        // which may somewhat discourage botting
        return _rerollWithSeed(oldId, uint160(msg.sender));
    }

    /**
     * @notice Burn a number of tokens you own and mint new ones with pseudorandom hex values.
     * @param ids The 6-hex-digit token IDs to burn in exchange for new tokens
     * @return The new token IDs
     */
    function batchReroll(uint256[] calldata ids) public payable returns (uint256[] memory) {
        _validatePayment(REROLL_PRICE, ids.length);
        // use the caller as the seed to derive the new token IDs
        // this means multiple calls in the same block will be gas-inefficient
        // which may somewhat discourage botting
        uint256 seed = uint256(uint160(msg.sender));
        uint256[] memory newIds = new uint256[](ids.length);
        for (uint256 i; i < ids.length;) {
            newIds[i] = _rerollWithSeed(ids[i], seed);
            unchecked {
                ++i;
                ++seed;
            }
        }
        return newIds;
    }

    /**
     * @notice Burn and re-mint a token with a specific hex ID. Uses a commit-reveal scheme to prevent front-running.
     *         Only callable by the owner of the token. Users must call `commit(bytes32)` with the result of
     *         `computeCommitment(address,uint256,bytes32)` and wait at least COMMITMENT_LIFESPAN seconds before
     *         calling `rerollSpecific`.
     * @param oldId The 6-hex-digit token ID to burn
     * @param newId The 6-hex-digit token ID to mint
     * @param salt The salt used in the commitment for the new ID commitment
     */
    function rerollSpecific(uint256 oldId, uint256 newId, bytes32 salt) public payable {
        _validatePayment(REROLL_PRICE, 1);
        _rerollSpecificWithSalt(oldId, newId, salt);
    }

    /**
     * @notice Burn and re-mint a number of tokens with specific hex values. Uses a commit-reveal scheme to prevent
     *         front-running. Only callable by the owner of the tokens. Users must call `commit(bytes32)` with the
     *         result of `computeBatchCommitment(address,uint256[],bytes32)` and wait at least COMMITMENT_LIFESPAN
     *         seconds before calling `batchRerollSpecific`.
     * @param oldIds The 6-hex-digit token IDs to burn
     * @param newIds The 6-hex-digit token IDs to mint
     * @param salt The salt used in the commitment for the new IDs commitment
     * @return An array of booleans indicating whether each token was successfully rerolled
     */
    function batchRerollSpecific(uint256[] calldata oldIds, uint256[] calldata newIds, bytes32 salt)
        public
        payable
        returns (bool[] memory)
    {
        _validateRerollBatchAndPayment(oldIds, newIds, REROLL_PRICE);
        bytes32 computedCommitment = computeBatchCommitment(msg.sender, newIds, salt);
        _assertCommittedReveal(computedCommitment);

        return _batchRerollAndRefund(oldIds, newIds);
    }

    /**
     * @notice Burn and re-mint a token with a specific hex ID, then finalize it. Uses a commit-reveal scheme to
     *         prevent front-running. Only callable by the owner of the token. Users must call `commit(bytes32)`
     *         with the result of `computeCommitment(address,uint256,bytes32)` and wait at least COMMITMENT_LIFESPAN
     *         seconds before calling `rerollSpecificAndFinalize`.
     * @param oldId The 6-hex-digit token ID to burn
     * @param newId The 6-hex-digit token ID to mint
     * @param salt The salt used in the commitment for the new ID commitment
     */
    function rerollSpecificAndFinalize(uint256 oldId, uint256 newId, bytes32 salt) public payable {
        _validatePayment(REROLL_AND_FINALIZE_PRICE, 1);

        _rerollSpecificWithSalt(oldId, newId, salt);
        // won't re-validate price, but above function already did
        _finalizeToken(newId, msg.sender);
    }

    /**
     * @notice Burn and re-mint a number of tokens with specific hex values, then finalize them.
     * @param oldIds The 6-hex-digit token IDs to burn
     * @param newIds The 6-hex-digit token IDs to mint
     * @param salt The salt used in the batch commitment for the new ID commitment
     * @return An array of booleans indicating whether each token was successfully rerolled
     */
    function batchRerollSpecificAndFinalize(uint256[] calldata oldIds, uint256[] calldata newIds, bytes32 salt)
        public
        payable
        returns (bool[] memory)
    {
        _validateRerollBatchAndPayment(oldIds, newIds, REROLL_AND_FINALIZE_PRICE);
        bytes32 computedCommitment = computeBatchCommitment(msg.sender, newIds, salt);
        _assertCommittedReveal(computedCommitment);
        return _batchRerollAndFinalizeAndRefund(oldIds, newIds);
    }

    //////////////
    // FINALIZE //
    //////////////

    /**
     * @notice Finalize a token, which updates its metadata with a "Finalizer" trait and prevents it from being
     *         rerolled in the future.
     * @param id The 6-hex-digit token ID to finalize. Must be owned by the caller.
     */
    function finalize(uint256 id) public payable {
        _validatePayment(FINALIZE_PRICE, 1);
        _finalize(id);
    }

    /**
     * @notice Finalize a number of tokens, which updates their metadata with a "Finalizer" trait and prevents them
     *         from being rerolled in the future. The caller must pay the finalization price for each token, and must
     *         own all tokens.
     * @param ids The 6-hex-digit token IDs to finalize
     */
    function batchFinalize(uint256[] calldata ids) public payable {
        _validatePayment(FINALIZE_PRICE, ids.length);
        for (uint256 i; i < ids.length;) {
            _finalize(ids[i]);
            unchecked {
                ++i;
            }
        }
    }

    //////////////
    // INTERNAL //
    //////////////

    /**
     * @dev Internal function to burn and re-mint tokens with a specific hex ID. Does not check initial payment.
     *      Does refund any overpayment.
     * @param oldIds The 6-hex-digit token IDs to burn
     * @param newIds The 6-hex-digit token IDs to mint
     * @return An array of booleans indicating whether each token was successfully rerolled
     */
    function _batchRerollAndRefund(uint256[] calldata oldIds, uint256[] calldata newIds)
        internal
        returns (bool[] memory)
    {
        bool[] memory rerolled = new bool[](oldIds.length);
        uint256 quantityRerolled;
        for (uint256 i; i < oldIds.length;) {
            if (_rerollSpecificUnprotected(oldIds[i], newIds[i])) {
                rerolled[i] = true;
                unchecked {
                    ++quantityRerolled;
                }
            }
            unchecked {
                ++i;
            }
        }
        // if none were rerolled, revert to avoid wasting further gas
        if (quantityRerolled == 0) {
            revert NoneAvailable();
        }
        // refund any overpayment
        _refundOverpayment(REROLL_PRICE, quantityRerolled);

        return rerolled;
    }

    /**
     * @dev Internal function to burn and re-mint tokens with a specific hex ID, then finalize them. Does not check
     *     initial payment. Does refund any overpayment.
     * @param oldIds The 6-hex-digit token IDs to burn
     * @param newIds The 6-hex-digit token IDs to mint
     * @return An array of booleans indicating whether each token was successfully rerolled
     */
    function _batchRerollAndFinalizeAndRefund(uint256[] calldata oldIds, uint256[] calldata newIds)
        internal
        returns (bool[] memory)
    {
        bool[] memory rerolled = new bool[](oldIds.length);
        uint256 quantityRerolled;
        for (uint256 i; i < oldIds.length;) {
            if (_rerollSpecificUnprotected(oldIds[i], newIds[i])) {
                _finalizeToken(newIds[i], msg.sender);
                rerolled[i] = true;
                unchecked {
                    ++quantityRerolled;
                }
            }
            unchecked {
                ++i;
            }
        }
        // if none were rerolled, revert to avoid wasting gas
        if (quantityRerolled == 0) {
            revert NoneAvailable();
        }
        // refund any overpayment
        _refundOverpayment(REROLL_AND_FINALIZE_PRICE, quantityRerolled);

        return rerolled;
    }

    /**
     * @dev Validate an old tokenId is rerollable, burn it, then mint a token with a pseudorandom
     *      hex ID.
     * @param oldId The old ID to reroll
     * @param seed The seed to use for the reroll
     *
     */
    function _rerollWithSeed(uint256 oldId, uint256 seed) internal returns (uint256) {
        _checkCallerIsOwnerAndNotFinalized(oldId);
        // burn old token
        _burn(oldId);
        uint256 tokenId = _findAvailableHex(seed);
        _mint(msg.sender, tokenId);
        return tokenId;
    }

    /**
     * @dev Validate an old tokenId is rerollable, burn it, then mint a token with a specific
     *     hex ID, validating that the commit-reveal scheme was followed.
     * @param oldId The old ID to reroll
     * @param newId The new ID to mint
     * @param salt The salt used in the commit-reveal scheme
     */
    function _rerollSpecificWithSalt(uint256 oldId, uint256 newId, bytes32 salt) internal {
        _checkCallerIsOwnerAndNotFinalized(oldId);
        // burn old token
        _burn(oldId);
        _mintSpecific(newId, salt);
    }

    /**
     * @dev Validate an old tokenId is rerollable, mint a token with a specific new hex ID (if available)
     *      and burn the old token.
     * @param oldId The old ID to reroll
     * @param newId The new ID to mint
     * @return Whether the mint succeeded, ie, the new ID was available
     */
    function _rerollSpecificUnprotected(uint256 oldId, uint256 newId) internal returns (bool) {
        _checkCallerIsOwnerAndNotFinalized(oldId);
        // only burn old token if mint succeeded
        if (_mintSpecificUnprotected(newId)) {
            _burn(oldId);
            return true;
        }
        return false;
    }

    /**
     * @dev Internal function to finalize a token, first checking that the caller is the owner and that the token
     *      has not already been finalized.
     * @param id The 6-hex-digit token ID to finalize
     */
    function _finalize(uint256 id) internal {
        _checkCallerIsOwnerAndNotFinalized(id);
        // set finalized flag
        _finalizeToken(id, msg.sender);
        // emit onchain metadata update event
        emit MetadataUpdate(id);
    }

    /**
     * @dev Finalize a tokenId, updating its metadata with a "Finalizer" trait, and preventing it from being rerolled in the future.
     * @param id The 6-hex-digit token ID to finalize
     * @param finalizer The address of the account finalizing the token
     */
    function _finalizeToken(uint256 id, address finalizer) internal {
        finalizers[id] = finalizer;
        _setExtraData(id, 1);
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            if temp {
                // Store the function selector of `HexLengthInsufficient()`.
                mstore(0x00, 0x2194895a)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

            value := shl(96, value)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @notice Library to encode strings in Base64.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
/// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>.
library Base64 {
    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// See: https://datatracker.ietf.org/doc/html/rfc4648
    /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
    /// @param noPadding Whether to strip away the padding.
    function encode(bytes memory data, bool fileSafe, bool noPadding)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                // Multiply by 4/3 rounded up.
                // The `shl(2, ...)` is equivalent to multiplying by 4.
                let encodedLength := shl(2, div(add(dataLength, 2), 3))

                // Set `result` to point to the start of the free memory.
                result := mload(0x40)

                // Store the table into the scratch space.
                // Offsetted by -1 byte so that the `mload` will load the character.
                // We will rewrite the free memory pointer at `0x40` later with
                // the allocated size.
                // The magic constant 0x0230 will translate "-_" + "+/".
                mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230)))

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, encodedLength)

                // Run over the input, 3 bytes at a time.
                for {} 1 {} {
                    data := add(data, 3) // Advance 3 bytes.
                    let input := mload(data)

                    // Write 4 bytes. Optimized for fewer stack operations.
                    mstore8(0, mload(and(shr(18, input), 0x3F)))
                    mstore8(1, mload(and(shr(12, input), 0x3F)))
                    mstore8(2, mload(and(shr(6, input), 0x3F)))
                    mstore8(3, mload(and(input, 0x3F)))
                    mstore(ptr, mload(0x00))

                    ptr := add(ptr, 4) // Advance 4 bytes.
                    if iszero(lt(ptr, end)) { break }
                }
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                // Equivalent to `o = [0, 2, 1][dataLength % 3]`.
                let o := div(2, mod(dataLength, 3))
                // Offset `ptr` and pad with '='. We can simply write over the end.
                mstore(sub(ptr, o), shl(240, 0x3d3d))
                // Set `o` to zero if there is padding.
                o := mul(iszero(iszero(noPadding)), o)
                mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
                mstore(result, sub(encodedLength, o)) // Store the length.
            }
        }
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, false, false)`.
    function encode(bytes memory data) internal pure returns (string memory result) {
        result = encode(data, false, false);
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, fileSafe, false)`.
    function encode(bytes memory data, bool fileSafe)
        internal
        pure
        returns (string memory result)
    {
        result = encode(data, fileSafe, false);
    }

    /// @dev Decodes base64 encoded `data`.
    ///
    /// Supports:
    /// - RFC 4648 (both standard and file-safe mode).
    /// - RFC 3501 (63: ',').
    ///
    /// Does not support:
    /// - Line breaks.
    ///
    /// Note: For performance reasons,
    /// this function will NOT revert on invalid `data` inputs.
    /// Outputs for invalid inputs will simply be undefined behaviour.
    /// It is the user's responsibility to ensure that the `data`
    /// is a valid base64 encoded string.
    function decode(string memory data) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                let decodedLength := mul(shr(2, dataLength), 3)

                for {} 1 {} {
                    // If padded.
                    if iszero(and(dataLength, 3)) {
                        let t := xor(mload(add(data, dataLength)), 0x3d3d)
                        // forgefmt: disable-next-item
                        decodedLength := sub(
                            decodedLength,
                            add(iszero(byte(30, t)), iszero(byte(31, t)))
                        )
                        break
                    }
                    // If non-padded.
                    decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                    break
                }
                result := mload(0x40)

                // Write the length of the bytes.
                mstore(result, decodedLength)

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, decodedLength)

                // Load the table into the scratch space.
                // Constants are optimized for smaller bytecode with zero gas overhead.
                // `m` also doubles as the mask of the upper 6 bits.
                let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                mstore(0x5b, m)
                mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)

                for {} 1 {} {
                    // Read 4 bytes.
                    data := add(data, 4)
                    let input := mload(data)

                    // Write 3 bytes.
                    // forgefmt: disable-next-item
                    mstore(ptr, or(
                        and(m, mload(byte(28, input))),
                        shr(6, or(
                            and(m, mload(byte(29, input))),
                            shr(6, or(
                                and(m, mload(byte(30, input))),
                                shr(6, mload(byte(31, input)))
                            ))
                        ))
                    ))
                    ptr := add(ptr, 3)
                    if iszero(lt(ptr, end)) { break }
                }
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                mstore(end, 0) // Zeroize the slot after the bytes.
                mstore(0x60, 0) // Restore the zero slot.
            }
        }
    }
}

File 9 of 16 : XXYYZZMint.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

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

/**
 * @title XXYYZZMint
 * @author emo.eth
 * @notice This contract handles minting of XXYYZZ tokens.
 *         Tokens may be minted with a pseudorandom hex value, or with a specific hex value.
 *         The "Specific" methods allow for minting tokens with specific hex values with a commit-reveal scheme.
 *         Users may protect themselves against front-running by
 */
abstract contract XXYYZZMint is XXYYZZCore {
    uint256 public immutable MAX_MINT_CLOSE_TIMESTAMP;

    constructor(address initialOwner, uint256 maxBatchSize) XXYYZZCore(initialOwner, maxBatchSize) {
        MAX_MINT_CLOSE_TIMESTAMP = block.timestamp + 14 days;
    }

    //////////
    // MINT //
    //////////

    /**
     * @notice Mint a token with a pseudorandom hex value.
     * @return The token ID
     */
    function mint() public payable returns (uint256) {
        uint256 newAmount = _checkMintAndIncrementNumMinted(1);
        // get pseudorandom hex id – doesn't need to be derived from caller
        uint256 tokenId = _findAvailableHex(newAmount);
        _mint(msg.sender, tokenId);
        return tokenId;
    }

    /**
     * @notice Mint a number of tokens with pseudorandom hex values.
     * @param quantity The number of tokens to mint
     * @return The token IDs
     */
    function mint(uint256 quantity) public payable returns (uint256[] memory) {
        return _checkMintTo(msg.sender, quantity);
    }

    function mintTo(address to, uint256 quantity) public payable returns (uint256[] memory) {
        return _checkMintTo(to, quantity);
    }

    /**
     * @notice Mint a token with a specific hex value.
     *         A user must first call commit(bytes32) or batchCommit(bytes32[]) with the result(s) of
     *         computeCommittment(address,uint256,bytes32), and wait at least one minute.
     *         When calling mintSpecific, the "salt" should be the bytes32 salt provided to `computeCommitment` when
     *         creating the commitment hash.
     *
     *         Example: To register 0x123456 with salt bytes32(0xDEADBEEF)
     *             1. Call `computeCommitment(<minting addr>, 0x123456, bytes32(0xDEADBEEF))` for `bytes32 result`
     *             2. Call `commit(result)`
     *             3. Wait at least 1 minute, but less than 1 day
     *             4. Call `mintSpecific(0x123456, bytes32(0xDEADBEEF))`
     * @param id The 6-hex-digit token ID to mint
     * @param salt The salt used in the commitment for the commitment
     */
    function mintSpecific(uint256 id, bytes32 salt) public payable {
        _checkMintAndIncrementNumMinted(1);
        _mintSpecific(id, salt);
    }

    /**
     * @notice Mint a number of tokens with specific hex values.
     *         A user must first call commit(bytes32) with the result of
     *         `computeBatchCommitment(address,uint256[],bytes32)`, and wait at least COMMITMENT_LIFESPAN seconds.
     * @param ids The 6-hex-digit token IDs to mint
     * @param salt The salt used in the batch commitment
     * @return An array of booleans indicating whether each token was minted
     */
    function batchMintSpecific(uint256[] calldata ids, bytes32 salt) public payable returns (bool[] memory) {
        _validateBatchMintAndTimestamp(ids);
        bytes32 computedCommitment = computeBatchCommitment(msg.sender, ids, salt);
        _assertCommittedReveal(computedCommitment);
        return _batchMintAndIncrementAndRefund(ids);
    }

    /////////////
    // HELPERS //
    /////////////

    /**
     * @dev Mint tokens, validate that tokens were minted, and increment the number of minted tokens
     * @param to Recipient of the tokens
     * @param quantity Number of tokens to mint
     */
    function _checkMintTo(address to, uint256 quantity) internal returns (uint256[] memory) {
        // check payment and quantity once
        uint256 newAmount = _checkMintAndIncrementNumMinted(quantity);
        return _mintTo(to, quantity, newAmount);
    }

    /**
     * @dev Mint tokens, validate that tokens were minted, and increment the number of minted tokens
     * @param to Recipient of the tokens
     * @param quantity Number of tokens to mint
     */
    function _mintTo(address to, uint256 quantity, uint256 newAmount) internal returns (uint256[] memory) {
        uint256[] memory tokenIds = new uint256[](quantity);
        for (uint256 i; i < quantity;) {
            // get pseudorandom hex id
            uint256 tokenId = _findAvailableHex(newAmount);
            _mint(to, tokenId);
            tokenIds[i] = tokenId;
            unchecked {
                ++i;
                ++newAmount;
            }
        }
        return tokenIds;
    }

    /**
     * @dev Mint tokens, validate that tokens were minted, increment the number of minted tokens, and refund any
     *      overpayment
     * @param ids The 6-hex-digit token IDs to mint
     */
    function _batchMintAndIncrementAndRefund(uint256[] calldata ids) internal returns (bool[] memory) {
        bool[] memory minted = new bool[](ids.length);
        uint256 quantityMinted;
        for (uint256 i; i < ids.length;) {
            if (_mintSpecificUnprotected(ids[i])) {
                minted[i] = true;
                unchecked {
                    ++quantityMinted;
                }
            }
            unchecked {
                ++i;
            }
        }
        if (quantityMinted == 0) {
            revert NoneAvailable();
        }

        _incrementNumMintedAndRefundOverpayment(quantityMinted);
        return minted;
    }

    /**
     * @dev Check payment and quantity validation – quantityRequested for payment, quantityAvailable for updating
     *      the number of minted tokens, which may be different
     * @param quantityRequested The number of tokens requested by the user, which must be paid for
     * @return The new number of minted tokens
     */
    function _checkMintAndIncrementNumMinted(uint256 quantityRequested) internal returns (uint256) {
        if (block.timestamp > MAX_MINT_CLOSE_TIMESTAMP) {
            revert MintClosed();
        }
        _validatePayment(MINT_PRICE, quantityRequested);

        // increment supply before minting
        uint128 newAmount;
        // this can be unchecked because an ID can only be minted once, and all IDs are later validated to be uint24s
        unchecked {
            newAmount = _numMinted + uint128(quantityRequested);
        }
        _numMinted = newAmount;
        return newAmount;
    }

    /**
     * @dev Increment the number of minted tokens and refund any overpayment
     * @param quantity The number of tokens actually minted
     */
    function _incrementNumMintedAndRefundOverpayment(uint256 quantity) internal returns (uint256) {
        uint256 newAmount;
        // this can be unchecked because an ID can only be minted once, and all IDs are validated to be uint24s
        // overflow here implies invalid IDs down the line, which will cause a revert when minting
        unchecked {
            newAmount = _numMinted + quantity;
        }
        _numMinted = uint32(newAmount);
        _refundOverpayment(MINT_PRICE, quantity);
        return newAmount;
    }

    /**
     * @dev Validate the timestamp and payment for a batch mint
     * @param ids The 6-hex-digit token IDs to mint
     */
    function _validateBatchMintAndTimestamp(uint256[] calldata ids) internal view {
        if (block.timestamp > MAX_MINT_CLOSE_TIMESTAMP) {
            revert MintClosed();
        }
        if (ids.length > MAX_SPECIFIC_BATCH_SIZE) {
            revert MaxBatchSizeExceeded();
        }
        _validatePayment(ids.length, MINT_PRICE);
    }
}

File 10 of 16 : SeaDropSpecific.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

struct PublicDrop {
    uint80 mintPrice; // 80/256 bits
    uint48 startTime; // 128/256 bits
    uint48 endTime; // 176/256 bits
    uint16 maxTotalMintableByWallet; // 224/256 bits
    uint16 feeBps; // 240/256 bits
    bool restrictFeeRecipients; // 248/256 bits
}

interface ISeaDrop {
    /**
     * @notice Updates the public drop data for the nft contract
     *         and emits an event.
     *
     *         This method assume msg.sender is an nft contract and its
     *         ERC165 interface id matches INonFungibleSeaDropToken.
     *
     *         Note: Be sure only authorized users can call this from
     *         token contracts that implement INonFungibleSeaDropToken.
     *
     * @param publicDrop The public drop data.
     */
    function updatePublicDrop(PublicDrop calldata publicDrop) external;

    /**
     * @notice Updates the creator payout address and emits an event.
     *
     *         This method assume msg.sender is an nft contract and its
     *         ERC165 interface id matches INonFungibleSeaDropToken.
     *
     *         Note: Be sure only authorized users can call this from
     *         token contracts that implement INonFungibleSeaDropToken.
     *
     * @param payoutAddress The creator payout address.
     */
    function updateCreatorPayoutAddress(address payoutAddress) external;

    /**
     * @notice Updates the allowed fee recipient and emits an event.
     *
     *         This method assume msg.sender is an nft contract and its
     *         ERC165 interface id matches INonFungibleSeaDropToken.
     *
     *         Note: Be sure only authorized users can call this from
     *         token contracts that implement INonFungibleSeaDropToken.
     *
     * @param feeRecipient The fee recipient.
     * @param allowed      If the fee recipient is allowed.
     */
    function updateAllowedFeeRecipient(address feeRecipient, bool allowed) external;

    /**
     * @notice Updates the allowed payer and emits an event.
     *
     *         This method assume msg.sender is an nft contract and its
     *         ERC165 interface id matches INonFungibleSeaDropToken.
     *
     *         Note: Be sure only authorized users can call this from
     *         token contracts that implement INonFungibleSeaDropToken.
     *
     * @param payer   The payer to add or remove.
     * @param allowed Whether to add or remove the payer.
     */
    function updatePayer(address payer, bool allowed) external;
}

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

/// @notice Simple ERC721 implementation with storage hitchhiking.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol)
/// Note:
/// The ERC721 standard allows for self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
abstract contract ERC721 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev An account can hold up to 4294967295 tokens.
    uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff;

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

    /// @dev Only the token owner or an approved account can manage the token.
    error NotOwnerNorApproved();

    /// @dev The token does not exist.
    error TokenDoesNotExist();

    /// @dev The token already exists.
    error TokenAlreadyExists();

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

    /// @dev Cannot mint or transfer to the zero address.
    error TransferToZeroAddress();

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

    /// @dev The recipient's balance has overflowed.
    error AccountBalanceOverflow();

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

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

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

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

    /// @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 `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("ApprovalForAll(address,address,bool)"))`.
    uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
        0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;

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

    /// @dev The ownership data slot of `id` is given by:
    /// ```
    ///     mstore(0x00, id)
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
    /// ```
    /// Bits Layout:
    // - [0..159]   `addr`
    // - [160..223] `extraData`
    ///
    /// The approved address slot is given by: `add(1, ownershipSlot)`.
    ///
    /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip
    ///
    /// The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x1c)
    /// ```
    /// Bits Layout:
    /// - [0..31]   `balance`
    /// - [32..225] `aux`
    ///
    /// The `operator` approval slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
    ///     mstore(0x00, owner)
    ///     let operatorApprovalSlot := keccak256(0x0c, 0x30)
    /// ```
    uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192;

    /// @dev Pre-shifted and pre-masked constant.
    uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC721 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

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

    /// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
    function tokenURI(uint256 id) public view virtual returns (string memory);

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

    /// @dev Returns the owner of token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function ownerOf(uint256 id) public view virtual returns (address result) {
        result = _ownerOf(id);
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(result) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns the number of tokens owned by `owner`.
    ///
    /// Requirements:
    /// - `owner` must not be the zero address.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the `owner` is the zero address.
            if iszero(owner) {
                mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`.
                revert(0x1c, 0x04)
            }
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE)
        }
    }

    /// @dev Returns the account approved to managed token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function getApproved(uint256 id) public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            if iszero(shr(96, shl(96, sload(ownershipSlot)))) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            result := sload(add(1, ownershipSlot))
        }
    }

    /// @dev Sets `account` as the approved account to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - The caller must be the owner of the token,
    ///   or an approved operator for the token owner.
    ///
    /// Emits a {Approval} event.
    function approve(address account, uint256 id) public payable virtual {
        _approve(msg.sender, account, id);
    }

    /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
    function isApprovedForAll(address owner, address operator)
        public
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x30))
        }
    }

    /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
    ///
    /// Emits a {ApprovalForAll} event.
    function setApprovalForAll(address operator, bool isApproved) public virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`msg.sender`, `operator`).
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            log3(
                0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator))
            )
        }
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 id) public payable virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller()))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if `from` is not the owner, or does not exist.
            if iszero(mul(owner, eq(owner, from))) {
                if iszero(owner) {
                    mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x00, 0xa1148100) // `TransferFromIncorrectOwner()`.
                revert(0x1c, 0x04)
            }
            // Revert if `to` is the zero address.
            if iszero(to) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // Revert if the caller is not the owner, nor approved.
                if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                if iszero(and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE)) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
    function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)
        public
        payable
        virtual
    {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, 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, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.
            result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))
        }
    }

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

    /// @dev Returns if token `id` exists.
    function _exists(uint256 id) internal view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Returns the owner of token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _ownerOf(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            INTERNAL 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 (uint224 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := shr(32, sload(keccak256(0x0c, 0x1c)))
        }
    }

    /// @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, uint224 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            let balanceSlot := keccak256(0x0c, 0x1c)
            let packed := sload(balanceSlot)
            sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed)))))
        }
    }

    /// @dev Returns the extra data for token `id`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non existent token.
    function _getExtraData(uint256 id) internal view virtual returns (uint96 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Sets the extra data for token `id` to `value`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non existent token.
    function _setExtraData(uint256 id, uint96 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let packed := sload(ownershipSlot)
            sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed)))))
        }
    }

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

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 id) internal virtual {
        _beforeTokenTransfer(address(0), to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            to := shr(96, shl(96, to))
            // Revert if `to` is the zero address.
            if iszero(to) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Revert if the token already exists.
            if shl(96, ownershipPacked) {
                mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`.
                revert(0x1c, 0x04)
            }
            // Update with the owner.
            sstore(ownershipSlot, or(ownershipPacked, to))
            // Increment the balance of the owner.
            {
                mstore(0x00, to)
                let balanceSlot := keccak256(0x0c, 0x1c)
                let balanceSlotPacked := add(sload(balanceSlot), 1)
                if iszero(and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE)) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(balanceSlot, balanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
        }
        _afterTokenTransfer(address(0), to, id);
    }

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

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
        _mint(to, id);
        if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data);
    }

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

    /// @dev Equivalent to `_burn(address(0), id)`.
    function _burn(uint256 id) internal virtual {
        _burn(address(0), id);
    }

    /// @dev Destroys token `id`, using `by`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _burn(address by, uint256 id) internal virtual {
        address owner = ownerOf(id);
        _beforeTokenTransfer(owner, address(0), id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Reload the owner in case it is changed in `_beforeTokenTransfer`.
            owner := shr(96, shl(96, ownershipPacked))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Load and check the token approval.
            {
                mstore(0x00, owner)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Clear the owner.
            sstore(ownershipSlot, xor(ownershipPacked, owner))
            // Decrement the balance of `owner`.
            {
                let balanceSlot := keccak256(0x0c, 0x1c)
                sstore(balanceSlot, sub(sload(balanceSlot), 1))
            }
            // Emit the {Transfer} event.
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id)
        }
        _afterTokenTransfer(owner, address(0), id);
    }

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

    /// @dev Returns whether `account` is the owner of token `id`, or is approved to managed it.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function _isApprovedOrOwner(address account, uint256 id)
        internal
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            // Clear the upper 96 bits.
            account := shr(96, shl(96, account))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := shr(96, shl(96, sload(ownershipSlot)))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Check if `account` is the `owner`.
            if iszero(eq(account, owner)) {
                mstore(0x00, owner)
                // Check if `account` is approved to
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    result := eq(account, sload(add(1, ownershipSlot)))
                }
            }
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _getApproved(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Equivalent to `_approve(address(0), account, id)`.
    function _approve(address account, uint256 id) internal virtual {
        _approve(address(0), account, id);
    }

    /// @dev Sets `account` as the approved account to manage token `id`, using `by`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - If `by` is not the zero address, `by` must be the owner
    ///   or an approved operator for the token owner.
    ///
    /// Emits a {Transfer} event.
    function _approve(address by, address account, uint256 id) internal virtual {
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            account := and(bitmaskAddress, account)
            by := and(bitmaskAddress, by)
            // Load the owner of the token.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := and(bitmaskAddress, sload(ownershipSlot))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // If `by` is not the zero address, do the authorization check.
            // Revert if `by` is not the owner, nor approved.
            if iszero(or(iszero(by), eq(by, owner))) {
                mstore(0x00, owner)
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Sets `account` as the approved account to manage `id`.
            sstore(add(1, ownershipSlot), account)
            // Emit the {Approval} event.
            log4(0x00, 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id)
        }
    }

    /// @dev Approve or remove the `operator` as an operator for `by`,
    /// without authorization checks.
    ///
    /// Emits a {ApprovalForAll} event.
    function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            operator := shr(96, shl(96, operator))
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`by`, `operator`).
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
            mstore(0x00, by)
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator)
        }
    }

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

    /// @dev Equivalent to `_transfer(address(0), from, to, id)`.
    function _transfer(address from, address to, uint256 id) internal virtual {
        _transfer(address(0), from, to, id);
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _transfer(address by, address from, address to, uint256 id) internal virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            by := and(bitmaskAddress, by)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if `from` is not the owner, or does not exist.
            if iszero(mul(owner, eq(owner, from))) {
                if iszero(owner) {
                    mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x00, 0xa1148100) // `TransferFromIncorrectOwner()`.
                revert(0x1c, 0x04)
            }
            // Revert if `to` is the zero address.
            if iszero(to) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                if iszero(and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE)) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `_safeTransfer(from, to, id, "")`.
    function _safeTransfer(address from, address to, uint256 id) internal virtual {
        _safeTransfer(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(address(0), from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`.
    function _safeTransfer(address by, address from, address to, uint256 id) internal virtual {
        _safeTransfer(by, from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(by, from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    HOOKS FOR OVERRIDING                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any token transfers, including minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /// @dev Hook that is called after any token transfers, including minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @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 Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)
        private
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            let onERC721ReceivedSelector := 0x150b7a02
            mstore(m, onERC721ReceivedSelector)
            mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            mstore(add(m, 0x60), id)
            mstore(add(m, 0x80), 0x80)
            let n := mload(data)
            mstore(add(m, 0xa0), n)
            if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) }
            // Revert if the call reverts.
            if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(0x00, 0x00, returndatasize())
                    revert(0x00, returndatasize())
                }
                mstore(m, 0)
            }
            // Load the returndata and compare it.
            if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
                mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

File 12 of 16 : CommitReveal.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

contract CommitReveal {
    error InvalidCommitment(uint256 committedTimestamp);

    uint256 private constant INVALID_COMMITMENT_SELECTOR = 0x31e63ea0;

    uint256 public immutable COMMITMENT_LIFESPAN;
    uint256 public immutable COMMITMENT_DELAY;

    constructor(uint256 commitmentLifespan, uint256 commitmentDelay) {
        COMMITMENT_LIFESPAN = commitmentLifespan;
        COMMITMENT_DELAY = commitmentDelay;
    }

    ///@dev mapping of user to key to commitment hash to timestamp.
    mapping(address user => mapping(bytes32 commitment => uint256 timestamp)) public commitments;

    /**
     * @notice Commit a hash to the contract, to be retrieved and verified after a delay. A commitment is valid only
     *         after COMMITMENT_DELAY seconds have passed, and is only valid for COMMITMENT_LIFESPAN seconds.
     * @param commitment The hash to commit.
     */
    function commit(bytes32 commitment) public {
        commitments[msg.sender][commitment] = block.timestamp;
    }

    /**
     * @dev Assert that a commitment has been made and is within a valid time
     *      window.
     * @param computedCommitmentHash The derived commitment hash to verify.
     */
    function _assertCommittedReveal(bytes32 computedCommitmentHash) internal view {
        // retrieve the timestamp of the commitment (if it exists)
        uint256 retrievedTimestamp = commitments[msg.sender][computedCommitmentHash];
        // compute the time difference
        uint256 timeDiff;
        // unchecked; assume blockchain time is monotonically increasing
        unchecked {
            timeDiff = block.timestamp - retrievedTimestamp;
        }
        uint256 commitmentLifespan = COMMITMENT_LIFESPAN;
        uint256 commitmentDelay = COMMITMENT_DELAY;
        assembly {
            // if the time difference is greater than the commitment lifespan,
            // the commitment has expired
            // if the time difference is less than the commitment delay, the
            // commitment is pending
            let invalidCommitment := or(gt(timeDiff, commitmentLifespan), lt(timeDiff, commitmentDelay))
            if invalidCommitment {
                mstore(0, INVALID_COMMITMENT_SELECTOR)
                mstore(0x20, retrievedTimestamp)
                revert(0x1c, 0x24)
            }
        }
    }
}

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

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover
/// may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

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

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

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

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

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

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

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

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

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

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

    /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
    /// It is intentionally choosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    function ownershipHandoverValidFor() public view virtual returns (uint64) {
        return 48 * 3600;
    }

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

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

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

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

    /// @dev Suggested gas stipend for contract receiving ETH
    /// that disallows any storage writes.
    uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    /// Multiply by a small constant (e.g. 2), if needed.
    uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000;

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

    /// @dev Sends `amount` (in wei) ETH to `to`.
    /// Reverts upon failure.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                if iszero(create(amount, 0x0b, 0x16)) {
                    // For better gas estimation.
                    if iszero(gt(gas(), 1000000)) { revert(0, 0) }
                }
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
    /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
    /// for 99% of cases and can be overriden with the three-argument version of this
    /// function if necessary.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        // Manually inlined because the compiler doesn't inline functions with branches.
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                if iszero(create(amount, 0x0b, 0x16)) {
                    // For better gas estimation.
                    if iszero(gt(gas(), 1000000)) { revert(0, 0) }
                }
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
    ///
    /// Note: Does NOT revert upon failure.
    /// Returns whether the transfer of ETH is successful instead.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            success := call(gasStipend, to, amount, 0, 0, 0, 0)
        }
    }

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

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x0c, 0x23b872dd000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            // Store the function selector of `balanceOf(address)`.
            mstore(0x0c, 0x70a08231000000000000000000000000)
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x00, 0x23b872dd)
            // The `amount` argument is already written to the memory word at 0x6c.
            amount := mload(0x60)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            // Store the function selector of `transfer(address,uint256)`.
            mstore(0x00, 0xa9059cbb000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x14, to) // Store the `to` argument.
            // The `amount` argument is already written to the memory word at 0x34.
            amount := mload(0x34)
            // Store the function selector of `transfer(address,uint256)`.
            mstore(0x00, 0xa9059cbb000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            // Store the function selector of `approve(address,uint256)`.
            mstore(0x00, 0x095ea7b3000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `ApproveFailed()`.
                mstore(0x00, 0x3e3f8f73)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            // Store the function selector of `balanceOf(address)`.
            mstore(0x00, 0x70a08231000000000000000000000000)
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

File 15 of 16 : IERC4906.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IERC165} from "forge-std/interfaces/IERC165.sol";

interface IERC4906 is IERC165 {
    /// @dev This event emits when the metadata of a token is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFT.
    event MetadataUpdate(uint256 _tokenId);

    /// @dev This event emits when the metadata of a range of tokens is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFTs.
    event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}

File 16 of 16 : IERC165.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

interface IERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    /// uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    /// `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

Settings
{
  "remappings": [
    "create2-helpers/=lib/create2-helpers/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc721a/=lib/erc721a/contracts/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/create2-helpers/lib/openzeppelin-contracts/",
    "solady-test/=lib/solady/test/",
    "solady/=lib/solady/src/",
    "solarray/=lib/solarray/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"address","name":"creatorPayout","type":"address"},{"internalType":"uint256","name":"maxBatchSize","type":"uint256"},{"internalType":"uint24[]","name":"preMintIds","type":"uint24[]"},{"internalType":"address","name":"seaDrop","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyFinalized","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"BatchBurnerNotApprovedForAll","type":"error"},{"inputs":[{"internalType":"uint256","name":"committedTimestamp","type":"uint256"}],"name":"InvalidCommitment","type":"error"},{"inputs":[],"name":"InvalidHex","type":"error"},{"inputs":[],"name":"InvalidPayment","type":"error"},{"inputs":[],"name":"InvalidTimestamp","type":"error"},{"inputs":[],"name":"MaxBatchSizeExceeded","type":"error"},{"inputs":[],"name":"MaximumSupplyExceeded","type":"error"},{"inputs":[],"name":"MintClosed","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NoIdsProvided","type":"error"},{"inputs":[],"name":"NoneAvailable","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"OnlyFinalized","type":"error"},{"inputs":[],"name":"OnlySeadrop","type":"error"},{"inputs":[],"name":"OnlyTokenOwner","type":"error"},{"inputs":[],"name":"OwnerMismatch","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Unavailable","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","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":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"COMMITMENT_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COMMITMENT_LIFESPAN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FINALIZE_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_MINT_CLOSE_TIMESTAMP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SPECIFIC_BATCH_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REROLL_AND_FINALIZE_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REROLL_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"bool","name":"onlyFinalized","type":"bool"}],"name":"batchBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"batchFinalize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"batchMintSpecific","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"batchReroll","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"oldIds","type":"uint256[]"},{"internalType":"uint256[]","name":"newIds","type":"uint256[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"batchRerollSpecific","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"oldIds","type":"uint256[]"},{"internalType":"uint256[]","name":"newIds","type":"uint256[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"batchRerollSpecificAndFinalize","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"xxyyzz","type":"uint256"},{"internalType":"bool","name":"onlyFinalized","type":"bool"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"commit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"commitments","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"computeBatchCommitment","outputs":[{"internalType":"bytes32","name":"commitmentHash","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"computeCommitment","outputs":[{"internalType":"bytes32","name":"committmentHash","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"configureSeaDrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"finalize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"finalizers","outputs":[{"internalType":"address","name":"finalizer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getMintStats","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"isFinalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mintSeaDrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"mintSpecific","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mintTo","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"numBurned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"oldId","type":"uint256"}],"name":"reroll","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"oldId","type":"uint256"},{"internalType":"uint256","name":"newId","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"rerollSpecific","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"oldId","type":"uint256"},{"internalType":"uint256","name":"newId","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"rerollSpecificAndFinalize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

61016060405234801562000011575f80fd5b50604051620042cd380380620042cd8339810160408190526200003491620002d4565b62015180608052603060a0528084868581818181620000538262000156565b60c05250620000664262127500620003e4565b60e05250506001600160a01b038085166101005242610140528316610120527fd7aca75208b9be5ffc04c6a01922020ffd62b55e68e502e317f5344960279af85f80a1505050505f5b82518110156200012857620000ec86848381518110620000d357620000d36200040a565b602002602001015162ffffff166200019160201b60201c565b6200011f8382815181106200010557620001056200040a565b602002602001015162ffffff16876200023b60201b60201c565b600101620000af565b505051600280546001600160801b031663ffffffff909216600160801b02919091179055506200041e915050565b6001600160a01b0316638b78c6d819819055805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6001600160a01b039091169081620001b05763ea553b345f526004601cfd5b805f52673ec412a9852d173d60c11b601c5260205f208101810180548060601b15620001e35763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff81166200020e576301336cea5f526004601cfd5b905580825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a45050565b5f828152600160208181526040832080546001600160a01b0386166001600160a01b0319909116179055673ec412a9852d173d60c11b601c5290912083018301805460a081811c90931890921b90911890555050565b80516001600160a01b0381168114620002a8575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b805162ffffff81168114620002a8575f80fd5b5f805f805f60a08688031215620002e9575f80fd5b620002f48662000291565b945060206200030581880162000291565b6040880151606089015191965094506001600160401b038082111562000329575f80fd5b818901915089601f8301126200033d575f80fd5b815181811115620003525762000352620002ad565b8060051b604051601f19603f830116810181811085821117156200037a576200037a620002ad565b60405291825284820192508381018501918c83111562000398575f80fd5b938501935b82851015620003c157620003b185620002c1565b845293850193928501926200039d565b809750505050505050620003d86080870162000291565b90509295509295909350565b808201808211156200040457634e487b7160e01b5f52601160045260245ffd5b92915050565b634e487b7160e01b5f52603260045260245ffd5b60805160a05160c05160e051610100516101205161014051613e25620004a85f395f61133901525f6114b501525f818161126e015261140c01525f81816106f40152818161136301528181611f7d015261228f01525f818161047c01528181611cd401526122e901525f81816105160152611d7501525f81816104bd0152611d540152613e255ff3fe60806040526004361061037f575f3560e01c8063715018a6116101d3578063c002d23d116100fd578063dd686e261161009d578063f04e283e1161006d578063f04e283e14610a2c578063f14fcbc814610a3f578063f2fde38b14610a74578063fee81cf414610a87575f80fd5b8063dd686e2614610905578063e8a3d485146109d1578063e985e9c5146109e5578063ea08b46514610a19575f80fd5b8063d52079b4116100d8578063d52079b414610951578063d5abeb011461098b578063d7533f02146109a1578063d8176369146109be575f80fd5b8063c002d23d14610905578063c87b56dd1461091f578063ccedf5721461093e575f80fd5b80639fac68cb11610173578063ad7556d711610143578063ad7556d7146108ad578063afb34abe146108cc578063b88d4fde146108df578063b8a49eba146108f2575f80fd5b80639fac68cb14610849578063a0712d6814610868578063a22cb4651461087b578063ad3554661461089a575f80fd5b80638b1592b5116101ae5780638b1592b5146107ca5780638da5cb5b146107fd57806395d89b41146103f35780639f51758e14610830575f80fd5b8063715018a6146107165780637e92a4231461071e578063840e15d41461075f575f80fd5b806323b872dd116102b45780634e9edf1c1161025457806364869dad1161022457806364869dad146106915780636cd473cb146106b057806370a08231146106c45780637112ca4f146106e3575f80fd5b80634e9edf1c1461063d57806354d1f13d1461065757806360abc14b1461065f5780636352211e14610672575f80fd5b806333727c4d1161028f57806333727c4d146105d75780633ccfd60b146105f657806342842e0e1461060a578063449a52f81461061d575f80fd5b806323b872dd14610586578063256929621461059957806327e99a5c146105a1575f80fd5b80630affb8341161031f5780631249c58b116102fa5780631249c58b1461053857806318160ddd146105405780631c19c215146105545780631ced131814610567575f80fd5b80630affb834146104ac57806311a040ac146104df57806311a733f614610505575f80fd5b806306fdde031161035a57806306fdde03146103f3578063081812fc14610414578063095ea7b3146104585780630a157d9e1461046b575f80fd5b806301ffc9a71461038a57806305261aea146103be57806306740ff4146103d3575f80fd5b3661038657005b5f80fd5b348015610395575f80fd5b506103a96103a436600461316a565b610ab8565b60405190151581526020015b60405180910390f35b6103d16103cc3660046131a9565b610af5565b005b6103e66103e1366004613208565b610b13565b6040516103b59190613276565b3480156103fe575f80fd5b50610407610b57565b6040516103b591906132dd565b34801561041f575f80fd5b5061043361042e3660046131a9565b610b6e565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103b5565b6103d1610466366004613350565b610bd1565b348015610476575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016103b5565b3480156104b7575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104ea575f80fd5b506002546fffffffffffffffffffffffffffffffff1661049e565b348015610510575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000081565b61049e610be0565b34801561054b575f80fd5b5061049e610c04565b61049e6105623660046131a9565b610c53565b348015610572575f80fd5b506103d1610581366004613387565b610c6f565b6103d16105943660046133d7565b610f4b565b6103d1611098565b3480156105ac575f80fd5b5061049e6105bb366004613410565b60189290921b62ffffff91909116175f52602052604060092090565b3480156105e2575f80fd5b506103a96105f13660046131a9565b6110e5565b348015610601575f80fd5b506103d161112c565b6103d16106183660046133d7565b611149565b61063061062b366004613350565b611175565b6040516103b59190613440565b348015610648575f80fd5b5061049e6612a6d8e112200081565b6103d1611188565b6103e661066d366004613477565b6111c1565b34801561067d575f80fd5b5061043361068c3660046131a9565b6111f8565b34801561069c575f80fd5b506103d16106ab366004613350565b611256565b3480156106bb575f80fd5b506103d1611314565b3480156106cf575f80fd5b5061049e6106de3660046134bf565b6116d6565b3480156106ee575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000081565b6103d1611723565b348015610729575f80fd5b506104336107383660046131a9565b60016020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b34801561076a575f80fd5b506107af6107793660046134bf565b506002545f917001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff1690630100000090565b604080519384526020840192909252908201526060016103b5565b3480156107d5575f80fd5b5061049e6107e4366004613350565b5f60208181529281526040808220909352908152205481565b348015610808575f80fd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754610433565b34801561083b575f80fd5b5061049e65e35fa931a00081565b348015610854575f80fd5b506103d16108633660046134d8565b611736565b6106306108763660046131a9565b6117fc565b348015610886575f80fd5b506103d1610895366004613502565b611808565b6103d16108a836600461352a565b61185b565b3480156108b8575f80fd5b5061049e6108c7366004613569565b61189f565b6103d16108da3660046135bf565b6118cd565b6103d16108ed3660046135df565b6118e2565b61063061090036600461352a565b611935565b348015610910575f80fd5b5061049e6611c37937e0800081565b34801561092a575f80fd5b506104076109393660046131a9565b6119ec565b6103d161094c366004613672565b611a2e565b34801561095c575f80fd5b5060025470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661049e565b348015610996575f80fd5b50630100000061049e565b3480156109ac575f80fd5b506040516202a30081526020016103b5565b6103d16109cc366004613672565b611a4a565b3480156109dc575f80fd5b50610407611ae0565b3480156109f0575f80fd5b506103a96109ff36600461369b565b601c52670a5a2e7a000000006008525f526030600c205490565b6103e6610a27366004613208565b611b11565b6103d1610a3a3660046134bf565b611b4b565b348015610a4a575f80fd5b506103d1610a593660046131a9565b335f9081526020818152604080832093835292905220429055565b6103d1610a823660046134bf565b611b85565b348015610a92575f80fd5b5061049e610aa13660046134bf565b63389a75e1600c9081525f91909152602090205490565b5f610aef826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f8214176349064906821417631890fe8e9091141790565b92915050565b610b076611c37937e080006001611bab565b610b1081611be6565b50565b6060610b288686868665e35fa931a000611c99565b5f610b353386868661189f565b9050610b4081611d36565b610b4c87878787611db2565b979650505050505050565b6060602080526606585859595a5a60465260806020f35b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020812082018201805473ffffffffffffffffffffffffffffffffffffffff16610bc75763ceea21b65f526004601cfd5b6001015492915050565b610bdc338383611ecb565b5050565b5f80610bec6001611f7a565b90505f610bf882612023565b9050610aef3382612079565b6002545f90610c3c906fffffffffffffffffffffffffffffffff808216917001000000000000000000000000000000009004166136c3565b6fffffffffffffffffffffffffffffffff16905090565b5f610c6565e35fa931a0006001611bab565b610aef8233612142565b5f829003610ca9576040517fe663430d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610cf984845f818110610cbf57610cbf613718565b905060200201355f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015490565b9050808215610d505773ffffffffffffffffffffffffffffffffffffffff821015610d50576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163314610dc257610d8c8133601c52670a5a2e7a000000006008525f526030600c205490565b610dc2576040517fcbd6109100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546fffffffffffffffffffffffffffffffff80821663ffffffff881601167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116179055610e2985855f81610e1d57610e1d613718565b9050602002013561216b565b60015b84811015610f43575f868683818110610e4757610e47613718565b905060200201359050610e87815f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015490565b93508373ffffffffffffffffffffffffffffffffffffffff80821690851614610edc576040517fa8c8162300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8515610f305773ffffffffffffffffffffffffffffffffffffffff851015610f30576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f398261216b565b5050600101610e2c565b505050505050565b5f8181527f7d8825530a5a2e7a0000000000000000000000000000000000000000000000003317601c526020902081018101805473ffffffffffffffffffffffffffffffffffffffff9485169493841693811691908286148302610fc85782610fbb5763ceea21b65f526004601cfd5b63a11481005f526004601cfd5b84610fda5763ea553b345f526004601cfd5b855f528160010154925082331486331417611006576030600c205461100657634b6e7f185f526004601cfd5b8215611013575f82600101555b85851818905550601c600c81812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190555f84905220805460010163ffffffff8116611069576301336cea5f526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a45b505050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f6110ef82612175565b610aef825f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b6111346121b2565b5f8081828347335af1610b10573d81823e3d81fd5b611154838383610f4b565b813b156110935761109383838360405180602001604052805f8152506121e7565b6060611181838361226c565b9392505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b60606111cd848461228d565b5f6111da3386868661189f565b90506111e581611d36565b6111ef8585612352565b95945050505050565b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020810181015473ffffffffffffffffffffffffffffffffffffffff16806112515763ceea21b65f526004601cfd5b919050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146112c5576040517fa8eb05d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546fffffffffffffffffffffffffffffffff80821670010000000000000000000000000000000092839004821685019182169283021790925561130e908490849061244b565b50505050565b61131c6121b2565b6040805160c0810182526611c37937e08000815265ffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116602083019081527f0000000000000000000000000000000000000000000000000000000000000000821683850190815261ffff606085018181526103e860808701908152600160a0880190815297517f01308e65000000000000000000000000000000000000000000000000000000008152965169ffffffffffffffffffff166004880152935185166024870152915190931660448501525182166064840152511660848201529051151560a48201527f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff8216906301308e659060c4015f604051808303815f87803b158015611464575f80fd5b505af1158015611476573d5f803e3d5ffd5b50506040517f12738db800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166004830152841692506312738db891506024015f604051808303815f87803b158015611500575f80fd5b505af1158015611512573d5f803e3d5ffd5b50506040517f8e7d1e4300000000000000000000000000000000000000000000000000000000815271a26b00c1f0df003000390027140000faa71960048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250638e7d1e4391506044015f604051808303815f87803b158015611593575f80fd5b505af11580156115a5573d5f803e3d5ffd5b50506040517f7f2a5cca00000000000000000000000000000000000000000000000000000000815273f408bee3443d0397e2c1cde588fb060ac657006f60048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250637f2a5cca91506044015f604051808303815f87803b158015611628575f80fd5b505af115801561163a573d5f803e3d5ffd5b50506040517f7f2a5cca00000000000000000000000000000000000000000000000000000000815273e3d3d0ed702504e19825f44bc6542ff2ec45cb9a60048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250637f2a5cca91506044015f604051808303815f87803b1580156116bd575f80fd5b505af11580156116cf573d5f803e3d5ffd5b5050505050565b5f816116e957638f4eb6045f526004601cfd5b7f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52815f5263ffffffff601c600c2054169050919050565b61172b6121b2565b6117345f6124de565b565b600280546fffffffffffffffffffffffffffffffff808216600101167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617905580156117f2576117bc825f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b6117f2576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bdc3383612543565b6060610aef338361226c565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b61186c6611c37937e0800082611bab565b5f5b818110156110935761189783838381811061188b5761188b613718565b90506020020135611be6565b60010161186e565b5f6040518360051b8086833781205f969096526020959095525060409081526060600c209390525090919050565b6118d76001611f7a565b50610bdc828261264a565b6118ed858585610f4b565b833b156116cf576116cf85858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506121e792505050565b606061194765e35fa931a00083611bab565b335f8367ffffffffffffffff81111561196257611962613745565b60405190808252806020026020018201604052801561198b578160200160208202803683370190505b5090505f5b848110156119e3576119ba8686838181106119ad576119ad613718565b9050602002013584612142565b8282815181106119cc576119cc613718565b602090810291909101015260019283019201611990565b50949350505050565b60606119f782612175565b611a08611a03836126f1565b6128a2565b604051602001611a189190613772565b6040516020818303038152906040529050919050565b611a3f65e35fa931a0006001611bab565b6110938383836128af565b611a5c6612a6d8e11220006001611bab565b611a678383836128af565b5f828152600160208181526040832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5290912083018301805460a081811c90931890921b9091189055505050565b6060611aed611a036128cb565b604051602001611afd9190613772565b604051602081830303815290604052905090565b6060611b27868686866612a6d8e1122000611c99565b5f611b343386868661189f565b9050611b3f81611d36565b610b4c878787876128eb565b611b536121b2565b63389a75e1600c52805f526020600c208054421115611b7957636f5e88185f526004601cfd5b5f9055610b10816124de565b611b8d6121b2565b8060601b611ba257637448fbae5f526004601cfd5b610b10816124de565b8082023414610bdc576040517f3c6b4b2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bef81612a6a565b5f818152600160208181526040832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5290912082018201805460a081811c90931890921b90911890556040518181527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79060200160405180910390a150565b838214611cd2576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000841115611d2c576040517f7a7e96df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116cf8482611bab565b335f90815260208181526040808320848452909152902054428190037f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000818311818410178015610f43576331e63ea05f52846020526024601cfd5b60605f8467ffffffffffffffff811115611dce57611dce613745565b604051908082528060200260200182016040528015611df7578160200160208202803683370190505b5090505f805b86811015611e7757611e3f888883818110611e1a57611e1a613718565b90506020020135878784818110611e3357611e33613718565b90506020020135612b39565b15611e6f576001838281518110611e5857611e58613718565b911515602092830291909101909101526001909101905b600101611dfd565b50805f03611eb1576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec165e35fa931a00082612b6a565b5095945050505050565b5f1960601c82811692508381169350815f52837f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f208201820180548216915081611f235763ceea21b65f526004601cfd5b818514851517611f4757815f526030600c2054611f4757634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a450505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000421115611fd5576040517f589ed34b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fe66611c37937e0800083611bab565b50600280546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080830482169094018116938402911617905590565b5f818152446020526040600c2062ffffff165b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020810181015415610aef5760010162ffffff16612036565b73ffffffffffffffffffffffffffffffffffffffff90911690816120a45763ea553b345f526004601cfd5b805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f208101810180548060601b156120eb5763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff8116612115576301336cea5f526004601cfd5b905580825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a45050565b5f61214c83612a6a565b6121558361216b565b5f61215f83612023565b90506111813382612079565b610b105f82612543565b62ffffff811115610b10576040517fcbbc48a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314611734576382b429005f526004601cfd5b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a0840152801561222e578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1612253573d1561224f573d5f803e3d5ffd5b5f83525b508060e01b825114610f435763d1a57ed65f526004601cfd5b60605f61227883611f7a565b905061228584848361244b565b949350505050565b7f00000000000000000000000000000000000000000000000000000000000000004211156122e7576040517f589ed34b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000811115612341576040517f7a7e96df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bdc816611c37937e08000611bab565b60605f8267ffffffffffffffff81111561236e5761236e613745565b604051908082528060200260200182016040528015612397578160200160208202803683370190505b5090505f805b848110156123fe576123c68686838181106123ba576123ba613718565b90506020020135612b81565b156123f65760018382815181106123df576123df613718565b911515602092830291909101909101526001909101905b60010161239d565b50805f03612438576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61244181612bda565b5090949350505050565b60605f8367ffffffffffffffff81111561246757612467613745565b604051908082528060200260200182016040528015612490578160200160208202803683370190505b5090505f5b848110156119e3575f6124a785612023565b90506124b38782612079565b808383815181106124c6576124c6613718565b60209081029190910101525060019384019301612495565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927805473ffffffffffffffffffffffffffffffffffffffff9092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b5f61254d826111f8565b9050505f81815273ffffffffffffffffffffffffffffffffffffffff9283167f7d8825530a5a2e7a0000000000000000000000000000000000000000000000008117601c5260209091208201820180549193821691826125b45763ceea21b65f526004601cfd5b825f528160010154808614848714178615176125e1576030600c20546125e157634b6e7f185f526004601cfd5b80156125ee575f83600101555b5082189055601c600c2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055815f827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4505050565b3360181b62ffffff8316175f526020819052604060092061266a83612175565b61267381611d36565b6126b0835f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b156126e7576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110933384612079565b606061273a6040518060400160405280600481526020017f6e616d650000000000000000000000000000000000000000000000000000000081525061273584612c2d565b612c4a565b6127ae6040518060400160405280600d81526020017f65787465726e616c5f6c696e6b000000000000000000000000000000000000008152506040518060400160405280601281526020017f68747470733a2f2f787879797a7a2e6172740000000000000000000000000000815250612c4a565b6128056040518060400160405280600b81526020017f6465736372697074696f6e0000000000000000000000000000000000000000008152506040518060a0016040528060648152602001613d4760649139612c4a565b6128476040518060400160405280600581526020017f696d61676500000000000000000000000000000000000000000000000000000081525061273587612c76565b61288e6040518060400160405280600a81526020017f617474726962757465730000000000000000000000000000000000000000000081525061288988612c94565b612e99565b604051602001611a189594939291906137b6565b6060610aef825f80612eae565b6128b883612a6a565b6128c18361216b565b611093828261264a565b60606040518060a00160405280607a8152602001613dab607a9139905090565b60605f8467ffffffffffffffff81111561290757612907613745565b604051908082528060200260200182016040528015612930578160200160208202803683370190505b5090505f805b86811015612a1f57612953888883818110611e1a57611e1a613718565b15612a17576129ec86868381811061296d5761296d613718565b602090810292909201355f81815260018085526040822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52939020810101805460a081811c90941890931b90921890915550565b6001838281518110612a0057612a00613718565b911515602092830291909101909101526001909101905b600101612936565b50805f03612a59576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec16612a6d8e112200082612b6a565b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020902081018101548073ffffffffffffffffffffffffffffffffffffffff811115612aea576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163314611093576040517fcdf1f8f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612b4383612a6a565b612b4c82612b81565b15612b6257612b5a8361216b565b506001610aef565b505f92915050565b818102348181039114611093576110933382612fae565b5f612b8b82612175565b5f8281527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020820182015415612bc857505f919050565b612bd23383612079565b506001919050565b6002805463ffffffff6fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080840482168601928316029216919091179091555f90610aef6611c37937e0800084612b6a565b6060612c3a826003612fc7565b604051602001611a1891906138bc565b60608282604051602001612c5f929190613900565b604051602081830303815290604052905092915050565b6060612c84611a038361306b565b604051602001611a189190613989565b60605f612cde6040518060400160405280600581526020017f436f6c6f72000000000000000000000000000000000000000000000000000000815250612cd985612c2d565b613088565b9050612ce9836110e5565b15612dfc575f612d636040518060400160405280600981526020017f46696e616c697a656400000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f5965730000000000000000000000000000000000000000000000000000000000815250613088565b604080518082018252600981527f46696e616c697a657200000000000000000000000000000000000000000000006020808301919091525f8881526001909152919091205491925083918391612dd291612cd99073ffffffffffffffffffffffffffffffffffffffff1661309d565b604051602001612de4939291906139cd565b60405160208183030381529060405292505050919050565b80612e716040518060400160405280600981526020017f46696e616c697a656400000000000000000000000000000000000000000000008152506040518060400160405280600281526020017f4e6f000000000000000000000000000000000000000000000000000000000000815250613088565b604051602001612e82929190613a95565b604051602081830303815290604052915050919050565b60608282604051602001612c5f929190613b3f565b606083518015612fa6576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526102308515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f03603f52602083018181015b6003880197508751603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f518252600482019150808210612f1e576020016040527f3d3d00000000000000000000000000000000000000000000000000000000000060038406600204808303919091525f8615159091029182900352900382525b509392505050565b5f805f8084865af1610bdc5763b12d13eb5f526004601cfd5b6060601f1960428360011b0116604051019050602081016040525f8152806f30313233343536373839616263646566600f528283018203600119855b600f811651948201946001860153600f8160041c1651855360081c84830361300357801561303857632194895a5f526004601cfd5b5050508190037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090910190815292915050565b6060613078826003612fc7565b604051602001611a189190613bbf565b60608282604051602001612c5f929190613c9c565b60606130a8826130df565b805161307882526002017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90910190815292915050565b60606040519050608081016040526f30313233343536373839616263646566600f5260028101905060288152602081015f60288201528260601b92505f5b808101820184821a600f81165160018301538060041c518253505060018101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed0161311d575050919050565b5f6020828403121561317a575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611181575f80fd5b5f602082840312156131b9575f80fd5b5035919050565b5f8083601f8401126131d0575f80fd5b50813567ffffffffffffffff8111156131e7575f80fd5b6020830191508360208260051b8501011115613201575f80fd5b9250929050565b5f805f805f6060868803121561321c575f80fd5b853567ffffffffffffffff80821115613233575f80fd5b61323f89838a016131c0565b90975095506020880135915080821115613257575f80fd5b50613264888289016131c0565b96999598509660400135949350505050565b602080825282518282018190525f9190848201906040850190845b818110156132af578351151583529284019291840191600101613291565b50909695505050505050565b5f5b838110156132d55781810151838201526020016132bd565b50505f910152565b602081525f82518060208401526132fb8160408501602087016132bb565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611251575f80fd5b5f8060408385031215613361575f80fd5b61336a8361332d565b946020939093013593505050565b80358015158114611251575f80fd5b5f805f60408486031215613399575f80fd5b833567ffffffffffffffff8111156133af575f80fd5b6133bb868287016131c0565b90945092506133ce905060208501613378565b90509250925092565b5f805f606084860312156133e9575f80fd5b6133f28461332d565b92506134006020850161332d565b9150604084013590509250925092565b5f805f60608486031215613422575f80fd5b61342b8461332d565b95602085013595506040909401359392505050565b602080825282518282018190525f9190848201906040850190845b818110156132af5783518352928401929184019160010161345b565b5f805f60408486031215613489575f80fd5b833567ffffffffffffffff81111561349f575f80fd5b6134ab868287016131c0565b909790965060209590950135949350505050565b5f602082840312156134cf575f80fd5b6111818261332d565b5f80604083850312156134e9575f80fd5b823591506134f960208401613378565b90509250929050565b5f8060408385031215613513575f80fd5b61351c8361332d565b91506134f960208401613378565b5f806020838503121561353b575f80fd5b823567ffffffffffffffff811115613551575f80fd5b61355d858286016131c0565b90969095509350505050565b5f805f806060858703121561357c575f80fd5b6135858561332d565b9350602085013567ffffffffffffffff8111156135a0575f80fd5b6135ac878288016131c0565b9598909750949560400135949350505050565b5f80604083850312156135d0575f80fd5b50508035926020909101359150565b5f805f805f608086880312156135f3575f80fd5b6135fc8661332d565b945061360a6020870161332d565b935060408601359250606086013567ffffffffffffffff8082111561362d575f80fd5b818801915088601f830112613640575f80fd5b81358181111561364e575f80fd5b89602082850101111561365f575f80fd5b9699959850939650602001949392505050565b5f805f60608486031215613684575f80fd5b505081359360208301359350604090920135919050565b5f80604083850312156136ac575f80fd5b6136b58361332d565b91506134f96020840161332d565b6fffffffffffffffffffffffffffffffff828116828216039080821115613711577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f82516137a981601d8501602087016132bb565b91909101601d0192915050565b7f7b0000000000000000000000000000000000000000000000000000000000000081525f86516137ed816001850160208b016132bb565b80830190507f2c00000000000000000000000000000000000000000000000000000000000000806001830152875161382c816002850160208c016132bb565b600292019182018190528651613849816003850160208b016132bb565b600392019182018190528551613866816004850160208a016132bb565b600492019182015283516138818160058401602088016132bb565b016138ae600582017f7d000000000000000000000000000000000000000000000000000000000000009052565b600601979650505050505050565b7f230000000000000000000000000000000000000000000000000000000000000081525f82516138f38160018501602087016132bb565b9190910160010192915050565b5f7f220000000000000000000000000000000000000000000000000000000000000080835284516139388160018601602089016132bb565b7f223a22000000000000000000000000000000000000000000000000000000000060019185019182015284516139758160048401602089016132bb565b016004810191909152600501949350505050565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c00000000000081525f82516139c081601a8501602087016132bb565b91909101601a0192915050565b7f5b0000000000000000000000000000000000000000000000000000000000000081525f8451613a048160018501602089016132bb565b80830190507f2c000000000000000000000000000000000000000000000000000000000000008060018301528551613a43816002850160208a016132bb565b60029201918201528351613a5e8160038401602088016132bb565b7f5d000000000000000000000000000000000000000000000000000000000000006003929091019182015260040195945050505050565b7f5b0000000000000000000000000000000000000000000000000000000000000081525f8351613acc8160018501602088016132bb565b7f2c000000000000000000000000000000000000000000000000000000000000006001918401918201528351613b098160028401602088016132bb565b7f5d0000000000000000000000000000000000000000000000000000000000000060029290910191820152600301949350505050565b7f220000000000000000000000000000000000000000000000000000000000000081525f8351613b768160018501602088016132bb565b7f223a0000000000000000000000000000000000000000000000000000000000006001918401918201528351613bb38160038401602088016132bb565b01600301949350505050565b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d2236393022206865696768743d223639302260208201527f3e3c726563742077696474683d2236393022206865696768743d22363930222060408201527f66696c6c3d22230000000000000000000000000000000000000000000000000060608201525f8251613c688160678501602087016132bb565b7f22202f3e3c2f7376673e000000000000000000000000000000000000000000006067939091019283015250607101919050565b7f7b2274726169745f74797065223a22000000000000000000000000000000000081525f8351613cd381600f8501602088016132bb565b7f222c2276616c7565223a22000000000000000000000000000000000000000000600f918401918201528351613d1081601a8401602088016132bb565b7f227d000000000000000000000000000000000000000000000000000000000000601a9290910191820152601c0194935050505056fe50726f6f66206f6620636f6c6f722e20585859595a5a206973206120636f6c6c656374696f6e206f662066756c6c79206f6e636861696e2c20756e697175652c20636f6d706f7361626c652c20616e6420636f6c6c65637461626c6520636f6c6f72732e7b226e616d65223a22585859595a5a222c226465736372697074696f6e223a22436f6c6c65637469626c652c20636f6d706f7361626c652c20616e6420756e69717565206f6e636861696e20636f6c6f72732e222c2265787465726e616c5f6c696e6b223a2268747470733a2f2f787879797a7a2e617274227d0000000000000000000000007039813983ba47a5a46b19f3a269aef5c6c75abf0000000000000000000000007ade04fa0cbe2167e8bff758f48879bd0c6fff92000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf5000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000006969690000000000000000000000000000000000000000000000000000000000ff6000000000000000000000000000000000000000000000000000000000000000ff00

Deployed Bytecode

0x60806040526004361061037f575f3560e01c8063715018a6116101d3578063c002d23d116100fd578063dd686e261161009d578063f04e283e1161006d578063f04e283e14610a2c578063f14fcbc814610a3f578063f2fde38b14610a74578063fee81cf414610a87575f80fd5b8063dd686e2614610905578063e8a3d485146109d1578063e985e9c5146109e5578063ea08b46514610a19575f80fd5b8063d52079b4116100d8578063d52079b414610951578063d5abeb011461098b578063d7533f02146109a1578063d8176369146109be575f80fd5b8063c002d23d14610905578063c87b56dd1461091f578063ccedf5721461093e575f80fd5b80639fac68cb11610173578063ad7556d711610143578063ad7556d7146108ad578063afb34abe146108cc578063b88d4fde146108df578063b8a49eba146108f2575f80fd5b80639fac68cb14610849578063a0712d6814610868578063a22cb4651461087b578063ad3554661461089a575f80fd5b80638b1592b5116101ae5780638b1592b5146107ca5780638da5cb5b146107fd57806395d89b41146103f35780639f51758e14610830575f80fd5b8063715018a6146107165780637e92a4231461071e578063840e15d41461075f575f80fd5b806323b872dd116102b45780634e9edf1c1161025457806364869dad1161022457806364869dad146106915780636cd473cb146106b057806370a08231146106c45780637112ca4f146106e3575f80fd5b80634e9edf1c1461063d57806354d1f13d1461065757806360abc14b1461065f5780636352211e14610672575f80fd5b806333727c4d1161028f57806333727c4d146105d75780633ccfd60b146105f657806342842e0e1461060a578063449a52f81461061d575f80fd5b806323b872dd14610586578063256929621461059957806327e99a5c146105a1575f80fd5b80630affb8341161031f5780631249c58b116102fa5780631249c58b1461053857806318160ddd146105405780631c19c215146105545780631ced131814610567575f80fd5b80630affb834146104ac57806311a040ac146104df57806311a733f614610505575f80fd5b806306fdde031161035a57806306fdde03146103f3578063081812fc14610414578063095ea7b3146104585780630a157d9e1461046b575f80fd5b806301ffc9a71461038a57806305261aea146103be57806306740ff4146103d3575f80fd5b3661038657005b5f80fd5b348015610395575f80fd5b506103a96103a436600461316a565b610ab8565b60405190151581526020015b60405180910390f35b6103d16103cc3660046131a9565b610af5565b005b6103e66103e1366004613208565b610b13565b6040516103b59190613276565b3480156103fe575f80fd5b50610407610b57565b6040516103b591906132dd565b34801561041f575f80fd5b5061043361042e3660046131a9565b610b6e565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103b5565b6103d1610466366004613350565b610bd1565b348015610476575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000000581565b6040519081526020016103b5565b3480156104b7575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000001518081565b3480156104ea575f80fd5b506002546fffffffffffffffffffffffffffffffff1661049e565b348015610510575f80fd5b5061049e7f000000000000000000000000000000000000000000000000000000000000003081565b61049e610be0565b34801561054b575f80fd5b5061049e610c04565b61049e6105623660046131a9565b610c53565b348015610572575f80fd5b506103d1610581366004613387565b610c6f565b6103d16105943660046133d7565b610f4b565b6103d1611098565b3480156105ac575f80fd5b5061049e6105bb366004613410565b60189290921b62ffffff91909116175f52602052604060092090565b3480156105e2575f80fd5b506103a96105f13660046131a9565b6110e5565b348015610601575f80fd5b506103d161112c565b6103d16106183660046133d7565b611149565b61063061062b366004613350565b611175565b6040516103b59190613440565b348015610648575f80fd5b5061049e6612a6d8e112200081565b6103d1611188565b6103e661066d366004613477565b6111c1565b34801561067d575f80fd5b5061043361068c3660046131a9565b6111f8565b34801561069c575f80fd5b506103d16106ab366004613350565b611256565b3480156106bb575f80fd5b506103d1611314565b3480156106cf575f80fd5b5061049e6106de3660046134bf565b6116d6565b3480156106ee575f80fd5b5061049e7f00000000000000000000000000000000000000000000000000000000649dd9ef81565b6103d1611723565b348015610729575f80fd5b506104336107383660046131a9565b60016020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b34801561076a575f80fd5b506107af6107793660046134bf565b506002545f917001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff1690630100000090565b604080519384526020840192909252908201526060016103b5565b3480156107d5575f80fd5b5061049e6107e4366004613350565b5f60208181529281526040808220909352908152205481565b348015610808575f80fd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754610433565b34801561083b575f80fd5b5061049e65e35fa931a00081565b348015610854575f80fd5b506103d16108633660046134d8565b611736565b6106306108763660046131a9565b6117fc565b348015610886575f80fd5b506103d1610895366004613502565b611808565b6103d16108a836600461352a565b61185b565b3480156108b8575f80fd5b5061049e6108c7366004613569565b61189f565b6103d16108da3660046135bf565b6118cd565b6103d16108ed3660046135df565b6118e2565b61063061090036600461352a565b611935565b348015610910575f80fd5b5061049e6611c37937e0800081565b34801561092a575f80fd5b506104076109393660046131a9565b6119ec565b6103d161094c366004613672565b611a2e565b34801561095c575f80fd5b5060025470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661049e565b348015610996575f80fd5b50630100000061049e565b3480156109ac575f80fd5b506040516202a30081526020016103b5565b6103d16109cc366004613672565b611a4a565b3480156109dc575f80fd5b50610407611ae0565b3480156109f0575f80fd5b506103a96109ff36600461369b565b601c52670a5a2e7a000000006008525f526030600c205490565b6103e6610a27366004613208565b611b11565b6103d1610a3a3660046134bf565b611b4b565b348015610a4a575f80fd5b506103d1610a593660046131a9565b335f9081526020818152604080832093835292905220429055565b6103d1610a823660046134bf565b611b85565b348015610a92575f80fd5b5061049e610aa13660046134bf565b63389a75e1600c9081525f91909152602090205490565b5f610aef826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f8214176349064906821417631890fe8e9091141790565b92915050565b610b076611c37937e080006001611bab565b610b1081611be6565b50565b6060610b288686868665e35fa931a000611c99565b5f610b353386868661189f565b9050610b4081611d36565b610b4c87878787611db2565b979650505050505050565b6060602080526606585859595a5a60465260806020f35b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020812082018201805473ffffffffffffffffffffffffffffffffffffffff16610bc75763ceea21b65f526004601cfd5b6001015492915050565b610bdc338383611ecb565b5050565b5f80610bec6001611f7a565b90505f610bf882612023565b9050610aef3382612079565b6002545f90610c3c906fffffffffffffffffffffffffffffffff808216917001000000000000000000000000000000009004166136c3565b6fffffffffffffffffffffffffffffffff16905090565b5f610c6565e35fa931a0006001611bab565b610aef8233612142565b5f829003610ca9576040517fe663430d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610cf984845f818110610cbf57610cbf613718565b905060200201355f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015490565b9050808215610d505773ffffffffffffffffffffffffffffffffffffffff821015610d50576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163314610dc257610d8c8133601c52670a5a2e7a000000006008525f526030600c205490565b610dc2576040517fcbd6109100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546fffffffffffffffffffffffffffffffff80821663ffffffff881601167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116179055610e2985855f81610e1d57610e1d613718565b9050602002013561216b565b60015b84811015610f43575f868683818110610e4757610e47613718565b905060200201359050610e87815f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015490565b93508373ffffffffffffffffffffffffffffffffffffffff80821690851614610edc576040517fa8c8162300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8515610f305773ffffffffffffffffffffffffffffffffffffffff851015610f30576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f398261216b565b5050600101610e2c565b505050505050565b5f8181527f7d8825530a5a2e7a0000000000000000000000000000000000000000000000003317601c526020902081018101805473ffffffffffffffffffffffffffffffffffffffff9485169493841693811691908286148302610fc85782610fbb5763ceea21b65f526004601cfd5b63a11481005f526004601cfd5b84610fda5763ea553b345f526004601cfd5b855f528160010154925082331486331417611006576030600c205461100657634b6e7f185f526004601cfd5b8215611013575f82600101555b85851818905550601c600c81812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190555f84905220805460010163ffffffff8116611069576301336cea5f526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a45b505050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f6110ef82612175565b610aef825f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b6111346121b2565b5f8081828347335af1610b10573d81823e3d81fd5b611154838383610f4b565b813b156110935761109383838360405180602001604052805f8152506121e7565b6060611181838361226c565b9392505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b60606111cd848461228d565b5f6111da3386868661189f565b90506111e581611d36565b6111ef8585612352565b95945050505050565b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020810181015473ffffffffffffffffffffffffffffffffffffffff16806112515763ceea21b65f526004601cfd5b919050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf516146112c5576040517fa8eb05d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546fffffffffffffffffffffffffffffffff80821670010000000000000000000000000000000092839004821685019182169283021790925561130e908490849061244b565b50505050565b61131c6121b2565b6040805160c0810182526611c37937e08000815265ffffffffffff7f00000000000000000000000000000000000000000000000000000000648b64ef8116602083019081527f00000000000000000000000000000000000000000000000000000000649dd9ef821683850190815261ffff606085018181526103e860808701908152600160a0880190815297517f01308e65000000000000000000000000000000000000000000000000000000008152965169ffffffffffffffffffff166004880152935185166024870152915190931660448501525182166064840152511660848201529051151560a48201527f00000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf59073ffffffffffffffffffffffffffffffffffffffff8216906301308e659060c4015f604051808303815f87803b158015611464575f80fd5b505af1158015611476573d5f803e3d5ffd5b50506040517f12738db800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007ade04fa0cbe2167e8bff758f48879bd0c6fff9281166004830152841692506312738db891506024015f604051808303815f87803b158015611500575f80fd5b505af1158015611512573d5f803e3d5ffd5b50506040517f8e7d1e4300000000000000000000000000000000000000000000000000000000815271a26b00c1f0df003000390027140000faa71960048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250638e7d1e4391506044015f604051808303815f87803b158015611593575f80fd5b505af11580156115a5573d5f803e3d5ffd5b50506040517f7f2a5cca00000000000000000000000000000000000000000000000000000000815273f408bee3443d0397e2c1cde588fb060ac657006f60048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250637f2a5cca91506044015f604051808303815f87803b158015611628575f80fd5b505af115801561163a573d5f803e3d5ffd5b50506040517f7f2a5cca00000000000000000000000000000000000000000000000000000000815273e3d3d0ed702504e19825f44bc6542ff2ec45cb9a60048201526001602482015273ffffffffffffffffffffffffffffffffffffffff84169250637f2a5cca91506044015f604051808303815f87803b1580156116bd575f80fd5b505af11580156116cf573d5f803e3d5ffd5b5050505050565b5f816116e957638f4eb6045f526004601cfd5b7f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52815f5263ffffffff601c600c2054169050919050565b61172b6121b2565b6117345f6124de565b565b600280546fffffffffffffffffffffffffffffffff808216600101167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617905580156117f2576117bc825f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b6117f2576040517f532201f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bdc3383612543565b6060610aef338361226c565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b61186c6611c37937e0800082611bab565b5f5b818110156110935761189783838381811061188b5761188b613718565b90506020020135611be6565b60010161186e565b5f6040518360051b8086833781205f969096526020959095525060409081526060600c209390525090919050565b6118d76001611f7a565b50610bdc828261264a565b6118ed858585610f4b565b833b156116cf576116cf85858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506121e792505050565b606061194765e35fa931a00083611bab565b335f8367ffffffffffffffff81111561196257611962613745565b60405190808252806020026020018201604052801561198b578160200160208202803683370190505b5090505f5b848110156119e3576119ba8686838181106119ad576119ad613718565b9050602002013584612142565b8282815181106119cc576119cc613718565b602090810291909101015260019283019201611990565b50949350505050565b60606119f782612175565b611a08611a03836126f1565b6128a2565b604051602001611a189190613772565b6040516020818303038152906040529050919050565b611a3f65e35fa931a0006001611bab565b6110938383836128af565b611a5c6612a6d8e11220006001611bab565b611a678383836128af565b5f828152600160208181526040832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5290912083018301805460a081811c90931890921b9091189055505050565b6060611aed611a036128cb565b604051602001611afd9190613772565b604051602081830303815290604052905090565b6060611b27868686866612a6d8e1122000611c99565b5f611b343386868661189f565b9050611b3f81611d36565b610b4c878787876128eb565b611b536121b2565b63389a75e1600c52805f526020600c208054421115611b7957636f5e88185f526004601cfd5b5f9055610b10816124de565b611b8d6121b2565b8060601b611ba257637448fbae5f526004601cfd5b610b10816124de565b8082023414610bdc576040517f3c6b4b2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bef81612a6a565b5f818152600160208181526040832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5290912082018201805460a081811c90931890921b90911890556040518181527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79060200160405180910390a150565b838214611cd2576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000005841115611d2c576040517f7a7e96df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116cf8482611bab565b335f90815260208181526040808320848452909152902054428190037f00000000000000000000000000000000000000000000000000000000000151807f0000000000000000000000000000000000000000000000000000000000000030818311818410178015610f43576331e63ea05f52846020526024601cfd5b60605f8467ffffffffffffffff811115611dce57611dce613745565b604051908082528060200260200182016040528015611df7578160200160208202803683370190505b5090505f805b86811015611e7757611e3f888883818110611e1a57611e1a613718565b90506020020135878784818110611e3357611e33613718565b90506020020135612b39565b15611e6f576001838281518110611e5857611e58613718565b911515602092830291909101909101526001909101905b600101611dfd565b50805f03611eb1576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec165e35fa931a00082612b6a565b5095945050505050565b5f1960601c82811692508381169350815f52837f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f208201820180548216915081611f235763ceea21b65f526004601cfd5b818514851517611f4757815f526030600c2054611f4757634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a450505050565b5f7f00000000000000000000000000000000000000000000000000000000649dd9ef421115611fd5576040517f589ed34b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fe66611c37937e0800083611bab565b50600280546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080830482169094018116938402911617905590565b5f818152446020526040600c2062ffffff165b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020810181015415610aef5760010162ffffff16612036565b73ffffffffffffffffffffffffffffffffffffffff90911690816120a45763ea553b345f526004601cfd5b805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f208101810180548060601b156120eb5763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff8116612115576301336cea5f526004601cfd5b905580825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a45050565b5f61214c83612a6a565b6121558361216b565b5f61215f83612023565b90506111813382612079565b610b105f82612543565b62ffffff811115610b10576040517fcbbc48a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314611734576382b429005f526004601cfd5b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a0840152801561222e578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1612253573d1561224f573d5f803e3d5ffd5b5f83525b508060e01b825114610f435763d1a57ed65f526004601cfd5b60605f61227883611f7a565b905061228584848361244b565b949350505050565b7f00000000000000000000000000000000000000000000000000000000649dd9ef4211156122e7576040517f589ed34b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000005811115612341576040517f7a7e96df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bdc816611c37937e08000611bab565b60605f8267ffffffffffffffff81111561236e5761236e613745565b604051908082528060200260200182016040528015612397578160200160208202803683370190505b5090505f805b848110156123fe576123c68686838181106123ba576123ba613718565b90506020020135612b81565b156123f65760018382815181106123df576123df613718565b911515602092830291909101909101526001909101905b60010161239d565b50805f03612438576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61244181612bda565b5090949350505050565b60605f8367ffffffffffffffff81111561246757612467613745565b604051908082528060200260200182016040528015612490578160200160208202803683370190505b5090505f5b848110156119e3575f6124a785612023565b90506124b38782612079565b808383815181106124c6576124c6613718565b60209081029190910101525060019384019301612495565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927805473ffffffffffffffffffffffffffffffffffffffff9092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b5f61254d826111f8565b9050505f81815273ffffffffffffffffffffffffffffffffffffffff9283167f7d8825530a5a2e7a0000000000000000000000000000000000000000000000008117601c5260209091208201820180549193821691826125b45763ceea21b65f526004601cfd5b825f528160010154808614848714178615176125e1576030600c20546125e157634b6e7f185f526004601cfd5b80156125ee575f83600101555b5082189055601c600c2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019055815f827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4505050565b3360181b62ffffff8316175f526020819052604060092061266a83612175565b61267381611d36565b6126b0835f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52602090208101015460a01c60011490565b156126e7576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110933384612079565b606061273a6040518060400160405280600481526020017f6e616d650000000000000000000000000000000000000000000000000000000081525061273584612c2d565b612c4a565b6127ae6040518060400160405280600d81526020017f65787465726e616c5f6c696e6b000000000000000000000000000000000000008152506040518060400160405280601281526020017f68747470733a2f2f787879797a7a2e6172740000000000000000000000000000815250612c4a565b6128056040518060400160405280600b81526020017f6465736372697074696f6e0000000000000000000000000000000000000000008152506040518060a0016040528060648152602001613d4760649139612c4a565b6128476040518060400160405280600581526020017f696d61676500000000000000000000000000000000000000000000000000000081525061273587612c76565b61288e6040518060400160405280600a81526020017f617474726962757465730000000000000000000000000000000000000000000081525061288988612c94565b612e99565b604051602001611a189594939291906137b6565b6060610aef825f80612eae565b6128b883612a6a565b6128c18361216b565b611093828261264a565b60606040518060a00160405280607a8152602001613dab607a9139905090565b60605f8467ffffffffffffffff81111561290757612907613745565b604051908082528060200260200182016040528015612930578160200160208202803683370190505b5090505f805b86811015612a1f57612953888883818110611e1a57611e1a613718565b15612a17576129ec86868381811061296d5761296d613718565b602090810292909201355f81815260018085526040822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790557f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c52939020810101805460a081811c90941890931b90921890915550565b6001838281518110612a0057612a00613718565b911515602092830291909101909101526001909101905b600101612936565b50805f03612a59576040517fc17b61ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ec16612a6d8e112200082612b6a565b5f8181527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c526020902081018101548073ffffffffffffffffffffffffffffffffffffffff811115612aea576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163314611093576040517fcdf1f8f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612b4383612a6a565b612b4c82612b81565b15612b6257612b5a8361216b565b506001610aef565b505f92915050565b818102348181039114611093576110933382612fae565b5f612b8b82612175565b5f8281527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260209020820182015415612bc857505f919050565b612bd23383612079565b506001919050565b6002805463ffffffff6fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080840482168601928316029216919091179091555f90610aef6611c37937e0800084612b6a565b6060612c3a826003612fc7565b604051602001611a1891906138bc565b60608282604051602001612c5f929190613900565b604051602081830303815290604052905092915050565b6060612c84611a038361306b565b604051602001611a189190613989565b60605f612cde6040518060400160405280600581526020017f436f6c6f72000000000000000000000000000000000000000000000000000000815250612cd985612c2d565b613088565b9050612ce9836110e5565b15612dfc575f612d636040518060400160405280600981526020017f46696e616c697a656400000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f5965730000000000000000000000000000000000000000000000000000000000815250613088565b604080518082018252600981527f46696e616c697a657200000000000000000000000000000000000000000000006020808301919091525f8881526001909152919091205491925083918391612dd291612cd99073ffffffffffffffffffffffffffffffffffffffff1661309d565b604051602001612de4939291906139cd565b60405160208183030381529060405292505050919050565b80612e716040518060400160405280600981526020017f46696e616c697a656400000000000000000000000000000000000000000000008152506040518060400160405280600281526020017f4e6f000000000000000000000000000000000000000000000000000000000000815250613088565b604051602001612e82929190613a95565b604051602081830303815290604052915050919050565b60608282604051602001612c5f929190613b3f565b606083518015612fa6576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526102308515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f03603f52602083018181015b6003880197508751603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f518252600482019150808210612f1e576020016040527f3d3d00000000000000000000000000000000000000000000000000000000000060038406600204808303919091525f8615159091029182900352900382525b509392505050565b5f805f8084865af1610bdc5763b12d13eb5f526004601cfd5b6060601f1960428360011b0116604051019050602081016040525f8152806f30313233343536373839616263646566600f528283018203600119855b600f811651948201946001860153600f8160041c1651855360081c84830361300357801561303857632194895a5f526004601cfd5b5050508190037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090910190815292915050565b6060613078826003612fc7565b604051602001611a189190613bbf565b60608282604051602001612c5f929190613c9c565b60606130a8826130df565b805161307882526002017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90910190815292915050565b60606040519050608081016040526f30313233343536373839616263646566600f5260028101905060288152602081015f60288201528260601b92505f5b808101820184821a600f81165160018301538060041c518253505060018101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed0161311d575050919050565b5f6020828403121561317a575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611181575f80fd5b5f602082840312156131b9575f80fd5b5035919050565b5f8083601f8401126131d0575f80fd5b50813567ffffffffffffffff8111156131e7575f80fd5b6020830191508360208260051b8501011115613201575f80fd5b9250929050565b5f805f805f6060868803121561321c575f80fd5b853567ffffffffffffffff80821115613233575f80fd5b61323f89838a016131c0565b90975095506020880135915080821115613257575f80fd5b50613264888289016131c0565b96999598509660400135949350505050565b602080825282518282018190525f9190848201906040850190845b818110156132af578351151583529284019291840191600101613291565b50909695505050505050565b5f5b838110156132d55781810151838201526020016132bd565b50505f910152565b602081525f82518060208401526132fb8160408501602087016132bb565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611251575f80fd5b5f8060408385031215613361575f80fd5b61336a8361332d565b946020939093013593505050565b80358015158114611251575f80fd5b5f805f60408486031215613399575f80fd5b833567ffffffffffffffff8111156133af575f80fd5b6133bb868287016131c0565b90945092506133ce905060208501613378565b90509250925092565b5f805f606084860312156133e9575f80fd5b6133f28461332d565b92506134006020850161332d565b9150604084013590509250925092565b5f805f60608486031215613422575f80fd5b61342b8461332d565b95602085013595506040909401359392505050565b602080825282518282018190525f9190848201906040850190845b818110156132af5783518352928401929184019160010161345b565b5f805f60408486031215613489575f80fd5b833567ffffffffffffffff81111561349f575f80fd5b6134ab868287016131c0565b909790965060209590950135949350505050565b5f602082840312156134cf575f80fd5b6111818261332d565b5f80604083850312156134e9575f80fd5b823591506134f960208401613378565b90509250929050565b5f8060408385031215613513575f80fd5b61351c8361332d565b91506134f960208401613378565b5f806020838503121561353b575f80fd5b823567ffffffffffffffff811115613551575f80fd5b61355d858286016131c0565b90969095509350505050565b5f805f806060858703121561357c575f80fd5b6135858561332d565b9350602085013567ffffffffffffffff8111156135a0575f80fd5b6135ac878288016131c0565b9598909750949560400135949350505050565b5f80604083850312156135d0575f80fd5b50508035926020909101359150565b5f805f805f608086880312156135f3575f80fd5b6135fc8661332d565b945061360a6020870161332d565b935060408601359250606086013567ffffffffffffffff8082111561362d575f80fd5b818801915088601f830112613640575f80fd5b81358181111561364e575f80fd5b89602082850101111561365f575f80fd5b9699959850939650602001949392505050565b5f805f60608486031215613684575f80fd5b505081359360208301359350604090920135919050565b5f80604083850312156136ac575f80fd5b6136b58361332d565b91506134f96020840161332d565b6fffffffffffffffffffffffffffffffff828116828216039080821115613711577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f82516137a981601d8501602087016132bb565b91909101601d0192915050565b7f7b0000000000000000000000000000000000000000000000000000000000000081525f86516137ed816001850160208b016132bb565b80830190507f2c00000000000000000000000000000000000000000000000000000000000000806001830152875161382c816002850160208c016132bb565b600292019182018190528651613849816003850160208b016132bb565b600392019182018190528551613866816004850160208a016132bb565b600492019182015283516138818160058401602088016132bb565b016138ae600582017f7d000000000000000000000000000000000000000000000000000000000000009052565b600601979650505050505050565b7f230000000000000000000000000000000000000000000000000000000000000081525f82516138f38160018501602087016132bb565b9190910160010192915050565b5f7f220000000000000000000000000000000000000000000000000000000000000080835284516139388160018601602089016132bb565b7f223a22000000000000000000000000000000000000000000000000000000000060019185019182015284516139758160048401602089016132bb565b016004810191909152600501949350505050565b7f646174613a696d6167652f7376672b786d6c3b6261736536342c00000000000081525f82516139c081601a8501602087016132bb565b91909101601a0192915050565b7f5b0000000000000000000000000000000000000000000000000000000000000081525f8451613a048160018501602089016132bb565b80830190507f2c000000000000000000000000000000000000000000000000000000000000008060018301528551613a43816002850160208a016132bb565b60029201918201528351613a5e8160038401602088016132bb565b7f5d000000000000000000000000000000000000000000000000000000000000006003929091019182015260040195945050505050565b7f5b0000000000000000000000000000000000000000000000000000000000000081525f8351613acc8160018501602088016132bb565b7f2c000000000000000000000000000000000000000000000000000000000000006001918401918201528351613b098160028401602088016132bb565b7f5d0000000000000000000000000000000000000000000000000000000000000060029290910191820152600301949350505050565b7f220000000000000000000000000000000000000000000000000000000000000081525f8351613b768160018501602088016132bb565b7f223a0000000000000000000000000000000000000000000000000000000000006001918401918201528351613bb38160038401602088016132bb565b01600301949350505050565b7f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d2236393022206865696768743d223639302260208201527f3e3c726563742077696474683d2236393022206865696768743d22363930222060408201527f66696c6c3d22230000000000000000000000000000000000000000000000000060608201525f8251613c688160678501602087016132bb565b7f22202f3e3c2f7376673e000000000000000000000000000000000000000000006067939091019283015250607101919050565b7f7b2274726169745f74797065223a22000000000000000000000000000000000081525f8351613cd381600f8501602088016132bb565b7f222c2276616c7565223a22000000000000000000000000000000000000000000600f918401918201528351613d1081601a8401602088016132bb565b7f227d000000000000000000000000000000000000000000000000000000000000601a9290910191820152601c0194935050505056fe50726f6f66206f6620636f6c6f722e20585859595a5a206973206120636f6c6c656374696f6e206f662066756c6c79206f6e636861696e2c20756e697175652c20636f6d706f7361626c652c20616e6420636f6c6c65637461626c6520636f6c6f72732e7b226e616d65223a22585859595a5a222c226465736372697074696f6e223a22436f6c6c65637469626c652c20636f6d706f7361626c652c20616e6420756e69717565206f6e636861696e20636f6c6f72732e222c2265787465726e616c5f6c696e6b223a2268747470733a2f2f787879797a7a2e617274227d

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

0000000000000000000000007039813983ba47a5a46b19f3a269aef5c6c75abf0000000000000000000000007ade04fa0cbe2167e8bff758f48879bd0c6fff92000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf5000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000006969690000000000000000000000000000000000000000000000000000000000ff6000000000000000000000000000000000000000000000000000000000000000ff00

-----Decoded View---------------
Arg [0] : initialOwner (address): 0x7039813983ba47a5A46B19f3a269aeF5C6c75ABF
Arg [1] : creatorPayout (address): 0x7ade04fa0cbE2167E8bff758F48879BD0C6fFf92
Arg [2] : maxBatchSize (uint256): 5
Arg [3] : preMintIds (uint24[]): 0,16777215,6908265,16736256,65280
Arg [4] : seaDrop (address): 0x00005EA00Ac477B1030CE78506496e8C2dE24bf5

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 0000000000000000000000007039813983ba47a5a46b19f3a269aef5c6c75abf
Arg [1] : 0000000000000000000000007ade04fa0cbe2167e8bff758f48879bd0c6fff92
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 00000000000000000000000000005ea00ac477b1030ce78506496e8c2de24bf5
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000ffffff
Arg [8] : 0000000000000000000000000000000000000000000000000000000000696969
Arg [9] : 0000000000000000000000000000000000000000000000000000000000ff6000
Arg [10] : 000000000000000000000000000000000000000000000000000000000000ff00


Deployed Bytecode Sourcemap

1714:937:6:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2419:230;;;;;;;;;;-1:-1:-1;2419:230:6;;;;;:::i;:::-;;:::i;:::-;;;516:14:16;;509:22;491:41;;479:2;464:18;2419:230:6;;;;;;;;6218:120:11;;;;;;:::i;:::-;;:::i;:::-;;3659:435;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;3505:328:8:-;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;7313:541:2:-;;;;;;;;;;-1:-1:-1;7313:541:2;;;;;:::i;:::-;;:::i;:::-;;;3484:42:16;3472:55;;;3454:74;;3442:2;3427:18;7313:541:2;3308:226:16;8147:119:2;;;;;;:::i;:::-;;:::i;1261:48:8:-;;;;;;;;;;;;;;;;;;4145:25:16;;;4133:2;4118:18;1261:48:8;3999:177:16;215:44:14;;;;;;;;;;;;;;;3355:87:8;;;;;;;;;;-1:-1:-1;3425:10:8;;;;3355:87;;265:41:14;;;;;;;;;;;;;;;905:314:10;;;:::i;3018:100:8:-;;;;;;;;;;;;;:::i;914:367:11:-;;;;;;:::i;:::-;;:::i;1237:1691:7:-;;;;;;;;;;-1:-1:-1;1237:1691:7;;;;;:::i;:::-;;:::i;9956:2995:2:-;;;;;;:::i;:::-;;:::i;6825:616:1:-;;;:::i;5718:485:8:-;;;;;;;;;;-1:-1:-1;5718:485:8;;;;;:::i;:::-;5994:2;5990:15;;;;6015:10;6007:19;;;;5987:40;5832:23;5977:51;6048:4;6041:18;6182:4;6176;6166:21;;5718:485;4597:125;;;;;;;;;;-1:-1:-1;4597:125:8;;;;;:::i;:::-;;:::i;2504:358::-;;;;;;;;;;;;;:::i;13022:198:2:-;;;;;;:::i;:::-;;:::i;1529:138:10:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;1190:65:8:-;;;;;;;;;;;;1242:13;1190:65;;7523:456:1;;;:::i;3206:345:10:-;;;;;;:::i;:::-;;:::i;6170:332:2:-;;;;;;;;;;-1:-1:-1;6170:332:2;;;;;:::i;:::-;;:::i;2178:510:12:-;;;;;;;;;;-1:-1:-1;2178:510:12;;;;;:::i;:::-;;:::i;1436:736::-;;;;;;;;;;;;;:::i;6646:533:2:-;;;;;;;;;;-1:-1:-1;6646:533:2;;;;;:::i;:::-;;:::i;530:49:10:-;;;;;;;;;;;;;;;6563:100:1;;;:::i;1632:63:8:-;;;;;;;;;;-1:-1:-1;1632:63:8;;;;;:::i;:::-;;;;;;;;;;;;;;;;3359:130:12;;;;;;;;;;-1:-1:-1;3359:130:12;;;;;:::i;:::-;-1:-1:-1;3461:10:12;;3413:7;;3461:10;;;;;;;3473:8;;3359:130;;;;;7238:25:16;;;7294:2;7279:18;;7272:34;;;;7322:18;;;7315:34;7226:2;7211:18;3359:130:12;7036:319:16;553:92:14;;;;;;;;;;-1:-1:-1;553:92:14;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;9211:191:1;;;;;;;;;;-1:-1:-1;9365:20:1;9359:27;9211:191;;1074:52:8;;;;;;;;;;;;1113:13;1074:52;;522:380:7;;;;;;;;;;-1:-1:-1;522:380:7;;;;;:::i;:::-;;:::i;1391:132:10:-;;;;;;:::i;:::-;;:::i;8888:713:2:-;;;;;;;;;;-1:-1:-1;8888:713:2;;;;;:::i;:::-;;:::i;6681:266:11:-;;;;;;:::i;:::-;;:::i;6913:1312:8:-;;;;;;;;;;-1:-1:-1;6913:1312:8;;;;;:::i;:::-;;:::i;2598:147:10:-;;;;;;:::i;:::-;;:::i;13723:249:2:-;;;;;;:::i;:::-;;:::i;1510:668:11:-;;;;;;:::i;:::-;;:::i;1020:48:8:-;;;;;;;;;;;;1057:11;1020:48;;666:210:9;;;;;;;;;;-1:-1:-1;666:210:9;;;;;:::i;:::-;;:::i;2764:186:11:-;;;;;;:::i;:::-;;:::i;3193:87:8:-;;;;;;;;;;-1:-1:-1;3263:10:8;;;;;;;3193:87;;3554:85:12;;;;;;;;;;-1:-1:-1;3624:8:12;3554:85;;10031:107:1;;;;;;;;;;-1:-1:-1;10031:107:1;;10122:9;10688:50:16;;10676:2;10661:18;10031:107:1;10544:200:16;4709:321:11;;;;;;:::i;:::-;;:::i;947:167:9:-;;;;;;;;;;;;;:::i;8357:392:2:-;;;;;;;;;;-1:-1:-1;8357:392:2;;;;;:::i;:::-;8577:4;8570:22;8618:31;8612:4;8605:45;8477:11;8663:19;8727:4;8721;8711:21;8705:28;;8357:392;5434:469:11;;;;;;:::i;:::-;;:::i;8166:708:1:-;;;;;;:::i;:::-;;:::i;940:113:14:-;;;;;;;;;;-1:-1:-1;940:113:14;;;;;:::i;:::-;1005:10;993:11;:23;;;;;;;;;;;:35;;;;;;;1031:15;993:53;;940:113;6149:349:1;;;;;;:::i;:::-;;:::i;9505:435::-;;;;;;;;;;-1:-1:-1;9505:435:1;;;;;:::i;:::-;9775:19;9769:4;9762:33;;;9624:14;9808:26;;;;9918:4;9902:21;;9896:28;;9505:435;2419:230:6;2571:4;2598:44;2630:11;3128:10:12;2930:3;2926:21;;;;3122:17;;;3147:10;3141:17;;3119:40;3167:10;3161:17;;3116:63;3187:10;3181:17;;3113:86;3227:32;3221:39;;;3089:189;;2786:508;2598:44:6;2591:51;2419:230;-1:-1:-1;;2419:230:6:o;6218:120:11:-;6273:35;1173:11:8;6306:1:11;6273:16;:35::i;:::-;6318:13;6328:2;6318:9;:13::i;:::-;6218:120;:::o;3659:435::-;3804:13;3833:60;3864:6;;3872;;1113:13:8;3833:30:11;:60::i;:::-;3903:26;3932:48;3955:10;3967:6;;3975:4;3932:22;:48::i;:::-;3903:77;;3990:42;4013:18;3990:22;:42::i;:::-;4050:37;4072:6;;4080;;4050:21;:37::i;:::-;4043:44;3659:435;-1:-1:-1;;;;;;;3659:435:11:o;3505:328:8:-;3551:13;3738:4;;3725:18;3769:16;3763:4;3756:30;3812:4;3738;3799:18;7313:541:2;7375:14;7467:16;;;7509:24;7503:4;7496:38;7600:4;7584:21;;7576:30;;7568:39;;7646:20;;7630:38;;7620:167;;7701:10;7695:4;7688:24;7768:4;7762;7755:18;7620:167;7820:1;7816:21;7810:28;;7313:541;-1:-1:-1;;7313:541:2:o;8147:119::-;8226:33;8235:10;8247:7;8256:2;8226:8;:33::i;:::-;8147:119;;:::o;905:314:10:-;945:7;964:17;984:34;1016:1;984:31;:34::i;:::-;964:54;;1106:15;1124:28;1142:9;1124:17;:28::i;:::-;1106:46;;1162:26;1168:10;1180:7;1162:5;:26::i;3018:100:8:-;3101:10;;3062:7;;3088:23;;3101:10;;;;;3088;;;;:23;:::i;:::-;3081:30;;;;3018:100;:::o;914:367:11:-;969:7;988:33;1113:13:8;1019:1:11;988:16;:33::i;:::-;1231:43;1247:5;1262:10;1231:15;:43::i;1237:1691:7:-;1335:1;1321:15;;;1317:68;;1359:15;;;;;;;;;;;;;;1317:68;1394:32;1429:28;1450:3;;1454:1;1450:6;;;;;;;:::i;:::-;;;;;;;13924:14:8;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;14180:46;;13859:383;1429:28:7;1394:63;-1:-1:-1;1394:63:7;1547:148;;;;1611:17;1584:44;;1580:105;;;1655:15;;;;;;;;;;;;;;1580:105;1776:31;;;1797:10;1776:31;1772:185;;1828:47;1845:17;1864:10;8577:4:2;8570:22;8618:31;8612:4;8605:45;8477:11;8663:19;8727:4;8721;8711:21;8705:28;;8357:392;1828:47:7;1823:124;;1902:30;;;;;;;;;;;;;;1823:124;2072:10;:32;;;;;;;;;;;;;;;;;;2124:13;2093:3;;2072:10;2130:6;;;;;:::i;:::-;;;;;;;2124:5;:13::i;:::-;2164:1;2147:775;2167:14;;;2147:775;;;2198:10;2211:3;;2215:1;2211:6;;;;;;;:::i;:::-;;;;;;;2198:19;;2258:24;2279:2;13924:14:8;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;14180:46;;13859:383;2258:24:7;2231:51;-1:-1:-1;2231:51:7;2440:26;;;;;;;;2436:87;;2493:15;;;;;;;;;;;;;;2436:87;2540:13;2536:164;;;2604:17;2577:44;;2573:113;;;2652:15;;;;;;;;;;;;;;2573:113;2843:9;2849:2;2843:5;:9::i;:::-;-1:-1:-1;;2894:3:7;;2147:775;;;;1307:1621;;1237:1691;;;:::o;9956:2995:2:-;10233:1;10377:16;;;10422:24;10448:8;10419:38;10413:4;10406:52;10524:4;10508:21;;10500:30;;10492:39;;10567:20;;10221:15;10257:25;;;;10301:23;;;;10613:36;;;10492:39;10752:15;;;10741:27;;10731:328;;10798:5;10788:146;;10840:10;10834:4;10827:24;10911:4;10905;10898:18;10788:146;10964:10;10958:4;10951:24;11040:4;11034;11027:18;10731:328;11133:2;11123:135;;11168:10;11162:4;11155:24;11239:4;11233;11226:18;11123:135;11361:4;11355;11348:18;11419:13;11416:1;11412:21;11406:28;11383:51;;11569:15;11559:8;11556:29;11549:4;11539:8;11536:18;11533:53;11523:288;;11642:4;11636;11626:21;11620:28;11610:183;;11689:10;11683:4;11676:24;11766:4;11760;11753:18;11610:183;11886:15;11883:55;;;11934:1;11918:13;11915:1;11911:21;11904:32;11883:55;12050:13;;;12029:35;12007:58;;-1:-1:-1;12183:4:2;12177;12167:21;;;12233:22;;12229:30;;12205:55;;-1:-1:-1;12351:16:2;;;12405:21;12474:20;;12257:1;12470:28;12550:20;12525:46;;12515:192;;12608:10;12602:4;12595:24;12684:4;12678;12671:18;12515:192;12724:42;;12889:2;12885;12879:4;12852:25;12846:4;;12835:57;12911:33;9956:2995;;;:::o;6825:616:1:-;6918:15;10122:9;6936:45;;:15;:45;6918:63;;7149:19;7143:4;7136:33;7199:8;7193:4;7186:22;7255:7;7248:4;7242;7232:21;7225:38;7402:8;7355:45;7352:1;7349;7344:67;7051:374;6825:616::o;4597:125:8:-;4651:4;4667:15;4679:2;4667:11;:15::i;:::-;4699:16;4712:2;13302:4;17500:16:2;;;17542:24;17536:4;17529:38;17637:4;17621:21;;17613:30;;17605:39;17599:46;17594:3;17590:56;1434:1:8;13325:30;;13245:117;2504:358;10527:13:1;:11;:13::i;:::-;2648:1:8::1;;;;;2624:13;2614:8;2607:5;2602:48;2716:130;;2771:16;2648:1;;2750:38;2815:16;2648:1;2805:27;13022:198:2::0;13119:26;13132:4;13138:2;13142;13119:12;:26::i;:::-;35982:14;;13155:58;;;13173:40;13196:4;13202:2;13206;13173:40;;;;;;;;;;;;:22;:40::i;1529:138:10:-;1599:16;1634:26;1647:2;1651:8;1634:12;:26::i;:::-;1627:33;1529:138;-1:-1:-1;;;1529:138:10:o;7523:456:1:-;7725:19;7719:4;7712:33;7771:8;7765:4;7758:22;7823:1;7816:4;7810;7800:21;7793:32;7954:8;7908:44;7905:1;7902;7897:66;7523:456::o;3206:345:10:-;3295:13;3320:35;3351:3;;3320:30;:35::i;:::-;3365:26;3394:45;3417:10;3429:3;;3434:4;3394:22;:45::i;:::-;3365:74;;3449:42;3472:18;3449:22;:42::i;:::-;3508:36;3540:3;;3508:31;:36::i;:::-;3501:43;3206:345;-1:-1:-1;;;;;3206:345:10:o;6170:332:2:-;6228:14;15491:16;;;15533:24;15527:4;15520:38;15635:4;15619:21;;15611:30;;15603:39;;15597:46;15581:64;;;6351:135;;6400:10;6394:4;6387:24;6467:4;6461;6454:18;6351:135;6170:332;;;:::o;2178:510:12:-;2259:10;:21;2273:7;2259:21;;2255:72;;2303:13;;;;;;;;;;;;;;2255:72;2560:10;;;;2610:22;;;2560:10;;;;;;;:30;;2610:22;;;;;;;;;;2642:39;;2650:9;;2560:30;;2642:7;:39::i;:::-;;2245:443;2178:510;;:::o;1436:736::-;10527:13:1;:11;:13::i;:::-;1577:326:12::1;::::0;;::::1;::::0;::::1;::::0;;1624:11:::1;1577:326:::0;;::::1;1672:13;1577:326:::0;::::1;;::::0;::::1;::::0;;;1720:24:::1;1577:326:::0;::::1;::::0;;;;;;1789:16:::1;1577:326:::0;;;;;;1838:4:::1;1577:326:::0;;;;;;1884:4:::1;1577:326:::0;;;;;;1539:374;;;;;11972:13:16;;11987:22;11968:42;1539:374:12::1;::::0;::::1;11950:61:16::0;12040:24;;12135:21;;12113:20;;;12106:51;12199:24;;12195:33;;;12173:20;;;12166:63;12260:24;12347:23;;12325:20;;;12318:53;12413:24;12409:33;12387:20;;;12380:63;12495:24;;12488:32;12481:40;12459:20;;;12452:70;1521:7:12::1;::::0;1539:24:::1;::::0;::::1;::::0;::::1;::::0;11922:19:16;;1539:374:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;1923:50:12::1;::::0;;;;:34:::1;1958:14;3472:55:16::0;;1923:50:12::1;::::0;::::1;3454:74:16::0;1923:34:12;::::1;::::0;-1:-1:-1;1923:34:12::1;::::0;-1:-1:-1;3427:18:16;;1923:50:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;1983:62:12::1;::::0;;;;542:42:::1;1983:62;::::0;::::1;12701:74:16::0;2040:4:12::1;12791:18:16::0;;;12784:50;1983:33:12::1;::::0;::::1;::::0;-1:-1:-1;1983:33:12::1;::::0;-1:-1:-1;12674:18:16;;1983:62:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;2055:50:12::1;::::0;;;;641:42:::1;2055:50;::::0;::::1;12701:74:16::0;2100:4:12::1;12791:18:16::0;;;12784:50;2055:19:12::1;::::0;::::1;::::0;-1:-1:-1;2055:19:12::1;::::0;-1:-1:-1;12674:18:16;;2055:50:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;2115:50:12::1;::::0;;;;740:42:::1;2115:50;::::0;::::1;12701:74:16::0;2160:4:12::1;12791:18:16::0;;;12784:50;2115:19:12::1;::::0;::::1;::::0;-1:-1:-1;2115:19:12::1;::::0;-1:-1:-1;12674:18:16;;2115:50:12::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;1483:689;1436:736::o:0;6646:533:2:-;6709:14;6869:5;6859:143;;6907:10;6901:4;6894:24;6983:4;6977;6970:18;6859:143;7028:24;7022:4;7015:38;7079:5;7073:4;7066:19;7142:20;7134:4;7128;7118:21;7112:28;7108:55;7098:65;;6646:533;;;:::o;6563:100:1:-;10527:13;:11;:13::i;:::-;6635:21:::1;6653:1;6635:9;:21::i;:::-;6563:100::o:0;522:380:7:-;701:10;:15;;;;;;715:1;701:15;;;;;;;;;736:125;;;;774:20;787:6;13302:4:8;17500:16:2;;;17542:24;17536:4;17529:38;17637:4;17621:21;;17613:30;;17605:39;17599:46;17594:3;17590:56;1434:1:8;13325:30;;13245:117;774:20:7;769:82;;821:15;;;;;;;;;;;;;;769:82;870:25;876:10;888:6;870:5;:25::i;1391:132:10:-;1447:16;1482:34;1495:10;1507:8;1482:12;:34::i;8888:713:2:-;9103:10;9096:18;9089:26;9075:40;;9212:8;9206:4;9199:22;9247:31;9241:4;9234:45;9305:8;9299:4;9292:22;9357:10;9350:4;9344;9334:21;9327:41;9442:10;9436:4;9429:24;9561:8;9557:2;9553:17;9549:2;9545:26;9535:8;9500:33;9494:4;9488;9466:119;8888:713;;:::o;6681:266:11:-;6753:44;1173:11:8;6786:3:11;6753:16;:44::i;:::-;6812:9;6807:134;6823:14;;;6807:134;;;6854:17;6864:3;;6868:1;6864:6;;;;;;;:::i;:::-;;;;;;;6854:9;:17::i;:::-;6913:3;;6807:134;;6913:1312:8;7044:22;7183:4;7177:11;7296:10;7293:1;7289:18;7422:8;7410:10;7398;7385:46;7531:183;;7784:1;7777:17;;;;7868:4;7861:23;;;;-1:-1:-1;7957:4:8;7950:18;;;8124:4;8118;8108:21;8185:24;;;-1:-1:-1;8108:21:8;;6913:1312;-1:-1:-1;6913:1312:8:o;2598:147:10:-;2671:34;2703:1;2671:31;:34::i;:::-;;2715:23;2729:2;2733:4;2715:13;:23::i;13723:249:2:-;13869:26;13882:4;13888:2;13892;13869:12;:26::i;:::-;35982:14;;13905:60;;;13923:42;13946:4;13952:2;13956;13960:4;;13923:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;13923:22:2;;-1:-1:-1;;;13923:42:2:i;1510:668:11:-;1579:16;1607:42;1113:13:8;1638:3:11;1607:16;:42::i;:::-;1884:10;1853:12;1946:3;1932:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1932:25:11;;1906:51;;1972:9;1967:182;1983:14;;;1967:182;;;2026:29;2042:3;;2046:1;2042:6;;;;;;;:::i;:::-;;;;;;;2050:4;2026:15;:29::i;:::-;2014:6;2021:1;2014:9;;;;;;;;:::i;:::-;;;;;;;;;;:41;2097:3;2118:6;;;;2097:3;1967:182;;;-1:-1:-1;2165:6:11;1510:668;-1:-1:-1;;;;1510:668:11:o;666:210:9:-;734:13;759:15;771:2;759:11;:15::i;:::-;838:30;844:14;855:2;844:10;:14::i;:::-;838:28;:30::i;:::-;791:78;;;;;;;;:::i;:::-;;;;;;;;;;;;;784:85;;666:210;;;:::o;2764:186:11:-;2857:33;1113:13:8;2888:1:11;2857:16;:33::i;:::-;2900:43;2924:5;2931;2938:4;2900:23;:43::i;4709:321::-;4813:46;1242:13:8;4857:1:11;4813:16;:46::i;:::-;4870:43;4894:5;4901;4908:4;4870:23;:43::i;:::-;12084:14;;;;:10;:14;;;;;;;:26;;;;5012:10;12084:26;;;18050:24:2;18044:4;18037:38;18125:21;;;18117:30;;18109:39;;18175:20;;18266:3;18262:16;;;18251:28;;;18242:38;;;18230:51;;;18208:74;;9956:2995;;;:::o;947:167:9:-;991:13;1070:36;1076:20;:18;:20::i;1070:36::-;1023:84;;;;;;;;:::i;:::-;;;;;;;;;;;;;1016:91;;947:167;:::o;5434:469:11:-;5590:13;5619:73;5650:6;;5658;;1242:13:8;5619:30:11;:73::i;:::-;5702:26;5731:48;5754:10;5766:6;;5774:4;5731:22;:48::i;:::-;5702:77;;5789:42;5812:18;5789:22;:42::i;:::-;5848:48;5881:6;;5889;;5848:32;:48::i;8166:708:1:-;10527:13;:11;:13::i;:::-;8400:19:::1;8394:4;8387:33;8446:12;8440:4;8433:26;8508:4;8502;8492:21;8614:12;8608:19;8595:11;8592:36;8589:157;;;8660:10;8654:4;8647:24;8727:4;8721;8714:18;8589:157;8823:1;8802:23:::0;;8844::::1;8854:12:::0;8844:9:::1;:23::i;6149:349::-:0;10527:13;:11;:13::i;:::-;6321:8:::1;6317:2;6313:17;6303:150;;6363:10;6357:4;6350:24;6434:4;6428;6421:18;6303:150;6472:19;6482:8;6472:9;:19::i;12027:345:8:-:0;12289:8;12277:9;:20;12263:9;:35;12259:97;;12325:16;;;;;;;;;;;;;;11485:244:11;11535:38;11570:2;11535:34;:38::i;:::-;12084:14;;;;:10;:14;;;;;;;:26;;;;11632:10;12084:26;;;18050:24:2;18044:4;18037:38;18125:21;;;18117:30;;18109:39;;18175:20;;18266:3;18262:16;;;18251:28;;;18242:38;;;18230:51;;;18208:74;;11704:18:11;;4145:25:16;;;11704:18:11;;4133:2:16;4118:18;11704::11;;;;;;;11485:244;:::o;14979:382:8:-;15130:20;;;15126:79;;15173:21;;;;;;;;;;;;;;15126:79;15229:23;15218:34;;15214:94;;;15275:22;;;;;;;;;;;;;;15214:94;15317:37;15334:1;15344:9;15317:16;:37::i;1249:1134:14:-;1445:10;1404:26;1433:23;;;;;;;;;;;:47;;;;;;;;;1663:15;:36;;;1748:19;1803:16;2115:32;;;2149:29;;;2112:67;2192:175;;;;2241:27;2238:1;2231:38;2299:18;2293:4;2286:32;2348:4;2342;2335:18;7383:831:11;7502:13;7531:22;7567:6;7556:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7556:25:11;;7531:50;;7591:24;7630:9;7625:310;7641:17;;;7625:310;;;7679:48;7706:6;;7713:1;7706:9;;;;;;;:::i;:::-;;;;;;;7717:6;;7724:1;7717:9;;;;;;;:::i;:::-;;;;;;;7679:26;:48::i;:::-;7675:191;;;7761:4;7747:8;7756:1;7747:11;;;;;;;;:::i;:::-;:18;;;:11;;;;;;;;;;;:18;7815;;;;;7675:191;7907:3;;7625:310;;;;8018:16;8038:1;8018:21;8014:74;;8062:15;;;;;;;;;;;;;;8014:74;8131:50;1113:13:8;8164:16:11;8131:18;:50::i;:::-;-1:-1:-1;8199:8:11;7383:831;-1:-1:-1;;;;;7383:831:11:o;26552:1436:2:-;26735:1;26731:6;26727:2;26723:15;26782:7;26766:14;26762:28;26751:39;;26829:2;26813:14;26809:23;26803:29;;26902:2;26896:4;26889:16;26960:2;26934:24;26931:32;26925:4;26918:46;27030:4;27024;27014:21;27010:2;27006:30;27002:2;26998:39;27089:13;27083:20;27067:14;27063:41;27050:54;;27178:5;27168:134;;27216:10;27210:4;27203:24;27283:4;27277;27270:18;27168:134;27485:5;27481:2;27478:13;27473:2;27466:10;27463:29;27453:280;;27525:5;27519:4;27512:19;27580:4;27574;27564:21;27558:28;27548:171;;27623:10;27617:4;27610:24;27696:4;27690;27683:18;27548:171;27827:1;27823:21;27816:38;;;27969:2;27846:7;27953:5;27926:25;27920:4;;27909:63;;26552:1436;;;:::o;6005:602:10:-;6091:7;6132:24;6114:15;:42;6110:92;;;6179:12;;;;;;;;;;;;;;6110:92;6211:47;1057:11:8;6240:17:10;6211:16;:47::i;:::-;-1:-1:-1;6493:10:10;;;;;;;;;;:39;;;6552:22;;;;;;;;;;;6005:602::o;10974:791:8:-;11038:7;11105:15;;;11146:12;11140:4;11133:26;11337:4;11331;11321:21;11344:10;11317:38;11522:213;13924:14;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;;14180:46;11529:34;11522:213;;11695:1;11685:11;1392:8;11684:26;11522:213;;18786:1676:2;19014:20;;;;;;19098:135;;19143:10;19137:4;19130:24;19214:4;19208;19201:18;19098:135;19299:2;19293:4;19286:16;19328:24;19322:4;19315:38;19419:4;19413;19403:21;19399:2;19395:30;19391:2;19387:39;19468:13;19462:20;19557:15;19553:2;19549:24;19546:146;;;19605:10;19599:4;19592:24;19673:4;19667;19660:18;19546:146;19765:23;;19743:46;;19878:4;19871:16;;;19939:4;19933;19923:21;19990:18;;20010:1;19986:26;20062:20;20039:44;;20029:190;;20120:10;20114:4;20107:24;20196:4;20190;20183:18;20029:190;20236:38;;20394:2;20390;20387:1;20360:25;20387:1;;20343:54;8147:119;;:::o;9751:298:11:-;9823:7;9842:41;9877:5;9842:34;:41::i;:::-;9919:12;9925:5;9919;:12::i;:::-;9941:15;9959:23;9977:4;9959:17;:23::i;:::-;9941:41;;9992:26;9998:10;10010:7;9992:5;:26::i;21497:82:2:-;21551:21;21565:1;21569:2;21551:5;:21::i;11830:136:8:-;1392:8;11895:6;:19;11891:69;;;11937:12;;;;;;;;;;;;;;5425:364:1;5637:20;5631:27;5621:8;5618:41;5608:165;;5692:10;5686:4;5679:24;5754:4;5748;5741:18;36203:1405:2;36440:4;36434:11;36490:10;36523:24;36520:1;36513:35;36582:8;36575:4;36572:1;36568:12;36561:30;36690:4;36686:2;36682:13;36678:2;36674:22;36667:4;36664:1;36660:12;36653:44;36731:2;36724:4;36721:1;36717:12;36710:24;36768:4;36761;36758:1;36754:12;36747:26;36801:4;36795:11;36840:1;36833:4;36830:1;36826:12;36819:23;36858:1;36855:71;;;36921:1;36914:4;36911:1;36907:12;36904:1;36897:4;36891;36887:15;36884:1;36877:5;36866:57;36862:62;36855:71;37042:4;37039:1;37032:4;37029:1;37025:12;37018:4;37015:1;37011:12;37008:1;37004:2;36997:5;36992:55;36982:348;;37070:16;37067:220;;;37201:16;37195:4;37189;37174:44;37252:16;37246:4;37239:30;37067:220;37314:1;37311;37304:12;36982:348;;37426:24;37421:3;37417:34;37413:1;37407:8;37404:48;37394:198;;37485:10;37479:4;37472:24;37573:4;37567;37560:18;3818:258:10;3888:16;3959:17;3979:41;4011:8;3979:31;:41::i;:::-;3959:61;;4037:32;4045:2;4049:8;4059:9;4037:7;:32::i;:::-;4030:39;3818:258;-1:-1:-1;;;;3818:258:10:o;7436:341::-;7546:24;7528:15;:42;7524:92;;;7593:12;;;;;;;;;;;;;;7524:92;7642:23;7629:36;;7625:96;;;7688:22;;;;;;;;;;;;;;7625:96;7730:40;7747:3;1057:11:8;7730:16:10;:40::i;4999:658::-;5082:13;5107:20;5141:3;5130:22;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5130:22:10;;5107:45;;5162:22;5199:9;5194:287;5210:14;;;5194:287;;;5245:32;5270:3;;5274:1;5270:6;;;;;;;:::i;:::-;;;;;;;5245:24;:32::i;:::-;5241:171;;;5309:4;5297:6;5304:1;5297:9;;;;;;;;:::i;:::-;:16;;;:9;;;;;;;;;;;:16;5363;;;;;5241:171;5453:3;;5194:287;;;;5494:14;5512:1;5494:19;5490:72;;5536:15;;;;;;;;;;;;;;5490:72;5572:55;5612:14;5572:39;:55::i;:::-;-1:-1:-1;5644:6:10;;4999:658;-1:-1:-1;;;;4999:658:10:o;4288:500::-;4372:16;4400:25;4442:8;4428:23;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4428:23:10;;4400:51;;4466:9;4461:296;4481:8;4477:1;:12;4461:296;;;4545:15;4563:28;4581:9;4563:17;:28::i;:::-;4545:46;;4605:18;4611:2;4615:7;4605:5;:18::i;:::-;4651:7;4637:8;4646:1;4637:11;;;;;;;;:::i;:::-;;;;;;;;;;:21;-1:-1:-1;4700:3:10;4721:11;;;;4700:3;4461:296;;4872:495:1;5019:20;5248:16;;5104:26;;;;;;;5208:38;5205:1;;5197:78;5324:27;4872:495::o;21868:2121:2:-;21934:13;21950:11;21958:2;21950:7;:11::i;:::-;21934:27;;-1:-1:-1;22216:4:2;22209:16;;;22136:20;;;;22254:24;22251:32;;22245:4;22238:46;22350:4;22334:21;;;22326:30;;22318:39;;22393:20;;22136;;22516:33;;;;22613:134;;22661:10;22655:4;22648:24;22728:4;22722;22715:18;22613:134;22841:5;22835:4;22828:19;22900:13;22897:1;22893:21;22887:28;23132:15;23128:2;23125:23;23117:5;23113:2;23110:13;23107:42;23102:2;23095:10;23092:58;23082:293;;23206:4;23200;23190:21;23184:28;23174:183;;23253:10;23247:4;23240:24;23330:4;23324;23317:18;23174:183;23450:15;23447:55;;;23498:1;23482:13;23479:1;23475:21;23468:32;23447:55;-1:-1:-1;23583:27:2;;23561:50;;23726:4;23720;23710:21;23772:18;;23768:26;;23748:47;;23918:2;-1:-1:-1;23604:5:2;23881:25;-1:-1:-1;;23864:57:2;9956:2995;;;:::o;8490:518:8:-;8605:10;5994:2;5990:15;6015:10;6007:19;;5987:40;8558:26;5977:51;6048:4;6041:18;;;6182:4;6176;6166:21;8688:15;6007:19;8688:11;:15::i;:::-;8769:42;8792:18;8769:22;:42::i;:::-;8903:16;8916:2;13302:4;17500:16:2;;;17542:24;17536:4;17529:38;17637:4;17621:21;;17613:30;;17605:39;17599:46;17594:3;17590:56;1434:1:8;13325:30;;13245:117;8903:16;8899:72;;;8942:18;;;;;;;;;;;;;;8899:72;8980:21;8986:10;8998:2;8980:5;:21::i;1165:589:9:-;1228:13;1304:22;;;;;;;;;;;;;;;;;;1316:9;1322:2;1316:5;:9::i;:::-;1304:3;:22::i;:::-;1357:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:3;:42::i;:::-;1430:168;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:3;:168::i;:::-;1629:27;;;;;;;;;;;;;;;;;;1642:13;1652:2;1642:9;:13::i;1629:27::-;1687:33;;;;;;;;;;;;;;;;;;1708:11;1716:2;1708:7;:11::i;:::-;1687:6;:33::i;:::-;1260:487;;;;;;;;;;;;:::i;3416:132:3:-;3474:20;3515:26;3522:4;3528:5;3535;3515:6;:26::i;10380:228:11:-;10476:41;10511:5;10476:34;:41::i;:::-;10553:12;10559:5;10553;:12::i;:::-;10575:26;10589:5;10596:4;10575:13;:26::i;1808:224:9:-;1861:13;1886:139;;;;;;;;;;;;;;;;;;;1808:224;:::o;8611:902:11:-;8741:13;8770:22;8806:6;8795:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;8795:25:11;;8770:50;;8830:24;8869:9;8864:365;8880:17;;;8864:365;;;8918:48;8945:6;;8952:1;8945:9;;;;;;;:::i;8918:48::-;8914:246;;;8986:37;9001:6;;9008:1;9001:9;;;;;;;:::i;:::-;;;;;;;;;;12084:14;;;;:10;:14;;;;;;:26;;;;9012:10;12084:26;;;18050:24:2;18044:4;18037:38;18125:21;;;18117:30;;18109:39;18175:20;;18266:3;18262:16;;;18251:28;;;18242:38;;;18230:51;;;18208:74;;;-1:-1:-1;8147:119:2;8986:37:11;9055:4;9041:8;9050:1;9041:11;;;;;;;;:::i;:::-;:18;;;:11;;;;;;;;;;;:18;9109;;;;;8914:246;9201:3;;8864:365;;;;9304:16;9324:1;9304:21;9300:74;;9348:15;;;;;;;;;;;;;;9300:74;9417:63;1242:13:8;9463:16:11;9417:18;:63::i;14248:440:8:-;14328:18;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;;14180:46;;14493:17;14478:32;;14474:88;;;14533:18;;;;;;;;;;;;;;14474:88;14613:19;;;14622:10;14613:19;14609:73;;14655:16;;;;;;;;;;;;;;10924:327:11;11008:4;11024:41;11059:5;11024:34;:41::i;:::-;11128:31;11153:5;11128:24;:31::i;:::-;11124:99;;;11175:12;11181:5;11175;:12::i;:::-;-1:-1:-1;11208:4:11;11201:11;;11124:99;-1:-1:-1;11239:5:11;10924:327;;;;:::o;12618:464:8:-;12911:29;;;12898:9;:43;;;;12959:16;12955:111;;12995:56;13027:10;13039:11;12995:31;:56::i;9932:455::-;9996:4;10063:15;10075:2;10063:11;:15::i;:::-;13924:14;14090:16;;;14132:24;14126:4;14119:38;14218:4;14202:21;;14194:30;;14186:39;;14180:46;10178:29;10174:119;;-1:-1:-1;10277:5:8;;9932:455;-1:-1:-1;9932:455:8:o;10174:119::-;10338:21;10344:10;10356:2;10338:5;:21::i;:::-;-1:-1:-1;10376:4:8;;9932:455;-1:-1:-1;9932:455:8:o;6766:532:10:-;7144:10;;;7185:30;7144:10;;;;;;;:21;;7185:30;;;;;;;;;;;;;6851:7;;7225:40;1057:11:8;7157:8:10;7225:18;:40::i;2079:144:9:-;2129:13;2180:35;:2;2212:1;2180:22;:35::i;:::-;2161:55;;;;;;;;:::i;3719:157::-;3795:13;3846:3;3858:5;3827:42;;;;;;;;;:::i;:::-;;;;;;;;;;;;;3820:49;;3719:157;;;;:::o;2291:162::-;2345:13;2421:24;2427:8;2432:2;2427:4;:8::i;2421:24::-;2377:69;;;;;;;;:::i;2921:500::-;2973:13;2998:19;3020:26;;;;;;;;;;;;;;;;;;3036:9;3042:2;3036:5;:9::i;:::-;3020:6;:26::i;:::-;2998:48;;3060:15;3072:2;3060:11;:15::i;:::-;3056:359;;;3091:27;3121:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:6;:26::i;:::-;3236:49;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3256:14:9;;;:10;:14;;;;;;;;3091:56;;-1:-1:-1;3204:5:9;;3091:56;;3236:49;;3256:28;;:14;;:26;:28::i;3236:49::-;3168:136;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;3161:143;;;;2921:500;;;:::o;3056:359::-;3361:5;3373:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:6;:25::i;:::-;3342:62;;;;;;;;;:::i;:::-;;;;;;;;;;;;;3335:69;;;2921:500;;;:::o;3942:154::-;4021:13;4072:3;4083:5;4053:36;;;;;;;;;:::i;722:2559:3:-;835:20;961:4;955:11;983:10;980:2285;;;1183:1;1179;1167:10;1163:18;1159:26;1156:1;1152:34;1294:4;1288:11;1278:21;;1666:34;1660:4;1653:48;1793:6;1782:8;1775:16;1771:29;1735:34;1731:70;1725:4;1718:84;1908:4;1900:6;1896:17;1950:13;1945:3;1941:23;2040:623;2092:1;2086:4;2082:12;2074:20;;2154:4;2148:11;2294:4;2286:5;2282:2;2278:14;2274:25;2268:32;2265:1;2257:44;2359:4;2351:5;2347:2;2343:14;2339:25;2333:32;2330:1;2322:44;2423:4;2415:5;2412:1;2408:13;2404:24;2398:31;2395:1;2387:43;2479:4;2472:5;2468:16;2462:23;2459:1;2451:35;;2525:4;2519:11;2514:3;2507:24;2569:1;2564:3;2560:11;2553:18;;2630:3;2625;2622:12;2040:623;2612:33;2702:4;2693:14;2687:4;2680:28;2971:16;2847:1;2831:18;;2828:1;2824:26;2958:11;;;2951:37;;;;3136:1;3077:17;;3070:25;3066:33;;;3123:11;;;;3116:22;3208:21;;3193:37;;980:2285;;722:2559;;;;;:::o;2187:478:5:-;2434:1;2431;2428;2425;2417:6;2413:2;2406:5;2401:35;2391:258;;2542:10;2536:4;2529:24;2630:4;2624;2617:18;5429:1991:4;5536:17;5997:4;5993:9;5986:4;5977:6;5974:1;5970:14;5966:25;5962:41;5955:4;5949:11;5945:59;5938:66;;6075:4;6070:3;6066:14;6060:4;6053:28;6156:1;6151:3;6144:14;6243:3;6330:34;6324:4;6317:48;6413:6;6405;6401:19;6396:3;6392:29;6447:1;6443:6;6482:5;6668:288;6788:2;6778:13;;6772:20;6705:11;;;;6768:1;6759:11;;6751:42;6847:2;6840:4;6837:1;6833:12;6829:21;6823:28;6818:3;6810:42;6881:1;6877:12;6906:36;;;6668:288;6906:36;6973:4;6970:223;;;7086:10;7080:4;7073:24;7174:4;7168;7161:18;6970:223;-1:-1:-1;;;7268:13:4;;;7355:14;;;;7382:22;;;7355:14;5429:1991;-1:-1:-1;;5429:1991:4:o;2532:305:9:-;2581:13;2759:35;:2;2791:1;2759:22;:35::i;:::-;2613:217;;;;;;;;:::i;3490:183::-;3569:13;3634:3;3654:5;3601:65;;;;;;;;;:::i;11189:436:4:-;11248:17;11283:26;11303:5;11283:19;:26::i;:::-;11406:10;;11468:6;11456:19;;11418:1;11402:18;11521:11;;;;11566:22;;;11521:11;11189:436;-1:-1:-1;;11189:436:4:o;11762:1294::-;11829:17;11937:4;11931:11;11924:18;;12264:4;12259:3;12255:14;12249:4;12242:28;12355:34;12349:4;12342:48;12420:1;12415:3;12411:11;12404:18;;12447:2;12442:3;12435:15;12482:4;12477:3;12473:14;12519:1;12514:2;12511:1;12507:10;12500:21;12552:5;12548:2;12544:14;12535:23;;12755:1;12740:300;12805:1;12802;12798:9;12795:1;12791:17;12845:5;12842:1;12837:14;12903:2;12897:4;12893:13;12887:20;12883:1;12880;12876:9;12868:40;12949:4;12946:1;12942:12;12936:19;12933:1;12925:31;-1:-1:-1;;12985:1:4;12978:9;;;13007;;12740:300;13004:22;12744:14;;11762:1294;;;:::o;14:332:16:-;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;180:9;167:23;230:66;223:5;219:78;212:5;209:89;199:117;;312:1;309;302:12;543:180;602:6;655:2;643:9;634:7;630:23;626:32;623:52;;;671:1;668;661:12;623:52;-1:-1:-1;694:23:16;;543:180;-1:-1:-1;543:180:16:o;728:367::-;791:8;801:6;855:3;848:4;840:6;836:17;832:27;822:55;;873:1;870;863:12;822:55;-1:-1:-1;896:20:16;;939:18;928:30;;925:50;;;971:1;968;961:12;925:50;1008:4;1000:6;996:17;984:29;;1068:3;1061:4;1051:6;1048:1;1044:14;1036:6;1032:27;1028:38;1025:47;1022:67;;;1085:1;1082;1075:12;1022:67;728:367;;;;;:::o;1100:841::-;1231:6;1239;1247;1255;1263;1316:2;1304:9;1295:7;1291:23;1287:32;1284:52;;;1332:1;1329;1322:12;1284:52;1372:9;1359:23;1401:18;1442:2;1434:6;1431:14;1428:34;;;1458:1;1455;1448:12;1428:34;1497:70;1559:7;1550:6;1539:9;1535:22;1497:70;:::i;:::-;1586:8;;-1:-1:-1;1471:96:16;-1:-1:-1;1674:2:16;1659:18;;1646:32;;-1:-1:-1;1690:16:16;;;1687:36;;;1719:1;1716;1709:12;1687:36;;1758:72;1822:7;1811:8;1800:9;1796:24;1758:72;:::i;:::-;1100:841;;;;-1:-1:-1;1849:8:16;1931:2;1916:18;1903:32;;1100:841;-1:-1:-1;;;;1100:841:16:o;1946:642::-;2111:2;2163:21;;;2233:13;;2136:18;;;2255:22;;;2082:4;;2111:2;2334:15;;;;2308:2;2293:18;;;2082:4;2377:185;2391:6;2388:1;2385:13;2377:185;;;2466:13;;2459:21;2452:29;2440:42;;2537:15;;;;2502:12;;;;2413:1;2406:9;2377:185;;;-1:-1:-1;2579:3:16;;1946:642;-1:-1:-1;;;;;;1946:642:16:o;2593:250::-;2678:1;2688:113;2702:6;2699:1;2696:13;2688:113;;;2778:11;;;2772:18;2759:11;;;2752:39;2724:2;2717:10;2688:113;;;-1:-1:-1;;2835:1:16;2817:16;;2810:27;2593:250::o;2848:455::-;2997:2;2986:9;2979:21;2960:4;3029:6;3023:13;3072:6;3067:2;3056:9;3052:18;3045:34;3088:79;3160:6;3155:2;3144:9;3140:18;3135:2;3127:6;3123:15;3088:79;:::i;:::-;3219:2;3207:15;3224:66;3203:88;3188:104;;;;3294:2;3184:113;;2848:455;-1:-1:-1;;2848:455:16:o;3539:196::-;3607:20;;3667:42;3656:54;;3646:65;;3636:93;;3725:1;3722;3715:12;3740:254;3808:6;3816;3869:2;3857:9;3848:7;3844:23;3840:32;3837:52;;;3885:1;3882;3875:12;3837:52;3908:29;3927:9;3908:29;:::i;:::-;3898:39;3984:2;3969:18;;;;3956:32;;-1:-1:-1;;;3740:254:16:o;4181:160::-;4246:20;;4302:13;;4295:21;4285:32;;4275:60;;4331:1;4328;4321:12;4346:505;4438:6;4446;4454;4507:2;4495:9;4486:7;4482:23;4478:32;4475:52;;;4523:1;4520;4513:12;4475:52;4563:9;4550:23;4596:18;4588:6;4585:30;4582:50;;;4628:1;4625;4618:12;4582:50;4667:70;4729:7;4720:6;4709:9;4705:22;4667:70;:::i;:::-;4756:8;;-1:-1:-1;4641:96:16;-1:-1:-1;4810:35:16;;-1:-1:-1;4841:2:16;4826:18;;4810:35;:::i;:::-;4800:45;;4346:505;;;;;:::o;4856:328::-;4933:6;4941;4949;5002:2;4990:9;4981:7;4977:23;4973:32;4970:52;;;5018:1;5015;5008:12;4970:52;5041:29;5060:9;5041:29;:::i;:::-;5031:39;;5089:38;5123:2;5112:9;5108:18;5089:38;:::i;:::-;5079:48;;5174:2;5163:9;5159:18;5146:32;5136:42;;4856:328;;;;;:::o;5189:322::-;5266:6;5274;5282;5335:2;5323:9;5314:7;5310:23;5306:32;5303:52;;;5351:1;5348;5341:12;5303:52;5374:29;5393:9;5374:29;:::i;:::-;5364:39;5450:2;5435:18;;5422:32;;-1:-1:-1;5501:2:16;5486:18;;;5473:32;;5189:322;-1:-1:-1;;;5189:322:16:o;5698:632::-;5869:2;5921:21;;;5991:13;;5894:18;;;6013:22;;;5840:4;;5869:2;6092:15;;;;6066:2;6051:18;;;5840:4;6135:169;6149:6;6146:1;6143:13;6135:169;;;6210:13;;6198:26;;6279:15;;;;6244:12;;;;6171:1;6164:9;6135:169;;6335:505;6430:6;6438;6446;6499:2;6487:9;6478:7;6474:23;6470:32;6467:52;;;6515:1;6512;6505:12;6467:52;6555:9;6542:23;6588:18;6580:6;6577:30;6574:50;;;6620:1;6617;6610:12;6574:50;6659:70;6721:7;6712:6;6701:9;6697:22;6659:70;:::i;:::-;6748:8;;6633:96;;-1:-1:-1;6830:2:16;6815:18;;;;6802:32;;6335:505;-1:-1:-1;;;;6335:505:16:o;6845:186::-;6904:6;6957:2;6945:9;6936:7;6932:23;6928:32;6925:52;;;6973:1;6970;6963:12;6925:52;6996:29;7015:9;6996:29;:::i;7619:248::-;7684:6;7692;7745:2;7733:9;7724:7;7720:23;7716:32;7713:52;;;7761:1;7758;7751:12;7713:52;7797:9;7784:23;7774:33;;7826:35;7857:2;7846:9;7842:18;7826:35;:::i;:::-;7816:45;;7619:248;;;;;:::o;7872:254::-;7937:6;7945;7998:2;7986:9;7977:7;7973:23;7969:32;7966:52;;;8014:1;8011;8004:12;7966:52;8037:29;8056:9;8037:29;:::i;:::-;8027:39;;8085:35;8116:2;8105:9;8101:18;8085:35;:::i;8131:437::-;8217:6;8225;8278:2;8266:9;8257:7;8253:23;8249:32;8246:52;;;8294:1;8291;8284:12;8246:52;8334:9;8321:23;8367:18;8359:6;8356:30;8353:50;;;8399:1;8396;8389:12;8353:50;8438:70;8500:7;8491:6;8480:9;8476:22;8438:70;:::i;:::-;8527:8;;8412:96;;-1:-1:-1;8131:437:16;-1:-1:-1;;;;8131:437:16:o;8573:579::-;8677:6;8685;8693;8701;8754:2;8742:9;8733:7;8729:23;8725:32;8722:52;;;8770:1;8767;8760:12;8722:52;8793:29;8812:9;8793:29;:::i;:::-;8783:39;;8873:2;8862:9;8858:18;8845:32;8900:18;8892:6;8889:30;8886:50;;;8932:1;8929;8922:12;8886:50;8971:70;9033:7;9024:6;9013:9;9009:22;8971:70;:::i;:::-;8573:579;;9060:8;;-1:-1:-1;8945:96:16;;9142:2;9127:18;9114:32;;8573:579;-1:-1:-1;;;;8573:579:16:o;9157:248::-;9225:6;9233;9286:2;9274:9;9265:7;9261:23;9257:32;9254:52;;;9302:1;9299;9292:12;9254:52;-1:-1:-1;;9325:23:16;;;9395:2;9380:18;;;9367:32;;-1:-1:-1;9157:248:16:o;9410:808::-;9507:6;9515;9523;9531;9539;9592:3;9580:9;9571:7;9567:23;9563:33;9560:53;;;9609:1;9606;9599:12;9560:53;9632:29;9651:9;9632:29;:::i;:::-;9622:39;;9680:38;9714:2;9703:9;9699:18;9680:38;:::i;:::-;9670:48;;9765:2;9754:9;9750:18;9737:32;9727:42;;9820:2;9809:9;9805:18;9792:32;9843:18;9884:2;9876:6;9873:14;9870:34;;;9900:1;9897;9890:12;9870:34;9938:6;9927:9;9923:22;9913:32;;9983:7;9976:4;9972:2;9968:13;9964:27;9954:55;;10005:1;10002;9995:12;9954:55;10045:2;10032:16;10071:2;10063:6;10060:14;10057:34;;;10087:1;10084;10077:12;10057:34;10132:7;10127:2;10118:6;10114:2;10110:15;10106:24;10103:37;10100:57;;;10153:1;10150;10143:12;10100:57;9410:808;;;;-1:-1:-1;9410:808:16;;-1:-1:-1;10184:2:16;10176:11;;10206:6;9410:808;-1:-1:-1;;;9410:808:16:o;10223:316::-;10300:6;10308;10316;10369:2;10357:9;10348:7;10344:23;10340:32;10337:52;;;10385:1;10382;10375:12;10337:52;-1:-1:-1;;10408:23:16;;;10478:2;10463:18;;10450:32;;-1:-1:-1;10529:2:16;10514:18;;;10501:32;;10223:316;-1:-1:-1;10223:316:16:o;10749:260::-;10817:6;10825;10878:2;10866:9;10857:7;10853:23;10849:32;10846:52;;;10894:1;10891;10884:12;10846:52;10917:29;10936:9;10917:29;:::i;:::-;10907:39;;10965:38;10999:2;10988:9;10984:18;10965:38;:::i;11199:354::-;11268:34;11335:10;;;11323;;;11319:27;;11358:12;;;11355:192;;;11403:77;11400:1;11393:88;11504:4;11501:1;11494:15;11532:4;11529:1;11522:15;11355:192;;11199:354;;;;:::o;11558:184::-;11610:77;11607:1;11600:88;11707:4;11704:1;11697:15;11731:4;11728:1;11721:15;12845:184;12897:77;12894:1;12887:88;12994:4;12991:1;12984:15;13018:4;13015:1;13008:15;13034:451;13286:31;13281:3;13274:44;13256:3;13347:6;13341:13;13363:75;13431:6;13426:2;13421:3;13417:12;13410:4;13402:6;13398:17;13363:75;:::i;:::-;13458:16;;;;13476:2;13454:25;;13034:451;-1:-1:-1;;13034:451:16:o;13563:1911::-;14456:3;14451;14444:16;14426:3;14489:6;14483:13;14505:74;14572:6;14568:1;14563:3;14559:11;14552:4;14544:6;14540:17;14505:74;:::i;:::-;14607:6;14602:3;14598:16;14588:26;;14633:3;14664:2;14660:1;14656:2;14652:10;14645:22;14698:6;14692:13;14714:75;14780:8;14776:1;14772:2;14768:10;14761:4;14753:6;14749:17;14714:75;:::i;:::-;14849:1;14808:17;;14841:10;;;14834:22;;;14881:13;;14903:75;14881:13;14965:1;14957:10;;14950:4;14938:17;;14903:75;:::i;:::-;15038:1;14997:17;;15030:10;;;15023:22;;;15070:13;;15092:75;15070:13;15154:1;15146:10;;15139:4;15127:17;;15092:75;:::i;:::-;15227:1;15186:17;;15219:10;;;15212:22;15259:13;;15281:75;15259:13;15343:1;15335:10;;15328:4;15316:17;;15281:75;:::i;:::-;15375:17;15401:41;15439:1;15431:10;;13552:3;13540:16;;13490:68;15401:41;15466:1;15458:10;;13563:1911;-1:-1:-1;;;;;;;13563:1911:16:o;15479:420::-;15730:3;15725;15718:16;15700:3;15763:6;15757:13;15779:74;15846:6;15842:1;15837:3;15833:11;15826:4;15818:6;15814:17;15779:74;:::i;:::-;15873:16;;;;15891:1;15869:24;;15479:420;-1:-1:-1;;15479:420:16:o;15904:1037::-;16353:3;16381:66;16468:2;16463:3;16456:15;16500:6;16494:13;16516:74;16583:6;16579:1;16574:3;16570:11;16563:4;16555:6;16551:17;16516:74;:::i;:::-;16653:66;16649:1;16609:16;;;16641:10;;;16634:86;16745:13;;16767:75;16745:13;16829:1;16821:10;;16814:4;16802:17;;16767:75;:::i;:::-;16861:17;16902:1;16894:10;;16887:22;;;;16933:1;16925:10;;;-1:-1:-1;;;;15904:1037:16:o;16946:448::-;17198:28;17193:3;17186:41;17168:3;17256:6;17250:13;17272:75;17340:6;17335:2;17330:3;17326:12;17319:4;17311:6;17307:17;17272:75;:::i;:::-;17367:16;;;;17385:2;17363:25;;16946:448;-1:-1:-1;;16946:448:16:o;17399:1239::-;18016:3;18011;18004:16;17986:3;18049:6;18043:13;18065:74;18132:6;18128:1;18123:3;18119:11;18112:4;18104:6;18100:17;18065:74;:::i;:::-;18167:6;18162:3;18158:16;18148:26;;18193:3;18224:2;18220:1;18216:2;18212:10;18205:22;18258:6;18252:13;18274:75;18340:8;18336:1;18332:2;18328:10;18321:4;18313:6;18309:17;18274:75;:::i;:::-;18409:1;18368:17;;18401:10;;;18394:22;18441:13;;18463:75;18441:13;18525:1;18517:10;;18510:4;18498:17;;18463:75;:::i;:::-;18602:3;18598:1;18557:17;;;;18590:10;;;18583:23;18630:1;18622:10;;17399:1239;-1:-1:-1;;;;;17399:1239:16:o;18643:891::-;19122:3;19117;19110:16;19092:3;19155:6;19149:13;19171:74;19238:6;19234:1;19229:3;19225:11;19218:4;19210:6;19206:17;19171:74;:::i;:::-;19308:3;19304:1;19264:16;;;19296:10;;;19289:23;19337:13;;19359:75;19337:13;19421:1;19413:10;;19406:4;19394:17;;19359:75;:::i;:::-;19498:3;19494:1;19453:17;;;;19486:10;;;19479:23;19526:1;19518:10;;18643:891;-1:-1:-1;;;;18643:891:16:o;19539:874::-;19928:66;19923:3;19916:79;19898:3;20024:6;20018:13;20040:74;20107:6;20103:1;20098:3;20094:11;20087:4;20079:6;20075:17;20040:74;:::i;:::-;20177:66;20173:1;20133:16;;;20165:10;;;20158:86;20269:13;;20291:75;20269:13;20353:1;20345:10;;20338:4;20326:17;;20291:75;:::i;:::-;20386:17;20405:1;20382:25;;19539:874;-1:-1:-1;;;;19539:874:16:o;20418:996::-;20771:66;20766:3;20759:79;20868:66;20863:2;20858:3;20854:12;20847:88;20965:66;20960:2;20955:3;20951:12;20944:88;21062:66;21057:2;21052:3;21048:12;21041:88;20741:3;21158:6;21152:13;21174:74;21241:6;21235:3;21230;21226:13;21221:2;21213:6;21209:15;21174:74;:::i;:::-;21313:66;21307:3;21267:16;;;;21299:12;;;21292:88;-1:-1:-1;21404:3:16;21396:12;;20418:996;-1:-1:-1;20418:996:16:o;21419:1087::-;21900:66;21895:3;21888:79;21870:3;21996:6;21990:13;22012:75;22080:6;22075:2;22070:3;22066:12;22059:4;22051:6;22047:17;22012:75;:::i;:::-;22151:66;22146:2;22106:16;;;22138:11;;;22131:87;22243:13;;22265:76;22243:13;22327:2;22319:11;;22312:4;22300:17;;22265:76;:::i;:::-;22406:66;22401:2;22360:17;;;;22393:11;;;22386:87;22497:2;22489:11;;21419:1087;-1:-1:-1;;;;21419:1087:16:o

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

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