false
false
0
The new Blockscout UI is now open source! Learn how to deploy it here

Contract Address Details

0x2aD276b7B1B34c7B9c7852a8110137713Dbbb22B

Contract Name
PEMCertChainLib
Creator
0x4779d1–4df7b8 at 0x0f5c2d–0b880b
Balance
0 ETH
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
5106
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
PEMCertChainLib




Optimization enabled
true
Compiler version
v0.8.30+commit.73712a01




Optimization runs
200
EVM Version
prague




Verified at
2025-12-15T12:46:06.548767Z

contracts/layer1/automata-attestation/lib/PEMCertChainLib.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "../utils/Asn1Decode.sol";
import "../utils/BytesUtils.sol";
import "../utils/X509DateUtils.sol";
import "./interfaces/IPEMCertChainLib.sol";
import "solady/src/utils/LibString.sol";

/// @title PEMCertChainLib
/// @custom:security-contact security@taiko.xyz
contract PEMCertChainLib is IPEMCertChainLib {
    using Asn1Decode for bytes;
    using NodePtr for uint256;
    using BytesUtils for bytes;

    string internal constant HEADER = "-----BEGIN CERTIFICATE-----";
    string internal constant FOOTER = "-----END CERTIFICATE-----";
    uint256 internal constant HEADER_LENGTH = 27;
    uint256 internal constant FOOTER_LENGTH = 25;

    string internal constant PCK_COMMON_NAME = "Intel SGX PCK Certificate";
    string internal constant PLATFORM_ISSUER_NAME = "Intel SGX PCK Platform CA";
    string internal constant PROCESSOR_ISSUER_NAME = "Intel SGX PCK Processor CA";
    bytes internal constant SGX_EXTENSION_OID = hex"2A864886F84D010D01";
    bytes internal constant TCB_OID = hex"2A864886F84D010D0102";
    bytes internal constant PCESVN_OID = hex"2A864886F84D010D010211";
    bytes internal constant PCEID_OID = hex"2A864886F84D010D0103";
    bytes internal constant FMSPC_OID = hex"2A864886F84D010D0104";

    // https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/e7604e02331b3377f3766ed3653250e03af72d45/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/X509Constants.h#L64
    uint256 constant SGX_TCB_CPUSVN_SIZE = 16;

    struct PCKTCBFlags {
        bool fmspcFound;
        bool pceidFound;
        bool tcbFound;
    }

    function splitCertificateChain(
        bytes memory pemChain,
        uint256 size
    )
        external
        pure
        returns (bool success, bytes[] memory certs)
    {
        certs = new bytes[](size);
        string memory pemChainStr = string(pemChain);

        uint256 index = 0;
        uint256 len = pemChain.length;

        for (uint256 i; i < size; ++i) {
            string memory input;
            if (i != 0) {
                input = LibString.slice(pemChainStr, index, index + len);
            } else {
                input = pemChainStr;
            }
            uint256 increment;
            (success, certs[i], increment) = _removeHeadersAndFooters(input);

            if (!success) {
                return (false, certs);
            }

            index += increment;
        }

        success = true;
    }

    function decodeCert(
        bytes memory der,
        bool isPckCert
    )
        external
        pure
        returns (bool success, ECSha256Certificate memory cert)
    {
        uint256 root = der.root();

        // Entering tbsCertificate sequence
        uint256 tbsParentPtr = der.firstChildOf(root);

        // Begin iterating through the descendants of tbsCertificate
        uint256 tbsPtr = der.firstChildOf(tbsParentPtr);

        // The Serial Number is located one element below Version

        // The issuer commonName value is contained in the Issuer sequence
        // which is 3 elements below the first element of the tbsCertificate sequence

        // The Validity sequence is located 4 elements below the first element of the tbsCertificate
        // sequence

        // The subject commonName value is contained in the Subject sequence
        // which is 5 elements below the first element of the tbsCertificate sequence

        // The PublicKey is located in the second element of subjectPublicKeyInfo sequence
        // which is 6 elements below the first element of the tbsCertificate sequence

        tbsPtr = der.nextSiblingOf(tbsPtr);

        {
            bytes memory serialNumBytes = der.bytesAt(tbsPtr);
            cert.serialNumber = serialNumBytes;
        }

        tbsPtr = der.nextSiblingOf(tbsPtr);
        tbsPtr = der.nextSiblingOf(tbsPtr);

        if (isPckCert) {
            uint256 issuerPtr = der.firstChildOf(tbsPtr);
            issuerPtr = der.firstChildOf(issuerPtr);
            issuerPtr = der.firstChildOf(issuerPtr);
            issuerPtr = der.nextSiblingOf(issuerPtr);
            cert.pck.issuerName = string(der.bytesAt(issuerPtr));
            bool issuerNameIsValid = LibString.eq(cert.pck.issuerName, PLATFORM_ISSUER_NAME)
                || LibString.eq(cert.pck.issuerName, PROCESSOR_ISSUER_NAME);
            if (!issuerNameIsValid) {
                return (false, cert);
            }
        }

        tbsPtr = der.nextSiblingOf(tbsPtr);

        {
            uint256 notBeforePtr = der.firstChildOf(tbsPtr);
            uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);
            bytes1 notBeforeTag = der[notBeforePtr.ixs()];
            bytes1 notAfterTag = der[notAfterPtr.ixs()];
            if (
                (notBeforeTag != 0x17 && notBeforeTag != 0x18)
                    || (notAfterTag != 0x17 && notAfterTag != 0x18)
            ) {
                return (false, cert);
            }
            cert.notBefore = X509DateUtils.toTimestamp(der.bytesAt(notBeforePtr));
            cert.notAfter = X509DateUtils.toTimestamp(der.bytesAt(notAfterPtr));
        }

        tbsPtr = der.nextSiblingOf(tbsPtr);

        if (isPckCert) {
            uint256 subjectPtr = der.firstChildOf(tbsPtr);
            subjectPtr = der.firstChildOf(subjectPtr);
            subjectPtr = der.firstChildOf(subjectPtr);
            subjectPtr = der.nextSiblingOf(subjectPtr);
            cert.pck.commonName = string(der.bytesAt(subjectPtr));
            if (!LibString.eq(cert.pck.commonName, PCK_COMMON_NAME)) {
                return (false, cert);
            }
        }

        tbsPtr = der.nextSiblingOf(tbsPtr);

        {
            // Entering subjectPublicKeyInfo sequence
            uint256 subjectPublicKeyInfoPtr = der.firstChildOf(tbsPtr);
            subjectPublicKeyInfoPtr = der.nextSiblingOf(subjectPublicKeyInfoPtr);

            // The Signature sequence is located two sibling elements below the tbsCertificate
            // element
            uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);
            sigPtr = der.nextSiblingOf(sigPtr);

            // Skip three bytes to the right
            // the three bytes in question: 0x034700 or 0x034800 or 0x034900
            sigPtr = NodePtr.getPtr(sigPtr.ixs() + 3, sigPtr.ixf() + 3, sigPtr.ixl());

            sigPtr = der.firstChildOf(sigPtr);
            bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);

            sigPtr = der.nextSiblingOf(sigPtr);
            bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);

            cert.tbsCertificate = der.allBytesAt(tbsParentPtr);
            cert.pubKey = _trimBytes(der.bytesAt(subjectPublicKeyInfoPtr), 64);
            cert.signature = abi.encodePacked(sigX, sigY);
        }

        if (isPckCert) {
            // entering Extension sequence
            tbsPtr = der.nextSiblingOf(tbsPtr);

            // check for the extension tag
            if (der[tbsPtr.ixs()] != 0xA3) {
                return (false, cert);
            }

            tbsPtr = der.firstChildOf(tbsPtr);
            tbsPtr = der.firstChildOf(tbsPtr);

            bool sgxExtnTraversedSuccessfully;
            uint256 pcesvn;
            uint256[] memory cpuSvns;
            bytes memory fmspcBytes;
            bytes memory pceidBytes;
            (sgxExtnTraversedSuccessfully, pcesvn, cpuSvns, fmspcBytes, pceidBytes) =
                _findPckTcbInfo(der, tbsPtr, tbsParentPtr);
            if (!sgxExtnTraversedSuccessfully) {
                return (false, cert);
            }
            cert.pck.sgxExtension.pcesvn = pcesvn;
            cert.pck.sgxExtension.sgxTcbCompSvnArr = cpuSvns;
            cert.pck.sgxExtension.pceid = LibString.toHexStringNoPrefix(pceidBytes);
            cert.pck.sgxExtension.fmspc = LibString.toHexStringNoPrefix(fmspcBytes);
            cert.isPck = true;
        }

        success = true;
    }

    function _removeHeadersAndFooters(string memory pemData)
        private
        pure
        returns (bool success, bytes memory extracted, uint256 endIndex)
    {
        // Check if the input contains the "BEGIN" and "END" headers
        uint256 beginPos = LibString.indexOf(pemData, HEADER);
        uint256 endPos = LibString.indexOf(pemData, FOOTER);

        bool headerFound = beginPos != LibString.NOT_FOUND;
        bool footerFound = endPos != LibString.NOT_FOUND;

        if (!headerFound || !footerFound) {
            return (false, extracted, endIndex);
        }

        // Extract the content between the headers
        uint256 contentStart = beginPos + HEADER_LENGTH;

        // Extract and return the content
        bytes memory contentBytes;

        // do not include newline
        bytes memory delimiter = hex"0a";
        string memory contentSlice = LibString.slice(pemData, contentStart, endPos);
        string[] memory split = LibString.split(contentSlice, string(delimiter));
        string memory contentStr;
        uint256 size = split.length;

        for (uint256 i; i < size; ++i) {
            contentStr = LibString.concat(contentStr, split[i]);
        }

        contentBytes = bytes(contentStr);
        return (true, contentBytes, endPos + FOOTER_LENGTH);
    }

    function _trimBytes(
        bytes memory input,
        uint256 expectedLength
    )
        private
        pure
        returns (bytes memory output)
    {
        uint256 n = input.length;

        if (n <= expectedLength) {
            return input;
        }
        uint256 lengthDiff = n - expectedLength;
        output = input.substring(lengthDiff, expectedLength);
    }

    function _findPckTcbInfo(
        bytes memory der,
        uint256 tbsPtr,
        uint256 tbsParentPtr
    )
        private
        pure
        returns (
            bool success,
            uint256 pcesvn,
            uint256[] memory cpusvns,
            bytes memory fmspcBytes,
            bytes memory pceidBytes
        )
    {
        // iterate through the elements in the Extension sequence
        // until we locate the SGX Extension OID
        while (tbsPtr != 0) {
            uint256 internalPtr = der.firstChildOf(tbsPtr);
            if (der[internalPtr.ixs()] != 0x06) {
                return (false, pcesvn, cpusvns, fmspcBytes, pceidBytes);
            }

            if (BytesUtils.compareBytes(der.bytesAt(internalPtr), SGX_EXTENSION_OID)) {
                // 1.2.840.113741.1.13.1
                internalPtr = der.nextSiblingOf(internalPtr);
                uint256 extnValueParentPtr = der.rootOfOctetStringAt(internalPtr);
                uint256 extnValuePtr = der.firstChildOf(extnValueParentPtr);

                // Copy flags to memory to avoid stack too deep
                PCKTCBFlags memory flags;

                while (!(flags.fmspcFound && flags.pceidFound && flags.tcbFound)) {
                    uint256 extnValueOidPtr = der.firstChildOf(extnValuePtr);
                    if (der[extnValueOidPtr.ixs()] != 0x06) {
                        return (false, pcesvn, cpusvns, fmspcBytes, pceidBytes);
                    }
                    if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), TCB_OID)) {
                        // 1.2.840.113741.1.13.1.2
                        (flags.tcbFound, pcesvn, cpusvns) = _findTcb(der, extnValueOidPtr);
                    }
                    if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), PCEID_OID)) {
                        // 1.2.840.113741.1.13.1.3
                        uint256 pceidPtr = der.nextSiblingOf(extnValueOidPtr);
                        pceidBytes = der.bytesAt(pceidPtr);
                        flags.pceidFound = true;
                    }
                    if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), FMSPC_OID)) {
                        // 1.2.840.113741.1.13.1.4
                        uint256 fmspcPtr = der.nextSiblingOf(extnValueOidPtr);
                        fmspcBytes = der.bytesAt(fmspcPtr);
                        flags.fmspcFound = true;
                    }

                    if (extnValuePtr.ixl() < extnValueParentPtr.ixl()) {
                        extnValuePtr = der.nextSiblingOf(extnValuePtr);
                    } else {
                        break;
                    }
                }
                success = flags.fmspcFound && flags.pceidFound && flags.tcbFound;
                break;
            }

            if (tbsPtr.ixl() < tbsParentPtr.ixl()) {
                tbsPtr = der.nextSiblingOf(tbsPtr);
            } else {
                tbsPtr = 0; // exit
            }
        }
    }

    function _findTcb(
        bytes memory der,
        uint256 oidPtr
    )
        private
        pure
        returns (bool success, uint256 pcesvn, uint256[] memory cpusvns)
    {
        // sibling of tcbOid
        uint256 tcbPtr = der.nextSiblingOf(oidPtr);
        // get the first svn object in the sequence
        uint256 svnParentPtr = der.firstChildOf(tcbPtr);
        cpusvns = new uint256[](SGX_TCB_CPUSVN_SIZE);
        for (uint256 i; i < SGX_TCB_CPUSVN_SIZE + 1; ++i) {
            uint256 svnPtr = der.firstChildOf(svnParentPtr); // OID
            uint256 svnValuePtr = der.nextSiblingOf(svnPtr); // value
            bytes memory svnValueBytes = der.bytesAt(svnValuePtr);
            uint16 svnValue = svnValueBytes.length < 2
                ? uint16(bytes2(svnValueBytes)) / 256
                : uint16(bytes2(svnValueBytes));
            if (BytesUtils.compareBytes(der.bytesAt(svnPtr), PCESVN_OID)) {
                // pcesvn is 4 bytes in size
                pcesvn = uint256(svnValue);
            } else {
                // each cpusvn is at maximum two bytes in size
                uint256 cpusvn = uint256(svnValue);
                cpusvns[i] = cpusvn;
            }

            // iterate to the next svn object in the sequence
            svnParentPtr = der.nextSiblingOf(svnParentPtr);
        }
        success = true;
    }
}
        

contracts/layer1/automata-attestation/lib/interfaces/IPEMCertChainLib.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @title IPEMCertChainLib
/// @custom:security-contact security@taiko.xyz
interface IPEMCertChainLib {
    struct ECSha256Certificate {
        uint256 notBefore;
        uint256 notAfter;
        bytes serialNumber;
        bytes tbsCertificate;
        bytes pubKey;
        bytes signature;
        bool isPck;
        PCKCertificateField pck;
    }

    struct PCKCertificateField {
        string commonName;
        string issuerName;
        PCKTCBInfo sgxExtension;
    }

    struct PCKTCBInfo {
        string pceid;
        string fmspc;
        uint256 pcesvn;
        uint256[] sgxTcbCompSvnArr;
    }

    enum CRL {
        PCK,
        ROOT
    }

    function splitCertificateChain(
        bytes memory pemChain,
        uint256 size
    )
        external
        pure
        returns (bool success, bytes[] memory certs);

    function decodeCert(
        bytes memory der,
        bool isPckCert
    )
        external
        pure
        returns (bool success, ECSha256Certificate memory cert);
}
          

contracts/layer1/automata-attestation/utils/Asn1Decode.sol

// SPDX-License-Identifier: MIT
// Original source: https://github.com/JonahGroendal/asn1-decode
pragma solidity ^0.8.24;

// Inspired by PufferFinance/rave - Apache-2.0 license
// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol

import "./BytesUtils.sol";

/// @title NodePtr
/// @custom:security-contact security@taiko.xyz
library NodePtr {
    // Unpack first byte index
    function ixs(uint256 self) internal pure returns (uint256) {
        return uint80(self);
    }

    // Unpack first content byte index
    function ixf(uint256 self) internal pure returns (uint256) {
        return uint80(self >> 80);
    }

    // Unpack last content byte index
    function ixl(uint256 self) internal pure returns (uint256) {
        return uint80(self >> 160);
    }

    // Pack 3 uint80s into a uint256
    function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {
        _ixs |= _ixf << 80;
        _ixs |= _ixl << 160;
        return _ixs;
    }
}

/// @title Asn1Decode
/// @custom:security-contact security@taiko.xyz
library Asn1Decode {
    using NodePtr for uint256;
    using BytesUtils for bytes;

    error NOT_TYPE_OCTET_STRING();
    error NOT_A_CONSTRUCTED_TYPE();

    /*
    * @dev Get the root node. First step in traversing an ASN1 structure
    * @param der The DER-encoded ASN1 structure
    * @return A pointer to the outermost node
    */
    function root(bytes memory der) internal pure returns (uint256) {
        return _readNodeLength(der, 0);
    }

    /*
    * @dev Get the root node of an ASN1 structure that's within an octet string value
    * @param der The DER-encoded ASN1 structure
    * @return A pointer to the outermost node
    */
    function rootOfOctetStringAt(
        bytes memory der,
        uint256 ptr
    )
        internal
        pure
        returns (uint256)
    {
        require(der[ptr.ixs()] == 0x04, NOT_TYPE_OCTET_STRING());
        return _readNodeLength(der, ptr.ixf());
    }

    /*
    * @dev Get the next sibling node
    * @param der The DER-encoded ASN1 structure
    * @param ptr Points to the indices of the current node
    * @return A pointer to the next sibling node
    */
    function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {
        return _readNodeLength(der, ptr.ixl() + 1);
    }

    /*
    * @dev Get the first child node of the current node
    * @param der The DER-encoded ASN1 structure
    * @param ptr Points to the indices of the current node
    * @return A pointer to the first child node
    */
    function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {
        require(der[ptr.ixs()] & 0x20 == 0x20, NOT_A_CONSTRUCTED_TYPE());
        return _readNodeLength(der, ptr.ixf());
    }

    /*
    * @dev Extract value of node from DER-encoded structure
    * @param der The der-encoded ASN1 structure
    * @param ptr Points to the indices of the current node
    * @return Value bytes of node
    */
    function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {
        return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());
    }

    /*
    * @dev Extract entire node from DER-encoded structure
    * @param der The DER-encoded ASN1 structure
    * @param ptr Points to the indices of the current node
    * @return All bytes of node
    */
    function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {
        return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());
    }

    function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {
        return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());
    }

    function keccakOfAllBytesAt(
        bytes memory der,
        uint256 ptr
    )
        internal
        pure
        returns (bytes32)
    {
        return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());
    }

    function _readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {
        uint256 length;
        uint80 ixFirstContentByte;
        uint80 ixLastContentByte;
        if ((der[ix + 1] & 0x80) == 0) {
            length = uint8(der[ix + 1]);
            ixFirstContentByte = uint80(ix + 2);
            ixLastContentByte = uint80(ixFirstContentByte + length - 1);
        } else {
            uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);
            if (lengthbytesLength == 1) {
                length = der.readUint8(ix + 2);
            } else if (lengthbytesLength == 2) {
                length = der.readUint16(ix + 2);
            } else {
                length = uint256(
                    der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8
                );
            }
            ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);
            ixLastContentByte = uint80(ixFirstContentByte + length - 1);
        }
        return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);
    }
}
          

contracts/layer1/automata-attestation/utils/BytesUtils.sol

// SPDX-License-Identifier: BSD 2-Clause License
pragma solidity ^0.8.24;

// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license
// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol
/// @title BytesUtils
/// @custom:security-contact security@taiko.xyz
library BytesUtils {
    error INVALID_OFFSET();
    error INVALID_IDX();
    error UNEXPECTED_LEN();
    error UNEXPECTED_IDX();
    error UNEXPECTED_OFFSET();

    /*
    * @dev Returns the keccak-256 hash of a byte range.
    * @param self The byte string to hash.
    * @param offset The position to start hashing at.
    * @param len The number of bytes to hash.
    * @return The hash of the byte range.
    */
    function keccak(
        bytes memory self,
        uint256 offset,
        uint256 len
    )
        internal
        pure
        returns (bytes32 ret)
    {
        require(offset + len <= self.length, INVALID_OFFSET());
        assembly {
            ret := keccak256(add(add(self, 32), offset), len)
        }
    }

    /*
    * @dev Returns true if the two byte ranges are equal.
    * @param self The first byte range to compare.
    * @param offset The offset into the first byte range.
    * @param other The second byte range to compare.
    * @param otherOffset The offset into the second byte range.
    * @param len The number of bytes to compare
    * @return true if the byte ranges are equal, false otherwise.
    */
    function equals(
        bytes memory self,
        uint256 offset,
        bytes memory other,
        uint256 otherOffset,
        uint256 len
    )
        internal
        pure
        returns (bool)
    {
        return keccak(self, offset, len) == keccak(other, otherOffset, len);
    }

    /*
    * @dev Returns the 8-bit number at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes
    * @return The specified 8 bits of the string, interpreted as an integer.
    */
    function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {
        return uint8(self[idx]);
    }

    /*
    * @dev Returns the 16-bit number at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes
    * @return The specified 16 bits of the string, interpreted as an integer.
    */
    function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {
        require(idx + 2 <= self.length, INVALID_IDX());
        assembly {
            ret := and(mload(add(add(self, 2), idx)), 0xFFFF)
        }
    }

    /*
    * @dev Returns the n byte value at the specified index of self.
    * @param self The byte string.
    * @param idx The index into the bytes.
    * @param len The number of bytes.
    * @return The specified 32 bytes of the string.
    */
    function readBytesN(
        bytes memory self,
        uint256 idx,
        uint256 len
    )
        internal
        pure
        returns (bytes32 ret)
    {
        require(len <= 32, UNEXPECTED_LEN());
        require(idx + len <= self.length, UNEXPECTED_IDX());
        assembly {
            let mask := not(sub(exp(256, sub(32, len)), 1))
            ret := and(mload(add(add(self, 32), idx)), mask)
        }
    }

    function memcpy(uint256 dest, uint256 src, uint256 len) private pure {
        assembly {
            mcopy(dest, src, len)
        }
    }

    /*
    * @dev Copies a substring into a new byte string.
    * @param self The byte string to copy from.
    * @param offset The offset to start copying at.
    * @param len The number of bytes to copy.
    */
    function substring(
        bytes memory self,
        uint256 offset,
        uint256 len
    )
        internal
        pure
        returns (bytes memory)
    {
        require(offset + len <= self.length, UNEXPECTED_OFFSET());

        bytes memory ret = new bytes(len);
        uint256 dest;
        uint256 src;

        assembly {
            dest := add(ret, 32)
            src := add(add(self, 32), offset)
        }
        memcpy(dest, src, len);

        return ret;
    }

    function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {
        return keccak256(a) == keccak256(b);
    }
}
          

contracts/layer1/automata-attestation/utils/X509DateUtils.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

/// @title X509DateUtils
/// @custom:security-contact security@taiko.xyz
library X509DateUtils {
    function toTimestamp(bytes memory x509Time) internal pure returns (uint256) {
        uint16 yrs;
        uint8 mnths;
        uint8 dys;
        uint8 hrs;
        uint8 mins;
        uint8 secs;
        uint8 offset;

        if (x509Time.length == 13) {
            if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;
            else yrs += 1900;
        } else {
            yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;
            offset = 2;
        }
        yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;
        mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;
        dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;
        hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;
        mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;
        secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;

        return toUnixTimestamp(yrs, mnths, dys, hrs, mins, secs);
    }

    function toUnixTimestamp(
        uint16 year,
        uint8 month,
        uint8 day,
        uint8 hour,
        uint8 minute,
        uint8 second
    )
        internal
        pure
        returns (uint256)
    {
        uint256 timestamp = 0;

        for (uint16 i = 1970; i < year; ++i) {
            if (isLeapYear(i)) {
                timestamp += 31_622_400; // Leap year in seconds
            } else {
                timestamp += 31_536_000; // Normal year in seconds
            }
        }

        uint8[12] memory monthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        if (isLeapYear(year)) monthDays[1] = 29;

        for (uint8 i = 1; i < month; ++i) {
            timestamp += uint256(monthDays[i - 1]) * 86_400; // Days in seconds
        }

        timestamp += uint256(day - 1) * 86_400; // Days in seconds
        timestamp += uint256(hour) * 3600; // Hours in seconds
        timestamp += uint256(minute) * 60; // Minutes in seconds
        timestamp += second;

        return timestamp;
    }

    function isLeapYear(uint16 year) internal pure returns (bool) {
        if (year % 4 != 0) return false;
        if (year % 100 != 0) return true;
        if (year % 400 != 0) return false;
        return true;
    }
}
          

node_modules/solady/src/utils/LibBytes.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for byte related operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol)
library LibBytes {
    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                          STRUCTS                           */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

    /// @dev Goated bytes storage struct that totally MOGs, no cap, fr.
    /// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af.
    /// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
    struct BytesStorage {
        bytes32 _spacer;
    }

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

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

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                  BYTE STORAGE OPERATIONS                   */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

    /// @dev Sets the value of the bytes storage `$` to `s`.
    function set(BytesStorage storage $, bytes memory s) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(s)
            let packed := or(0xff, shl(8, n))
            for { let i := 0 } 1 {} {
                if iszero(gt(n, 0xfe)) {
                    i := 0x1f
                    packed := or(n, shl(8, mload(add(s, i))))
                    if iszero(gt(n, i)) { break }
                }
                let o := add(s, 0x20)
                mstore(0x00, $.slot)
                for { let p := keccak256(0x00, 0x20) } 1 {} {
                    sstore(add(p, shr(5, i)), mload(add(o, i)))
                    i := add(i, 0x20)
                    if iszero(lt(i, n)) { break }
                }
                break
            }
            sstore($.slot, packed)
        }
    }

    /// @dev Sets the value of the bytes storage `$` to `s`.
    function setCalldata(BytesStorage storage $, bytes calldata s) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let packed := or(0xff, shl(8, s.length))
            for { let i := 0 } 1 {} {
                if iszero(gt(s.length, 0xfe)) {
                    i := 0x1f
                    packed := or(s.length, shl(8, shr(8, calldataload(s.offset))))
                    if iszero(gt(s.length, i)) { break }
                }
                mstore(0x00, $.slot)
                for { let p := keccak256(0x00, 0x20) } 1 {} {
                    sstore(add(p, shr(5, i)), calldataload(add(s.offset, i)))
                    i := add(i, 0x20)
                    if iszero(lt(i, s.length)) { break }
                }
                break
            }
            sstore($.slot, packed)
        }
    }

    /// @dev Sets the value of the bytes storage `$` to the empty bytes.
    function clear(BytesStorage storage $) internal {
        delete $._spacer;
    }

    /// @dev Returns whether the value stored is `$` is the empty bytes "".
    function isEmpty(BytesStorage storage $) internal view returns (bool) {
        return uint256($._spacer) & 0xff == uint256(0);
    }

    /// @dev Returns the length of the value stored in `$`.
    function length(BytesStorage storage $) internal view returns (uint256 result) {
        result = uint256($._spacer);
        /// @solidity memory-safe-assembly
        assembly {
            let n := and(0xff, result)
            result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n))))
        }
    }

    /// @dev Returns the value stored in `$`.
    function get(BytesStorage storage $) internal view returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let o := add(result, 0x20)
            let packed := sload($.slot)
            let n := shr(8, packed)
            for { let i := 0 } 1 {} {
                if iszero(eq(or(packed, 0xff), packed)) {
                    mstore(o, packed)
                    n := and(0xff, packed)
                    i := 0x1f
                    if iszero(gt(n, i)) { break }
                }
                mstore(0x00, $.slot)
                for { let p := keccak256(0x00, 0x20) } 1 {} {
                    mstore(add(o, i), sload(add(p, shr(5, i))))
                    i := add(i, 0x20)
                    if iszero(lt(i, n)) { break }
                }
                break
            }
            mstore(result, n) // Store the length of the memory.
            mstore(add(o, n), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(o, n), 0x20)) // Allocate memory.
        }
    }

    /// @dev Returns the uint8 at index `i`. If out-of-bounds, returns 0.
    function uint8At(BytesStorage storage $, uint256 i) internal view returns (uint8 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for { let packed := sload($.slot) } 1 {} {
                if iszero(eq(or(packed, 0xff), packed)) {
                    if iszero(gt(i, 0x1e)) {
                        result := byte(i, packed)
                        break
                    }
                    if iszero(gt(i, and(0xff, packed))) {
                        mstore(0x00, $.slot)
                        let j := sub(i, 0x1f)
                        result := byte(and(j, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, j))))
                    }
                    break
                }
                if iszero(gt(i, shr(8, packed))) {
                    mstore(0x00, $.slot)
                    result := byte(and(i, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, i))))
                }
                break
            }
        }
    }

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                      BYTES OPERATIONS                      */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

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

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

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

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

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

    /// @dev Returns the byte index of the first location of `needle` in `subject`,
    /// needleing from left to right, starting from `from`. Optimized for byte needles.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function indexOfByte(bytes memory subject, bytes1 needle, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := not(0) // Initialize to `NOT_FOUND`.
            if gt(mload(subject), from) {
                let start := add(subject, 0x20)
                let end := add(start, mload(subject))
                let m := div(not(0), 255) // `0x0101 ... `.
                let h := mul(byte(0, needle), m) // Replicating needle mask.
                m := not(shl(7, m)) // `0x7f7f ... `.
                for { let i := add(start, from) } 1 {} {
                    let c := xor(mload(i), h) // Load 32-byte chunk and xor with mask.
                    c := not(or(or(add(and(c, m), m), c), m)) // Each needle byte will be `0x80`.
                    if c {
                        c := and(not(shr(shl(3, sub(end, i)), not(0))), c) // Truncate bytes past the end.
                        if c {
                            let r := shl(7, lt(0x8421084210842108cc6318c6db6d54be, c)) // Save bytecode.
                            r := or(shl(6, lt(0xffffffffffffffff, shr(r, c))), r)
                            // forgefmt: disable-next-item
                            result := add(sub(i, start), shr(3, xor(byte(and(0x1f, shr(byte(24,
                                mul(0x02040810204081, shr(r, c))), 0x8421084210842108cc6318c6db6d54be)),
                                0xc0c8c8d0c8e8d0d8c8e8e0e8d0d8e0f0c8d0e8d0e0e0d8f0d0d0e0d8f8f8f8f8), r)))
                            break
                        }
                    }
                    i := add(i, 0x20)
                    if iszero(lt(i, end)) { break }
                }
            }
        }
    }

    /// @dev Returns the byte index of the first location of `needle` in `subject`,
    /// needleing from left to right. Optimized for byte needles.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function indexOfByte(bytes memory subject, bytes1 needle)
        internal
        pure
        returns (uint256 result)
    {
        return indexOfByte(subject, needle, 0);
    }

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

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

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

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

    /// @dev Returns the byte index of the first location of `needle` in `subject`,
    /// needleing from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function lastIndexOf(bytes memory subject, bytes memory needle)
        internal
        pure
        returns (uint256)
    {
        return lastIndexOf(subject, needle, type(uint256).max);
    }

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

    /// @dev Returns whether `subject` starts with `needle`.
    function startsWith(bytes memory subject, bytes memory needle)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(needle)
            // Just using keccak256 directly is actually cheaper.
            let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n))
            result := lt(gt(n, mload(subject)), t)
        }
    }

    /// @dev Returns whether `subject` ends with `needle`.
    function endsWith(bytes memory subject, bytes memory needle)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(needle)
            let notInRange := gt(n, mload(subject))
            // `subject + 0x20 + max(subject.length - needle.length, 0)`.
            let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n)))
            // Just using keccak256 directly is actually cheaper.
            result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange)
        }
    }

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

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

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

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets. Faster than Solidity's native slicing.
    function sliceCalldata(bytes calldata subject, uint256 start, uint256 end)
        internal
        pure
        returns (bytes calldata result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))
            start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
            result.offset := add(subject.offset, start)
            result.length := mul(lt(start, end), sub(end, start))
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
    /// `start` is a byte offset. Faster than Solidity's native slicing.
    function sliceCalldata(bytes calldata subject, uint256 start)
        internal
        pure
        returns (bytes calldata result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
            result.offset := add(subject.offset, start)
            result.length := mul(lt(start, subject.length), sub(subject.length, start))
        }
    }

    /// @dev Reduces the size of `subject` to `n`.
    /// If `n` is greater than the size of `subject`, this will be a no-op.
    function truncate(bytes memory subject, uint256 n)
        internal
        pure
        returns (bytes memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := subject
            mstore(mul(lt(n, mload(result)), result), n)
        }
    }

    /// @dev Returns a copy of `subject`, with the length reduced to `n`.
    /// If `n` is greater than the size of `subject`, this will be a no-op.
    function truncatedCalldata(bytes calldata subject, uint256 n)
        internal
        pure
        returns (bytes calldata result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result.offset := subject.offset
            result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n)))
        }
    }

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

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

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

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

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

    /// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`.
    /// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1.
    function cmp(bytes memory a, bytes memory b) internal pure returns (int256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let aLen := mload(a)
            let bLen := mload(b)
            let n := and(xor(aLen, mul(xor(aLen, bLen), lt(bLen, aLen))), not(0x1f))
            if n {
                for { let i := 0x20 } 1 {} {
                    let x := mload(add(a, i))
                    let y := mload(add(b, i))
                    if iszero(or(xor(x, y), eq(i, n))) {
                        i := add(i, 0x20)
                        continue
                    }
                    result := sub(gt(x, y), lt(x, y))
                    break
                }
            }
            // forgefmt: disable-next-item
            if iszero(result) {
                let l := 0x201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201
                let x := and(mload(add(add(a, 0x20), n)), shl(shl(3, byte(sub(aLen, n), l)), not(0)))
                let y := and(mload(add(add(b, 0x20), n)), shl(shl(3, byte(sub(bLen, n), l)), not(0)))
                result := sub(gt(x, y), lt(x, y))
                if iszero(result) { result := sub(gt(aLen, bLen), lt(aLen, bLen)) }
            }
        }
    }

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

    /// @dev Directly returns `a` with minimal copying.
    function directReturn(bytes[] memory a) internal pure {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(a) // `a.length`.
            let o := add(a, 0x20) // Start of elements in `a`.
            let u := a // Highest memory slot.
            let w := not(0x1f)
            for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } {
                let c := add(o, shl(5, i)) // Location of pointer to `a[i]`.
                let s := mload(c) // `a[i]`.
                let l := mload(s) // `a[i].length`.
                let r := and(l, 0x1f) // `a[i].length % 32`.
                let z := add(0x20, and(l, w)) // Offset of last word in `a[i]` from `s`.
                // If `s` comes before `o`, or `s` is not zero right padded.
                if iszero(lt(lt(s, o), or(iszero(r), iszero(shl(shl(3, r), mload(add(s, z))))))) {
                    let m := mload(0x40)
                    mstore(m, l) // Copy `a[i].length`.
                    for {} 1 {} {
                        mstore(add(m, z), mload(add(s, z))) // Copy `a[i]`, backwards.
                        z := add(z, w) // `sub(z, 0x20)`.
                        if iszero(z) { break }
                    }
                    let e := add(add(m, 0x20), l)
                    mstore(e, 0) // Zeroize the slot after the copied bytes.
                    mstore(0x40, add(e, 0x20)) // Allocate memory.
                    s := m
                }
                mstore(c, sub(s, o)) // Convert to calldata offset.
                let t := add(l, add(s, 0x20))
                if iszero(lt(t, u)) { u := t }
            }
            let retStart := add(a, w) // Assumes `a` doesn't start from scratch space.
            mstore(retStart, 0x20) // Store the return offset.
            return(retStart, add(0x40, sub(u, retStart))) // End the transaction.
        }
    }

    /// @dev Returns the word at `offset`, without any bounds checks.
    function load(bytes memory a, uint256 offset) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(add(add(a, 0x20), offset))
        }
    }

    /// @dev Returns the word at `offset`, without any bounds checks.
    function loadCalldata(bytes calldata a, uint256 offset)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := calldataload(add(a.offset, offset))
        }
    }

    /// @dev Returns a slice representing a static struct in the calldata. Performs bounds checks.
    function staticStructInCalldata(bytes calldata a, uint256 offset)
        internal
        pure
        returns (bytes calldata result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let l := sub(a.length, 0x20)
            result.offset := add(a.offset, offset)
            result.length := sub(a.length, offset)
            if or(shr(64, or(l, a.offset)), gt(offset, l)) { revert(l, 0x00) }
        }
    }

    /// @dev Returns a slice representing a dynamic struct in the calldata. Performs bounds checks.
    function dynamicStructInCalldata(bytes calldata a, uint256 offset)
        internal
        pure
        returns (bytes calldata result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let l := sub(a.length, 0x20)
            let s := calldataload(add(a.offset, offset)) // Relative offset of `result` from `a.offset`.
            result.offset := add(a.offset, s)
            result.length := sub(a.length, s)
            if or(shr(64, or(s, or(l, a.offset))), gt(offset, l)) { revert(l, 0x00) }
        }
    }

    /// @dev Returns bytes in calldata. Performs bounds checks.
    function bytesInCalldata(bytes calldata a, uint256 offset)
        internal
        pure
        returns (bytes calldata result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let l := sub(a.length, 0x20)
            let s := calldataload(add(a.offset, offset)) // Relative offset of `result` from `a.offset`.
            result.offset := add(add(a.offset, s), 0x20)
            result.length := calldataload(add(a.offset, s))
            // forgefmt: disable-next-item
            if or(shr(64, or(result.length, or(s, or(l, a.offset)))),
                or(gt(add(s, result.length), l), gt(offset, l))) { revert(l, 0x00) }
        }
    }

    /// @dev Returns empty calldata bytes. For silencing the compiler.
    function emptyCalldata() internal pure returns (bytes calldata result) {
        /// @solidity memory-safe-assembly
        assembly {
            result.length := 0
        }
    }
}
          

node_modules/solady/src/utils/LibString.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

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

    /// @dev Goated string storage struct that totally MOGs, no cap, fr.
    /// Uses less gas and bytecode than Solidity's native string storage. It's meta af.
    /// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
    struct StringStorage {
        bytes32 _spacer;
    }

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

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

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

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

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

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

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

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

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

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

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

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

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

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

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

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

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                 STRING STORAGE OPERATIONS                  */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

    /// @dev Sets the value of the string storage `$` to `s`.
    function set(StringStorage storage $, string memory s) internal {
        LibBytes.set(bytesStorage($), bytes(s));
    }

    /// @dev Sets the value of the string storage `$` to `s`.
    function setCalldata(StringStorage storage $, string calldata s) internal {
        LibBytes.setCalldata(bytesStorage($), bytes(s));
    }

    /// @dev Sets the value of the string storage `$` to the empty string.
    function clear(StringStorage storage $) internal {
        delete $._spacer;
    }

    /// @dev Returns whether the value stored is `$` is the empty string "".
    function isEmpty(StringStorage storage $) internal view returns (bool) {
        return uint256($._spacer) & 0xff == uint256(0);
    }

    /// @dev Returns the length of the value stored in `$`.
    function length(StringStorage storage $) internal view returns (uint256) {
        return LibBytes.length(bytesStorage($));
    }

    /// @dev Returns the value stored in `$`.
    function get(StringStorage storage $) internal view returns (string memory) {
        return string(LibBytes.get(bytesStorage($)));
    }

    /// @dev Returns the uint8 at index `i`. If out-of-bounds, returns 0.
    function uint8At(StringStorage storage $, uint256 i) internal view returns (uint8) {
        return LibBytes.uint8At(bytesStorage($), i);
    }

    /// @dev Helper to cast `$` to a `BytesStorage`.
    function bytesStorage(StringStorage storage $)
        internal
        pure
        returns (LibBytes.BytesStorage storage casted)
    {
        /// @solidity memory-safe-assembly
        assembly {
            casted.slot := $.slot
        }
    }

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                     DECIMAL OPERATIONS                     */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

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

            let end := result // Cache the end of the memory to calculate the length later.
            let w := not(0) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                result := add(result, w) // `sub(result, 1)`.
                // Store the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(result, add(48, mod(temp, 10)))
                temp := div(temp, 10) // Keep dividing `temp` until zero.
                if iszero(temp) { break }
            }
            let n := sub(end, result)
            result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
            mstore(result, n) // Store the length.
        }
    }

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

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                   HEXADECIMAL OPERATIONS                   */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

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

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

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

            let start := sub(result, add(byteCount, byteCount))
            let w := not(1) // Tsk.
            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {} 1 {} {
                result := add(result, w) // `sub(result, 2)`.
                mstore8(add(result, 1), mload(and(temp, 15)))
                mstore8(result, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(result, start)) { break }
            }
            if temp {
                mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                revert(0x1c, 0x04)
            }
            let n := sub(end, result)
            result := sub(result, 0x20)
            mstore(result, n) // Store the length.
        }
    }

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

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

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

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

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

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

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

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

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(address value) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            // Allocate memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(result, 0x80))
            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.

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

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

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

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

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                   RUNE STRING OPERATIONS                   */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

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

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

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

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

    /*Ā“:°•.°+.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°•.*•“.*:˚.°*.Ėšā€¢Ā“.°:°•.°+.*•“.*:*/
    /*                   BYTE STRING OPERATIONS                   */
    /*.•°:°.Ā“+˚.*°.˚:*.“•*.+°.•°:Ā“*.“•*.•°.•°:°.Ā“:ā€¢ĖšĀ°.*°.˚:*.Ā“+°.•*/

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

    /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
    function replace(string memory subject, string memory needle, string memory replacement)
        internal
        pure
        returns (string memory)
    {
        return string(LibBytes.replace(bytes(subject), bytes(needle), bytes(replacement)));
    }

    /// @dev Returns the byte index of the first location of `needle` in `subject`,
    /// needleing from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function indexOf(string memory subject, string memory needle, uint256 from)
        internal
        pure
        returns (uint256)
    {
        return LibBytes.indexOf(bytes(subject), bytes(needle), from);
    }

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

    /// @dev Returns the byte index of the first location of `needle` in `subject`,
    /// needleing from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function lastIndexOf(string memory subject, string memory needle, uint256 from)
        internal
        pure
        returns (uint256)
    {
        return LibBytes.lastIndexOf(bytes(subject), bytes(needle), from);
    }

    /// @dev Returns the byte index of the first location of `needle` in `subject`,
    /// needleing from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function lastIndexOf(string memory subject, string memory needle)
        internal
        pure
        returns (uint256)
    {
        return LibBytes.lastIndexOf(bytes(subject), bytes(needle), type(uint256).max);
    }

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

    /// @dev Returns whether `subject` starts with `needle`.
    function startsWith(string memory subject, string memory needle) internal pure returns (bool) {
        return LibBytes.startsWith(bytes(subject), bytes(needle));
    }

    /// @dev Returns whether `subject` ends with `needle`.
    function endsWith(string memory subject, string memory needle) internal pure returns (bool) {
        return LibBytes.endsWith(bytes(subject), bytes(needle));
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times) internal pure returns (string memory) {
        return string(LibBytes.repeat(bytes(subject), times));
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(string memory subject, uint256 start, uint256 end)
        internal
        pure
        returns (string memory)
    {
        return string(LibBytes.slice(bytes(subject), start, end));
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start) internal pure returns (string memory) {
        return string(LibBytes.slice(bytes(subject), start, type(uint256).max));
    }

    /// @dev Returns all the indices of `needle` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory needle)
        internal
        pure
        returns (uint256[] memory)
    {
        return LibBytes.indicesOf(bytes(subject), bytes(needle));
    }

    /// @dev Returns an arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter)
        internal
        pure
        returns (string[] memory result)
    {
        bytes[] memory a = LibBytes.split(bytes(subject), bytes(delimiter));
        /// @solidity memory-safe-assembly
        assembly {
            result := a
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b) internal pure returns (string memory) {
        return string(LibBytes.concat(bytes(a), bytes(b)));
    }

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

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

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

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

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

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

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

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

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

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

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

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

    /// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`.
    /// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1.
    function cmp(string memory a, string memory b) internal pure returns (int256) {
        return LibBytes.cmp(bytes(a), bytes(b));
    }

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

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

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

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

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

Compiler Settings

{"viaIR":true,"remappings":["openzeppelin/=node_modules/@openzeppelin/","@openzeppelin/=node_modules/@openzeppelin/","@openzeppelin-upgrades/contracts/=node_modules/@openzeppelin/contracts-upgradeable/","@risc0/contracts/=node_modules/risc0-ethereum/contracts/src/","@solady/=node_modules/solady/","solady/src/=node_modules/solady/src/","solady/utils/=node_modules/solady/src/utils/","@optimism/=node_modules/optimism/","@sp1-contracts/=node_modules/sp1-contracts/contracts/","forge-std/=node_modules/forge-std/","@p256-verifier/contracts/=node_modules/p256-verifier/src/","@eth-fabric/urc/=node_modules/urc/src/","ds-test/=node_modules/ds-test/","src/=contracts/","test/=test/","script/=script/","optimism/=node_modules/optimism/","p256-verifier/=node_modules/p256-verifier/","risc0-ethereum/=node_modules/risc0-ethereum/","sp1-contracts/=node_modules/sp1-contracts/","urc/=node_modules/urc/"],"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{"contracts/layer1/core/libs/LibInboxSetup.sol":{"LibInboxSetup":"0xf88Ef5437749A225621101BE8C1BE1A0cE967758"},"contracts/layer1/core/libs/LibForcedInclusion.sol":{"LibForcedInclusion":"0xd1a27F331c17eD8Cbb6DAbce67A42d6b8a6B0e14"}},"evmVersion":"prague"}
              

Contract ABI

[{"type":"error","name":"INVALID_IDX","inputs":[]},{"type":"error","name":"NOT_A_CONSTRUCTED_TYPE","inputs":[]},{"type":"error","name":"NOT_TYPE_OCTET_STRING","inputs":[]},{"type":"error","name":"UNEXPECTED_IDX","inputs":[]},{"type":"error","name":"UNEXPECTED_LEN","inputs":[]},{"type":"error","name":"UNEXPECTED_OFFSET","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"success","internalType":"bool"},{"type":"tuple","name":"cert","internalType":"struct IPEMCertChainLib.ECSha256Certificate","components":[{"type":"uint256","name":"notBefore","internalType":"uint256"},{"type":"uint256","name":"notAfter","internalType":"uint256"},{"type":"bytes","name":"serialNumber","internalType":"bytes"},{"type":"bytes","name":"tbsCertificate","internalType":"bytes"},{"type":"bytes","name":"pubKey","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"},{"type":"bool","name":"isPck","internalType":"bool"},{"type":"tuple","name":"pck","internalType":"struct IPEMCertChainLib.PCKCertificateField","components":[{"type":"string","name":"commonName","internalType":"string"},{"type":"string","name":"issuerName","internalType":"string"},{"type":"tuple","name":"sgxExtension","internalType":"struct IPEMCertChainLib.PCKTCBInfo","components":[{"type":"string","name":"pceid","internalType":"string"},{"type":"string","name":"fmspc","internalType":"string"},{"type":"uint256","name":"pcesvn","internalType":"uint256"},{"type":"uint256[]","name":"sgxTcbCompSvnArr","internalType":"uint256[]"}]}]}]}],"name":"decodeCert","inputs":[{"type":"bytes","name":"der","internalType":"bytes"},{"type":"bool","name":"isPckCert","internalType":"bool"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"success","internalType":"bool"},{"type":"bytes[]","name":"certs","internalType":"bytes[]"}],"name":"splitCertificateChain","inputs":[{"type":"bytes","name":"pemChain","internalType":"bytes"},{"type":"uint256","name":"size","internalType":"uint256"}]}]
              

Contract Creation Code

Verify & Publish
0x6080806040523460155761207f908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c8063b6e656fa146100345763c1c1d5c11461002f575f80fd5b6102b4565b346100e15760403660031901126100e15760043567ffffffffffffffff81116100e1576100686100719136906004016101a1565b60243590610480565b906040519182916040830190151583526040602084015281518091526060830190602060608260051b8601019301915f905b8282106100b257505050500390f35b919360019193955060206100d18192605f198a820301865288516101e7565b96019201920185949391926100a3565b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b610100810190811067ffffffffffffffff82111761011657604052565b6100e5565b6060810190811067ffffffffffffffff82111761011657604052565b6080810190811067ffffffffffffffff82111761011657604052565b90601f8019910116810190811067ffffffffffffffff82111761011657604052565b906101836040519283610153565b565b67ffffffffffffffff811161011657601f01601f191660200190565b81601f820112156100e1578035906101b882610185565b926101c66040519485610153565b828452602083830101116100e157815f926020809301838601378301015290565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90604061023661022484516060855260608501906101e7565b602085015184820360208601526101e7565b920151906040818403910152606061026c61025a83516080865260808601906101e7565b602084015185820360208701526101e7565b91604081015160408501520151916060818303910152602080835192838152019201905f5b81811061029e5750505090565b8251845260209384019390920191600101610291565b346100e15760403660031901126100e15760043567ffffffffffffffff81116100e1576102e59036906004016101a1565b60243580151581036100e1576102fa916106a9565b906103a96040519283921515835260406020840152805160408401526020810151606084015260e061038761037261035c610346604086015161010060808a01526101408901906101e7565b6060860151888203603f190160a08a01526101e7565b6080850151878203603f190160c08901526101e7565b60a0840151868203603f1901848801526101e7565b60c08301511515610100860152910151838203603f190161012085015261020b565b0390f35b67ffffffffffffffff81116101165760051b60200190565b634e487b7160e01b5f52601160045260245ffd5b90600382018092116103e757565b6103c5565b90601b82018092116103e757565b90601982018092116103e757565b90600182018092116103e757565b90600282018092116103e757565b906301e1338082018092116103e757565b906301e2850082018092116103e757565b919082018092116103e757565b634e487b7160e01b5f52603260045260245ffd5b805182101561047b5760209160051b010190565b610453565b9061048a816103ad565b6104976040519182610153565b818152601f196104a6836103ad565b015f5b818110610529575050905f83515f915b8383106104cb57505050509050600191565b8215610520576104ed6104e86104e18484610446565b83896115f6565b610b26565b9291906104fa8689610467565b52156105145760019161050c91610446565b9201916104b9565b505f9694955050505050565b6104ed86610b26565b8060606020809386010152016104a9565b60405190610547826100f9565b815f81525f60208201526060604082015260608082015260606080820152606060a08201525f60c082015260e0604051916105818361011b565b606083526060602084015260405161059881610137565b60608152606060208201525f604082015260608082015260408401520152565b604051906105c7604083610153565b601982527f496e74656c205347582050434b20506c6174666f726d204341000000000000006020830152565b60405190610602604083610153565b601a82527f496e74656c205347582050434b2050726f636573736f722043410000000000006020830152565b80511561047b5760200190565b80516001101561047b5760210190565b90815181101561047b570160200190565b6040519061066b604083610153565b601982527f496e74656c205347582050434b204365727469666963617465000000000000006020830152565b805191908290602001825e015f815290565b906106b261053a565b916106c56106bf8261166f565b82610c09565b91806106fb6106f56106e06106da8787610c09565b86610c51565b6106ea8187610c81565b604089015285610c51565b84610c51565b90610a54575b61070b9083610c51565b6107158184610c09565b61071f8185610c51565b906107546107466107406001600160501b0384165b6001600160501b031690565b8761064b565b516001600160f81b03191690565b61077261074661076c6001600160501b038616610734565b8861064b565b601760f81b6001600160f81b0319909216918214159182610a45575b508115610a15575b50610a0a57916107c16107b46107cc936107b96107b488978a610c81565b610e7d565b8a5287610c81565b602088015284610c51565b9061099f575b6107dc9083610c51565b906107e78284610c09565b6107f19084610c51565b6107fb8585610c51565b6108059085610c51565b6108176001600160501b0382166103d9565b9061082e6001600160501b03605083901c166103d9565b60501b90911769ffffffffffffffffffff60a01b909116176108509085610c09565b61085a8186610c81565b610863906111af565b9061086e9086610c51565b6108789086610c81565b610881906111af565b9161088c87876111fc565b606089015261089b9086610c81565b6108a4906111d8565b6080880152604051918291602083016108bc91610697565b6108c591610697565b03601f19810182526108d79082610153565b60a08601526108e9575b505050600191565b6108f39082610c51565b60a360f81b6001600160f81b031961091f6107466109196001600160501b038616610734565b8661064b565b160361099757906109376106bf61093d949383610c09565b906112ed565b919092931561098e5760209261096e6040936109799360e089019786808a5101510152606086895101510152611590565b838651015152611590565b925101510152600160c08201525f80806108e1565b505f9492505050565b505f93915050565b6109fc6109f86109d36109cd6109c76109c16109bb878a610c09565b89610c09565b88610c09565b87610c51565b86610c81565b60e088019081515251516109e561065c565b6020815191012090602081519101201490565b1590565b156107d257505f9492505050565b505f96945050505050565b6001600160f81b031916601760f81b811415915081610a36575b505f610796565b600360fb1b141590505f610a2f565b600360fb1b141591505f61078e565b610a82610a7c610a76610a70610a6a8588610c09565b87610c09565b86610c09565b85610c51565b84610c81565b60e0860190602082510152610a9e6020825101516109e56105b8565b908115610ab4575b5061070157505f9492505050565b5160200151610ac691506109e56105f3565b5f610aa6565b60405190610adb604083610153565b601982527f2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d000000000000006020830152565b60405190610b16604083610153565b60018252600560f91b6020830152565b90610b68604051610b38604082610153565b601b81527f2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0000000000602082015283611c77565b91610b7a610b74610acc565b82611c77565b925f1981148015610bff575b610bf35790610ba984610b9b610bae946103ec565b610ba3610b07565b936115f6565b611d4f565b9160609280515f915b818310610bd057505050610bca906103fa565b60019291565b909194610bea600191610be38885610467565b5190611e11565b95019190610bb7565b505f9250606091839150565b505f198414610b86565b600160fd1b80610c226001600160501b0385168461064b565b511603610c42576001600160501b03610c3f9260501c16906117aa565b90565b632347bfa760e01b5f5260045ffd5b906001600160501b039060a01c16600181018091116103e757610c3f916117aa565b5f198101919082116103e757565b906001600160501b03808260501c169160a01c169060018201918281116103e7578160019103019182116103e757610c3f926118b0565b60ff602f199116019060ff82116103e757565b60ff166020039060ff82116103e757565b60ff5f199116019060ff82116103e757565b61ffff6103e89116029061ffff82169182036103e757565b60ff60649116029060ff82169182036103e757565b60ff600a9116029060ff82169182036103e757565b60031b906107f860f88316921682036103e757565b61ffff169061ffff82116103e757565b61ffff61076c9116019061ffff82116103e757565b61ffff6107d09116019061ffff82116103e757565b9061ffff8091169116019061ffff82116103e757565b60ff169060ff82116103e757565b60ff60019116019060ff82116103e757565b60ff60029116019060ff82116103e757565b60ff60039116019060ff82116103e757565b60ff60049116019060ff82116103e757565b60ff60059116019060ff82116103e757565b60ff60069116019060ff82116103e757565b60ff60079116019060ff82116103e757565b60ff60089116019060ff82116103e757565b60ff60099116019060ff82116103e757565b60ff600a9116019060ff82116103e757565b60ff600b9116019060ff82116103e757565b9060ff8091169116019060ff82116103e757565b5f5f90600d8351145f1461115457600560ff610eac610ea7610ea16107468861062e565b60f81c90565b610cb8565b16101561114657610ebc90610d6a565b610ec582610d95565b60ff16610ed2908461064b565b51610edf9060f81c610cb8565b610ee890610d1b565b610ef183610da3565b60ff16610efe908561064b565b516001600160f81b03191660f81c610f1591610e69565b610f1e90610cb8565b60ff16610f2a91610d7f565b91610f3482610db5565b60ff16610f41908261064b565b51610f4e9060f81c610cb8565b610f5790610d1b565b610f6083610dc7565b60ff16610f6d908361064b565b516001600160f81b03191660f81c610f8491610e69565b610f8d90610cb8565b91610f9781610dd9565b60ff16610fa4908361064b565b51610fb19060f81c610cb8565b610fba90610d1b565b610fc382610deb565b60ff16610fd0908461064b565b516001600160f81b03191660f81c610fe791610e69565b610ff090610cb8565b610ff990610d95565b61100282610dfd565b60ff1661100f908461064b565b5161101c9060f81c610cb8565b61102590610d1b565b61102e83610e0f565b60ff1661103b908561064b565b516001600160f81b03191660f81c61105291610e69565b61105b90610cb8565b61106490610d95565b9161106e81610e21565b60ff1661107b908561064b565b516110889060f81c610cb8565b61109190610d1b565b61109a82610e33565b60ff166110a7908661064b565b516001600160f81b03191660f81c6110be91610e69565b6110c790610cb8565b6110d090610d95565b936110da82610e45565b60ff166110e7908261064b565b516110f49060f81c610cb8565b6110fd90610d1b565b9161110790610e57565b60ff166111139161064b565b516001600160f81b03191660f81c61112a91610e69565b61113390610cb8565b61113c90610d95565b93610c3f95611964565b61114f90610d55565b610ebc565b50506111a76111a261117960ff611173610ea7610ea16107468861062e565b16610cee565b61119c611196611191610ea7610ea16107468961063b565b610d06565b60ff1690565b90610d7f565b610d45565b600290610ebc565b80519060208211156111d357601f1982019182116103e757610c3f916020916118b0565b905090565b80519060408211156111d357603f1982019182116103e757610c3f916040916118b0565b906001600160501b038082169160a01c169060018201918281116103e7578160019103019182116103e757610c3f926118b0565b6040519061123f604083610153565b60098252682a864886f84d010d0160b81b6020830152565b604051906112648261011b565b5f6040838281528260208201520152565b60405190611284604083610153565b600a825269154324437c268086808160b11b6020830152565b604051906112ac604083610153565b600a8252692a864886f84d010d010360b01b6020830152565b604051906112d4604083610153565b600a8252690aa19221be134043404160b21b6020830152565b9092915f935f9360609384938386945b61130657505050565b6113108184610c09565b600360f91b61133b61132e6107466001600160501b0385168861064b565b6001600160f81b03191690565b036115875761136961134d8286610c81565b611355611230565b906020815191012090602081519101201490565b6113b1575061137e6107346107348360a01c90565b61138e6107346107348560a01c90565b11156113a85761139e9083610c51565b90815b90916112fd565b505f90816113a1565b9050826113cf93979598969499506113c99250610c51565b85611acb565b946113da8686610c09565b956113f36107346107346113ec611257565b9360a01c90565b965b815115158061157a575b8061156d575b611560576114138188610c09565b600360f91b61143161132e6107466001600160501b0385168c61064b565b0361154e5761144b611443828a610c81565b611355611275565b61152b575b61146561145d828a610c81565b61135561129d565b611509575b61147f611477828a610c81565b6113556112c5565b6114e8575b50876114966107346107348460a01c90565b10156114ab576114a69087610c51565b6113f5565b50919395509193505b8051151590816114d3575b816114c8575094565b604001511515905094565b90506114e26020820151151590565b906114bf565b6114fe9194506114f89088610c51565b87610c81565b60018252925f611484565b925061151e6115188489610c51565b88610c81565b600160208401529261146a565b9550611549945061153c8688611ba1565b9115156040850152969095565b611450565b505050929591945092505f9493929190565b50919395509193506114b4565b5060408201511515611405565b50602082015115156113ff565b505f9850505050565b90815191604051926022600285019482800186526f30313233343536373839616263646566600f5201908201915b8281036115d257505f815260200160405250565b60016002910191600f835116516001820153600f835160041c1651815301906115be565b805160609493929083811115611667575b8181111561165f575b5082811061161d57505050565b6040519450918290039101601f8201601f19165b8181015185820152601f190190811561164a5790611631565b505060408184015f6020820152016040528252565b90505f611610565b925082611607565b610c3f90600160ff1b61168d6107466116875f610408565b8461064b565b166116e757611196610ea16107466116ae936116a85f610408565b9061064b565b6001600160501b03806116da6107346116d56116cc6107345f610416565b95848716610446565b610c73565b1660a01b911660501b1790565b6001600160501b03806117496107346116d56116cc610734607f6117166107466117105f610408565b8b61064b565b60f81c16976001890361174e57611196611739916117335f610416565b90611efc565b975b6117445f610416565b610446565b6116da565b886002810361177b575061176e611775916117685f610416565b90611ecf565b61ffff1690565b9761173b565b611792906117759261178c5f610416565b90611e80565b6117a361179e8b610ccb565b610d30565b60ff161c90565b610c3f9190600160ff1b6117c361074661168785610408565b1661181657611196610ea16107466117de936116a886610408565b906001600160501b03806118066107346116d56117fd61073487610416565b96848816610446565b1692169160a01b9160501b171790565b906001600160501b038061186e6107346116d56117fd610734607f6118466107466118408b610408565b8c61064b565b60f81c169860018a0361187357611196611863916117338b610416565b985b61174489610416565b611806565b8960028103611893575061176e61188d916117688b610416565b98611865565b6118a49061188d9261178c8c610416565b6117a361179e8c610ccb565b9190918183018084116103e7578151106118fc5760206118cf83610185565b936118dd6040519586610153565b8385526118e984610185565b8583019390601f19013685370101905e90565b6343733a0960e11b5f5260045ffd5b90600c81101561047b5760051b0190565b90620151808202918083046201518014901517156103e757565b90610e10820291808304610e1014901517156103e757565b90603c820291808304603c14901517156103e757565b95949392915f916107b25b61ffff891661ffff821610611a9557506119e861198d610180610175565b601f815298601c60208b0152601f60408b0152601e60608b0152601f60808b0152601e60a08b0152601f60c08b0152601f60e08b0152601e6101008b0152601f6101208b0152601e6101408b0152601f6101608b0152611f0d565b611a89575b6001905b60ff811660ff831610611a4c575050610c3f95965060ff611a3d611a4494611a3083611a36819997611a30611a2b611196611a309a610cdc565b61191c565b90610446565b9216611936565b921661194e565b911690610446565b909260ff6001611a7e8293611a30611a2b6111968f611a7790611a716111968e610cdc565b9061190b565b5160ff1690565b9501169190506119f1565b601d60208901526119ed565b92600161ffff91611aa586611f0d565b15611abc57611ab390610435565b945b011661196f565b611ac590610424565b94611ab5565b600160fa1b6001600160f81b0319611aec6001600160501b0385168461064b565b511603611b09576001600160501b03610c3f9260501c16906117aa565b63a040ad4b60e01b5f5260045ffd5b6040516102209190611b2a8382610153565b6010815291601f1901366020840137565b80516020909101516001600160f01b0319811692919060028210611b5d575050565b6001600160f01b031960029290920360031b82901b16169150565b60405190611b87604083610153565b600b82526a2a864886f84d010d01021160a81b6020830152565b611baf6106bf5f9383610c51565b611bb7611b18565b915f6011915b828210611bcd5750505050600192565b90919294611bdb8683610c09565b90611c28611c20611bef6109cd8587610c51565b60028151105f14611c6557611c0f611c09611c1892611b3b565b60f01c90565b60081c60ff1690565b935b85610c81565b611355611b78565b15611c495750611c4061ffff60019216965b83610c51565b93920190611bbd565b95611c409061ffff60019316611c5f8689610467565b52611c3a565b611c09611c7191611b3b565b93611c1a565b805182515f199493909290821515828515611d4457856020602192019585010301946020601f8216810360031b93018051928787101615611d1d576020821015611cf6575050925b83815118821c15611ce3576001019284841015611cdc5792611cbf565b5050505050565b91909103601f1901945061018392505050565b90809295939120925b85815118821c15611d26575b6001019486861015611d1d5794611cff565b50505050505050565b8383822003611d0b5793909303601f19019650610183945050505050565b505f96505050505050565b919091611d5c8382611f46565b90602082018091600184510160051b840191602083019282519052600185510185525f5b81519060608352808203611dbc575b50602090885101910190838210611d80575050505081935115611db0575050565b90516001190181529150565b604051818303808252949091601f8601601f19165b82820181015184820152601f1901918215611dec5791611dd1565b9590506020939291505f848284010152603f601f199101168101604052835290611d8f565b6040518151909392909160208301601f19165b8181015186820152601f1901908115611e3d5790611e24565b505080519084830160208301601f19165b8281015182820152601f1901918215611e675791611e4e565b50505060409101808401905f6020830152845201604052565b919060208211611ec0578181018082116103e757835110611eb1576020915f199083036101000a0119920101511690565b634e0a27d960e01b5f5260045ffd5b63cbb7a73160e01b5f5260045ffd5b600282018083116103e757815110611eed57016002015161ffff1690565b63653582fb60e01b5f5260045ffd5b90611f069161064b565b5160f81c90565b60038116611f415761ffff60648183160616611f3b5761019061ffff8092160616611f3757600190565b5f90565b50600190565b505f90565b91909160609280519180519182841115611f61575b50505050565b9091929450604051946020830191602182602089019686010301935f91602084101561203b575b6020015193601f841660200360031b905b85815118821c15611fdc575b6001019486861015611fb8575b94611f99565b505050505050506020905b838103601f190160051c8452016040525f808080611f5b565b968484612011575b60208192601f19868c030181520198019015611fa55794868610611fb25750505050505050602090611fc3565b84908920036120205784611fe4565b9660010194868610611fb25750505050505050602090611fc3565b602081018490209250611f8856fea26469706673582212202108f8c9e1697ad9b588f12b49ee652dba223485b3e466f4e42f75f76a74a3f764736f6c634300081e0033

Deployed ByteCode

0x60806040526004361015610011575f80fd5b5f3560e01c8063b6e656fa146100345763c1c1d5c11461002f575f80fd5b6102b4565b346100e15760403660031901126100e15760043567ffffffffffffffff81116100e1576100686100719136906004016101a1565b60243590610480565b906040519182916040830190151583526040602084015281518091526060830190602060608260051b8601019301915f905b8282106100b257505050500390f35b919360019193955060206100d18192605f198a820301865288516101e7565b96019201920185949391926100a3565b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b610100810190811067ffffffffffffffff82111761011657604052565b6100e5565b6060810190811067ffffffffffffffff82111761011657604052565b6080810190811067ffffffffffffffff82111761011657604052565b90601f8019910116810190811067ffffffffffffffff82111761011657604052565b906101836040519283610153565b565b67ffffffffffffffff811161011657601f01601f191660200190565b81601f820112156100e1578035906101b882610185565b926101c66040519485610153565b828452602083830101116100e157815f926020809301838601378301015290565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90604061023661022484516060855260608501906101e7565b602085015184820360208601526101e7565b920151906040818403910152606061026c61025a83516080865260808601906101e7565b602084015185820360208701526101e7565b91604081015160408501520151916060818303910152602080835192838152019201905f5b81811061029e5750505090565b8251845260209384019390920191600101610291565b346100e15760403660031901126100e15760043567ffffffffffffffff81116100e1576102e59036906004016101a1565b60243580151581036100e1576102fa916106a9565b906103a96040519283921515835260406020840152805160408401526020810151606084015260e061038761037261035c610346604086015161010060808a01526101408901906101e7565b6060860151888203603f190160a08a01526101e7565b6080850151878203603f190160c08901526101e7565b60a0840151868203603f1901848801526101e7565b60c08301511515610100860152910151838203603f190161012085015261020b565b0390f35b67ffffffffffffffff81116101165760051b60200190565b634e487b7160e01b5f52601160045260245ffd5b90600382018092116103e757565b6103c5565b90601b82018092116103e757565b90601982018092116103e757565b90600182018092116103e757565b90600282018092116103e757565b906301e1338082018092116103e757565b906301e2850082018092116103e757565b919082018092116103e757565b634e487b7160e01b5f52603260045260245ffd5b805182101561047b5760209160051b010190565b610453565b9061048a816103ad565b6104976040519182610153565b818152601f196104a6836103ad565b015f5b818110610529575050905f83515f915b8383106104cb57505050509050600191565b8215610520576104ed6104e86104e18484610446565b83896115f6565b610b26565b9291906104fa8689610467565b52156105145760019161050c91610446565b9201916104b9565b505f9694955050505050565b6104ed86610b26565b8060606020809386010152016104a9565b60405190610547826100f9565b815f81525f60208201526060604082015260608082015260606080820152606060a08201525f60c082015260e0604051916105818361011b565b606083526060602084015260405161059881610137565b60608152606060208201525f604082015260608082015260408401520152565b604051906105c7604083610153565b601982527f496e74656c205347582050434b20506c6174666f726d204341000000000000006020830152565b60405190610602604083610153565b601a82527f496e74656c205347582050434b2050726f636573736f722043410000000000006020830152565b80511561047b5760200190565b80516001101561047b5760210190565b90815181101561047b570160200190565b6040519061066b604083610153565b601982527f496e74656c205347582050434b204365727469666963617465000000000000006020830152565b805191908290602001825e015f815290565b906106b261053a565b916106c56106bf8261166f565b82610c09565b91806106fb6106f56106e06106da8787610c09565b86610c51565b6106ea8187610c81565b604089015285610c51565b84610c51565b90610a54575b61070b9083610c51565b6107158184610c09565b61071f8185610c51565b906107546107466107406001600160501b0384165b6001600160501b031690565b8761064b565b516001600160f81b03191690565b61077261074661076c6001600160501b038616610734565b8861064b565b601760f81b6001600160f81b0319909216918214159182610a45575b508115610a15575b50610a0a57916107c16107b46107cc936107b96107b488978a610c81565b610e7d565b8a5287610c81565b602088015284610c51565b9061099f575b6107dc9083610c51565b906107e78284610c09565b6107f19084610c51565b6107fb8585610c51565b6108059085610c51565b6108176001600160501b0382166103d9565b9061082e6001600160501b03605083901c166103d9565b60501b90911769ffffffffffffffffffff60a01b909116176108509085610c09565b61085a8186610c81565b610863906111af565b9061086e9086610c51565b6108789086610c81565b610881906111af565b9161088c87876111fc565b606089015261089b9086610c81565b6108a4906111d8565b6080880152604051918291602083016108bc91610697565b6108c591610697565b03601f19810182526108d79082610153565b60a08601526108e9575b505050600191565b6108f39082610c51565b60a360f81b6001600160f81b031961091f6107466109196001600160501b038616610734565b8661064b565b160361099757906109376106bf61093d949383610c09565b906112ed565b919092931561098e5760209261096e6040936109799360e089019786808a5101510152606086895101510152611590565b838651015152611590565b925101510152600160c08201525f80806108e1565b505f9492505050565b505f93915050565b6109fc6109f86109d36109cd6109c76109c16109bb878a610c09565b89610c09565b88610c09565b87610c51565b86610c81565b60e088019081515251516109e561065c565b6020815191012090602081519101201490565b1590565b156107d257505f9492505050565b505f96945050505050565b6001600160f81b031916601760f81b811415915081610a36575b505f610796565b600360fb1b141590505f610a2f565b600360fb1b141591505f61078e565b610a82610a7c610a76610a70610a6a8588610c09565b87610c09565b86610c09565b85610c51565b84610c81565b60e0860190602082510152610a9e6020825101516109e56105b8565b908115610ab4575b5061070157505f9492505050565b5160200151610ac691506109e56105f3565b5f610aa6565b60405190610adb604083610153565b601982527f2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d000000000000006020830152565b60405190610b16604083610153565b60018252600560f91b6020830152565b90610b68604051610b38604082610153565b601b81527f2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0000000000602082015283611c77565b91610b7a610b74610acc565b82611c77565b925f1981148015610bff575b610bf35790610ba984610b9b610bae946103ec565b610ba3610b07565b936115f6565b611d4f565b9160609280515f915b818310610bd057505050610bca906103fa565b60019291565b909194610bea600191610be38885610467565b5190611e11565b95019190610bb7565b505f9250606091839150565b505f198414610b86565b600160fd1b80610c226001600160501b0385168461064b565b511603610c42576001600160501b03610c3f9260501c16906117aa565b90565b632347bfa760e01b5f5260045ffd5b906001600160501b039060a01c16600181018091116103e757610c3f916117aa565b5f198101919082116103e757565b906001600160501b03808260501c169160a01c169060018201918281116103e7578160019103019182116103e757610c3f926118b0565b60ff602f199116019060ff82116103e757565b60ff166020039060ff82116103e757565b60ff5f199116019060ff82116103e757565b61ffff6103e89116029061ffff82169182036103e757565b60ff60649116029060ff82169182036103e757565b60ff600a9116029060ff82169182036103e757565b60031b906107f860f88316921682036103e757565b61ffff169061ffff82116103e757565b61ffff61076c9116019061ffff82116103e757565b61ffff6107d09116019061ffff82116103e757565b9061ffff8091169116019061ffff82116103e757565b60ff169060ff82116103e757565b60ff60019116019060ff82116103e757565b60ff60029116019060ff82116103e757565b60ff60039116019060ff82116103e757565b60ff60049116019060ff82116103e757565b60ff60059116019060ff82116103e757565b60ff60069116019060ff82116103e757565b60ff60079116019060ff82116103e757565b60ff60089116019060ff82116103e757565b60ff60099116019060ff82116103e757565b60ff600a9116019060ff82116103e757565b60ff600b9116019060ff82116103e757565b9060ff8091169116019060ff82116103e757565b5f5f90600d8351145f1461115457600560ff610eac610ea7610ea16107468861062e565b60f81c90565b610cb8565b16101561114657610ebc90610d6a565b610ec582610d95565b60ff16610ed2908461064b565b51610edf9060f81c610cb8565b610ee890610d1b565b610ef183610da3565b60ff16610efe908561064b565b516001600160f81b03191660f81c610f1591610e69565b610f1e90610cb8565b60ff16610f2a91610d7f565b91610f3482610db5565b60ff16610f41908261064b565b51610f4e9060f81c610cb8565b610f5790610d1b565b610f6083610dc7565b60ff16610f6d908361064b565b516001600160f81b03191660f81c610f8491610e69565b610f8d90610cb8565b91610f9781610dd9565b60ff16610fa4908361064b565b51610fb19060f81c610cb8565b610fba90610d1b565b610fc382610deb565b60ff16610fd0908461064b565b516001600160f81b03191660f81c610fe791610e69565b610ff090610cb8565b610ff990610d95565b61100282610dfd565b60ff1661100f908461064b565b5161101c9060f81c610cb8565b61102590610d1b565b61102e83610e0f565b60ff1661103b908561064b565b516001600160f81b03191660f81c61105291610e69565b61105b90610cb8565b61106490610d95565b9161106e81610e21565b60ff1661107b908561064b565b516110889060f81c610cb8565b61109190610d1b565b61109a82610e33565b60ff166110a7908661064b565b516001600160f81b03191660f81c6110be91610e69565b6110c790610cb8565b6110d090610d95565b936110da82610e45565b60ff166110e7908261064b565b516110f49060f81c610cb8565b6110fd90610d1b565b9161110790610e57565b60ff166111139161064b565b516001600160f81b03191660f81c61112a91610e69565b61113390610cb8565b61113c90610d95565b93610c3f95611964565b61114f90610d55565b610ebc565b50506111a76111a261117960ff611173610ea7610ea16107468861062e565b16610cee565b61119c611196611191610ea7610ea16107468961063b565b610d06565b60ff1690565b90610d7f565b610d45565b600290610ebc565b80519060208211156111d357601f1982019182116103e757610c3f916020916118b0565b905090565b80519060408211156111d357603f1982019182116103e757610c3f916040916118b0565b906001600160501b038082169160a01c169060018201918281116103e7578160019103019182116103e757610c3f926118b0565b6040519061123f604083610153565b60098252682a864886f84d010d0160b81b6020830152565b604051906112648261011b565b5f6040838281528260208201520152565b60405190611284604083610153565b600a825269154324437c268086808160b11b6020830152565b604051906112ac604083610153565b600a8252692a864886f84d010d010360b01b6020830152565b604051906112d4604083610153565b600a8252690aa19221be134043404160b21b6020830152565b9092915f935f9360609384938386945b61130657505050565b6113108184610c09565b600360f91b61133b61132e6107466001600160501b0385168861064b565b6001600160f81b03191690565b036115875761136961134d8286610c81565b611355611230565b906020815191012090602081519101201490565b6113b1575061137e6107346107348360a01c90565b61138e6107346107348560a01c90565b11156113a85761139e9083610c51565b90815b90916112fd565b505f90816113a1565b9050826113cf93979598969499506113c99250610c51565b85611acb565b946113da8686610c09565b956113f36107346107346113ec611257565b9360a01c90565b965b815115158061157a575b8061156d575b611560576114138188610c09565b600360f91b61143161132e6107466001600160501b0385168c61064b565b0361154e5761144b611443828a610c81565b611355611275565b61152b575b61146561145d828a610c81565b61135561129d565b611509575b61147f611477828a610c81565b6113556112c5565b6114e8575b50876114966107346107348460a01c90565b10156114ab576114a69087610c51565b6113f5565b50919395509193505b8051151590816114d3575b816114c8575094565b604001511515905094565b90506114e26020820151151590565b906114bf565b6114fe9194506114f89088610c51565b87610c81565b60018252925f611484565b925061151e6115188489610c51565b88610c81565b600160208401529261146a565b9550611549945061153c8688611ba1565b9115156040850152969095565b611450565b505050929591945092505f9493929190565b50919395509193506114b4565b5060408201511515611405565b50602082015115156113ff565b505f9850505050565b90815191604051926022600285019482800186526f30313233343536373839616263646566600f5201908201915b8281036115d257505f815260200160405250565b60016002910191600f835116516001820153600f835160041c1651815301906115be565b805160609493929083811115611667575b8181111561165f575b5082811061161d57505050565b6040519450918290039101601f8201601f19165b8181015185820152601f190190811561164a5790611631565b505060408184015f6020820152016040528252565b90505f611610565b925082611607565b610c3f90600160ff1b61168d6107466116875f610408565b8461064b565b166116e757611196610ea16107466116ae936116a85f610408565b9061064b565b6001600160501b03806116da6107346116d56116cc6107345f610416565b95848716610446565b610c73565b1660a01b911660501b1790565b6001600160501b03806117496107346116d56116cc610734607f6117166107466117105f610408565b8b61064b565b60f81c16976001890361174e57611196611739916117335f610416565b90611efc565b975b6117445f610416565b610446565b6116da565b886002810361177b575061176e611775916117685f610416565b90611ecf565b61ffff1690565b9761173b565b611792906117759261178c5f610416565b90611e80565b6117a361179e8b610ccb565b610d30565b60ff161c90565b610c3f9190600160ff1b6117c361074661168785610408565b1661181657611196610ea16107466117de936116a886610408565b906001600160501b03806118066107346116d56117fd61073487610416565b96848816610446565b1692169160a01b9160501b171790565b906001600160501b038061186e6107346116d56117fd610734607f6118466107466118408b610408565b8c61064b565b60f81c169860018a0361187357611196611863916117338b610416565b985b61174489610416565b611806565b8960028103611893575061176e61188d916117688b610416565b98611865565b6118a49061188d9261178c8c610416565b6117a361179e8c610ccb565b9190918183018084116103e7578151106118fc5760206118cf83610185565b936118dd6040519586610153565b8385526118e984610185565b8583019390601f19013685370101905e90565b6343733a0960e11b5f5260045ffd5b90600c81101561047b5760051b0190565b90620151808202918083046201518014901517156103e757565b90610e10820291808304610e1014901517156103e757565b90603c820291808304603c14901517156103e757565b95949392915f916107b25b61ffff891661ffff821610611a9557506119e861198d610180610175565b601f815298601c60208b0152601f60408b0152601e60608b0152601f60808b0152601e60a08b0152601f60c08b0152601f60e08b0152601e6101008b0152601f6101208b0152601e6101408b0152601f6101608b0152611f0d565b611a89575b6001905b60ff811660ff831610611a4c575050610c3f95965060ff611a3d611a4494611a3083611a36819997611a30611a2b611196611a309a610cdc565b61191c565b90610446565b9216611936565b921661194e565b911690610446565b909260ff6001611a7e8293611a30611a2b6111968f611a7790611a716111968e610cdc565b9061190b565b5160ff1690565b9501169190506119f1565b601d60208901526119ed565b92600161ffff91611aa586611f0d565b15611abc57611ab390610435565b945b011661196f565b611ac590610424565b94611ab5565b600160fa1b6001600160f81b0319611aec6001600160501b0385168461064b565b511603611b09576001600160501b03610c3f9260501c16906117aa565b63a040ad4b60e01b5f5260045ffd5b6040516102209190611b2a8382610153565b6010815291601f1901366020840137565b80516020909101516001600160f01b0319811692919060028210611b5d575050565b6001600160f01b031960029290920360031b82901b16169150565b60405190611b87604083610153565b600b82526a2a864886f84d010d01021160a81b6020830152565b611baf6106bf5f9383610c51565b611bb7611b18565b915f6011915b828210611bcd5750505050600192565b90919294611bdb8683610c09565b90611c28611c20611bef6109cd8587610c51565b60028151105f14611c6557611c0f611c09611c1892611b3b565b60f01c90565b60081c60ff1690565b935b85610c81565b611355611b78565b15611c495750611c4061ffff60019216965b83610c51565b93920190611bbd565b95611c409061ffff60019316611c5f8689610467565b52611c3a565b611c09611c7191611b3b565b93611c1a565b805182515f199493909290821515828515611d4457856020602192019585010301946020601f8216810360031b93018051928787101615611d1d576020821015611cf6575050925b83815118821c15611ce3576001019284841015611cdc5792611cbf565b5050505050565b91909103601f1901945061018392505050565b90809295939120925b85815118821c15611d26575b6001019486861015611d1d5794611cff565b50505050505050565b8383822003611d0b5793909303601f19019650610183945050505050565b505f96505050505050565b919091611d5c8382611f46565b90602082018091600184510160051b840191602083019282519052600185510185525f5b81519060608352808203611dbc575b50602090885101910190838210611d80575050505081935115611db0575050565b90516001190181529150565b604051818303808252949091601f8601601f19165b82820181015184820152601f1901918215611dec5791611dd1565b9590506020939291505f848284010152603f601f199101168101604052835290611d8f565b6040518151909392909160208301601f19165b8181015186820152601f1901908115611e3d5790611e24565b505080519084830160208301601f19165b8281015182820152601f1901918215611e675791611e4e565b50505060409101808401905f6020830152845201604052565b919060208211611ec0578181018082116103e757835110611eb1576020915f199083036101000a0119920101511690565b634e0a27d960e01b5f5260045ffd5b63cbb7a73160e01b5f5260045ffd5b600282018083116103e757815110611eed57016002015161ffff1690565b63653582fb60e01b5f5260045ffd5b90611f069161064b565b5160f81c90565b60038116611f415761ffff60648183160616611f3b5761019061ffff8092160616611f3757600190565b5f90565b50600190565b505f90565b91909160609280519180519182841115611f61575b50505050565b9091929450604051946020830191602182602089019686010301935f91602084101561203b575b6020015193601f841660200360031b905b85815118821c15611fdc575b6001019486861015611fb8575b94611f99565b505050505050506020905b838103601f190160051c8452016040525f808080611f5b565b968484612011575b60208192601f19868c030181520198019015611fa55794868610611fb25750505050505050602090611fc3565b84908920036120205784611fe4565b9660010194868610611fb25750505050505050602090611fc3565b602081018490209250611f8856fea26469706673582212202108f8c9e1697ad9b588f12b49ee652dba223485b3e466f4e42f75f76a74a3f764736f6c634300081e0033

External libraries