ETH Price: $3,116.30 (+1.56%)
Gas: 6 Gwei

Contract Diff Checker

Contract Name:
ButerinCards

Contract Source Code:

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT

/*
 * @dev Modified from original Array.sol from Clement Walter <[email protected]>
 */
pragma solidity ^0.8.0;

error EmptyArray();
error GlueOutOfBounds(uint256 length);

library Array {
    function join(bytes[] memory a, bytes memory glue) private pure returns (bytes memory) {
        uint256 inputPointer;
        uint256 gluePointer;

        assembly {
            inputPointer := a
            gluePointer := glue
        }
        return _joinReferenceType(inputPointer, gluePointer);
    }

    function join(bytes[] memory a) internal pure returns (bytes memory) {
        return join(a, bytes(""));
    }

    function _joinReferenceType(
        uint256 inputPointer,
        uint256 gluePointer
    ) private pure returns (bytes memory tempBytes) {
        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Skip the first 32 bytes where we will store the length of the result
            let memoryPointer := add(tempBytes, 0x20)

            // Load glue
            let glueLength := mload(gluePointer)
            if gt(glueLength, 0x20) {
                revert(gluePointer, 0x20)
            }
            let glue := mload(add(gluePointer, 0x20))

            // Load the length (first 32 bytes)
            let inputLength := mload(inputPointer)
            let inputData := add(inputPointer, 0x20)
            let end := add(inputData, mul(inputLength, 0x20))

            // Initialize the length of the final string
            let stringLength := 0

            // Iterate over all strings (a string is itself an array).
            for {
                let pointer := inputData
            } lt(pointer, end) {
                pointer := add(pointer, 0x20)
            } {
                let currentStringArray := mload(pointer)
                let currentStringLength := mload(currentStringArray)
                stringLength := add(stringLength, currentStringLength)
                let currentStringBytesCount := add(
                    div(currentStringLength, 0x20),
                    gt(mod(currentStringLength, 0x20), 0)
                )

                let currentPointer := add(currentStringArray, 0x20)

                for {
                    let copiedBytesCount := 0
                } lt(copiedBytesCount, currentStringBytesCount) {
                    copiedBytesCount := add(copiedBytesCount, 1)
                } {
                    mstore(add(memoryPointer, mul(copiedBytesCount, 0x20)), mload(currentPointer))
                    currentPointer := add(currentPointer, 0x20)
                }
                memoryPointer := add(memoryPointer, currentStringLength)
                mstore(memoryPointer, glue)
                memoryPointer := add(memoryPointer, glueLength)
            }

            mstore(tempBytes, add(stringLength, mul(sub(inputLength, 1), glueLength)))
            mstore(0x40, and(add(memoryPointer, 31), not(31)))
        }
        return tempBytes;
    }
}

//SPDX-License-Identifier: MIT

/// @title JPEG Mining
/// @author Xatarrer
/// @notice Unaudited
pragma solidity ^0.8.0;

import "./Array.sol";
import "./ButerinCardsLib.sol";
import "./ButerinCardsBackA.sol";
import "./ButerinCardsBackB.sol";
import "./ERC721Enumerable.sol";
import "./SSTORE2.sol";
import "./Ownable.sol";
import "./LibString.sol";
import "./MerkleProof.sol";

contract ButerinCards is ERC721Enumerable, Ownable {
    event Mined(
        address indexed minerAddress,
        uint256 indexed uploadedKB,
        uint256 indexed tokenId,
        uint8 phaseId,
        uint16 tokenIdWithinPhase,
        uint8 quoteId,
        uint8 bgDirectionId,
        uint8 bgPaletteId,
        uint16 lastTokenIdInScan,
        uint32 Nbytes,
        uint8 Nicons,
        uint32 seed
    );

    // State variables
    mapping(address => uint256) public Nmined; // Number of cards mined by an address
    uint256[] public chunks; // Array of tightly pack card data and metadata
    string public baseURLAnimation =
        "https://yellow-immense-spider-81.mypinata.cloud/ipfs/Qmczf1nd4uHzLxWRZ68pc2PWXBPKcRkoc39ZgQkB8X5bgy/index.html";

    // Constants
    string private constant _NAME = "Buterin Cards";
    string private constant _SYMBOL = "VITALIK";
    bytes32 private immutable _ROOT;
    uint256 public immutable TOKEN_ID_FIRST_BLUE_CHROMINANCE; // TokenId of first blue chrominance chunk
    uint256 public immutable TOKEN_ID_FIRST_RED_CHROMINANCE; // TokenId of first red chrominance chunk
    uint256 public immutable N_EMPTY_BLUE_COLOR_CHUNKS;
    uint256 public immutable N_EMPTY_RED_COLOR_CHUNKS;
    address public immutable JPEG_HEADER_POINTER; // Pointer to JPEG header

    constructor(
        bytes32 root,
        string memory jpegHeader,
        uint256 tokenIdFirstBlueChrominance,
        uint256 tokenIdFirstRedChrominance,
        uint256 NemptyBlueColorChunks,
        uint256 NemptyRedColorChunks
    ) ERC721(_NAME, _SYMBOL) {
        _ROOT = root;
        JPEG_HEADER_POINTER = SSTORE2.write(bytes(jpegHeader));
        TOKEN_ID_FIRST_BLUE_CHROMINANCE = tokenIdFirstBlueChrominance;
        TOKEN_ID_FIRST_RED_CHROMINANCE = tokenIdFirstRedChrominance;
        N_EMPTY_BLUE_COLOR_CHUNKS = NemptyBlueColorChunks;
        N_EMPTY_RED_COLOR_CHUNKS = NemptyRedColorChunks;
    }

    function setBaseURLAnimation(string calldata newBaseURLAnimation) external onlyOwner {
        baseURLAnimation = newBaseURLAnimation;
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        require(_exists(tokenId), "Token does not exist");

        // Retrieve chunk data
        ButerinCardsLib.ChunkUnpacked memory chunk = unpackChunk(tokenId);
        string memory cardIdStr = LibString.toString(tokenId + 1);
        string memory NiconsStr = LibString.toString(chunk.Nicons);
        string memory NkilobytesStr = LibString.toString(chunk.Nbytes / 1024);

        bytes[] memory bytesSegments = new bytes[](43);
        bytesSegments[0] = bytes("data:application/json;charset=UTF-8,%7B%22name%22%3A%22Buterin%20Card%20%23");
        bytesSegments[1] = bytes(cardIdStr);
        bytesSegments[2] = bytes(
            "%22%2C%22description%22%3A%22Introducing%20the%20Buterin%20Cards%2C%20a%20unique%20on-chain%20collection%20of%202%2C015%20cards%20celebrating%20Ethereum's%20co-founder%2C%20Vitalik%20Buterin.%20Inspired%20by%20the%20iconic%20Nakamoto%20Cards%20on%20Bitcoin%2C%20the%20Buterin%20Cards%20aim%20to%20pay%20tribute%20to%20Vitalik's%20immense%20contributions%20to%20blockchain%20technology.%20%20%5Cn%5CnPermanently%20stored%20on%20the%20Ethereum%20blockchain%2C%20these%20cards%20are%20the%20result%20of%20a%20collaborative%20effort%20by%20JPEG%20miners%20who%20work%20together%20to%20upload%20each%20card's%20data%20on-chain.%20The%20face%20side%20of%20each%20card%20features%20an%20HTML%20and%20SVG-coded%20frame%20surrounding%20a%20JPEG%20image%20of%20Vitalik%20Buterin%2C%20designed%20by%20Xatarrer.%20At%20over%2010%20MB%2C%20this%20image%20holds%20the%20record%20for%20the%20largest%20stored%20JPEG%20on-chain%20at%20the%20time%20of%20minting.%20%20%5Cn%5CnAdding%20to%20the%20uniqueness%20of%20the%20cards%2C%20the%20face%20side%20background%2C%20designed%20by%20Pawe%C5%82%20Dudko%2C%20showcases%20mesmerizing%2C%20dynamically%20moving%20rays%20of%20color.%20As%20a%20fusion%20of%20profile%20picture%20(pfp)%20NFTs%20and%20generative%20art%2C%20the%20Buterin%20Cards%20possess%20randomly-selected%20attributes%20during%20minting%2C%20such%20as%20one%20of%2045%20possible%20quotes%20from%20Vitalik.%20%20%5Cn%5CnThe%20Buterin%20Cards%20are%20the%20second%20collection%20to%20utilize%20a%20technique%20called%20JPEG%20Mining.%20In%20this%20process%2C%20the%20miners%20are%20responsible%20for%20uploading%20the%20NFT%20components%2C%20including%20the%20HTML%2C%20SVG%2C%20and%20Vitalik%20JPEG.%20Using%20Progressive%20JPEG%20technology%2C%20the%20image%20is%20revealed%20as%20it%20is%20mined%2C%20and%20miners%20are%20rewarded%20with%20a%20Buterin%20Card%20for%20their%20efforts.%20%20%5Cn%5CnThe%20JPEG%20mining%20process%20of%20the%20Buterin%20Cards%20consists%20of%20six%20phases%3A%20%20%5Cn%5Cu270F%5CuFE0F%20Pencil%20Drawing%3A%20Miners%20upload%20the%20HTML%20and%20SVG%2C%20receiving%20a%20card%20with%20a%20hand-drawn%20vectorized%20SVG%20version%20of%20the%20JPEG%2C%20as%20no%20JPEG%20data%20is%20available%20yet.%20%20%5Cn%5CuD83D%5CuDD33%20Black%20%26%20White%3A%20The%20intensity%20component%20of%20the%20progressive%20JPEG%20is%20uploaded%2C%20rendering%20the%20image%20in%20pure%20black%20and%20white.%20%20%5Cn%5CuD83C%5CuDF2B%5CuFE0F%20Grey%20Shades%3A%20Additional%20bits%20for%20the%20intensity%20component%20reveal%20a%20range%20of%20grey%20tones.%20%20%5Cn%5CuD83D%5CuDFE6%20Blue%20Chroma%3A%20The%20blue%20chroma%20is%20uploaded%2C%20introducing%20blue%20and%20green%20hues%20to%20the%20JPEG.%20%20%5Cn%5CuD83D%5CuDFE5%20Red%20Chroma%3A%20The%20red%20chroma%20is%20added%2C%20infusing%20red%20and%20pink%20shades%20into%20the%20image.%20%20%5Cn%5CuD83C%5CuDF04%20In%20the%20final%20phase%2C%20the%20AC%20components%20are%20uploaded%2C%20enhancing%20the%20image%20resolution.%22%2C%22attributes%22%3A%5B%7B%22trait_type%22%3A%22Quote%20Title%22%2C%22value%22%3A%22"
        );
        bytesSegments[3] = bytes(ButerinCardsLib.quoteName(chunk.quoteId));
        bytesSegments[4] = bytes("%22%7D%2C%7B%22trait_type%22%3A%22Phase%22%2C%22value%22%3A%22");
        bytesSegments[5] = bytes(ButerinCardsLib.phaseName(chunk.phaseId));
        bytesSegments[6] = bytes("%22%7D%2C%7B%22trait_type%22%3A%22Background%20Direction%22%2C%22value%22%3A%22");
        bytesSegments[7] = bytes(ButerinCardsLib.bgDirection(chunk.bgDirectionId));
        bytesSegments[8] = bytes("%22%7D%2C%7B%22trait_type%22%3A%22Background%20Palette%22%2C%22value%22%3A%22");
        bytesSegments[9] = bytes(ButerinCardsLib.bgPalette(chunk.bgPaletteId));
        bytesSegments[10] = bytes(
            "%22%7D%2C%7B%22display_type%22%3A%22boost_number%22%2C%22trait_type%22%3A%22Number%20of%20Icons%22%2C%22value%22%3A"
        );
        bytesSegments[11] = bytes(NiconsStr);
        bytesSegments[12] = bytes("%7D%2C%7B%22trait_type%22%3A%22Uploaded%20%5BKB%5D%22%2C%22value%22%3A");
        bytesSegments[13] = bytes(NkilobytesStr);
        bytesSegments[14] = bytes("%7D%5D%2C%22animation_url%22%3A%22");
        bytesSegments[15] = bytes(baseURLAnimation);
        bytesSegments[16] = bytes("?cardId=");
        bytesSegments[17] = bytes(cardIdStr);
        bytesSegments[18] = bytes("&phaseId=");
        bytesSegments[19] = bytes(LibString.toString(chunk.phaseId));
        bytesSegments[20] = bytes("&tokenIdWithinPhase=");
        bytesSegments[21] = bytes(LibString.toString(chunk.tokenIdWithinPhase));
        bytesSegments[22] = bytes("&kiloBytes=");
        bytesSegments[23] = bytes(NkilobytesStr);
        bytesSegments[24] = bytes("&quoteId=");
        bytesSegments[25] = bytes(LibString.toString(chunk.quoteId));
        bytesSegments[26] = bytes("&bgDirection=");
        bytesSegments[27] = bytes(LibString.toString(chunk.bgDirectionId));
        bytesSegments[28] = bytes("&bgPalette=");
        bytesSegments[29] = bytes(LibString.toString(chunk.bgPaletteId));
        bytesSegments[30] = bytes("&seed=");
        bytesSegments[31] = bytes(LibString.toString(chunk.seed));
        bytesSegments[32] = bytes("&Nicons=");
        bytesSegments[33] = bytes(NiconsStr);
        bytesSegments[34] = bytes("%22%2C%22image%22%3A%22");
        bytesSegments[35] = bytes(ButerinCardsBackA.cardBackPiece0());
        bytesSegments[36] = bytes(ButerinCardsBackA.cardBackPiece1(chunk.Nicons));
        bytesSegments[37] = bytes(ButerinCardsBackB.cardBackPiece2());
        bytesSegments[38] = bytes(chunk.quoteId < 8 ? ".2" : "0");
        bytesSegments[39] = bytes(ButerinCardsBackB.cardBackPiece3());
        bytesSegments[40] = bytes(ButerinCardsLib.cardBackPiece4());
        bytesSegments[41] = bytes(ButerinCardsLib.cardBackPiece5(chunk.phaseId));
        bytesSegments[42] = bytes("%22%7D");

        return string(Array.join(bytesSegments));
    }

    function onchainAnimation(uint256 tokenId) public view returns (string memory) {
        require(_exists(tokenId), "Token does not exist");

        // Retrieve chunk data
        ButerinCardsLib.ChunkUnpacked memory chunk = unpackChunk(tokenId);

        // Communicates to the miner that its card is not fully uploaded just yet
        if (chunk.lastTokenIdInScan >= totalSupply()) ButerinCardsLib.cardNotAvailable(chunk.lastTokenIdInScan);

        uint256 NHTMLChunks = unpackChunk(0).lastTokenIdInScan + 1;
        uint256 NJPEGChunks;

        /**
            Let's fuse all the parts together:
            If phaseId == 0, then
                1. _HTML_BEGINNING
                2. User parameters separated by commas
                3...M+1. HTML scans
            If phaseId > 0, then
                1. _HTML_BEGINNING
                2. User parameters separated by commas
                3. JPEG header
                4...(N+2). JPEG scans
                N+4. JPEG footer
                N+5...N+M+4. HTML scans
            Know that
                N + M = tokenId +1
         */

        // Create big array of bytes and copy necessary segments
        bytes[] memory bytesSegments = new bytes[](
            chunk.phaseId == 0 ? chunk.lastTokenIdInScan + 3 : chunk.phaseId == 1 || chunk.phaseId == 2
                ? chunk.lastTokenIdInScan + 5
                : chunk.phaseId == 3 // Ads red empty chroma NOT blue empty chroma NOR red chroma
                ? chunk.lastTokenIdInScan + 5 - N_EMPTY_BLUE_COLOR_CHUNKS
                : chunk.phaseId == 4 // Add empty blue chroma NOT red empty chroma NOR blue chroma
                ? chunk.lastTokenIdInScan +
                    5 +
                    TOKEN_ID_FIRST_BLUE_CHROMINANCE -
                    TOKEN_ID_FIRST_RED_CHROMINANCE -
                    N_EMPTY_RED_COLOR_CHUNKS
                : chunk.lastTokenIdInScan + 5 - N_EMPTY_BLUE_COLOR_CHUNKS - N_EMPTY_RED_COLOR_CHUNKS
        );

        bytesSegments[0] = htmlHeader();

        uint256 ind;
        if (chunk.phaseId > 0) {
            // JPEG header
            bytesSegments[2] = SSTORE2.read(JPEG_HEADER_POINTER);

            // Number JPEG chunks (includes all chromas (including empty) regardless)
            NJPEGChunks = chunk.lastTokenIdInScan + 1 - NHTMLChunks;

            bytes memory tempChunk;
            // Add JPEG chunks
            for (
                uint i = NHTMLChunks + N_EMPTY_BLUE_COLOR_CHUNKS + N_EMPTY_RED_COLOR_CHUNKS;
                i < NHTMLChunks + NJPEGChunks;
                i++
            ) {
                if (chunk.phaseId != 4 || i < TOKEN_ID_FIRST_BLUE_CHROMINANCE) {
                    tempChunk = SSTORE2.read(unpackChunk(i).dataPointer);
                    ind = i - NHTMLChunks - N_EMPTY_BLUE_COLOR_CHUNKS - N_EMPTY_RED_COLOR_CHUNKS + 3;
                    bytesSegments[ind] = tempChunk;
                } else if (i >= TOKEN_ID_FIRST_RED_CHROMINANCE) {
                    // We skip blue chroma in phase 4
                    tempChunk = SSTORE2.read(unpackChunk(i).dataPointer);
                    ind =
                        i -
                        NHTMLChunks -
                        N_EMPTY_BLUE_COLOR_CHUNKS -
                        N_EMPTY_RED_COLOR_CHUNKS +
                        TOKEN_ID_FIRST_BLUE_CHROMINANCE -
                        TOKEN_ID_FIRST_RED_CHROMINANCE +
                        3;
                    bytesSegments[ind] = tempChunk;
                }
            }

            // Add empty blue color chunks if necessary
            for (uint256 i = NHTMLChunks; i < NHTMLChunks + N_EMPTY_BLUE_COLOR_CHUNKS; i++) {
                if (chunk.phaseId == 1 || chunk.phaseId == 2) {
                    // B&W, Grey Tones and Red Chroma phases need empty blue chroma
                    tempChunk = SSTORE2.read(unpackChunk(i).dataPointer);
                    ind = i + NJPEGChunks - NHTMLChunks - N_EMPTY_BLUE_COLOR_CHUNKS - N_EMPTY_RED_COLOR_CHUNKS + 3;
                    bytesSegments[ind] = tempChunk;
                } else if (chunk.phaseId == 4) {
                    // Red Chroma phase needs empty blue chroma
                    tempChunk = SSTORE2.read(unpackChunk(i).dataPointer);
                    ind =
                        i +
                        NJPEGChunks -
                        NHTMLChunks -
                        N_EMPTY_BLUE_COLOR_CHUNKS -
                        N_EMPTY_RED_COLOR_CHUNKS +
                        TOKEN_ID_FIRST_BLUE_CHROMINANCE -
                        TOKEN_ID_FIRST_RED_CHROMINANCE +
                        3;
                    bytesSegments[ind] = tempChunk;
                }
            }

            // Add empty red color chunks if necessary
            for (
                uint i = NHTMLChunks + N_EMPTY_BLUE_COLOR_CHUNKS;
                i < NHTMLChunks + N_EMPTY_BLUE_COLOR_CHUNKS + N_EMPTY_RED_COLOR_CHUNKS;
                i++
            ) {
                if (chunk.phaseId == 1 || chunk.phaseId == 2) {
                    // B&W and Grey Tones phases need empty red chroma
                    tempChunk = SSTORE2.read(unpackChunk(i).dataPointer);
                    ind = i + NJPEGChunks - NHTMLChunks - N_EMPTY_BLUE_COLOR_CHUNKS - N_EMPTY_RED_COLOR_CHUNKS + 3;
                    bytesSegments[ind] = tempChunk;
                } else if (chunk.phaseId == 3) {
                    // Blue Chroma phase needs empty red chroma
                    tempChunk = SSTORE2.read(unpackChunk(i).dataPointer);
                    ind = i + NJPEGChunks - NHTMLChunks - 2 * N_EMPTY_BLUE_COLOR_CHUNKS - N_EMPTY_RED_COLOR_CHUNKS + 3;
                    bytesSegments[ind] = tempChunk;
                }
            }

            // JPEG footer
            bytesSegments[ind + 1] = jpegFooter();

            // Correct number JPEG chunks (exclude empty chroma and blue chroma if necessary)
            if (chunk.phaseId == 3 || chunk.phaseId == 5) NJPEGChunks -= N_EMPTY_BLUE_COLOR_CHUNKS;
            if (chunk.phaseId >= 4) NJPEGChunks -= N_EMPTY_RED_COLOR_CHUNKS;
            if (chunk.phaseId == 4) NJPEGChunks -= TOKEN_ID_FIRST_RED_CHROMINANCE - TOKEN_ID_FIRST_BLUE_CHROMINANCE;
        }

        // HTML chunks
        for (uint256 i = 0; i < NHTMLChunks; i++) {
            ind = i + NJPEGChunks + (chunk.phaseId > 0 ? 4 : 2);
            bytesSegments[ind] = SSTORE2.read(unpackChunk(i).dataPointer);
        }

        // HTML parameters
        bytesSegments[1] = ButerinCardsLib.paramsHTML(tokenId, chunk);

        return string(Array.join(bytesSegments));
    }

    function unpackChunk(uint tokenId) public view returns (ButerinCardsLib.ChunkUnpacked memory) {
        uint chunk = chunks[tokenId];
        return
            ButerinCardsLib.ChunkUnpacked({
                dataPointer: address(uint160(chunk)), // 20 bytes
                phaseId: uint8((chunk >> 160) & 0x7), // 3 bits
                tokenIdWithinPhase: uint16((chunk >> 163) & 0x7FF), // 11 bits
                lastTokenIdInScan: uint16((chunk >> 174) & 0x7FF), // 11 bits
                quoteId: uint8((chunk >> 185) & 0x3F), // 6 bits
                bgDirectionId: uint8((chunk >> 191) & 0x3), // 2 bits
                bgPaletteId: uint8((chunk >> 193) & 0xF), // 4 bits
                Nicons: uint8((chunk >> 197) & 0x3), // 2 bits
                Nbytes: uint32((chunk >> 199) & 0x1FFFFFF), // 25 bits
                seed: uint32((chunk >> 224) & 0xFFFFFFFF) // 32 bits
            });
    }

    function _packChunk(ButerinCardsLib.ChunkUnpacked memory chunk) private pure returns (uint) {
        return
            uint(uint160(chunk.dataPointer)) |
            (uint(chunk.phaseId) << 160) |
            (uint(chunk.tokenIdWithinPhase) << 163) |
            (uint(chunk.lastTokenIdInScan) << 174) |
            (uint(chunk.quoteId) << 185) |
            (uint(chunk.bgDirectionId) << 191) |
            (uint(chunk.bgPaletteId) << 193) |
            (uint(chunk.Nicons) << 197) |
            (uint(chunk.Nbytes) << 199) |
            (uint(chunk.seed) << 224);
    }

    function _rndParams(ButerinCardsLib.ChunkUnpacked memory chunk, uint256 tokenId_) private view {
        uint256 rndUniform = uint256(
            keccak256(
                abi.encodePacked(block.number, block.timestamp, block.basefee, block.coinbase, msg.sender, tokenId_)
            )
        );

        chunk.seed = uint32(rndUniform); // Uniform distribution between 0 and 2**32-1

        uint temp = rndUniform >> 32;
        chunk.bgDirectionId = temp & 0x3 > 1 ? 2 : temp & 0x1 == 1 ? 1 : 0; // Diagonal (2) has 0.5 probability, vertical(1) and horizontal(0) have 0.25 probability

        temp = (rndUniform >> 34) & 0xFFFF; // Use 16 bits to approximate custom distribution between 1 and 10
        if (temp < 0x30A4) chunk.bgPaletteId = 1;
        else if (temp < 0x5C29) chunk.bgPaletteId = 2;
        else if (temp < 0x8290) chunk.bgPaletteId = 3;
        else if (temp < 0xA3D8) chunk.bgPaletteId = 4;
        else if (temp < 0xC000) chunk.bgPaletteId = 5;
        else if (temp < 0xD70B) chunk.bgPaletteId = 6;
        else if (temp < 0xE8F6) chunk.bgPaletteId = 7;
        else if (temp < 0xF5C3) chunk.bgPaletteId = 8;
        else if (temp < 0xFD71) chunk.bgPaletteId = 9;
        else chunk.bgPaletteId = 10;

        temp = (rndUniform >> 240) & 0xFFFF; // Use 16 bits to approximate custom distribution between 0 and 44
        if (temp < 0x02ba) chunk.quoteId = 0;
        else if (temp < 0x0597) chunk.quoteId = 1;
        else if (temp < 0x0897) chunk.quoteId = 2;
        else if (temp < 0x0bb9) chunk.quoteId = 3;
        else if (temp < 0x0efd) chunk.quoteId = 4;
        else if (temp < 0x1264) chunk.quoteId = 5;
        else if (temp < 0x15ed) chunk.quoteId = 6;
        else if (temp < 0x1999) chunk.quoteId = 7;
        else if (temp < 0x1d67) chunk.quoteId = 8;
        else if (temp < 0x2158) chunk.quoteId = 9;
        else if (temp < 0x256b) chunk.quoteId = 10;
        else if (temp < 0x29a0) chunk.quoteId = 11;
        else if (temp < 0x2df8) chunk.quoteId = 12;
        else if (temp < 0x3273) chunk.quoteId = 13;
        else if (temp < 0x3710) chunk.quoteId = 14;
        else if (temp < 0x3bcf) chunk.quoteId = 15;
        else if (temp < 0x40b1) chunk.quoteId = 16;
        else if (temp < 0x45b5) chunk.quoteId = 17;
        else if (temp < 0x4adb) chunk.quoteId = 18;
        else if (temp < 0x5024) chunk.quoteId = 19;
        else if (temp < 0x5590) chunk.quoteId = 20;
        else if (temp < 0x5b1e) chunk.quoteId = 21;
        else if (temp < 0x60ce) chunk.quoteId = 22;
        else if (temp < 0x66a1) chunk.quoteId = 23;
        else if (temp < 0x6c96) chunk.quoteId = 24;
        else if (temp < 0x72ae) chunk.quoteId = 25;
        else if (temp < 0x78e8) chunk.quoteId = 26;
        else if (temp < 0x7f45) chunk.quoteId = 27;
        else if (temp < 0x85c4) chunk.quoteId = 28;
        else if (temp < 0x8c65) chunk.quoteId = 29;
        else if (temp < 0x9329) chunk.quoteId = 30;
        else if (temp < 0x9a0f) chunk.quoteId = 31;
        else if (temp < 0xa118) chunk.quoteId = 32;
        else if (temp < 0xa843) chunk.quoteId = 33;
        else if (temp < 0xaf91) chunk.quoteId = 34;
        else if (temp < 0xb701) chunk.quoteId = 35;
        else if (temp < 0xbe93) chunk.quoteId = 36;
        else if (temp < 0xc648) chunk.quoteId = 37;
        else if (temp < 0xce20) chunk.quoteId = 38;
        else if (temp < 0xd61a) chunk.quoteId = 39;
        else if (temp < 0xde36) chunk.quoteId = 40;
        else if (temp < 0xe675) chunk.quoteId = 41;
        else if (temp < 0xeed6) chunk.quoteId = 42;
        else if (temp < 0xf759) chunk.quoteId = 43;
        else chunk.quoteId = 44;
    }

    /// @param dataChunk will be a piece of HTML in UTF-8 or a piece of JPEG in base64
    function mine(
        string calldata dataChunk,
        uint8 phaseId,
        uint16 tokenIdWithinPhase,
        uint16 lastTokenIdInScan,
        bytes32[] calldata proof
    ) external {
        // Get the next tokenIdx
        uint256 tokenId = totalSupply();

        // Check hash matches
        _verifyDataChunk(dataChunk, tokenId, phaseId, tokenIdWithinPhase, lastTokenIdInScan, proof);

        // Generate random color, quote and seed
        ButerinCardsLib.ChunkUnpacked memory chunk;
        _rndParams(chunk, tokenId);

        // Pass rest of data
        chunk.dataPointer = SSTORE2.write(bytes(dataChunk));
        chunk.phaseId = phaseId;
        chunk.tokenIdWithinPhase = tokenIdWithinPhase;
        chunk.lastTokenIdInScan = lastTokenIdInScan;
        chunk.Nicons = uint8(Nmined[msg.sender] % 3);
        chunk.Nbytes = uint32(bytes(dataChunk).length);
        if (tokenId > 0) chunk.Nbytes += unpackChunk(tokenId - 1).Nbytes;

        // Pack and store chunk
        chunks.push(_packChunk(chunk));

        // Mint card
        _mint(msg.sender, tokenId);

        // Increment counter of mined cards by sender
        Nmined[msg.sender]++;

        // Emit event
        emit Mined(
            msg.sender,
            bytes(dataChunk).length,
            tokenId,
            phaseId,
            tokenIdWithinPhase,
            chunk.quoteId,
            chunk.bgDirectionId,
            chunk.bgPaletteId,
            lastTokenIdInScan,
            chunk.Nbytes,
            chunk.Nicons,
            chunk.seed
        );
    }

    function _verifyDataChunk(
        string calldata dataChunk,
        uint256 tokenId,
        uint8 phaseId,
        uint16 tokenIdWithinPhase,
        uint16 lastTokenIdInScan,
        bytes32[] calldata proof
    ) private view {
        bytes32 leaf = keccak256(
            bytes.concat(keccak256(abi.encode(dataChunk, tokenId, phaseId, tokenIdWithinPhase, lastTokenIdInScan)))
        );
        require(MerkleProof.verifyCalldata(proof, _ROOT, leaf), "Invalid data");
    }

    function htmlHeader() public pure returns (bytes memory) {
        return
            bytes(
                "data:text/html;charset=utf-8,%3C!DOCTYPE%20html%3E%0D%0A%3Chtml%3E%0D%0A%20%20%20%20%3Chead%3E%0D%0A%20%20%20%20%20%20%20%20%3Cscript%3E%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20%5B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cardId%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20phaseId%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tokenIdWithinPhase%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20kiloBytes%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20quoteId%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bgDirection%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bgPalette%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Nicons%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20seed%2C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20jpegB64%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%20%3D%20%5B"
            );
    }

    function jpegFooter() public pure returns (bytes memory) {
        return bytes("/9k=");
    }
}

//SPDX-License-Identifier: MIT

/// @title JPEG Mining
/// @author Xatarrer
/// @notice Unaudited
pragma solidity ^0.8.0;

library ButerinCardsBackA {
    function cardBackPiece0() external pure returns (string memory piece) {
        return
            "data%3Aimage%2Fsvg%2Bxml%2C%253Csvg%2520viewBox%253D%25220%25200%2520100%2520140%2522%2520shape-rendering%253D%2522geometricPrecision%2522%2520text-rendering%253D%2522geometricPrecision%2522%2520image-rendering%253D%2522optimizeQuality%2522%2520xmlns%253D%2522http%253A%252F%252Fwww.w3.org%252F2000%252Fsvg%2522%2520style%253D%2522font-size%253A23%2525%2522%253E%253Cstyle%253E%2540font-face%257Bfont-family%253A%2526quot%253BOrbitron%2526quot%253B%253Bsrc%253Aurl(data%253Aapplication%252Ffont-woff%253Bcharset%253Dutf-8%253Bbase64%252Cd09GRgABAAAAAAmQABAAAAAADzAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABbAAAABwAAAAchNLgWUdERUYAAAGIAAAAHAAAAB4AJwAjR1BPUwAAAaQAAACuAAAA%252Bi6OJkdHU1VCAAACVAAAACwAAAAwuP%252B4%252Fk9TLzIAAAKAAAAATgAAAGBgRBFTY21hcAAAAtAAAACYAAABommEYxtjdnQgAAADaAAAAAQAAAAEACECeWdhc3AAAANsAAAACAAAAAgAAAAQZ2x5ZgAAA3QAAAJRAAADOFBj1JNoZWFkAAAFyAAAADUAAAA2DIm08mhoZWEAAAYAAAAAHQAAACQG4gJQaG10eAAABiAAAABaAAAAdC4RAhpsb2NhAAAGfAAAACMAAAA8EKwRtG1heHAAAAagAAAAHwAAACAAYgBgbmFtZQAABsAAAAJKAAAFZHYKuF9wb3N0AAAJDAAAAIEAAADiyViAhQAAAAEAAAAA2odvjwAAAADKAw0xAAAAAOBIY5h42mNgZGBg4AFiMSBmYmAEQhkgZgHzGAAE0gBLeNpNjqsOwkAURE9LH3T7SDVBESQCjUA1KCSahAQUpIKg%252BQq%252BgW8jhIQKFBXL7CrE3DuPu5MlAAxzloTNar2hOu0uLSMi%252BViLy%252F95cDycW4aOeUSEUoHH3V9mjJkwY0HDlj1Xbi63vS6N%252FZDbNwPpp1hPaR%252Buwb6kviQktiMlV1%252Bsf1ViNYWma%252BhcLp7Kc3kmboRSiJXUSqZyQ68LIZIT%252B3Yjt9AO1RTrBT%252Ff6iYLAAB42mNgZGBg4GLQYdBjYHJx8wlh4MtJLMljkGBgAYoz%252FP8PJBAsIAAAnsoHa3jaY2BhXMUUwcDKwMLUBaQZGLwhNGMcgxFjDAMSaGBgSBdgYHCC8f38%252FVwZDjAoqP5hVvhvwcDAfJWRDyjMCJJjcmG6AKQUGJgAMO8L%252FgAAeNpjYGBgZoBgGQZGBhCYA%252BQxgvksDA1gWgAowsOgwODMkMiQypDJkMdQxFDKsECBS0FfIV71z%252F%252F%252FQFUKDE5A2RSobAlQlgEm%252B%252F%252Fx%252F0P%252Fl%252F5f%252FH%252FB%252Fzn%252FZ%252F6f8T%252FvAf%252F91%252Ff33FKA2ogTMLIxwJUwMgEJJnQFEKfjByyseKWBdrBzAGlOEIcLiLkZeHgZaAD4yNIFABvXJVAAIQJ5AAEAAf%252F%252FAA942l2Sv2sUQRTH35szN2vm0Ft29yRIhM2SBEKIcpu73ZBcYTy1kQxiYyFYiMbeYo2VP0DFxkabQArrwyIHIl40ooJnFa5RLPwDrvEPELKD31lPCQ7s29m38z7zfd8ZEhQS8ZzYpBJJWthmOrnSlYfoZ327PPZjpVsSmNJ2yabHbLory7y%252F0mWbj93QnY3dKOTDw35fbOY3QnGZQFpGUGJAR%252BgELRBN%252B%252BUodMN6bZJ9WQumw3qzxYszUThVRqbm%252FfddUjrNX4v5VI%252FP5j3ekmrcMdekUpJ3bDRnER%252BlWgx0%252BnRiIpl%252FqORj%252BwPB7Cn53M4RCIMphZYytHhE8dRMw11sxmGAnQLXr2H2PtU6FefzvarvV0Vdp%252Fwx1Rte1dysehtEghYR7ok2VelYQWhx2LBaG%252BgncKfxDvxaL9H8rSJNi%252FvpGuIF80Lp5PsVR2U6MavZnXVHgQWCuA5LFR0%252FoKYRu9YSfBQWWGFbidYJX8yAWpIVgPuOApDTRO%252Fum0w5fAoJxwyABTdGuAuNHk0e4Fq%252FrTh0OdIM%252BJuCfBoq5yqSP5k5vgpOX1b%252B0Eei%252BdK65Y%252F6pw402%252FtBXgSxkRt3Mgzx5Pa%252Bwa5MTSx8UHhEceQWxkR%252Fu3mZ8ZdkLcOxfXaUaFs7uFfA2bI5R50q6v6dTAem8RnzwVG8m%252BjM6mBqIDwTv%252BjoqMM4iIMIVi1zsINFw2FFftWJuPUqM%252B%252FQAY1qxP3Cl1FNcebWaszKka2DCUtYvpZiy11zzgrjnllNtK3HEG3Ul4hw1UPRzt%252Fah%252Bg3ne%252BnnQAAAHjaY2BkYGBgYnDqZWV4Hc9v85VBnvkFUIThFDOvIYh%252B4JE8A0QzczMzASkOBhDJAAD%252F5Qe3AAAAeNpjYGRgYL767xOQdGAAAmZuBkYGVCALAFtpAzkAAAB42mPMYVBkAAJGXyDWYmBgdmCwZjZjsGDaxmDCNJtBnGkLgzHDbSB7B4MZEzuDCeMGBlOm7QymILWMjUD1TEg0yKADQNwAxQlAPAuItaA0UI7JhYEBAGFKDk4AAHjaY2Bg0ILCAoYJDNsYHjEKMCox2jGGMOYx9uGFcwBTyQ12AHjaY2BkYGCQZdBnYGYAASYgZmQAiTkw6IEEAAsKAMMAeNqdVF1rE1EQPbtZS4taKkiRImXJgyhoTOJXrU9VLBTSptqgb0KSxiS6m43ZTUv%252Bgw%252F%252BDvGH%252BPUHfPMn%252BOSzZyazZrNBCrLcvefOnXtm7pnZBXAZv1GA460ARDDsoPjX7mLVuWK4gC3nmmEPVeeV4XPYcD4YXkLR%252BWh4Gfed74ZXcNV1DZ%252FHobtp%252BAI23YHhi47vvje8iruFT4bXsFb4afgS8S%252FDn7HupTl%252FQdlbN%252FwVy96u4W9Y8g6m%252BEcBG14DTxBhiAlG6KOLHhL4qKKMCraIGrR0ONe536JHwjnCgJZDRW%252B429YzOxhz7tE2Qsz1deVKyB1jG7f5dPV8j34tlHgqQkhrohECjiY9xpwjvKY9ykW8gZtkPTUOH8%252FpGXOMcML3MS276ie5HJArpHWbuLiQe5HRF%252B9zRP%252BQlsdcB%252BQTfsknINeIipSoiajyiOx1jqdEeZZbCyxnx%252FFzkV7onWJ6TP1nkc%252BOllarllHTVz197HM%252BUXtL1ZbaTlidDneaWinxaWOPzJKBvCcLNTzVp%252FSPqoW5GIlFmFU75ZuyhRZZ9ttcS%252F597Z5ZLY%252BUOWFUUUfynnoEnNtcD7QLRMcx8bHm7lt%252BcnqPakj%252FDtU3y1ybY5DuymtfYWYy%252FExm83HTbJq8d5M4yOg769Wmxt3BM8WJ9uW8EjE55Qsc0hYzYqxcJf0Kutyv83ztv87kuybfMy%252B5blHhVJWK9lvDemdfVZculL9BGQ8Zu4oHfN%252FDHftTVIne0rvDTALthGz13pGpTw6pXfAHMwTavwAAeNptxbsSwVAUBdC9E5J4P3ol9cnhCmWGCYUmheEPMGOMRuFf%252FCvDPbqsZiHAz%252FsFRZURwIAhQowxwRobbLFDiT0OOLLGOiPGTNhgky222WGXPfY54DA6XZ%252F3c%252BrT%252BHG7iOTyXUX%252Bp7baU3tmO3tuZ%252FbCXtq5Xwu%252F87ti9QG%252F1CoKAAAA)%2520format(%2526quot%253Bwoff%2526quot%253B)%257D%253C%252Fstyle%253E%253Cdefs%253E%253Cpath%2520id%253D%2522eth_right%2522%2520stroke-width%253D%2522142%2522%2520stroke-linejoin%253D%2522miter%2522%2520d%253D%2522M71%2520263v1860l792-499Z%2522%2520fill%253D%2522none%2522%252F%253E%253Cpath%2520id%253D%2522eth_right-back%2522%2520stroke-width%253D%2522142%2522%2520stroke-linejoin%253D%2522miter%2522%2520d%253D%2522m71%25201230%2520754%2520419%2522%252F%253E%253Cpath%2520id%253D%2522eth_bottom%2522%2520stroke-width%253D%2522142%2522%2520stroke-linejoin%253D%2522miter%2522%2520stroke-miterlimit%253D%2522100%2522%2520d%253D%2522m71%25202461%2520629-398-629%2520916Z%2522%2520fill%253D%2522none%2522%252F%253E%253Cpath%2520id%253D%2522eth_top_right%2522%2520d%253D%2522m0%252041%2520131%2520218L0%2520199Z%2522%252F%253E%253Cpath%2520id%253D%2522eth_middle_right%2522%2520d%253D%2522M0%2520199v138l131-78%2522%252F%253E%253Cpath%2520id%253D%2522eth_bottom_right%2522%2520d%253D%2522M0%2520362v107l131-185z%2522%252F%253E%253Cg%2520id%253D%2522eth_diamond%2522%2520filter%253D%2522url(%2523blackGlow)%2522%253E%253Cg%2520transform%253D%2522translate(32.68%252063.5)%2520scale(.00392)%2522%253E%253Cuse%2520href%253D%2522%2523eth_right-back%2522%2520stroke%253D%2522%2523CC71C2%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_right%2522%2520stroke%253D%2522%2523FF9C92%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom%2522%2520stroke%253D%2522%25235A9DED%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_right-back%2522%2520stroke%253D%2522%252388D848%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_right%2522%2520stroke%253D%2522%2523FFE94D%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom%2522%2520stroke%253D%2522%252353D3E0%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253C%252Fg%253E%253C%252Fg%253E%253Cg%2520id%253D%2522eth_color_b%2522%2520filter%253D%2522url(%2523blackGlow)%2522%253E%253Cg%2520transform%253D%2522matrix(.028%25200%25200%2520.028%252032.68%252082.42)%2522%253E%253Cuse%2520href%253D%2522%2523eth_top_right%2522%2520fill%253D%2522%2523F48B76%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_middle_right%2522%2520fill%253D%2522%25239D68AB%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom_right%2522%2520fill%253D%2522%2523647BBD%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_top_right%2522%2520fill%253D%2522%2523F9EC66%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_middle_right%2522%2520fill%253D%2522%252350BB78%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom_right%2522%2520fill%253D%2522%252310AFD3%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253C%252Fg%253E%253C%252Fg%253E%253Cg%2520id%253D%2522eth_pencil%2522%2520filter%253D%2522url(%2523glow)%2522%2520fill%253D%2522none%2522%2520stroke%253D%2522%2523000%2522%2520stroke-width%253D%252212%2522%253E%253Cpath%2520d%253D%2522M50.176%252062.258c-.213.42-.383.87-.387%25201.352.02.309.032.623.032.936-.059.252-.155.518-.115.776l.03.022M46.85%252060.193c-.065.223.103.472.183.682.14.207.241.428.38.633.154.218.242.47.388.694%2522%2520stroke-width%253D%2522.504%2522%252F%253E%253Cpath%2520d%253D%2522M46.85%252060.181c.281.135.508.385.796.538.443.284.836.613%25201.303.86.287.09.585.178.845.338.241.346.61-.004.811-.192.213-.187.535-.181.778-.331.776-.275%25201.59-.437%25202.341-.783.364-.084-.065.338-.133.466-.357.38-.704.773-1.015%25201.191-.4.321-.674.781-1.123%25201.047-.343.387-.76.71-1.056%25201.136-.202.29-.31.695-.621.88-.117.089-.007.443-.279.343-.356-.336-.493-.82-.72-1.241-.132-.207-.16-.485-.3-.7-.212-.261-.29-.603-.486-.878-.024-.154-.373-.603-.135-.632l.057.014%2522%2520stroke-width%253D%2522.504%2522%252F%253E%253Cpath%2520d%253D%2522M47.279%252059.701c.352.35.775.621%25201.227.837.372.232.72.513%25201.095.734l.045.007M50.21%252058.547c.08.26-.047.592-.064.873-.107.311-.11.65-.25.953-.115.302-.092.648-.243.931-.045.151.126.11.162.22%2522%2520stroke-width%253D%2522.504%2522%252F%253E%253Cpath%2520d%253D%2522M50.218%252058.537c-.418.177-.786.472-1.236.584-.487.153-.99.269-1.456.484-.488.294-.147-.303-.07-.52.218-.579.559-1.1.873-1.63.214-.444.455-.873.758-1.264.333-.596.507-1.26.731-1.9.027-.033.071-.04.109-.056%2522%2520stroke-width%253D%2522.504%2522%252F%253E%253Cpath%2520d%253D%2522M49.583%252061.628c.364-.158.767-.19%25201.14-.337.65-.203%25201.27-.486%25201.92-.687.207-.053.978-.328.369-.401-.524-.177-.975-.526-1.454-.796-.384-.276-.798-.497-1.143-.824-.339.015-.148-.423-.276-.624-.045-.55.037-1.094.005-1.644-.041-.378-.134-.749-.168-1.127.016-.303.055-.62.009-.917-.08-.015-.067-.123.004-.131M50.007%252053.802c-.183.382.27.395.347.711.289.244.262.71.609.906.248.313.458.67.583%25201.052.1.538.434.99.664%25201.474.294.594.688%25201.14%25201.019%25201.719.118.212.254.412.337.641%2522%2520stroke-width%253D%2522.504%2522%252F%253E%253C%252Fg%253E%253Cg%2520id%253D%2522eth_bw%2522%2520filter%253D%2522url(%2523glow)%2522%253E%253Cg%2520transform%253D%2522matrix(.028%25200%25200%2520.028%252067.32%252062.42)%2522%253E%253Cuse%2520href%253D%2522%2523eth_top_right%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_middle_right%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom_right%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_top_right%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_middle_right%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom_right%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253C%252Fg%253E%253C%252Fg%253E%253Cg%2520id%253D%2522eth_grey%2522%2520filter%253D%2522url(%2523glow)%2522%253E%253Cg%2520transform%253D%2522matrix(.028%25200%25200%2520.028%252067.32%252082.42)%2522%253E%253Cuse%2520href%253D%2522%2523eth_top_right%2522%2520fill%253D%2522%2523343434%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_middle_right%2522%2520fill%253D%2522%2523141414%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom_right%2522%2520fill%253D%2522%25233C3C3B%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_top_right%2522%2520fill%253D%2522%25238C8C8C%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_middle_right%2522%2520fill%253D%2522%2523393939%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom_right%2522%2520fill%253D%2522%25238C8C8C%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253C%252Fg%253E%253C%252Fg%253E%253Cg%2520id%253D%2522eth_color_a%2522%2520filter%253D%2522url(%2523blackGlow)%2522%253E%253Cg%2520transform%253D%2522matrix(.028%25200%25200%2520.028%252050%252092.42)%2522%253E%253Cuse%2520href%253D%2522%2523eth_top_right%2522%2520fill%253D%2522%2523B8FBF6%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_middle_right%2522%2520fill%253D%2522%2523CAB3F5%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom_right%2522%2520fill%253D%2522%2523C8B2F5%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_top_right%2522%2520fill%253D%2522%2523EECBC0%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_middle_right%2522%2520fill%253D%2522%252387A9F0%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_bottom_right%2522%2520fill%253D%2522%2523EECBC0%2522%2520transform%253D%2522scale(-1%25201)%2522%252F%253E%253C%252Fg%253E%253C%252Fg%253E%253Cfilter%2520id%253D%2522blackGlow%2522%253E%253CfeGaussianBlur%2520in%253D%2522SourceAlpha%2522%2520stdDeviation%253D%2522.4%2522%2520result%253D%2522blurredAlpha%2522%252F%253E%253CfeMerge%253E%253CfeMergeNode%2520in%253D%2522blurredAlpha%2522%252F%253E%253CfeMergeNode%2520in%253D%2522blurredAlpha%2522%252F%253E%253CfeMergeNode%2520in%253D%2522blurredAlpha%2522%252F%253E%253CfeMergeNode%2520in%253D%2522SourceGraphic%2522%252F%253E%253C%252FfeMerge%253E%253C%252Ffilter%253E%253Cfilter%2520id%253D%2522blur%2522%2520x%253D%2522-50%2525%2522%2520width%253D%2522200%2525%2522%253E%253CfeGaussianBlur%2520in%253D%2522SourceAlpha%2522%2520stdDeviation%253D%2522.5%2522%252F%253E%253C%252Ffilter%253E%253Cfilter%2520id%253D%2522rainbowGlow%2522%2520x%253D%2522-50%2525%2522%2520y%253D%2522-50%2525%2522%2520width%253D%2522200%2525%2522%2520height%253D%2522200%2525%2522%253E%253CfeGaussianBlur%2520in%253D%2522SourceAlpha%2522%2520stdDeviation%253D%25221.5%2522%2520result%253D%2522blurredAlpha%2522%252F%253E%253CfeOffset%2520in%253D%2522blurredAlpha%2522%2520dx%253D%25223%2522%2520dy%253D%25223%2522%2520result%253D%2522redGlow%2522%252F%253E%253CfeOffset%2520in%253D%2522blurredAlpha%2522%2520dx%253D%2522-3%2522%2520dy%253D%25223%2522%2520result%253D%2522blueGlow%2522%252F%253E%253CfeOffset%2520in%253D%2522blurredAlpha%2522%2520dx%253D%2522-3%2522%2520dy%253D%2522-3%2522%2520result%253D%2522greenGlow%2522%252F%253E%253CfeOffset%2520in%253D%2522blurredAlpha%2522%2520dx%253D%25223%2522%2520dy%253D%2522-3%2522%2520result%253D%2522yellowGlow%2522%252F%253E%253CfeComponentTransfer%2520in%253D%2522redGlow%2522%2520result%253D%2522redGlow%2522%253E%253CfeFuncR%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253CfeFuncG%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253CfeFuncB%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253C%252FfeComponentTransfer%253E%253CfeComponentTransfer%2520in%253D%2522blueGlow%2522%2520result%253D%2522blueGlow%2522%253E%253CfeFuncR%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253CfeFuncG%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253CfeFuncB%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253C%252FfeComponentTransfer%253E%253CfeComponentTransfer%2520in%253D%2522greenGlow%2522%2520result%253D%2522greenGlow%2522%253E%253CfeFuncR%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253CfeFuncG%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253CfeFuncB%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253C%252FfeComponentTransfer%253E%253CfeComponentTransfer%2520in%253D%2522yellowGlow%2522%2520result%253D%2522yellowGlow%2522%253E%253CfeFuncR%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253CfeFuncG%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253CfeFuncB%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253C%252FfeComponentTransfer%253E%253CfeBlend%2520mode%253D%2522screen%2522%2520in%253D%2522redGlow%2522%2520in2%253D%2522greenGlow%2522%2520result%253D%2522redGreenGlow%2522%252F%253E%253CfeBlend%2520mode%253D%2522screen%2522%2520in%253D%2522yellowGlow%2522%2520in2%253D%2522blueGlow%2522%2520result%253D%2522yellowBlueGlow%2522%252F%253E%253CfeBlend%2520mode%253D%2522screen%2522%2520in%253D%2522redGreenGlow%2522%2520in2%253D%2522yellowBlueGlow%2522%2520result%253D%2522rainbowGlow%2522%252F%253E%253CfeMorphology%2520in%253D%2522SourceAlpha%2522%2520operator%253D%2522dilate%2522%2520radius%253D%2522.4%2522%2520result%253D%2522dilatedAlpha%2522%252F%253E%253CfeGaussianBlur%2520in%253D%2522dilatedAlpha%2522%2520stdDeviation%253D%2522.3%2522%2520result%253D%2522blurredDilatedAlpha%2522%252F%253E%253CfeComposite%2520in%253D%2522rainbowGlow%2522%2520in2%253D%2522blurredDilatedAlpha%2522%2520operator%253D%2522in%2522%2520result%253D%2522shrunkGlow%2522%252F%253E%253CfeMerge%253E%253CfeMergeNode%2520in%253D%2522shrunkGlow%2522%252F%253E%253CfeMergeNode%2520in%253D%2522shrunkGlow%2522%252F%253E%253CfeMergeNode%2520in%253D%2522SourceGraphic%2522%252F%253E%253C%252FfeMerge%253E%253C%252Ffilter%253E%253Cfilter%2520id%253D%2522glow%2522%253E%253CfeGaussianBlur%2520in%253D%2522SourceAlpha%2522%2520stdDeviation%253D%2522.2%2522%2520result%253D%2522blurredAlpha%2522%252F%253E%253CfeColorMatrix%2520in%253D%2522blurredAlpha%2522%2520values%253D%25220%25200%25200%25200%25201%25200%25200%25200%25200%25201%25200%25200%25200%25200%25201%25200%25200%25200%25201%25200%2522%2520result%253D%2522whiteBlurredAlpha%2522%252F%253E%253CfeMerge%253E%253CfeMergeNode%2520in%253D%2522whiteBlurredAlpha%2522%252F%253E%253CfeMergeNode%2520in%253D%2522whiteBlurredAlpha%2522%252F%253E%253CfeMergeNode%2520in%253D%2522SourceGraphic%2522%252F%253E%253C%252FfeMerge%253E%253C%252Ffilter%253E%253Cfilter%2520id%253D%2522rainbowGlowLarge%2522%2520x%253D%2522-100%2525%2522%2520y%253D%2522-100%2525%2522%2520width%253D%2522400%2525%2522%2520height%253D%2522400%2525%2522%253E%253CfeGaussianBlur%2520in%253D%2522SourceAlpha%2522%2520stdDeviation%253D%25221.5%2522%2520result%253D%2522blurredAlpha%2522%252F%253E%253CfeOffset%2520in%253D%2522blurredAlpha%2522%2520dx%253D%25223%2522%2520dy%253D%25223%2522%2520result%253D%2522redGlow%2522%252F%253E%253CfeOffset%2520in%253D%2522blurredAlpha%2522%2520dx%253D%2522-3%2522%2520dy%253D%25223%2522%2520result%253D%2522blueGlow%2522%252F%253E%253CfeOffset%2520in%253D%2522blurredAlpha%2522%2520dx%253D%2522-3%2522%2520dy%253D%2522-3%2522%2520result%253D%2522greenGlow%2522%252F%253E%253CfeOffset%2520in%253D%2522blurredAlpha%2522%2520dx%253D%25223%2522%2520dy%253D%2522-3%2522%2520result%253D%2522yellowGlow%2522%252F%253E%253CfeComponentTransfer%2520in%253D%2522redGlow%2522%2520result%253D%2522redGlow%2522%253E%253CfeFuncR%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253CfeFuncG%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253CfeFuncB%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253C%252FfeComponentTransfer%253E%253CfeComponentTransfer%2520in%253D%2522blueGlow%2522%2520result%253D%2522blueGlow%2522%253E%253CfeFuncR%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253CfeFuncG%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253CfeFuncB%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253C%252FfeComponentTransfer%253E%253CfeComponentTransfer%2520in%253D%2522greenGlow%2522%2520result%253D%2522greenGlow%2522%253E%253CfeFuncR%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253CfeFuncG%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253CfeFuncB%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253C%252FfeComponentTransfer%253E%253CfeComponentTransfer%2520in%253D%2522yellowGlow%2522%2520result%253D%2522yellowGlow%2522%253E%253CfeFuncR%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253CfeFuncG%2520type%253D%2522table%2522%2520tableValues%253D%25221%2522%252F%253E%253CfeFuncB%2520type%253D%2522table%2522%2520tableValues%253D%25220%2522%252F%253E%253C%252FfeComponentTransfer%253E%253CfeBlend%2520mode%253D%2522screen%2522%2520in%253D%2522redGlow%2522%2520in2%253D%2522greenGlow%2522%2520result%253D%2522redGreenGlow%2522%252F%253E%253CfeBlend%2520mode%253D%2522screen%2522%2520in%253D%2522yellowGlow%2522%2520in2%253D%2522blueGlow%2522%2520result%253D%2522yellowBlueGlow%2522%252F%253E%253CfeBlend%2520mode%253D%2522screen%2522%2520in%253D%2522redGreenGlow%2522%2520in2%253D%2522yellowBlueGlow%2522%2520result%253D%2522rainbowGlow%2522%252F%253E%253CfeMorphology%2520in%253D%2522SourceAlpha%2522%2520operator%253D%2522dilate%2522%2520radius%253D%25221.2%2522%2520result%253D%2522dilatedAlpha%2522%252F%253E%253CfeGaussianBlur%2520in%253D%2522dilatedAlpha%2522%2520stdDeviation%253D%25221.5%2522%2520result%253D%2522blurredDilatedAlpha%2522%252F%253E%253CfeComposite%2520in%253D%2522rainbowGlow%2522%2520in2%253D%2522blurredDilatedAlpha%2522%2520operator%253D%2522in%2522%2520result%253D%2522shrunkGlow%2522%252F%253E%253CfeMerge%253E%253CfeMergeNode%2520in%253D%2522shrunkGlow%2522%252F%253E%253CfeMergeNode%2520in%253D%2522shrunkGlow%2522%252F%253E%253CfeMergeNode%2520in%253D%2522SourceGraphic%2522%252F%253E%253C%252FfeMerge%253E%253C%252Ffilter%253E%253CradialGradient%2520id%253D%2522lightGlow%2522%2520cx%253D%252250%2525%2522%2520cy%253D%252250%2525%2522%2520r%253D%252250%2525%2522%2520fx%253D%252250%2525%2522%2520fy%253D%252250%2525%2522%253E%253Cstop%2520offset%253D%252250%2525%2522%2520style%253D%2522stop-color%253Awhite%253Bstop-opacity%253A.1%2522%252F%253E%253Cstop%2520offset%253D%2522100%2525%2522%2520style%253D%2522stop-color%253Awhite%253Bstop-opacity%253A0%2522%252F%253E%253C%252FradialGradient%253E%253ClinearGradient%2520id%253D%2522circuitGrad%2522%2520x2%253D%2522100%2525%2522%2520y2%253D%2522100%2525%2522%253E";
    }

    function cardBackPiece1(uint8 Nicons) external pure returns (string memory piece) {
        if (Nicons == 0)
            return "%253Cstop%2520offset%253D%25220%2525%2522%2520stop-color%253D%2522%25232A2F3A%2522%2520%252F%253E";
        if (Nicons == 1)
            return
                "%253Cstop%2520offset%253D%252275%2525%2522%2520stop-color%253D%2522%25232A2F3A%2522%252F%253E%253Cstop%2520offset%253D%252290%2525%2522%2520stop-color%253D%2522%2523fff%2522%252F%253E";
        if (Nicons == 2)
            return
                "%253Cstop%2520offset%253D%252210%2525%2522%2520stop-color%253D%2522pink%2522%252F%253E%253Cstop%2520offset%253D%252225%2525%2522%2520stop-color%253D%2522%25232A2F3A%2522%252F%253E%253Cstop%2520offset%253D%252275%2525%2522%2520stop-color%253D%2522%25232A2F3A%2522%252F%253E%253Cstop%2520offset%253D%252290%2525%2522%2520stop-color%253D%2522%2523fff%2522%252F%253E";
    }
}


//SPDX-License-Identifier: MIT

/// @title JPEG Mining
/// @author Xatarrer
/// @notice Unaudited
pragma solidity ^0.8.0;

library ButerinCardsBackB {
    function cardBackPiece2() external pure returns (string memory piece) {
        return
            "%253C%252FlinearGradient%253E%253CclipPath%2520id%253D%2522clipFrame%2522%253E%253Cpath%2520d%253D%2522M6%25204%25204%25206v128l2%25202h88l2-2V6l-2-2ZM0%25200h100v140H0Z%2522%252F%253E%253C%252FclipPath%253E%253Cpattern%2520id%253D%2522circuit%2522%2520width%253D%2522121.6%2522%2520height%253D%2522182.4%2522%2520patternUnits%253D%2522userSpaceOnUse%2522%253E%253Cpath%2520fill%253D%2522%25231A1D2A%2522%2520d%253D%2522M0%25200h100v140H0z%2522%252F%253E%253Cpath%2520fill%253D%2522url(%2523circuitGrad)%2522%2520mask%253D%2522url(%2523groupMask)%2522%2520d%253D%2522M0%25200h100v140H0z%2522%252F%253E%253C%252Fpattern%253E%253Cmask%2520id%253D%2522groupMask%2522%253E%253Cg%2520fill%253D%2522%2523fff%2522%253E%253Cpath%2520id%253D%2522circuitPath%2522%2520transform%253D%2522scale(.2)%2522%2520d%253D%2522M44.1%2520224a5%25205%25200%25201%25201%25200%25202H0v-2h44.1zm160%252048a5%25205%25200%25201%25201%25200%25202H82v-2h122.1zm57.8-46a5%25205%25200%25201%25201%25200-2H304v2h-42.1zm0%252016a5%25205%25200%25201%25201%25200-2H304v2h-42.1zm6.2-114a5%25205%25200%25201%25201%25200%25202h-86.2a5%25205%25200%25201%25201%25200-2h86.2zm-256-48a5%25205%25200%25201%25201%25200%25202H0v-2h12.1zm185.8%252034a5%25205%25200%25201%25201%25200-2h86.2a5%25205%25200%25201%25201%25200%25202h-86.2zM258%252012.1a5%25205%25200%25201%25201-2%25200V0h2v12.1zm-64%2520208a5%25205%25200%25201%25201-2%25200v-54.2a5%25205%25200%25201%25201%25202%25200v54.2zm48-198.2V80h62v2h-64V21.9a5%25205%25200%25201%25201%25202%25200zm16%252016V64h46v2h-48V37.9a5%25205%25200%25201%25201%25202%25200zm-128%252096V208h16v12.1a5%25205%25200%25201%25201-2%25200V210h-16v-76.1a5%25205%25200%25201%25201%25202%25200zm-5.9-21.9a5%25205%25200%25201%25201%25200%25202H114v48H85.9a5%25205%25200%25201%25201%25200-2H112v-48h12.1zm-6.2%2520130a5%25205%25200%25201%25201%25200-2H176v-74.1a5%25205%25200%25201%25201%25202%25200V242h-60.1zm-16-64a5%25205%25200%25201%25201%25200-2H114v48h10.1a5%25205%25200%25201%25201%25200%25202H112v-48h-10.1zM66%2520284.1a5%25205%25200%25201%25201-2%25200V274H50v30h-2v-32h18v12.1zM236.1%2520176a5%25205%25200%25201%25201%25200%25202H226v94h48v32h-2v-30h-48v-98h12.1zm25.8-30a5%25205%25200%25201%25201%25200-2H274v44.1a5%25205%25200%25201%25201-2%25200V146h-10.1zm-64%252096a5%25205%25200%25201%25201%25200-2H208v-80h16v-14h-42.1a5%25205%25200%25201%25201%25200-2H226v18h-16v80h-12.1zm86.2-210a5%25205%25200%25201%25201%25200%25202H272V0h2v32h10.1zM98%2520101.9V146H53.9a5%25205%25200%25201%25201%25200-2H96v-42.1a5%25205%25200%25201%25201%25202%25200zM53.9%252034a5%25205%25200%25201%25201%25200-2H80V0h2v34H53.9zm60.1%25203.9V66H82v64H69.9a5%25205%25200%25201%25201%25200-2H80V64h32V37.9a5%25205%25200%25201%25201%25202%25200zM101.9%252082a5%25205%25200%25201%25201%25200-2H128V37.9a5%25205%25200%25201%25201%25202%25200V82h-28.1zm16-64a5%25205%25200%25201%25201%25200-2H146v44.1a5%25205%25200%25201%25201-2%25200V18h-26.1zm102.2%2520270a5%25205%25200%25201%25201%25200%25202H98v14h-2v-16h124.1zM242%2520149.9V160h16v34h-16v62h48v48h-2v-46h-48v-66h16v-30h-16v-12.1a5%25205%25200%25201%25201%25202%25200zM53.9%252018a5%25205%25200%25201%25201%25200-2H64V2H48V0h18v18H53.9zm112%252032a5%25205%25200%25201%25201%25200-2H192V0h50v2h-48v48h-28.1zm-48-48a5%25205%25200%25200%25201-9.8-2h2.07a3%25203%25200%25201%25200%25205.66%25200H178v34h-18V21.9a5%25205%25200%25201%25201%25202%25200V32h14V2h-58.1zm0%252096a5%25205%25200%25201%25201%25200-2H137l32-32h39V21.9a5%25205%25200%25201%25201%25202%25200V66h-40.17l-32%252032H117.9zm28.1%252090.1a5%25205%25200%25201%25201-2%25200v-76.51L175.59%252080H224V21.9a5%25205%25200%25201%25201%25202%25200V82h-49.59L146%2520112.41v75.69zm16%252032a5%25205%25200%25201%25201-2%25200v-99.51L184.59%252096H300.1a5%25205%25200%25200%25201%25203.9-3.9v2.07a3%25203%25200%25200%25200%25200%25205.66v2.07a5%25205%25200%25200%25201-3.9-3.9H185.41L162%2520121.41v98.69zm-144-64a5%25205%25200%25201%25201-2%25200v-3.51l48-48V48h32V0h2v50H66v55.41l-48%252048v2.69zM50%252053.9v43.51l-48%252048V208h26.1a5%25205%25200%25201%25201%25200%25202H0v-65.41l48-48V53.9a5%25205%25200%25201%25201%25202%25200zm-16%252016v19.51l-34%252034v-2.82l32-32V69.9a5%25205%25200%25201%25201%25202%25200zM12.1%252032a5%25205%25200%25201%25201%25200%25202H9.41L0%252043.41V40.6L8.59%252032h3.51zm265.8%252018a5%25205%25200%25201%25201%25200-2h18.69l7.41-7.41v2.82L297.41%252050H277.9zm-16%2520160a5%25205%25200%25201%25201%25200-2H288v-71.41l16-16v2.82l-14%252014V210h-28.1zm-208%252032a5%25205%25200%25201%25201%25200-2H64v-22.59L40.59%2520194H21.9a5%25205%25200%25201%25201%25200-2h19.51L66%2520216.59V242H53.9zm150.2%252014a5%25205%25200%25201%25201%25200%25202H96v-56.6L56.6%2520162H37.9a5%25205%25200%25201%25201%25200-2h19.5L98%2520200.6V256h106.1zm-150.2%25202a5%25205%25200%25201%25201%25200-2H80v-46.59L48.59%2520178H21.9a5%25205%25200%25201%25201%25200-2h27.51L82%2520208.59V258H53.9zM34%252039.8v1.61L9.41%252066H0v-2h8.59L32%252040.59V0h2v39.8zM2%2520300.1a5%25205%25200%25200%25201%25203.9%25203.9H3.83A3%25203%25200%25200%25200%25200%2520302.17V256h18v48h-2v-46H2v42.1zM34%2520241v63h-2v-62H0v-2h34v1zM17%252018H0v-2h16V0h2v18h-1zm273-2h14v2h-16V0h2v16zm-32%2520273v15h-2v-14h-14v14h-2v-16h18v1zM0%252092.1A5.02%25205.02%25200%25200%25201%25206%252097a5%25205%25200%25200%25201-6%25204.9v-2.07a3%25203%25200%25201%25200%25200-5.66V92.1zM80%2520272h2v32h-2v-32zm37.9%252032h-2.07a3%25203%25200%25200%25200-5.66%25200h-2.07a5%25205%25200%25200%25201%25209.8%25200zM5.9%25200A5.02%25205.02%25200%25200%25201%25200%25205.9V3.83A3%25203%25200%25200%25200%25203.83%25200H5.9zm294.2%25200h2.07A3%25203%25200%25200%25200%2520304%25203.83V5.9a5%25205%25200%25200%25201-3.9-5.9zm3.9%2520300.1v2.07a3%25203%25200%25200%25200-1.83%25201.83h-2.07a5%25205%25200%25200%25201%25203.9-3.9zM97%2520100a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-48%252032a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm32%252048a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm32-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0-32a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%252032a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm32%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16-64a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%252096a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16-144a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%252032a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16-32a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-96%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16-32a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm96%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16-64a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-32%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zM49%252036a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-32%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm32%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zM33%252068a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16-48a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%2520240a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%252032a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16-64a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16-32a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm80-176a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm32%252048a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0-32a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm112%2520176a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm-16%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zM17%2520180a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0%252016a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm0-32a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16%25200a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zM17%252084a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm32%252064a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206zm16-16a3%25203%25200%25201%25200%25200-6%25203%25203%25200%25200%25200%25200%25206z%2522%252F%253E%253Cuse%2520href%253D%2522%2523circuitPath%2522%2520transform%253D%2522translate(60.8)%2522%252F%253E%253Cuse%2520href%253D%2522%2523circuitPath%2522%2520transform%253D%2522translate(0%252060.8)%2522%252F%253E%253Cuse%2520href%253D%2522%2523circuitPath%2522%2520transform%253D%2522translate(60.8%252060.8)%2522%252F%253E%253Cuse%2520href%253D%2522%2523circuitPath%2522%2520transform%253D%2522translate(0%2520121.6)%2522%252F%253E%253Cuse%2520href%253D%2522%2523circuitPath%2522%2520transform%253D%2522translate(60.8%2520121.6)%2522%252F%253E%253C%252Fg%253E%253C%252Fmask%253E%253C%252Fdefs%253E%253Cpath%2520fill%253D%2522url(%2523circuit)%2522%2520d%253D%2522M4%25204h92v132H4z%2522%252F%253E%253Cg%2520opacity%253D%2522";
    }

    function cardBackPiece3() external pure returns (string memory piece) {
        return
            "%2522%253E%253Cpath%2520fill%253D%2522%2523d7d7d9%2522%2520d%253D%2522M22.15%252089.032c4.966-5.885%25205.334-6.989%252010.115-9.563%25204.781-2.575%25205.333-1.104%252011.402-4.414%25206.069-3.31%25201.103-7.724%25204.23-14.712C51.023%252053.355%252060.402%252046.367%252063.712%252046c3.31-.368%25205.517-.551%25206.069-.551.551%25200-.184%25208.827-.184%25208.827l2.574%25203.678.552%252014.16-2.759%25202.207-3.494-1.288-1.839-6.252-2.758%25202.574%25207.908%252019.494-4.966%25204.781-43.584%25207.724-1.839-10.666%2522%252F%253E%253Cpath%2520fill%253D%2522%2523b9c6d0%2522%2520d%253D%2522M65.394%2520137.294c-15.294-.164-30.605-.15-45.87-1.227-2.886-1.243%25205.682-.903%25201.585-3.303-1.285-4.576%25204.416-8.54.955-13.404-.757-3.281%25205.746-8.4.21-5.013%25204.092-1.375.057-12.156.388-4.659%25201.12%25204.992-7.6%252011.326-8.573%25209.516%25202.528-1.734%25206.602-9.55%25201.691-4.038-4.917%25205.073-13.446-4.098-10.782-4.804%25201.668%25206.46%252014.071%25201.75%25207.185-3.305-6.503-4.096-9.663-14.142-1.971-18.864%25203.25-3.864%252017.818-5.924%252012.27%25201.72-1.982%25203.21-1.166%25209.747%25201.956%25209.377-1.65-2.26%25206.34.254%25208.374-1.637%25205.293-2.21%252010.876-3.907%252016.415-5.38%25206.128-.621%252012.579-3.43%252018.575-2.761%25201.453.434-3.55-3.117-.844-5.045.235-5.848-6.785-9.071-6.005-14.793.254-3.303-1.14-12.79%25202.096-5.585%25202.87%25202.319%25203.836%25206.601%25204.977%25209.101%25201.13-2.3%25201.857-3.089%25202.888-.844%25201.88-3.853.445-10.732-.021-14.44-3.653-1.526.884-9.892-3.52-6.927%25201.327-2.7%25203.246-8.172-1.144-3.323-5.684%25203.716-14.033%25204.662-16.746%252011.822-.205%25206.599-4.837%252015.375-12.403%252010.303-2.675-2.972-7.114%25201.937-7.14.586-.183-5.405%252013.23.211%25206.26-4.018-2.467-3.382-9.283-4.931-7.478-10.292%25203.575%25206.994%252014.159%25201.963%252016.15-3.899-2.608%25206.254-8.057%25203.26-13.344%25204.08-.154-5.61%25209.411-2.638%25209.506-5.243-4.359%25201.027-8.53-4.291-5.387-7.377%25202.047%25207.007%252013.259%25201.251%252015.576.785-2.868-1.018-.864-8.216.187-3.135%25202.578.519%252010.317%25203.479%25209.764%25202.224-1.206-5.103%25202.476-5.06%25202.673-.359%25201.837%25205.152%25209.093-1.067%25206.587%25204.574%25202.222-1.669%25204.879-3.407%25202.085.371-3.046%25204.832%25205.36-1.193%25207.322-1.966%25204.681-2.616%25209.251-5.473%252014.04-7.872-1.05%25202.179-7.845%25206.375-11.303%25209.078-3.192%25201.88-8.56%25207.18-10.263%25206.322%25204.23%25204.345.707%252010.72%25202.42%252016.137%25201.625%25205.673-9.295%25208.83-9.065%25202.35-1.28-6.84-2.348%25202.623%25201.103%25203.932%25205.221%25203.44.453%252011.815%25206.937%252013.837%25207.055-.443%25209.692%25206.987%25206.796%252012.449.186%25204.343-1.424%252014.89-6.487%252012.853-4.432-5.004.716-8.582%25202.717-13.018%25204.14-8.45-7.612-3.347-12.047-4.313-5.386-2.57-12.203%25201.887-15.768%25204.152.758%25207.43-10.693%25208.745-9.245%252015.278%25202.989%25204.843%25206.566%25209.269%25206.768%252015.245%25204.883%25204.898%252014.623.777%252021.34%25202.265%25208.434.098%252016.87.25%252025.292.748-4.714%25203.727-13.165.796-19.366%25201.773-2.772-.007-5.544-.02-8.316-.034zm-24.787-3.179c-4.296-4.086%25203.12-8.327-2.394-11.806-3.387-2.468-6.181-7.28-5.344-10.533-6.107%25203.13-6.046%25209.98-5.437%252015.882-.139%25203.902-.114%25208.147%25204.721%25206.76h8.634l-.176-.295zm-26.964-29.5c-.93-2.757-5.3-9.513-2.657-2.76.75.826%25201.387%25202.813%25202.657%25202.76zM40.32%252065.366c4.75-3.897-9.158-2.045-1.71-.015.56.22%25201.135.042%25201.71.015zm15.796-20.792c-.735-2.564-4.333.562-.316.06zm10.109%252077.137c-3.375-3.62-1.506-8.1%25201.797-9.774%25203.377-4.012%25204.902%25207.562.71%25208.746-.46.71-1.756%25202.66-2.507%25201.028zm-33.49-27.594c-2.324-.907-5.798-.512-4.328-4.238%25202.748.952%25204.155-2.564%25204.072.89%25201.878.174%25201.303%25203.12.256%25203.348zm8.223-18.58c-2.565-1.812-8.968-6.036-8.71-5.766%25205.277-1.661%252011.09%25205.987%252015.629.736-.93%25202.862-3.753%25205.422-6.92%25205.03zm28.339-15.684c-7.569-2.416%25203.694-4.806%25200%25200z%2522%252F%253E%253Cpath%2520fill%253D%2522%2523eda45f%2522%2520d%253D%2522M67.578%2520137.252c-15.963-.212-31.946-.09-47.88-1.22-3.246-.95%25205.89-1.205%25201.354-3.607-.914-4.361%25204.101-7.97%25201.266-12.706-1.193-3.318%25205.994-9.223.121-5.543%25203.872-1.96-.158-12.126.31-4.595.544%25204.28-6.066%252011.202-8.307%25209.348%25202.219-1.162%25205.916-9.415%25201.55-3.958-3.478%25205.236-13.577-2.929-10.5-3.973%25203.269%25206.486%252013.617-.125%25206.14-4.765-4.833-3.428-8.409-10.218-3.886-15.375%25203.678-5.021%252011.064-7.642%252016.806-4.688-2.887%25203.5-5.294%252010.857-.379%252013.355%25204.84%25202.841%25202.299%252010.909%25209.217%25208.513%25207.401%25201.71%25204.114-10.632%25209.64-9.654%25206.162%25201.003%252012.808-6.419%252017.48-3.393%25204.519%25202.907%25209.453-.96%252014.224-.784%25205.566-1.57-4.992-3.693-6.69-5.23-1.385-5.18-2.217-11.296-6.645-15.582-.72-3.384-.89-10.497.138-12.116%25202.213%25204.657%25205.859%25207.918%25206.474%252012.775%25207.06-.379%25202.15-10.19%25203.71-14.689-1.518-3.025-3.087-5.058-2.358-8.747-5.649%25202.49%25203.771-8.122-2.084-3.836-5.15%25204.437-13.274%25204.608-16.95%252010.95-1.95%25205.425-3.12%252017.016-11.712%252012.632-3.063-2.766-8.606-.203-8.563-.012-.014-5.415%252012.58.285%25206.01-4.56-3.406-2.337-8.558-4.919-7.487-9.932%25203.245%25207.272%252013.764%25202.27%252016.316-3.145-1.778-1.072-3.013%25207.215-7.12%25203.395-2.514-1.862-8.715%25202.415-4.502-2.492%25201.273-2.545%25209.79-.273%25207.331-2.903-5.506%25203.883-7.915-11.669-3.128-4.209%25204.718%25201.903%252010.182-1.414%252014.564-2.028-3.349-.486-2.151-8.398-.51-2.934%25202.947.592%25209.284%25202.84%25209.929%25202.346-1.706-3.568%25201.184-6.407%25201.925-1.41.85%25204.052%25207.707%25203.045%25207.352%25202.775-3.159%25206.012%25206.238-3.063%25202.033%25202.263-4.653%25205.995%25205.9-.955%25208.107-2.028%25204.692-2.236%25208.847-5.72%252013.76-7.357C87.822%252043.941%252080.24%252048.745%252073.154%252054.2c-3.012-2.548%25203.562%25203.96%25201.008%25206.134-.68%25204.916%25202.963%252012.384-3.051%252015.172-5.28%25202.276-5.21-4.425-6.636-6.478-1.505%25205.958%25206.862%25209.5%25205.683%252016.101-1.013%25206.762%252010.317%25203.618%252011.535%25209.8-1.646%25205.588-.853%252011.798-2.813%252017.106-1.62%25204.818-6.946%25204.54-6.618-1.052.11-4.493%25208.818-9.766%25204.087-13.642-5.557-.438-11.76%25201.09-16.795-.135-5.005%25202.122-11.902%25202.093-11.971%25208.89-2.871%25205.023-12.398%25208.06-6.12%252014.625%25204.937%25203.595%25202.664%252012.598%25208.304%252014.375%25207.573-.89%252015.341-.016%252022.993-.15%25204.418.716%252011.69-1.03%252014.292%25201.413-4.705%25201.86-10.61.47-15.826.957a126.381%2520126.381%25200%25200%25201-3.647-.063zm-26.915-2.823c-2.47-2.848-1.589-6.431%25200-8.393-2.06-5.076-10.061-8.693-7.203-14.791-6.099%25203.188-7.12%25209.906-6.108%252016.11-.134%25204.23.002%25208.621%25205.365%25207.085%25202.648.036%25205.297.04%25207.946-.01zm-27.015-30.112c-.86-1.712-4.75-9.447-3.184-3.252.306.46%25203.04%25205.204%25203.184%25203.252zm26.893-39.02c4.08-4.027-9.527-1.66-1.778.107.6.218%25201.178-.022%25201.778-.107zM51.134%252048.28c3.132-1.42%252011.806-1.074%252011.547-1.45-4.156-.392-12.93%25201.295-14.14%25201.588.867.02%25201.735-.023%25202.593-.138zm4.858-3.534c-.244-3.009-4.655.313-.22.031zm9.612%252075.695c-3.408-3.628-.069-6.772%25202.41-8.316%25203.263-4.28%25204.501%25206.99.978%25208.512-.958%25201.514-3.102%25202.09-3.388-.196zM28.856%252093.65c-.28-1.638-1.605-5.564%25201.613-3.324%25202.352-4.257%25204.798%25206.34.047%25202.685l-.839.081zm12.102-18.113c-2.4-2.058-8.56-5.19-8.493-6.03%25205.35-1.02%252010.558%25206.137%252015.457%25201.188-1.186%25202.024-4.303%25206.147-6.964%25204.842zm28.494-2.21c-2.457-3.675%25203.308-3.249%25200%25200zm-.155-13.522c-6.898-1.167%25202.62-5.486%25200%25200z%2522%252F%253E%253Cpath%2520fill%253D%2522%2523eda032%2522%2520d%253D%2522M54.291%2520137.105c-11.816-.332-23.664-.112-35.443-1.265%25202.076-.516%25204.01-2.106%25203.14-2.782%25202.461%25201.042%25206.61-2.585%25201.805-1.548-7.001-.493%25202.024-5.8-1.018-10.03-2.747-4.168%25204.142-8.151%25204.955-3.387-.48%25205.004-1.58%252012.544%25201.252%252016.417h11.903c-5.18-3.617%25208.956-2.394%25201.658-3.734-2.88.288-4.808-1.187-2.171-3.247.853-6.79-11.322-9.534-6.531-16.58%25204.15-3.275%25205.73-7.556%25206.904-12.317%25206.453.103%252012.379-3.037%252018.503-4.809%25204.817%25204.8%252012.247.207%252017.631-.477-3.85-3.28-12.308-3.37-9.88-10.701-.891-5.11-7.461-8.144-5.953-13.852.187-2.586-.86-11.661%25201.481-5.185%25203.612%25202.898%25203.925%25207.41%25205.794%252010.49%25206.18-.322%25202.576-9.005%25203.304-13.164.14-2.725-1.333-10.723%25202.336-4.933.042%25205.049.593%252010.49.473%252015.772-2.265%25206.544-10.545%25203.964-9.348-2.701-3.163%25205.595%25207.534%252010.598%25205.013%252017.664%25201.524%25204.893%252014.706%25203.26%252010.84%252011.553-1.665%25205.204.704%252011.676-3.633%252015.797-2.822%25205.453-7.418-4.23-1.042-2.515%25206.99-2.43-8.563-1.625-2.092-3.771%25201.798-3.742%25207.374-12.578-1.302-10.54-5.344%25201.86-10.722-1.39-15.785.444-3.976%25202.563-9.258%25201.816-9.218%25207.56-1.177%25205.76-12.54%25207.217-7.253%252013.76%25204.323%25204.302%25204.14%252010.754%25207.199%252015.62%252011.313.442%252022.672-.26%252033.953.933-4.043%25204.041-11.295.196-16.667%25201.602-3.603-.01-7.206-.033-10.808-.074zm11.359-16.668c-4.435-3.667%25208.421-1.861%25202.337-4.491-2.818.462-5.673-1.28-1.177-2.188%25201.878-2.608%25203.328-4.422%25204.057-.156%25201.712%25202.397-2.594%252011.122-5.217%25206.835zm-52.195-.689c2.965-1.328%25207.267-10.393%25201.964-4.532-3.968%25203.73-12.944-2.34-9.82-4.418%25202.832%25207.454%252013.29-.292%25205.9-4.728-7.347-4.139-7.597-14.924.143-18.793%25203.004-3.224%252016.256-3.829%252010.524%25202.805-1.248%25202.598.555%252010.033-3.705%25204.681-1.725-2.67%25202.636-10.803-2.712-4.99-4.312%25204.762-.078%252012.998%25202.658%252015.93-2.668-4.427-6.398-10.034-2.645-15.003%25204.214-5.447-.369%25204.263%25204.063%25205.752%25204.73%25202.894%252010.285%25208.137%25206.832%252014.174-1.688%25203.084-5.224%25204.656-2.693-.031%25201.937-2.653-1.93-6.759-1.204-1.816-.13%25205.185-3.881%252010.613-9.305%252010.969zm.464-14.543c-.617-2.554-5.475-10.652-3.245-3.719.843%25201.414%25201.816%25202.838%25203.245%25203.72zm4.404-8.256c-2.218-1.444-.595-10.205-1.548-3.527.123%25201.293.584%25202.613%25201.548%25203.527zm11.47-3.775c-4.9-4.357%25206.822-3.385%25201.81-.323-.467.298-1.405-.246-1.81.323zm10.7-18.093c-1.811-2.135-11.79-7.108-3.99-4.744%25202.995%25202.47%25209.223%25202.422%252010.6%25201.409-1.19%25202.205-4.007%25204.33-6.61%25203.335zm28.946-1.797c-2.38-3.685%25203.39-3.024%25200%25200zm-39.311-2.165c1.505-7.758%252011.316%25201.402%252014.08-5.06%25201.257-6.952%25202.83-15.35%252011.22-16.83%25203.281-1.459%252012.46-1.79%25204.985%25201.279-7.02%25201.775-12.166%25207.769-12.687%252015.034-.993%25208.04-9.203%25204.646-14.152%25203.36-1.493-.087-2.828.921-3.446%25202.217zm9.366-3.05c-4.091-2.369-9.25-5.39-10.822-9.803-.193-4.002%25203.613%25203.62%25206.66%25201.114%25203.73-.555%25208.945-3.666%25209.29-7.133-1.946%25203.017-5.333%25207.087-8.596%25203.082-2.775.728-6.169%25201.709-2.886-1.872%25201.69-1.305%252010.958-1.269%25207.969-2.52-3.688%25202.029-8.54-5.505-5.352-6.307%25203.795%25205.774%25209.63.167%252014.888.65%25202.724-.527-3.312-6.28.288-4.907%25202.384%25202.95%25207.708%25201.822%252010.624%25204.392-.514-2.49-.318-8.022%25201.488-2.831.503%25204.663%25208.113%25202.904%25207.375%25203.96-2%25204.874%25206.252-3.35%25202.05%25202.306-1.5%25202.753-7.71%25204.256-3.765-.36%25203.364-6.948-4.708%25204.313-4.89-.983-4.517-.761-14.364%25201.26-15.373%25201.527%25205.2.003%252010.302-2.312%252015.512-1.159-6.798%25201.35-15.67%25202.305-18.245%25209.973-1.217%25203.838.367%252012.093-6.215%252010.872zm1.17-2.82c3.407-4.633-9.658-1.052-1.732.245.616.153%25201.151-.069%25201.732-.245zm15.511-20.547c-1.207-3.563-4.684.935%25200%25200zM68.693%252059.71c-5.548-4.057%25204.373-1.526%25200%25200zm1.993-2.442c-3.563-2.834.556-7.743%25202.643-7.276%25202.745-2.748%25204.746%25204.46-.162%25203.736-3.837-3.064-1.023%25203.205-2.481%25203.54zm5.626-7c-.31-4.038%25206.2-3";
    }
}

//SPDX-License-Identifier: MIT

/// @title JPEG Mining
/// @author Xatarrer
/// @notice Unaudited
pragma solidity ^0.8.0;

import "./Array.sol";
import "./LibString.sol";

library ButerinCardsLib {
    struct ChunkUnpacked {
        address dataPointer; // uploadedKB is obtained by getting the data and checking its length.
        uint8 phaseId; // 0 = HTML, 1 = B&W, 2= Grey tones, 3 = Blue chroma, 4 = Red chroma, 5 = Resolution. Value passed by miner.
        uint16 tokenIdWithinPhase; // Value passed by miner.
        uint16 lastTokenIdInScan; // Necessary for knowing when the NFT will be available. Value passed by miner.
        uint8 quoteId; // Range [0, 44]. Fixed randomly during mining.
        uint8 bgDirectionId; // Range [0, 2]. Fixed randomly during mining.
        uint8 bgPaletteId; // Range [1, 10]. Fixed randomly during mining.
        uint8 Nicons; // Range [0, 2]. Based on the #mint of a miner modulo 3
        uint32 Nbytes; // Counts bytes uploaded up to this chunk.
        uint32 seed; // Fixed randomly during mining.
    }

    function paramsHTML(uint256 tokenId, ChunkUnpacked memory chunk) external pure returns (bytes memory) {
        return
            bytes(
                string.concat(
                    LibString.toString(tokenId + 1),
                    "%2C",
                    LibString.toString(chunk.phaseId),
                    "%2C",
                    LibString.toString(chunk.tokenIdWithinPhase),
                    "%2C",
                    LibString.toString(chunk.Nbytes / 1024),
                    "%2C",
                    LibString.toString(chunk.quoteId),
                    "%2C",
                    LibString.toString(chunk.bgDirectionId),
                    "%2C",
                    LibString.toString(chunk.bgPaletteId),
                    "%2C",
                    LibString.toString(chunk.Nicons),
                    "%2C",
                    LibString.toString(chunk.seed),
                    "%2C%22"
                )
            );
    }

    function quoteName(uint quoteId) public pure returns (string memory) {
        string[45] memory quotes = [
            "%F0%9F%A6%84%20WoW",
            "%F0%9F%A6%84%20The%20Hard%20Forkoooooor",
            "%F0%9F%A6%84%20Bitcoiner",
            "%F0%9F%A6%84%20Weeeeeeee!",
            "%F0%9F%A6%84%20Humanton",
            "%F0%9F%A6%84%20That%20Darn%20Rock",
            "%F0%9F%A6%84%20Jason",
            "%F0%9F%A6%84%20Ultra-Sound%20Money",
            "Incentives",
            "Sourceful",
            "Paradoxical",
            "Currency%20%26%20Protocol",
            "Abnormal",
            "Institutional",
            "Revolution",
            "Peer-to-peer",
            "Cross-platform",
            "Dub",
            "Fundamentalism",
            "Signal",
            "Programmable",
            "Scripter",
            "E-God",
            "Credibly%20Insane",
            "Exponential",
            "Asian%20Fusion",
            "Blockchain%20Centrist",
            "2017%20Dec%2013",
            "Slippery%20Slope%20Fallacy",
            "Buterin's%20Law",
            "Elliptic%20Brains",
            "Social%20Capital",
            "Order",
            "Nope",
            "Legitimate",
            "Theatrical",
            "Blockchain",
            "Metaverse",
            "Paradox%20of%20Diversity",
            "Freedom",
            "Reminder",
            "Pluralist",
            "Alfalfa",
            "Beware",
            "Sound"
        ];
        return quotes[quoteId];
    }

    function phaseName(uint phaseId) public pure returns (string memory) {
        string[6] memory phases = [
            "Pencil%20Drawing",
            "Black%20%26%20White",
            "Grey%20Shades",
            "Blue%20Chroma",
            "Red%20Chroma",
            "Resolution"
        ];
        return phases[phaseId];
    }

    function bgDirection(uint bgDirectionId) public pure returns (string memory) {
        string[3] memory bgDirections = ["Horizontal", "Vertical", "Diagonal"];
        return bgDirections[bgDirectionId];
    }

    function bgPalette(uint bgPaletteId) public pure returns (string memory) {
        string[10] memory bgPalettes = [
            "Oceanic%20Twilight",
            "Earthen%20Tones",
            "Calm%20Coastline",
            "Warm%20Sunset",
            "Tropical%20Skies",
            "Mystic%20Blossoms",
            "Oceanic%20Contrast",
            "Autumn%20Breeze",
            "Enchanted%20Forest",
            "Vibrant%20Spectrum"
        ];
        return bgPalettes[bgPaletteId - 1];
    }

    function cardNotAvailable(uint256 lastTokenIdInScan) external pure returns (string memory dataURI) {
        return
            string.concat(
                "data%3Aimage%2Fsvg%2Bxml%3Bcharset%3DUTF-8%2C%253Csvg%250D%250A%2520%2520%2520%2520viewBox%253D%2522-20%25200%252040%252030%2522%250D%250A%2520%2520%2520%2520xmlns%253D%2522http%253A%252F%252Fwww.w3.org%252F2000%252Fsvg%2522%250D%250A%2520%2520%2520%2520xmlns%253Axlink%253D%2522http%253A%252F%252Fwww.w3.org%252F1999%252Fxlink%2522%250D%250A%2520%2520%2520%2520style%253D%2522font-size%253A%252023%2525%2522%250D%250A%253E%250D%250A%2520%2520%2520%2520%253Ctext%2520x%253D%25220%2522%2520y%253D%25225%2522%2520text-anchor%253D%2522middle%2522%253EThis%2520card%253C%252Ftext%253E%250D%250A%2520%2520%2520%2520%253Ctext%2520x%253D%25220%2522%2520y%253D%252210%2522%2520text-anchor%253D%2522middle%2522%253Ewill%2520be%2520visible%253C%252Ftext%253E%250D%250A%2520%2520%2520%2520%253Ctext%2520x%253D%25220%2522%2520y%253D%252215%2522%2520text-anchor%253D%2522middle%2522%253Ewhen%2520card%2520%2523",
                LibString.toString(lastTokenIdInScan),
                "%253C%252Ftext%253E%250D%250A%2520%2520%2520%2520%253Ctext%2520x%253D%25220%2522%2520y%253D%252220%2522%2520text-anchor%253D%2522middle%2522%253Eis%2520mined.%253C%252Ftext%253E%250D%250A%253C%252Fsvg%253E%250D%250A"
            );
    }

    function cardBackPiece4() external pure returns (string memory piece) {
        return
            ".828%25208.54-6.626%25202.396-1.748%25207.753-3.776%25202.757-.368-3.731%25201.454-7.517%25207.77-11.297%25206.995zm13.766-9.146c1.432-2.348%25205.907-3.241%25201.49-.842-.434.313-.786%25201.223-1.49.842z%2522%252F%253E%253Cpath%2520fill%253D%2522%25234ea2a6%2522%2520d%253D%2522M54.291%2520137.105c-11.816-.332-23.664-.112-35.443-1.265%25202.076-.516%25204.01-2.106%25203.14-2.782%25202.461%25201.042%25206.61-2.585%25201.805-1.548-7.001-.493%25202.024-5.8-1.018-10.03-2.747-4.168%25204.142-8.151%25204.955-3.387-.48%25205.004-1.58%252012.544%25201.252%252016.417h11.903c-5.18-3.617%25208.956-2.394%25201.658-3.734-2.88.288-4.808-1.187-2.171-3.247.853-6.79-11.322-9.534-6.531-16.58%25204.15-3.275%25205.73-7.556%25206.904-12.317%25206.453.103%252012.379-3.037%252018.503-4.809%25204.817%25204.8%252012.247.207%252017.631-.477-3.85-3.28-12.308-3.37-9.88-10.701-.891-5.11-7.461-8.144-5.953-13.852.187-2.586-.86-11.661%25201.481-5.185%25203.612%25202.898%25203.925%25207.41%25205.794%252010.49%25206.18-.322%25202.576-9.005%25203.304-13.164.224-2.375-1.394-11.042%25202.287-5.01.508%25204.707.008%25209.848.694%252014.789-.324%25206.677-11.087%25206.38-9.05-1.014-3.461-1.727-.532%25206.13%25201.715%25206.805%25204.529%25204.016-.129%252012.879%25207.235%252013.51%25206.835.423%25208.05%25206.516%25205.862%252011.874-.264%25205.065.046%252012.105-5.815%252013.911-5.136-4.211%25202.259-3.518%25203.592-5.538-1.038-1.388-8.929-.432-3.973-2.461%25201.798-3.742%25207.374-12.578-1.302-10.54-5.344%25201.86-10.722-1.39-15.785.444-3.976%25202.563-9.258%25201.816-9.218%25207.56-1.177%25205.76-12.54%25207.217-7.253%252013.76%25204.323%25204.302%25204.14%252010.754%25207.199%252015.62%252011.313.442%252022.672-.26%252033.953.933-4.043%25204.041-11.295.196-16.667%25201.602-3.603-.01-7.206-.033-10.808-.074zm11.359-16.668c-4.435-3.667%25208.421-1.861%25202.337-4.491-2.818.462-5.673-1.28-1.177-2.188%25201.878-2.608%25203.328-4.422%25204.057-.156%25201.712%25202.397-2.594%252011.122-5.217%25206.835zm-52.195-.689c2.965-1.328%25207.267-10.393%25201.964-4.532-3.968%25203.73-12.944-2.34-9.82-4.418%25202.832%25207.454%252013.29-.292%25205.9-4.728-7.347-4.139-7.597-14.924.143-18.793%25203.004-3.224%252016.256-3.829%252010.524%25202.805-1.248%25202.598.555%252010.033-3.705%25204.681-1.726-2.67%25202.636-10.802-2.71-4.992-4.002%25205.093-.68%252014.904%25204.69%252016.967%25206.516-3.126-4.114-7.218-4.381-11.557-.75-2.785%25201.157-5.387.967-.771%25201.432%25205.034%25208.216%25207.242%25208.124%252013.425-.734%25203.085%25203.498%25201.121.145%25204.278-4.922%25205.244%25201.854-4.196-2.375-5.767.13%25205.958-2.648%252012.751-9.466%252013.402zm.464-14.543c-.617-2.554-5.475-10.652-3.245-3.719.843%25201.414%25201.816%25202.838%25203.245%25203.72zm55.52-31.921c-2.38-3.685%25203.39-3.024%25200%25200zm-39.311-2.165c-1.416-6.001%252011.871-2.063%25204.173-2.13-1.633-.105-3.54.438-4.173%25202.13zm9.366-3.05c-4.091-2.369-9.25-5.39-10.822-9.803-.193-4.002%25203.613%25203.62%25206.66%25201.114%25203.73-.555%25208.945-3.666%25209.29-7.133-1.946%25203.017-5.333%25207.087-8.596%25203.082-2.775.728-6.169%25201.709-2.886-1.872%25201.69-1.305%252010.958-1.269%25207.969-2.52-3.688%25202.029-8.54-5.505-5.352-6.307%25203.795%25205.774%25209.63.167%252014.888.65%25202.724-.527-3.312-6.28.288-4.907%25202.384%25202.95%25207.708%25201.822%252010.624%25204.392-.514-2.49-.318-8.022%25201.488-2.831.503%25204.663%25208.113%25202.904%25207.375%25203.96-2%25204.874%25206.252-3.35%25202.05%25202.306-1.5%25202.753-7.71%25204.256-3.765-.36%25203.364-6.948-4.708%25204.313-4.89-.983-4.517-.761-14.364%25201.26-15.373%25201.527%25205.2.003%252010.302-2.312%252015.512-1.159-6.798%25201.35-15.67%25202.305-18.245%25209.973-1.217%25203.838.367%252012.093-6.215%252010.872zm1.17-2.82c3.407-4.633-9.658-1.052-1.732.245.616.153%25201.151-.069%25201.732-.245zm15.511-20.547c-1.207-3.563-4.684.935%25200%25200zM68.693%252059.71c-5.548-4.057%25204.373-1.526%25200%25200zm6.278-7.368c-6.526-6.566%25206.88-1.223%25200%25200z%2522%252F%253E%253Cpath%2520fill%253D%2522%2523bb438c%2522%2520d%253D%2522M54.291%2520137.105c-11.816-.332-23.664-.112-35.443-1.265%25202.076-.516%25204.01-2.106%25203.14-2.782%25202.461%25201.042%25206.61-2.585%25201.805-1.548-7.001-.493%25202.024-5.8-1.018-10.03-2.67-4.147%25203.904-8.095%25205-3.448-.768%25205.124-1.225%252011.884.864%252016.478h12.246c-5.18-3.617%25208.956-2.394%25201.658-3.734-2.88.288-4.808-1.187-2.171-3.247.853-6.79-11.322-9.534-6.531-16.579%25204.15-3.276%25205.73-7.557%25206.904-12.318%25206.453.103%252012.379-3.037%252018.503-4.809%25204.817%25204.8%252012.247.207%252017.631-.477-3.85-3.28-12.308-3.37-9.88-10.701-.891-5.11-7.461-8.144-5.953-13.852.187-2.586-.86-11.661%25201.481-5.185%25203.612%25202.898%25203.925%25207.41%25205.794%252010.49%25206.18-.322%25202.576-9.005%25203.304-13.164.372-2.166-1.578-10.744%25202.151-5.247.738%25204.773.157%252010.043.799%252015.083-.038%25206.414-11.118%25206.426-8.88-.705-3.097-3.39-1.33%25205.412%25201.324%25206.08%25204.73%25204.496.125%252013.918%25208.513%252014.2%25208.325.826%25205.123%25208.843%25204.62%252014.276.835%25203.798-2.372%252013.305-6.722%25209.95-3.751-4.128%25209.391-2.733%25202.772-5.073-1.986.561-5.932-.851-2.03-1.698%25201.798-3.742%25207.374-12.578-1.302-10.54-5.344%25201.86-10.722-1.39-15.785.444-3.976%25202.563-9.258%25201.816-9.218%25207.56-1.177%25205.76-12.54%25207.217-7.253%252013.76%25204.323%25204.302%25204.14%252010.754%25207.199%252015.62%252011.313.442%252022.672-.26%252033.953.933-4.043%25204.041-11.295.196-16.667%25201.602-3.603-.01-7.206-.033-10.808-.074zm11.359-16.668c-4.435-3.667%25208.421-1.861%25202.337-4.491-2.818.462-5.673-1.28-1.177-2.188%25201.878-2.608%25203.328-4.422%25204.057-.156%25201.712%25202.397-2.594%252011.122-5.217%25206.835zm-52.195-.689c2.965-1.328%25207.267-10.393%25201.964-4.532-3.86%25203.762-13.056-2.4-9.715-4.296%25203.046%25207.013%252012.95.01%25206.116-4.577-2.288-2.012-10.037-9.375-2.621-8.432-.42-3.242%25205.62-9.401%25201.38-3.139-2.272%25202.99%25201.69%252011.017%25202.892%25209.286-.646-2.26-5.228-7.29-.54-5.011%25201.5-2.991.345-3.77%25201.574-.077.19%25203.943%25207.555%252011.977%25207.812%25204.592-2.35-3.465-8.896-8.167-5.484-12.064-1.387%25207.178%25209.927%25209.852%25207.93%252017.108-.406%25202.358%25204.267.685.726%25203.405-1.819%25202.57-3.914%25202.05-1.39-.565%25201.678-2.6-2.433-7.987-1.492-2.311.148%25205.073-4.011%252010.177-9.152%252010.613zm7.386-22.997c-4.707-2.048%25205.25-13.376.835-5.89-1.048%25201.426%25201.261%25205.786-.835%25205.89zm48.598-23.467c-2.38-3.685%25203.39-3.024%25200%25200zm-39.311-2.165c-1.416-6.001%252011.871-2.063%25204.173-2.13-1.633-.105-3.54.438-4.173%25202.13zm9.366-3.05c-4.091-2.369-9.25-5.39-10.822-9.803-.193-4.002%25203.613%25203.62%25206.66%25201.114%25203.73-.555%25208.945-3.666%25209.29-7.133-1.946%25203.017-5.333%25207.087-8.596%25203.082-2.775.728-6.169%25201.709-2.886-1.872%25201.69-1.305%252010.958-1.269%25207.969-2.52-3.687%25202.025-8.537-5.497-5.354-6.31%25203.588%25205.81%25209.195.293%252014.181.824%25204.734%25201.576%25209.646.674%252014.618%25201.32%25202.677.038%25208.273-4.197%25205.436%25201.182%25202.315-1.258%25205.569-4.222%25202.645.22-1.455%25202.746-8.046%25204.27-3.916-.373%25203.326-6.875-4.738%25204.359-4.904-.943-4.517-.761-14.364%25201.26-15.373%25201.527%25205.2.003%252010.302-2.312%252015.512-1.159-6.798%25201.35-15.67%25202.305-18.245%25209.973-1.217%25203.838.367%252012.093-6.215%252010.872zm1.17-2.82c3.407-4.633-9.658-1.052-1.732.245.616.153%25201.151-.069%25201.732-.245zm28.029-5.54c-5.548-4.056%25204.373-1.525%25200%25200zm6.278-7.367c-6.526-6.566%25206.88-1.223%25200%25200zm-23.713-8.24c-3.366-2.186.323-6.77.17-1.577-.203.032.682%25202.179-.17%25201.577z%2522%252F%253E%253Cpath%2520fill%253D%2522%25235c5d98%2522%2520d%253D%2522M54.291%2520137.105c-11.816-.332-23.664-.112-35.443-1.265%25202.148-.485%25203.906-2.175%25203.252-2.776%25206.22%25201.38%25207.008-11.583.793-10.188-2.084-3.796.916-9.775%25204.697-5.707-.294%25205.685-1.312%252011.858.899%252017.195%25203.128.428%25206.503-.006%25209.756.146%25204.633%25201.241-.888-3.75%25204.734-2.523%25206.365.072-1.428-11.851-3.016-7.372-3.508-4.414-11.384-10.982-4.093-16.032%25205.205-3.024%25201.84-12.048%25209.046-10.405%25206.673.04%252012.319-6.91%252017.98-2.245%25204.302-.665%252011.386-.543%252013.867-3.034-4.603-2.18-12.08-3.698-9.938-10.532-1.42-4.92-7.028-8.295-5.794-13.935.139-2.552-.725-11.233%25201.496-4.824%25203.612%25202.898%25203.925%25207.41%25205.794%252010.49%25206.18-.322%25202.576-9.005%25203.304-13.164.372-2.166-1.578-10.744%25202.151-5.247.738%25204.773.157%252010.043.799%252015.083-.038%25206.414-11.118%25206.426-8.88-.705-3.097-3.39-1.33%25205.412%25201.324%25206.08%25204.73%25204.496.125%252013.918%25208.513%252014.2%25208.325.826%25205.123%25208.843%25204.62%252014.276.777%25203.8-2.25%252013.049-6.633%252010.105-2.446-4.885%25209.415-2.626%25204.951-9.21-8.511%25201.84%25204.849-8.562-3.132-8.456-5.254.677-10.589%25201.112-15.68-.17-5.257%25202.444-11.79%25202.522-12.216%25209.222-1.968%25205.127-12.361%25207.206-6.44%252013.509%25204.782%25203.768%25202.996%252012.408%25207.87%252015.061%252010.963.362%252021.966-.26%252032.894.895-4.043%25204.041-11.295.196-16.667%25201.602-3.603-.01-7.206-.033-10.808-.074zm11.359-16.668c-3.506-4.237%25206.951-.457%25204.37-6.157-.754-3.32-1.22-4.513.847-.677%25201.711%25202.397-2.595%252011.12-5.217%25206.834zm-56.332-4.509c-3.912%25201.694-5.899-9.464-2.664-3.633%25206.035%25205.979%252011.062-6.072%25203.197-7.643-1.868-1.969-6.685-7.438-1.187-6.706%25201.462-1.865%25205.019-10.046%25202.115-3.657-2.55%25203.058%25201.31%252011.426%25202.692%25209.769-.625-2.182-5.217-7.386-.588-4.926%25201.5-2.393.45-4.46%25201.45-.507%25201.938%25204.63%25207.309%252010.38%25202.924%252015.222-2.03%25202.055-5.186%25202.588-7.94%25202.081zM20.84%252096.751c-4.707-2.048%25205.25-13.376.835-5.89-1.048%25201.426%25201.261%25205.786-.835%25205.89zm48.598-23.467c-2.38-3.685%25203.39-3.024%25200%25200zm-.746-13.574c-5.548-4.057%25204.373-1.526%25200%25200zm-37.107-3.226c-.522-6.111%252010.462-2.279%25209.682-5.974-4.124%25201.722-7.952-3.136-5.974-6.966%25203.2%25208.274%252012.377-.89%252018.016%25202.831%25205.78-.373%252011.771%25201.782%252016.835-1.541%25201.512-.747-1.629%25205.093%25201.8%25202.074%25205.76-3.085-6.02%25208.277-4.052%25202.485%25202.137-2.701%25202.115-6.097-1.292-1.997-2.74%25202.919-3.359-2.535-7.064-.64-5.492%25201.098-13.577-.493-15.437%25206.32-2.99%25207.387-7.835-.2-12.514%25203.408zm19.672-12.382c-3.366-2.186.323-6.77.17-1.577-.203.032.682%25202.179-.17%25201.577z%2522%252F%253E%253Cpath%2520fill%253D%2522%252335384c%2522%2520d%253D%2522M53.13%2520137.013c-11.361-.278-22.747-.16-34.074-1.202.612-1.018%25205.354-.648%25202.204-2.927%25207.724%25201.916%25205.434-8.035%25205.697-12.567%25201.527%25201.516-1.744%252010.94%25201.902%252014.02%25203.434-.07%252010.517%25201.079%252011.4-.736-1.171-3.17%25207.848.022%25204.741-5.113-2.305-5.067-4.427-12.766-10.055-14.42-5.294-1.99%25204.885-7.295%25204.948-11.776.965-7.667%252012.052-2.317%252015.995-7.829%25204.864-1.034%25208.668%25203.7%252013.653.646%25204.338-.207%252010.717-1.854%25203.527-4.104-8.196-.356-4.635-9.348-8.906-13.935-4.538-3.784-2.913-9.648-3.09-14.875-.438-2.747%25202.425%25204.057%25204.375%25204.932%25202.366%25202.315.446%25209.645%25205.76%25206.313%25201.484-5.301.282-11.75.096-17.187.434-6.352%25204.779%25202.883%25202.558%25205.411%25201.494%25205.203%25201.417%252016.047-6.751%252013.628-2.011-1.208-1.431-9.897-3.203-4.199.833%25204.556%25206.3%25207.137%25205.963%252012.17-2.02%25207.53%25207.547%25205.856%252010.842%252010.138%25201.824%25205.71-1.099%252012.654-2.15%252018.701-.78%25204.757-6.825%25205.004-4.81-.04%25207.417.382%25205.157-8.89%25204.138-13.61-3.38-4.06-10.606%25201.478-15.042-1.444-4.995.424-12.718%25201.258-14.714%25206.688.327%25206.902-11.546%25207.345-8.345%252014.319%25204.206%25204.6%25205.633%252012.37%25208.199%252016.696%25209.659.282%252019.329.055%252028.982.555-2.002%25204.238-10.428.639-14.85%25201.815-2.997-.012-5.994-.034-8.99-.068zm13.307-15.209c-5.3-5.132%25205.594-2.024%25203.868-7.423.073-4.554%25201.9%25205.297-1.632%25206.369-.447.33-1.51%25201.838-2.236%25201.054zm-52.843-16.289c-5.844-2.461-4.009-12.912-1.208-13.868-3.783%25204.309-2.69%252011.493%25201.208%252013.868zm7.199-8.84c-3.9-2.46%25204.68-13.187.814-5.155-.492%25201.35.803%25204.898-.814%25205.156zm48.52-23.885c-1.491-3.886%25202.915-.93%25200%25200zm-1.557-13.345c-3.457-5.99%25205.857%25201.238%25200%25200zm-.528-8.177c.784-2.42%25204.42-8.375-.39-4.219-6.722%25203.902%25207.535-6.662%25202.979%25201.12%25201.954-1.91%25205.38-2.948%25202.255.352-.53%25203.145-3.753.417-4.844%25202.747z%2522%252F%253E%253C%252Fg%253E%253Crect%2520width%253D%2522100%2522%2520height%253D%2522140%2522%2520rx%253D%25225%2522%2520clip-path%253D%2522url(%2523clipFrame)%2522%252F%253E%253Cg%2520font-family%253D%2522Orbitron%2522%2520text-anchor%253D%2522middle%2522%2520filter%253D%2522url(%2523rainbowGlow)%2522%253E%253Ctext%2520x%253D%252250%2522%2520y%253D%252235%2522%2520font-size%253D%25223em%2522%253EButerin%2520Card%253C%252Ftext%253E%253C%252Fg%253E%253Ccircle%2520cx%253D%252250%2522%2520cy%253D%252285%2522%2520r%253D%252250%2522%2520fill%253D%2522url(%2523lightGlow)%2522%252F%253E%253Cuse%2520href%253D%2522%2523eth_pencil%2522";
    }

    function cardBackPiece5(uint8 phaseId) external pure returns (string memory piece) {
        bytes[] memory bytesSegments = new bytes[](12);
        string memory glowStr = "%2520filter%253D%2522url(%2523rainbowGlowLarge)%2522";

        if (phaseId == 0) bytesSegments[0] = bytes(glowStr);
        bytesSegments[1] = bytes("%252F%253E%253Cuse%2520href%253D%2522%2523eth_bw%2522");
        if (phaseId == 1) bytesSegments[2] = bytes(glowStr);
        bytesSegments[3] = bytes("%252F%253E%253Cuse%2520href%253D%2522%2523eth_grey%2522");
        if (phaseId == 2) bytesSegments[4] = bytes(glowStr);
        bytesSegments[5] = bytes("%252F%253E%253Cuse%2520href%253D%2522%2523eth_color_a%2522");
        if (phaseId == 3) bytesSegments[6] = bytes(glowStr);
        bytesSegments[7] = bytes("%252F%253E%253Cuse%2520href%253D%2522%2523eth_color_b%2522");
        if (phaseId == 4) bytesSegments[8] = bytes(glowStr);
        bytesSegments[9] = bytes("%252F%253E%253Cuse%2520href%253D%2522%2523eth_diamond%2522");
        if (phaseId == 5) bytesSegments[10] = bytes(glowStr);
        bytesSegments[11] = bytes("%252F%253E%253C%252Fsvg%253E");

        return string(Array.join(bytesSegments));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./IERC721Metadata.sol";
import "./Address.sol";
import "./Context.sol";
import "./Strings.sol";
import "./ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

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

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

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner or approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - 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 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownerOf(tokenId) != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - 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 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId, 1);

        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
        require(!_exists(tokenId), "ERC721: token already minted");

        unchecked {
            // Will not overflow unless all 2**256 token ids are minted to the same owner.
            // Given that tokens are minted one by one, it is impossible in practice that
            // this ever happens. Might change if we allow batch minting.
            // The ERC fails to describe this case.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId, 1);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId, 1);

        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
        owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        delete _tokenApprovals[tokenId];

        unchecked {
            // Cannot overflow, as that would require more tokens to be burned/transferred
            // out than the owner initially received through minting and transferring in.
            _balances[owner] -= 1;
        }
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId, 1);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId, 1);

        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];

        unchecked {
            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
            // `from`'s balance is the number of token held, which is at least one before the current
            // transfer.
            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
            // all 2**256 token ids to be minted, which in practice is impossible.
            _balances[from] -= 1;
            _balances[to] += 1;
        }
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId, 1);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
     * - When `from` is zero, the tokens will be minted for `to`.
     * - When `to` is zero, ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256, /* firstTokenId */
        uint256 batchSize
    ) internal virtual {
        if (batchSize > 1) {
            if (from != address(0)) {
                _balances[from] -= batchSize;
            }
            if (to != address(0)) {
                _balances[to] += batchSize;
            }
        }
    }

    /**
     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
     * - When `from` is zero, the tokens were minted for `to`.
     * - When `to` is zero, ``from``'s tokens were burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize
    ) internal virtual {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "./ERC721.sol";
import "./IERC721Enumerable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    /**
     * @dev See {ERC721-_beforeTokenTransfer}.
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, firstTokenId, batchSize);

        if (batchSize > 1) {
            // Will only trigger during construction. Batch transferring (minting) is not available afterwards.
            revert("ERC721Enumerable: consecutive transfers not supported");
        }

        uint256 tokenId = firstTokenId;

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Efficient library for creating string representations of integers.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibString.sol)
library LibString {
    function toString(uint256 value) internal pure returns (string memory str) {
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes
            // to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the
            // trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes.
            let newFreeMemoryPointer := add(mload(0x40), 160)

            // Update the free memory pointer to avoid overriding our string.
            mstore(0x40, newFreeMemoryPointer)

            // Assign str to the end of the zone of newly allocated memory.
            str := sub(newFreeMemoryPointer, 32)

            // Clean the last word of memory it may not be overwritten.
            mstore(str, 0)

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

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                // Move the pointer 1 byte to the left.
                str := sub(str, 1)

                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))

                // Keep dividing temp until zero.
                temp := div(temp, 10)

                 // prettier-ignore
                if iszero(temp) { break }
            }

            // Compute and cache the final total length of the string.
            let length := sub(end, str)

            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 32)

            // Store the string's length at the start of memory allocated for our string.
            mstore(str, length)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates merkle trees that are safe
 * against this attack out of the box.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "./Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

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

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

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

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

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

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
    uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.

    /*//////////////////////////////////////////////////////////////
                               WRITE LOGIC
    //////////////////////////////////////////////////////////////*/

    function write(bytes memory data) internal returns (address pointer) {
        // Prefix the bytecode with a STOP opcode to ensure it cannot be called.
        bytes memory runtimeCode = abi.encodePacked(hex"00", data);

        bytes memory creationCode = abi.encodePacked(
            //---------------------------------------------------------------------------------------------------------------//
            // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
            //---------------------------------------------------------------------------------------------------------------//
            // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
            // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
            // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
            // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
            // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
            // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
            // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
            // 0xf3    |  0xf3               | RETURN       |                                                                //
            //---------------------------------------------------------------------------------------------------------------//
            hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes.
            runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
        );

        assembly {
            // Deploy a new contract with the generated creation code.
            // We start 32 bytes into the code to avoid copying the byte length.
            pointer := create(0, add(creationCode, 32), mload(creationCode))
        }

        require(pointer != address(0), "DEPLOYMENT_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                               READ LOGIC
    //////////////////////////////////////////////////////////////*/

    function read(address pointer) internal view returns (bytes memory) {
        return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
    }

    function read(address pointer, uint256 start) internal view returns (bytes memory) {
        start += DATA_OFFSET;

        return readBytecode(pointer, start, pointer.code.length - start);
    }

    function read(
        address pointer,
        uint256 start,
        uint256 end
    ) internal view returns (bytes memory) {
        start += DATA_OFFSET;
        end += DATA_OFFSET;

        require(pointer.code.length >= end, "OUT_OF_BOUNDS");

        return readBytecode(pointer, start, end - start);
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function readBytecode(
        address pointer,
        uint256 start,
        uint256 size
    ) private view returns (bytes memory data) {
        assembly {
            // Get a pointer to some free memory.
            data := mload(0x40)

            // Update the free memory pointer to prevent overriding our data.
            // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
            // Adding 31 to size and running the result through the logic above ensures
            // the memory pointer remains word-aligned, following the Solidity convention.
            mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))

            // Store the size of the data in the first 32 byte chunk of free memory.
            mstore(data, size)

            // Copy the code into memory right after the 32 bytes we used to store the size.
            extcodecopy(pointer, add(data, 32), start, size)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./Math.sol";

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):