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
- 2026-06-23T13:32:26.644661Z
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.26;
// 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.26;
// 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 ""&'<>" 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":false,"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":{},"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
0x6080604052348015600e575f5ffd5b5061215d8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063b6e656fa14610038578063c1c1d5c114610062575b5f5ffd5b61004b610046366004611cbe565b610083565b604051610059929190611d2e565b60405180910390f35b610075610070366004611d9a565b610177565b604051610059929190611eb4565b5f60608267ffffffffffffffff81111561009f5761009f611c1f565b6040519080825280602002602001820160405280156100d257816020015b60608152602001906001900390816100bd5790505b50845190915084905f90815b8681101561016757606081156101095761010285856100fd8682611f8e565b61070c565b905061010c565b50835b5f61011682610723565b89868151811061012857610128611fa1565b60200260200101819450829052829a505050508761014e575f9750505050505050610170565b6101588186611f8e565b945050508060010190506100de565b50600194505050505b9250929050565b5f610180611b93565b5f61018a85610880565b90505f6101978683610891565b90505f6101a48783610891565b90506101b087826108f4565b90505f6101bd8883610911565b6040860152506101cd87826108f4565b90506101d987826108f4565b9050851561030e575f6101ec8883610891565b90506101f88882610891565b90506102048882610891565b905061021088826108f4565b905061021c8882610911565b60e0860180516020908101929092525181015160408051808201909152601981527f496e74656c205347582050434b20506c6174666f726d204341000000000000009083015280519101205f907ff2cdee66d0c46bef721c372ff5fe990263f3608a970b3a22fe0de6d8e7f3cfac14806102f7575060e086015160209081015160408051808201909152601a81527f496e74656c205347582050434b2050726f636573736f722043410000000000009083015280519101207f84eb72543fac46d69ca77faea23e8f689f4eee6d0444cfe8bd1996f61feeba79145b90508061030b575f96505050505050610170565b50505b61031887826108f4565b90505f6103258883610891565b90505f61033289836108f4565b90505f896001600160501b0384168151811061035057610350611fa1565b01602001516001600160f81b03191690505f8a610373846001600160501b031690565b8151811061038357610383611fa1565b01602001516001600160f81b03199081169150601760f81b908316148015906103ba5750600360fb1b6001600160f81b0319831614155b806103ec5750601760f81b6001600160f81b03198216148015906103ec5750600360fb1b6001600160f81b0319821614155b15610400575f985050505050505050610170565b61041261040d8c86610911565b610950565b885261042161040d8c85610911565b60208901525061043792508991508390506108f4565b905085156104f4575f61044a8883610891565b90506104568882610891565b90506104628882610891565b905061046e88826108f4565b905061047a8882610911565b60e08601805191909152515160408051808201909152601981527f496e74656c205347582050434b2043657274696669636174650000000000000060209182015281519101207f66708796b5f0a1c722c0ba537c9d8345601277bf29d5a1a5afb176d886bf5cda146104f2575f955050505050610170565b505b6104fe87826108f4565b90505f61050b8883610891565b905061051788826108f4565b90505f61052489856108f4565b905061053089826108f4565b90506105796105496001600160501b0383166003611f8e565b6105616001600160501b03605085901c166003611f8e565b60501b1769ffffffffffffffffffff60a01b83161790565b90506105858982610891565b90505f61059c6105958b84610911565b6020610d73565b90506105a88a836108f4565b91505f6105b86105958c85610911565b90506105c48b87610da9565b60608901526105dd6105d68c86610911565b6040610d73565b60808901526040516105f59083908390602001611fcc565b6040516020818303038152906040528860a001819052505050505085156106fe5761062087826108f4565b9050866001600160501b0382168151811061063d5761063d611fa1565b6020910101516001600160f81b03191660a360f81b14610662575f9450505050610170565b61066c8782610891565b90506106788782610891565b90505f5f606080606061068c8c8789610dbe565b939850919650945092509050846106ad575f99505050505050505050610170565b60e0890180516040908101518101869052905101516060018390526106d181611147565b60e08a015160400151526106e482611147565b60e08a015160400151602001525050600160c08801525050505b600194505050509250929050565b60606107198484846111ac565b90505b9392505050565b5f60605f5f610767856040518060400160405280601b81526020017f2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0000000000815250611211565b90505f6107a9866040518060400160405280601981526020017f2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d00000000000000815250611211565b90505f1982811480159183141590806107c0575080155b156107d1575f965050505050610879565b5f6107dd601b86611f8e565b6040805180820190915260018152600560f91b60208201529091506060905f6108078c858961070c565b90505f610814828461121d565b80519091506060905f5b81811015610853576108498385838151811061083c5761083c611fa1565b6020026020010151611232565b925060010161081e565b5081955060018660198c6108679190611f8e565b9d509d509d5050505050505050505050505b9193909250565b5f61088b825f61123e565b92915050565b5f826001600160501b038316815181106108ad576108ad611fa1565b0160200151600160fd1b908116146108d857604051632347bfa760e01b815260040160405180910390fd5b61071c83605084901c6001600160501b031661123e565b61123e565b5f61071c836108ef6001600160501b0360a086901c166001611f8e565b606061071c6001600160501b03605084901c16805b61093e6001600160501b0360a087901c166001611f8e565b6109489190611fe0565b8591906113f1565b5f5f5f5f5f5f5f5f8851600d036109b157600560308a5f8151811061097757610977611fa1565b0160200151610989919060f81c611ff3565b60ff1610156109a55761099e6107d08861200c565b9650610a36565b61099e61076c8861200c565b6030896001815181106109c6576109c6611fa1565b01602001516109d8919060f81c611ff3565b6109e3906064612026565b60ff1660308a5f815181106109fa576109fa611fa1565b0160200151610a0c919060f81c611ff3565b610a1b9060ff166103e8612042565b610a25919061200c565b610a2f908861200c565b9650600290505b603089610a4483600161205f565b60ff1681518110610a5757610a57611fa1565b016020015160f81c60308b610a6c855f61205f565b60ff1681518110610a7f57610a7f611fa1565b0160200151610a91919060f81c611ff3565b610a9c90600a612026565b610aa6919061205f565b610ab09190611ff3565b610abd9060ff168861200c565b9650603089610acd83600361205f565b60ff1681518110610ae057610ae0611fa1565b016020015160f81c60308b610af685600261205f565b60ff1681518110610b0957610b09611fa1565b0160200151610b1b919060f81c611ff3565b610b2690600a612026565b610b30919061205f565b610b3a9190611ff3565b9550603089610b4a83600561205f565b60ff1681518110610b5d57610b5d611fa1565b016020015160f81c60308b610b7385600461205f565b60ff1681518110610b8657610b86611fa1565b0160200151610b98919060f81c611ff3565b610ba390600a612026565b610bad919061205f565b610bb79190611ff3565b610bc1908661205f565b9450603089610bd183600761205f565b60ff1681518110610be457610be4611fa1565b016020015160f81c60308b610bfa85600661205f565b60ff1681518110610c0d57610c0d611fa1565b0160200151610c1f919060f81c611ff3565b610c2a90600a612026565b610c34919061205f565b610c3e9190611ff3565b610c48908561205f565b9350603089610c5883600961205f565b60ff1681518110610c6b57610c6b611fa1565b016020015160f81c60308b610c8185600861205f565b60ff1681518110610c9457610c94611fa1565b0160200151610ca6919060f81c611ff3565b610cb190600a612026565b610cbb919061205f565b610cc59190611ff3565b610ccf908461205f565b9250603089610cdf83600b61205f565b60ff1681518110610cf257610cf2611fa1565b016020015160f81c60308b610d0885600a61205f565b60ff1681518110610d1b57610d1b611fa1565b0160200151610d2d919060f81c611ff3565b610d3890600a612026565b610d42919061205f565b610d4c9190611ff3565b610d56908361205f565b9150610d66878787878787611486565b9998505050505050505050565b8151606090828111610d88578391505061088b565b5f610d938483611fe0565b9050610da08582866113f1565b95945050505050565b606061071c6001600160501b03831680610926565b5f5f60608060605b861561113c575f610dd78989610891565b9050886001600160501b03821681518110610df457610df4611fa1565b6020910101516001600160f81b031916600360f91b14610e17575f95505061113c565b610e6f610e248a83610911565b6040805180820190915260098152682a864886f84d010d0160b81b60209182015281519101207fa96edd26eb8242cf5f749e4aec407af87b1c7b2c956655a550a382711f0710811490565b156110ff57610e7e89826108f4565b90505f610e8b8a83611613565b90505f610e988b83610891565b604080516060810182525f80825260208201819052918101919091529091505b80518015610ec7575080602001515b8015610ed4575080604001515b6110d8575f610ee38d84610891565b90508c6001600160501b03821681518110610f0057610f00611fa1565b6020910101516001600160f81b031916600360f91b14610f27575f9950505050505061113c565b610f80610f348e83610911565b60408051808201909152600a815269154324437c268086808160b11b60209182015281519101207f2bfc41847160b94934a1868d8bce9e9047266ffdcdf86867979fc5a94546a56a1490565b15610f9c57610f8f8d82611662565b9115156040850152995097505b610ff5610fa98e83610911565b60408051808201909152600a8152692a864886f84d010d010360b01b60209182015281519101207f561f8f952263c2f52cbd2ab803754679dbfbf5cabe0e845fa356d40994acb8301490565b1561101c575f6110058e836108f4565b90506110118e82610911565b600160208501529650505b6110756110298e83610911565b60408051808201909152600a8152690aa19221be134043404160b21b60209182015281519101207fecbf2bb44d2c57a26a41a9482a9d5d8183468ac2d57a7b1c139bee60afea6dd51490565b15611099575f6110858e836108f4565b90506110918e82610911565b600184529750505b60a084901c6001600160501b031660a084901c6001600160501b031610156110cc576110c58d846108f4565b92506110d2565b506110d8565b50610eb8565b805180156110e7575080602001515b80156110f4575080604001515b98505050505061113c565b60a087901c6001600160501b031660a089901c6001600160501b031610156111325761112b89896108f4565b9750611136565b5f97505b50610dc6565b939792965093509350565b8051604051818001600282019081526f30313233343536373839616263646566600f52918301906022015b81841461119d57600184019350600f845116516001820153600f845160041c16518153600201611172565b5f815260200160405250919050565b606083518281116111bb578092505b8381116111c6578093505b508183101561071c5750604051828203848401601f19601f830181165b82810151858201528101806111e3575050508060208301015f81526020810160405250808252509392505050565b5f61071c83835f6117d2565b60605f61122a848461189c565b949350505050565b606061071c8383611944565b5f8080808561124e866001611f8e565b8151811061125e5761125e611fa1565b0160200151600160ff1b165f036112c8578561127b866001611f8e565b8151811061128b5761128b611fa1565b016020015160f81c92506112a0856002611f8e565b915060016112b7846001600160501b038516611f8e565b6112c19190611fe0565b90506113bb565b5f866112d5876001611f8e565b815181106112e5576112e5611fa1565b60209101015160f81c607f169050600181900361131b5761131161130a876002611f8e565b889061199f565b60ff16935061137d565b8060ff166002036113465761133b611334876002611f8e565b88906119c2565b61ffff16935061137d565b611351816020611ff3565b61135c906008612026565b60ff1661137861136d886002611f8e565b899060ff85166119fd565b901c93505b60ff811661138c876002611f8e565b6113969190611f8e565b925060016113ad856001600160501b038616611f8e565b6113b79190611fe0565b9150505b69ffffffffffffffffffff60a01b60a082901b1669ffffffffffffffffffff60501b605084901b168617175b9695505050505050565b82516060906114008385611f8e565b111561141f576040516343733a0960e11b815260040160405180910390fd5b5f8267ffffffffffffffff81111561143957611439611c1f565b6040519080825280601f01601f191660200182016040528015611463576020820181803683370190505b5090506020808201908686010161147b828287611a64565b509095945050505050565b5f806107b25b8861ffff168161ffff1610156114d8576114a581611a6d565b156114bf576114b86301e2850083611f8e565b91506114d0565b6114cd6301e1338083611f8e565b91505b60010161148c565b506040805161018081018252601f808252601c6020830152918101829052601e606082018190526080820183905260a0820181905260c0820183905260e082018390526101008201819052610120820183905261014082015261016081019190915261154389611a6d565b1561155057601d60208201525b60015b8860ff168160ff1610156115a9578161156d600183611ff3565b60ff16600c811061158057611580611fa1565b60200201516115959060ff1662015180612078565b61159f9084611f8e565b9250600101611553565b506115b5600188611ff3565b6115c59060ff1662015180612078565b6115cf9083611f8e565b91506115e060ff8716610e10612078565b6115ea9083611f8e565b91506115fa60ff8616603c612078565b6116049083611f8e565b9150610d6660ff851683611f8e565b5f826001600160501b0383168151811061162f5761162f611fa1565b6020910101516001600160f81b031916600160fa1b146108d85760405163a040ad4b60e01b815260040160405180910390fd5b5f8060608161167186866108f4565b90505f61167e8783610891565b604080516010808252610220820190925291925060208201610200803683370190505092505f5b6116b160106001611f8e565b8110156117c4575f6116c38984610891565b90505f6116d08a836108f4565b90505f6116dd8b83610911565b90505f60028251106116fa576116f28261208f565b60f01c611713565b6101006117068361208f565b60f01c61171391906120e1565b905061176f6117228d86610911565b60408051808201909152600b81526a2a864886f84d010d01021160a81b60209182015281519101207f4d427d13319aa428e67638dd0f7e7f1f22835710f9156ea643a24a02217799fa1490565b15611780578061ffff1698506117a9565b5f8161ffff1690508089878151811061179b5761179b611fa1565b602002602001018181525050505b6117b38c876108f4565b9550505050508060010190506116a5565b506001945050509250925092565b82515f199083516117f0578291508083111561189457809150611894565b83516020860184810196506001828483010301601f831660200360031b6020880151858810838b1016611827575050505050611894565b6020851061186c578460208a01205b818b5118831c6118535780868c200361185357848b039750611861565b60018b019a50838b10611836575b505050505050611894565b808a5118821c61188057838a03965061188e565b60018a019950828a1061186c575b50505050505b509392505050565b60605f6118a98484611ac9565b9050601f1960208201600183510160051b8101865183820152600184510184525f5b82516060845281811461190f5760405182820380825286601f8201165b8b8501810151838201528701806118e857505f82820160200152603f018616810160405284525b8751602094909401930190508183106118cb5750505050809150825161193d57602081019150600281510382525b5092915050565b6040518251601f19906020810182165b8581015184820152820180611954575083518184018360208301165b86810151828201528401806119705750505f910183810160208101929092528352604090810190525092915050565b5f8282815181106119b2576119b2611fa1565b016020015160f81c905092915050565b81515f906119d1836002611f8e565b11156119f05760405163653582fb60e01b815260040160405180910390fd5b50016002015161ffff1690565b5f6020821115611a205760405163cbb7a73160e01b815260040160405180910390fd5b8351611a2c8385611f8e565b1115611a4b57604051634e0a27d960e01b815260040160405180910390fd5b506020919092018101519190036101000a5f1901191690565b8082845e505050565b5f611a79600483612104565b61ffff1615611a8957505f919050565b611a94606483612104565b61ffff1615611aa557506001919050565b611ab161019083612104565b61ffff1615611ac157505f919050565b506001919050565b606081518351811161193d57604051915060208401602083016001838751840103015f60208510611afd5784602088012090505b6020870151601f861660200360031b5b8551828118821c611b66578315611b40578388882014611b4057600187019650848710611b3a5750611b75565b50611b0d565b60208b018703865260208601955087870196508715611b6657848710611b3a5750611b75565b50600186019550838610611b0d575b50505050601f198482030160051c8452602001604052505092915050565b6040518061010001604052805f81526020015f8152602001606081526020016060815260200160608152602001606081526020015f15158152602001611bd7611bdc565b905290565b60405180606001604052806060815260200160608152602001611bd7604051806080016040528060608152602001606081526020015f8152602001606081525090565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112611c42575f5ffd5b813567ffffffffffffffff811115611c5c57611c5c611c1f565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715611c8b57611c8b611c1f565b604052818152838201602001851015611ca2575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f5f60408385031215611ccf575f5ffd5b823567ffffffffffffffff811115611ce5575f5ffd5b611cf185828601611c33565b95602094909401359450505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f6040820184151583526040602084015280845180835260608501915060608160051b8601019250602086015f5b82811015611d8d57605f19878603018452611d78858351611d00565b94506020938401939190910190600101611d5c565b5092979650505050505050565b5f5f60408385031215611dab575f5ffd5b823567ffffffffffffffff811115611dc1575f5ffd5b611dcd85828601611c33565b92505060208301358015158114611de2575f5ffd5b809150509250929050565b5f815160608452611e016060850182611d00565b905060208301518482036020860152611e1a8282611d00565b91505060408301518482036040860152805160808352611e3d6080840182611d00565b905060208201518382036020850152611e568282611d00565b6040848101519086015260609384015185820394909501939093525050815180825260209182019291909101905f905b80821015611ea95782518452602084019350602083019250600182019150611e86565b509195945050505050565b82151581526040602082015281516040820152602082015160608201525f60408301516101006080840152611eed610140840182611d00565b90506060840151603f198483030160a0850152611f0a8282611d00565b9150506080840151603f198483030160c0850152611f288282611d00565b91505060a0840151603f198483030160e0850152611f468282611d00565b91505060c0840151611f5d61010085018215159052565b5060e0840151838203603f19016101208501526113e78282611ded565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561088b5761088b611f7a565b634e487b7160e01b5f52603260045260245ffd5b5f81518060208401855e5f93019283525090919050565b5f610719611fda8386611fb5565b84611fb5565b8181038181111561088b5761088b611f7a565b60ff828116828216039081111561088b5761088b611f7a565b61ffff818116838216019081111561088b5761088b611f7a565b60ff818116838216029081169081811461193d5761193d611f7a565b61ffff818116838216029081169081811461193d5761193d611f7a565b60ff818116838216019081111561088b5761088b611f7a565b808202811582820484141761088b5761088b611f7a565b805160208201516001600160f01b03198116919060028210156120c6576001600160f01b0319600283900360031b81901b82161692505b5050919050565b634e487b7160e01b5f52601260045260245ffd5b5f61ffff8316806120f4576120f46120cd565b8061ffff84160491505092915050565b5f61ffff831680612117576121176120cd565b8061ffff8416069150509291505056fea26469706673582212203e89e6c698226306fae87622615b58a291cb992afa4dbe56e23a350fb9e1da9a64736f6c634300081e0033
Deployed ByteCode
0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063b6e656fa14610038578063c1c1d5c114610062575b5f5ffd5b61004b610046366004611cbe565b610083565b604051610059929190611d2e565b60405180910390f35b610075610070366004611d9a565b610177565b604051610059929190611eb4565b5f60608267ffffffffffffffff81111561009f5761009f611c1f565b6040519080825280602002602001820160405280156100d257816020015b60608152602001906001900390816100bd5790505b50845190915084905f90815b8681101561016757606081156101095761010285856100fd8682611f8e565b61070c565b905061010c565b50835b5f61011682610723565b89868151811061012857610128611fa1565b60200260200101819450829052829a505050508761014e575f9750505050505050610170565b6101588186611f8e565b945050508060010190506100de565b50600194505050505b9250929050565b5f610180611b93565b5f61018a85610880565b90505f6101978683610891565b90505f6101a48783610891565b90506101b087826108f4565b90505f6101bd8883610911565b6040860152506101cd87826108f4565b90506101d987826108f4565b9050851561030e575f6101ec8883610891565b90506101f88882610891565b90506102048882610891565b905061021088826108f4565b905061021c8882610911565b60e0860180516020908101929092525181015160408051808201909152601981527f496e74656c205347582050434b20506c6174666f726d204341000000000000009083015280519101205f907ff2cdee66d0c46bef721c372ff5fe990263f3608a970b3a22fe0de6d8e7f3cfac14806102f7575060e086015160209081015160408051808201909152601a81527f496e74656c205347582050434b2050726f636573736f722043410000000000009083015280519101207f84eb72543fac46d69ca77faea23e8f689f4eee6d0444cfe8bd1996f61feeba79145b90508061030b575f96505050505050610170565b50505b61031887826108f4565b90505f6103258883610891565b90505f61033289836108f4565b90505f896001600160501b0384168151811061035057610350611fa1565b01602001516001600160f81b03191690505f8a610373846001600160501b031690565b8151811061038357610383611fa1565b01602001516001600160f81b03199081169150601760f81b908316148015906103ba5750600360fb1b6001600160f81b0319831614155b806103ec5750601760f81b6001600160f81b03198216148015906103ec5750600360fb1b6001600160f81b0319821614155b15610400575f985050505050505050610170565b61041261040d8c86610911565b610950565b885261042161040d8c85610911565b60208901525061043792508991508390506108f4565b905085156104f4575f61044a8883610891565b90506104568882610891565b90506104628882610891565b905061046e88826108f4565b905061047a8882610911565b60e08601805191909152515160408051808201909152601981527f496e74656c205347582050434b2043657274696669636174650000000000000060209182015281519101207f66708796b5f0a1c722c0ba537c9d8345601277bf29d5a1a5afb176d886bf5cda146104f2575f955050505050610170565b505b6104fe87826108f4565b90505f61050b8883610891565b905061051788826108f4565b90505f61052489856108f4565b905061053089826108f4565b90506105796105496001600160501b0383166003611f8e565b6105616001600160501b03605085901c166003611f8e565b60501b1769ffffffffffffffffffff60a01b83161790565b90506105858982610891565b90505f61059c6105958b84610911565b6020610d73565b90506105a88a836108f4565b91505f6105b86105958c85610911565b90506105c48b87610da9565b60608901526105dd6105d68c86610911565b6040610d73565b60808901526040516105f59083908390602001611fcc565b6040516020818303038152906040528860a001819052505050505085156106fe5761062087826108f4565b9050866001600160501b0382168151811061063d5761063d611fa1565b6020910101516001600160f81b03191660a360f81b14610662575f9450505050610170565b61066c8782610891565b90506106788782610891565b90505f5f606080606061068c8c8789610dbe565b939850919650945092509050846106ad575f99505050505050505050610170565b60e0890180516040908101518101869052905101516060018390526106d181611147565b60e08a015160400151526106e482611147565b60e08a015160400151602001525050600160c08801525050505b600194505050509250929050565b60606107198484846111ac565b90505b9392505050565b5f60605f5f610767856040518060400160405280601b81526020017f2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0000000000815250611211565b90505f6107a9866040518060400160405280601981526020017f2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d00000000000000815250611211565b90505f1982811480159183141590806107c0575080155b156107d1575f965050505050610879565b5f6107dd601b86611f8e565b6040805180820190915260018152600560f91b60208201529091506060905f6108078c858961070c565b90505f610814828461121d565b80519091506060905f5b81811015610853576108498385838151811061083c5761083c611fa1565b6020026020010151611232565b925060010161081e565b5081955060018660198c6108679190611f8e565b9d509d509d5050505050505050505050505b9193909250565b5f61088b825f61123e565b92915050565b5f826001600160501b038316815181106108ad576108ad611fa1565b0160200151600160fd1b908116146108d857604051632347bfa760e01b815260040160405180910390fd5b61071c83605084901c6001600160501b031661123e565b61123e565b5f61071c836108ef6001600160501b0360a086901c166001611f8e565b606061071c6001600160501b03605084901c16805b61093e6001600160501b0360a087901c166001611f8e565b6109489190611fe0565b8591906113f1565b5f5f5f5f5f5f5f5f8851600d036109b157600560308a5f8151811061097757610977611fa1565b0160200151610989919060f81c611ff3565b60ff1610156109a55761099e6107d08861200c565b9650610a36565b61099e61076c8861200c565b6030896001815181106109c6576109c6611fa1565b01602001516109d8919060f81c611ff3565b6109e3906064612026565b60ff1660308a5f815181106109fa576109fa611fa1565b0160200151610a0c919060f81c611ff3565b610a1b9060ff166103e8612042565b610a25919061200c565b610a2f908861200c565b9650600290505b603089610a4483600161205f565b60ff1681518110610a5757610a57611fa1565b016020015160f81c60308b610a6c855f61205f565b60ff1681518110610a7f57610a7f611fa1565b0160200151610a91919060f81c611ff3565b610a9c90600a612026565b610aa6919061205f565b610ab09190611ff3565b610abd9060ff168861200c565b9650603089610acd83600361205f565b60ff1681518110610ae057610ae0611fa1565b016020015160f81c60308b610af685600261205f565b60ff1681518110610b0957610b09611fa1565b0160200151610b1b919060f81c611ff3565b610b2690600a612026565b610b30919061205f565b610b3a9190611ff3565b9550603089610b4a83600561205f565b60ff1681518110610b5d57610b5d611fa1565b016020015160f81c60308b610b7385600461205f565b60ff1681518110610b8657610b86611fa1565b0160200151610b98919060f81c611ff3565b610ba390600a612026565b610bad919061205f565b610bb79190611ff3565b610bc1908661205f565b9450603089610bd183600761205f565b60ff1681518110610be457610be4611fa1565b016020015160f81c60308b610bfa85600661205f565b60ff1681518110610c0d57610c0d611fa1565b0160200151610c1f919060f81c611ff3565b610c2a90600a612026565b610c34919061205f565b610c3e9190611ff3565b610c48908561205f565b9350603089610c5883600961205f565b60ff1681518110610c6b57610c6b611fa1565b016020015160f81c60308b610c8185600861205f565b60ff1681518110610c9457610c94611fa1565b0160200151610ca6919060f81c611ff3565b610cb190600a612026565b610cbb919061205f565b610cc59190611ff3565b610ccf908461205f565b9250603089610cdf83600b61205f565b60ff1681518110610cf257610cf2611fa1565b016020015160f81c60308b610d0885600a61205f565b60ff1681518110610d1b57610d1b611fa1565b0160200151610d2d919060f81c611ff3565b610d3890600a612026565b610d42919061205f565b610d4c9190611ff3565b610d56908361205f565b9150610d66878787878787611486565b9998505050505050505050565b8151606090828111610d88578391505061088b565b5f610d938483611fe0565b9050610da08582866113f1565b95945050505050565b606061071c6001600160501b03831680610926565b5f5f60608060605b861561113c575f610dd78989610891565b9050886001600160501b03821681518110610df457610df4611fa1565b6020910101516001600160f81b031916600360f91b14610e17575f95505061113c565b610e6f610e248a83610911565b6040805180820190915260098152682a864886f84d010d0160b81b60209182015281519101207fa96edd26eb8242cf5f749e4aec407af87b1c7b2c956655a550a382711f0710811490565b156110ff57610e7e89826108f4565b90505f610e8b8a83611613565b90505f610e988b83610891565b604080516060810182525f80825260208201819052918101919091529091505b80518015610ec7575080602001515b8015610ed4575080604001515b6110d8575f610ee38d84610891565b90508c6001600160501b03821681518110610f0057610f00611fa1565b6020910101516001600160f81b031916600360f91b14610f27575f9950505050505061113c565b610f80610f348e83610911565b60408051808201909152600a815269154324437c268086808160b11b60209182015281519101207f2bfc41847160b94934a1868d8bce9e9047266ffdcdf86867979fc5a94546a56a1490565b15610f9c57610f8f8d82611662565b9115156040850152995097505b610ff5610fa98e83610911565b60408051808201909152600a8152692a864886f84d010d010360b01b60209182015281519101207f561f8f952263c2f52cbd2ab803754679dbfbf5cabe0e845fa356d40994acb8301490565b1561101c575f6110058e836108f4565b90506110118e82610911565b600160208501529650505b6110756110298e83610911565b60408051808201909152600a8152690aa19221be134043404160b21b60209182015281519101207fecbf2bb44d2c57a26a41a9482a9d5d8183468ac2d57a7b1c139bee60afea6dd51490565b15611099575f6110858e836108f4565b90506110918e82610911565b600184529750505b60a084901c6001600160501b031660a084901c6001600160501b031610156110cc576110c58d846108f4565b92506110d2565b506110d8565b50610eb8565b805180156110e7575080602001515b80156110f4575080604001515b98505050505061113c565b60a087901c6001600160501b031660a089901c6001600160501b031610156111325761112b89896108f4565b9750611136565b5f97505b50610dc6565b939792965093509350565b8051604051818001600282019081526f30313233343536373839616263646566600f52918301906022015b81841461119d57600184019350600f845116516001820153600f845160041c16518153600201611172565b5f815260200160405250919050565b606083518281116111bb578092505b8381116111c6578093505b508183101561071c5750604051828203848401601f19601f830181165b82810151858201528101806111e3575050508060208301015f81526020810160405250808252509392505050565b5f61071c83835f6117d2565b60605f61122a848461189c565b949350505050565b606061071c8383611944565b5f8080808561124e866001611f8e565b8151811061125e5761125e611fa1565b0160200151600160ff1b165f036112c8578561127b866001611f8e565b8151811061128b5761128b611fa1565b016020015160f81c92506112a0856002611f8e565b915060016112b7846001600160501b038516611f8e565b6112c19190611fe0565b90506113bb565b5f866112d5876001611f8e565b815181106112e5576112e5611fa1565b60209101015160f81c607f169050600181900361131b5761131161130a876002611f8e565b889061199f565b60ff16935061137d565b8060ff166002036113465761133b611334876002611f8e565b88906119c2565b61ffff16935061137d565b611351816020611ff3565b61135c906008612026565b60ff1661137861136d886002611f8e565b899060ff85166119fd565b901c93505b60ff811661138c876002611f8e565b6113969190611f8e565b925060016113ad856001600160501b038616611f8e565b6113b79190611fe0565b9150505b69ffffffffffffffffffff60a01b60a082901b1669ffffffffffffffffffff60501b605084901b168617175b9695505050505050565b82516060906114008385611f8e565b111561141f576040516343733a0960e11b815260040160405180910390fd5b5f8267ffffffffffffffff81111561143957611439611c1f565b6040519080825280601f01601f191660200182016040528015611463576020820181803683370190505b5090506020808201908686010161147b828287611a64565b509095945050505050565b5f806107b25b8861ffff168161ffff1610156114d8576114a581611a6d565b156114bf576114b86301e2850083611f8e565b91506114d0565b6114cd6301e1338083611f8e565b91505b60010161148c565b506040805161018081018252601f808252601c6020830152918101829052601e606082018190526080820183905260a0820181905260c0820183905260e082018390526101008201819052610120820183905261014082015261016081019190915261154389611a6d565b1561155057601d60208201525b60015b8860ff168160ff1610156115a9578161156d600183611ff3565b60ff16600c811061158057611580611fa1565b60200201516115959060ff1662015180612078565b61159f9084611f8e565b9250600101611553565b506115b5600188611ff3565b6115c59060ff1662015180612078565b6115cf9083611f8e565b91506115e060ff8716610e10612078565b6115ea9083611f8e565b91506115fa60ff8616603c612078565b6116049083611f8e565b9150610d6660ff851683611f8e565b5f826001600160501b0383168151811061162f5761162f611fa1565b6020910101516001600160f81b031916600160fa1b146108d85760405163a040ad4b60e01b815260040160405180910390fd5b5f8060608161167186866108f4565b90505f61167e8783610891565b604080516010808252610220820190925291925060208201610200803683370190505092505f5b6116b160106001611f8e565b8110156117c4575f6116c38984610891565b90505f6116d08a836108f4565b90505f6116dd8b83610911565b90505f60028251106116fa576116f28261208f565b60f01c611713565b6101006117068361208f565b60f01c61171391906120e1565b905061176f6117228d86610911565b60408051808201909152600b81526a2a864886f84d010d01021160a81b60209182015281519101207f4d427d13319aa428e67638dd0f7e7f1f22835710f9156ea643a24a02217799fa1490565b15611780578061ffff1698506117a9565b5f8161ffff1690508089878151811061179b5761179b611fa1565b602002602001018181525050505b6117b38c876108f4565b9550505050508060010190506116a5565b506001945050509250925092565b82515f199083516117f0578291508083111561189457809150611894565b83516020860184810196506001828483010301601f831660200360031b6020880151858810838b1016611827575050505050611894565b6020851061186c578460208a01205b818b5118831c6118535780868c200361185357848b039750611861565b60018b019a50838b10611836575b505050505050611894565b808a5118821c61188057838a03965061188e565b60018a019950828a1061186c575b50505050505b509392505050565b60605f6118a98484611ac9565b9050601f1960208201600183510160051b8101865183820152600184510184525f5b82516060845281811461190f5760405182820380825286601f8201165b8b8501810151838201528701806118e857505f82820160200152603f018616810160405284525b8751602094909401930190508183106118cb5750505050809150825161193d57602081019150600281510382525b5092915050565b6040518251601f19906020810182165b8581015184820152820180611954575083518184018360208301165b86810151828201528401806119705750505f910183810160208101929092528352604090810190525092915050565b5f8282815181106119b2576119b2611fa1565b016020015160f81c905092915050565b81515f906119d1836002611f8e565b11156119f05760405163653582fb60e01b815260040160405180910390fd5b50016002015161ffff1690565b5f6020821115611a205760405163cbb7a73160e01b815260040160405180910390fd5b8351611a2c8385611f8e565b1115611a4b57604051634e0a27d960e01b815260040160405180910390fd5b506020919092018101519190036101000a5f1901191690565b8082845e505050565b5f611a79600483612104565b61ffff1615611a8957505f919050565b611a94606483612104565b61ffff1615611aa557506001919050565b611ab161019083612104565b61ffff1615611ac157505f919050565b506001919050565b606081518351811161193d57604051915060208401602083016001838751840103015f60208510611afd5784602088012090505b6020870151601f861660200360031b5b8551828118821c611b66578315611b40578388882014611b4057600187019650848710611b3a5750611b75565b50611b0d565b60208b018703865260208601955087870196508715611b6657848710611b3a5750611b75565b50600186019550838610611b0d575b50505050601f198482030160051c8452602001604052505092915050565b6040518061010001604052805f81526020015f8152602001606081526020016060815260200160608152602001606081526020015f15158152602001611bd7611bdc565b905290565b60405180606001604052806060815260200160608152602001611bd7604051806080016040528060608152602001606081526020015f8152602001606081525090565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112611c42575f5ffd5b813567ffffffffffffffff811115611c5c57611c5c611c1f565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715611c8b57611c8b611c1f565b604052818152838201602001851015611ca2575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f5f60408385031215611ccf575f5ffd5b823567ffffffffffffffff811115611ce5575f5ffd5b611cf185828601611c33565b95602094909401359450505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f6040820184151583526040602084015280845180835260608501915060608160051b8601019250602086015f5b82811015611d8d57605f19878603018452611d78858351611d00565b94506020938401939190910190600101611d5c565b5092979650505050505050565b5f5f60408385031215611dab575f5ffd5b823567ffffffffffffffff811115611dc1575f5ffd5b611dcd85828601611c33565b92505060208301358015158114611de2575f5ffd5b809150509250929050565b5f815160608452611e016060850182611d00565b905060208301518482036020860152611e1a8282611d00565b91505060408301518482036040860152805160808352611e3d6080840182611d00565b905060208201518382036020850152611e568282611d00565b6040848101519086015260609384015185820394909501939093525050815180825260209182019291909101905f905b80821015611ea95782518452602084019350602083019250600182019150611e86565b509195945050505050565b82151581526040602082015281516040820152602082015160608201525f60408301516101006080840152611eed610140840182611d00565b90506060840151603f198483030160a0850152611f0a8282611d00565b9150506080840151603f198483030160c0850152611f288282611d00565b91505060a0840151603f198483030160e0850152611f468282611d00565b91505060c0840151611f5d61010085018215159052565b5060e0840151838203603f19016101208501526113e78282611ded565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561088b5761088b611f7a565b634e487b7160e01b5f52603260045260245ffd5b5f81518060208401855e5f93019283525090919050565b5f610719611fda8386611fb5565b84611fb5565b8181038181111561088b5761088b611f7a565b60ff828116828216039081111561088b5761088b611f7a565b61ffff818116838216019081111561088b5761088b611f7a565b60ff818116838216029081169081811461193d5761193d611f7a565b61ffff818116838216029081169081811461193d5761193d611f7a565b60ff818116838216019081111561088b5761088b611f7a565b808202811582820484141761088b5761088b611f7a565b805160208201516001600160f01b03198116919060028210156120c6576001600160f01b0319600283900360031b81901b82161692505b5050919050565b634e487b7160e01b5f52601260045260245ffd5b5f61ffff8316806120f4576120f46120cd565b8061ffff84160491505092915050565b5f61ffff831680612117576121176120cd565b8061ffff8416069150509291505056fea26469706673582212203e89e6c698226306fae87622615b58a291cb992afa4dbe56e23a350fb9e1da9a64736f6c634300081e0033