Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- PEMCertChainLib
- Optimization enabled
- true
- Compiler version
- v0.8.30+commit.73712a01
- Optimization runs
- 200
- EVM Version
- prague
- Verified at
- 2025-12-15T12:40:04.623793Z
contracts/layer1/automata-attestation/lib/PEMCertChainLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "../utils/Asn1Decode.sol";
import "../utils/BytesUtils.sol";
import "../utils/X509DateUtils.sol";
import "./interfaces/IPEMCertChainLib.sol";
import "solady/src/utils/LibString.sol";
/// @title PEMCertChainLib
/// @custom:security-contact security@taiko.xyz
contract PEMCertChainLib is IPEMCertChainLib {
using Asn1Decode for bytes;
using NodePtr for uint256;
using BytesUtils for bytes;
string internal constant HEADER = "-----BEGIN CERTIFICATE-----";
string internal constant FOOTER = "-----END CERTIFICATE-----";
uint256 internal constant HEADER_LENGTH = 27;
uint256 internal constant FOOTER_LENGTH = 25;
string internal constant PCK_COMMON_NAME = "Intel SGX PCK Certificate";
string internal constant PLATFORM_ISSUER_NAME = "Intel SGX PCK Platform CA";
string internal constant PROCESSOR_ISSUER_NAME = "Intel SGX PCK Processor CA";
bytes internal constant SGX_EXTENSION_OID = hex"2A864886F84D010D01";
bytes internal constant TCB_OID = hex"2A864886F84D010D0102";
bytes internal constant PCESVN_OID = hex"2A864886F84D010D010211";
bytes internal constant PCEID_OID = hex"2A864886F84D010D0103";
bytes internal constant FMSPC_OID = hex"2A864886F84D010D0104";
// https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/e7604e02331b3377f3766ed3653250e03af72d45/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/X509Constants.h#L64
uint256 constant SGX_TCB_CPUSVN_SIZE = 16;
struct PCKTCBFlags {
bool fmspcFound;
bool pceidFound;
bool tcbFound;
}
function splitCertificateChain(
bytes memory pemChain,
uint256 size
)
external
pure
returns (bool success, bytes[] memory certs)
{
certs = new bytes[](size);
string memory pemChainStr = string(pemChain);
uint256 index = 0;
uint256 len = pemChain.length;
for (uint256 i; i < size; ++i) {
string memory input;
if (i != 0) {
input = LibString.slice(pemChainStr, index, index + len);
} else {
input = pemChainStr;
}
uint256 increment;
(success, certs[i], increment) = _removeHeadersAndFooters(input);
if (!success) {
return (false, certs);
}
index += increment;
}
success = true;
}
function decodeCert(
bytes memory der,
bool isPckCert
)
external
pure
returns (bool success, ECSha256Certificate memory cert)
{
uint256 root = der.root();
// Entering tbsCertificate sequence
uint256 tbsParentPtr = der.firstChildOf(root);
// Begin iterating through the descendants of tbsCertificate
uint256 tbsPtr = der.firstChildOf(tbsParentPtr);
// The Serial Number is located one element below Version
// The issuer commonName value is contained in the Issuer sequence
// which is 3 elements below the first element of the tbsCertificate sequence
// The Validity sequence is located 4 elements below the first element of the tbsCertificate
// sequence
// The subject commonName value is contained in the Subject sequence
// which is 5 elements below the first element of the tbsCertificate sequence
// The PublicKey is located in the second element of subjectPublicKeyInfo sequence
// which is 6 elements below the first element of the tbsCertificate sequence
tbsPtr = der.nextSiblingOf(tbsPtr);
{
bytes memory serialNumBytes = der.bytesAt(tbsPtr);
cert.serialNumber = serialNumBytes;
}
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
if (isPckCert) {
uint256 issuerPtr = der.firstChildOf(tbsPtr);
issuerPtr = der.firstChildOf(issuerPtr);
issuerPtr = der.firstChildOf(issuerPtr);
issuerPtr = der.nextSiblingOf(issuerPtr);
cert.pck.issuerName = string(der.bytesAt(issuerPtr));
bool issuerNameIsValid = LibString.eq(cert.pck.issuerName, PLATFORM_ISSUER_NAME)
|| LibString.eq(cert.pck.issuerName, PROCESSOR_ISSUER_NAME);
if (!issuerNameIsValid) {
return (false, cert);
}
}
tbsPtr = der.nextSiblingOf(tbsPtr);
{
uint256 notBeforePtr = der.firstChildOf(tbsPtr);
uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);
bytes1 notBeforeTag = der[notBeforePtr.ixs()];
bytes1 notAfterTag = der[notAfterPtr.ixs()];
if (
(notBeforeTag != 0x17 && notBeforeTag != 0x18)
|| (notAfterTag != 0x17 && notAfterTag != 0x18)
) {
return (false, cert);
}
cert.notBefore = X509DateUtils.toTimestamp(der.bytesAt(notBeforePtr));
cert.notAfter = X509DateUtils.toTimestamp(der.bytesAt(notAfterPtr));
}
tbsPtr = der.nextSiblingOf(tbsPtr);
if (isPckCert) {
uint256 subjectPtr = der.firstChildOf(tbsPtr);
subjectPtr = der.firstChildOf(subjectPtr);
subjectPtr = der.firstChildOf(subjectPtr);
subjectPtr = der.nextSiblingOf(subjectPtr);
cert.pck.commonName = string(der.bytesAt(subjectPtr));
if (!LibString.eq(cert.pck.commonName, PCK_COMMON_NAME)) {
return (false, cert);
}
}
tbsPtr = der.nextSiblingOf(tbsPtr);
{
// Entering subjectPublicKeyInfo sequence
uint256 subjectPublicKeyInfoPtr = der.firstChildOf(tbsPtr);
subjectPublicKeyInfoPtr = der.nextSiblingOf(subjectPublicKeyInfoPtr);
// The Signature sequence is located two sibling elements below the tbsCertificate
// element
uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);
sigPtr = der.nextSiblingOf(sigPtr);
// Skip three bytes to the right
// the three bytes in question: 0x034700 or 0x034800 or 0x034900
sigPtr = NodePtr.getPtr(sigPtr.ixs() + 3, sigPtr.ixf() + 3, sigPtr.ixl());
sigPtr = der.firstChildOf(sigPtr);
bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);
sigPtr = der.nextSiblingOf(sigPtr);
bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);
cert.tbsCertificate = der.allBytesAt(tbsParentPtr);
cert.pubKey = _trimBytes(der.bytesAt(subjectPublicKeyInfoPtr), 64);
cert.signature = abi.encodePacked(sigX, sigY);
}
if (isPckCert) {
// entering Extension sequence
tbsPtr = der.nextSiblingOf(tbsPtr);
// check for the extension tag
if (der[tbsPtr.ixs()] != 0xA3) {
return (false, cert);
}
tbsPtr = der.firstChildOf(tbsPtr);
tbsPtr = der.firstChildOf(tbsPtr);
bool sgxExtnTraversedSuccessfully;
uint256 pcesvn;
uint256[] memory cpuSvns;
bytes memory fmspcBytes;
bytes memory pceidBytes;
(sgxExtnTraversedSuccessfully, pcesvn, cpuSvns, fmspcBytes, pceidBytes) =
_findPckTcbInfo(der, tbsPtr, tbsParentPtr);
if (!sgxExtnTraversedSuccessfully) {
return (false, cert);
}
cert.pck.sgxExtension.pcesvn = pcesvn;
cert.pck.sgxExtension.sgxTcbCompSvnArr = cpuSvns;
cert.pck.sgxExtension.pceid = LibString.toHexStringNoPrefix(pceidBytes);
cert.pck.sgxExtension.fmspc = LibString.toHexStringNoPrefix(fmspcBytes);
cert.isPck = true;
}
success = true;
}
function _removeHeadersAndFooters(string memory pemData)
private
pure
returns (bool success, bytes memory extracted, uint256 endIndex)
{
// Check if the input contains the "BEGIN" and "END" headers
uint256 beginPos = LibString.indexOf(pemData, HEADER);
uint256 endPos = LibString.indexOf(pemData, FOOTER);
bool headerFound = beginPos != LibString.NOT_FOUND;
bool footerFound = endPos != LibString.NOT_FOUND;
if (!headerFound || !footerFound) {
return (false, extracted, endIndex);
}
// Extract the content between the headers
uint256 contentStart = beginPos + HEADER_LENGTH;
// Extract and return the content
bytes memory contentBytes;
// do not include newline
bytes memory delimiter = hex"0a";
string memory contentSlice = LibString.slice(pemData, contentStart, endPos);
string[] memory split = LibString.split(contentSlice, string(delimiter));
string memory contentStr;
uint256 size = split.length;
for (uint256 i; i < size; ++i) {
contentStr = LibString.concat(contentStr, split[i]);
}
contentBytes = bytes(contentStr);
return (true, contentBytes, endPos + FOOTER_LENGTH);
}
function _trimBytes(
bytes memory input,
uint256 expectedLength
)
private
pure
returns (bytes memory output)
{
uint256 n = input.length;
if (n <= expectedLength) {
return input;
}
uint256 lengthDiff = n - expectedLength;
output = input.substring(lengthDiff, expectedLength);
}
function _findPckTcbInfo(
bytes memory der,
uint256 tbsPtr,
uint256 tbsParentPtr
)
private
pure
returns (
bool success,
uint256 pcesvn,
uint256[] memory cpusvns,
bytes memory fmspcBytes,
bytes memory pceidBytes
)
{
// iterate through the elements in the Extension sequence
// until we locate the SGX Extension OID
while (tbsPtr != 0) {
uint256 internalPtr = der.firstChildOf(tbsPtr);
if (der[internalPtr.ixs()] != 0x06) {
return (false, pcesvn, cpusvns, fmspcBytes, pceidBytes);
}
if (BytesUtils.compareBytes(der.bytesAt(internalPtr), SGX_EXTENSION_OID)) {
// 1.2.840.113741.1.13.1
internalPtr = der.nextSiblingOf(internalPtr);
uint256 extnValueParentPtr = der.rootOfOctetStringAt(internalPtr);
uint256 extnValuePtr = der.firstChildOf(extnValueParentPtr);
// Copy flags to memory to avoid stack too deep
PCKTCBFlags memory flags;
while (!(flags.fmspcFound && flags.pceidFound && flags.tcbFound)) {
uint256 extnValueOidPtr = der.firstChildOf(extnValuePtr);
if (der[extnValueOidPtr.ixs()] != 0x06) {
return (false, pcesvn, cpusvns, fmspcBytes, pceidBytes);
}
if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), TCB_OID)) {
// 1.2.840.113741.1.13.1.2
(flags.tcbFound, pcesvn, cpusvns) = _findTcb(der, extnValueOidPtr);
}
if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), PCEID_OID)) {
// 1.2.840.113741.1.13.1.3
uint256 pceidPtr = der.nextSiblingOf(extnValueOidPtr);
pceidBytes = der.bytesAt(pceidPtr);
flags.pceidFound = true;
}
if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), FMSPC_OID)) {
// 1.2.840.113741.1.13.1.4
uint256 fmspcPtr = der.nextSiblingOf(extnValueOidPtr);
fmspcBytes = der.bytesAt(fmspcPtr);
flags.fmspcFound = true;
}
if (extnValuePtr.ixl() < extnValueParentPtr.ixl()) {
extnValuePtr = der.nextSiblingOf(extnValuePtr);
} else {
break;
}
}
success = flags.fmspcFound && flags.pceidFound && flags.tcbFound;
break;
}
if (tbsPtr.ixl() < tbsParentPtr.ixl()) {
tbsPtr = der.nextSiblingOf(tbsPtr);
} else {
tbsPtr = 0; // exit
}
}
}
function _findTcb(
bytes memory der,
uint256 oidPtr
)
private
pure
returns (bool success, uint256 pcesvn, uint256[] memory cpusvns)
{
// sibling of tcbOid
uint256 tcbPtr = der.nextSiblingOf(oidPtr);
// get the first svn object in the sequence
uint256 svnParentPtr = der.firstChildOf(tcbPtr);
cpusvns = new uint256[](SGX_TCB_CPUSVN_SIZE);
for (uint256 i; i < SGX_TCB_CPUSVN_SIZE + 1; ++i) {
uint256 svnPtr = der.firstChildOf(svnParentPtr); // OID
uint256 svnValuePtr = der.nextSiblingOf(svnPtr); // value
bytes memory svnValueBytes = der.bytesAt(svnValuePtr);
uint16 svnValue = svnValueBytes.length < 2
? uint16(bytes2(svnValueBytes)) / 256
: uint16(bytes2(svnValueBytes));
if (BytesUtils.compareBytes(der.bytesAt(svnPtr), PCESVN_OID)) {
// pcesvn is 4 bytes in size
pcesvn = uint256(svnValue);
} else {
// each cpusvn is at maximum two bytes in size
uint256 cpusvn = uint256(svnValue);
cpusvns[i] = cpusvn;
}
// iterate to the next svn object in the sequence
svnParentPtr = der.nextSiblingOf(svnParentPtr);
}
success = true;
}
}
contracts/layer1/automata-attestation/lib/interfaces/IPEMCertChainLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title IPEMCertChainLib
/// @custom:security-contact security@taiko.xyz
interface IPEMCertChainLib {
struct ECSha256Certificate {
uint256 notBefore;
uint256 notAfter;
bytes serialNumber;
bytes tbsCertificate;
bytes pubKey;
bytes signature;
bool isPck;
PCKCertificateField pck;
}
struct PCKCertificateField {
string commonName;
string issuerName;
PCKTCBInfo sgxExtension;
}
struct PCKTCBInfo {
string pceid;
string fmspc;
uint256 pcesvn;
uint256[] sgxTcbCompSvnArr;
}
enum CRL {
PCK,
ROOT
}
function splitCertificateChain(
bytes memory pemChain,
uint256 size
)
external
pure
returns (bool success, bytes[] memory certs);
function decodeCert(
bytes memory der,
bool isPckCert
)
external
pure
returns (bool success, ECSha256Certificate memory cert);
}
contracts/layer1/automata-attestation/utils/Asn1Decode.sol
// SPDX-License-Identifier: MIT
// Original source: https://github.com/JonahGroendal/asn1-decode
pragma solidity ^0.8.24;
// Inspired by PufferFinance/rave - Apache-2.0 license
// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol
import "./BytesUtils.sol";
/// @title NodePtr
/// @custom:security-contact security@taiko.xyz
library NodePtr {
// Unpack first byte index
function ixs(uint256 self) internal pure returns (uint256) {
return uint80(self);
}
// Unpack first content byte index
function ixf(uint256 self) internal pure returns (uint256) {
return uint80(self >> 80);
}
// Unpack last content byte index
function ixl(uint256 self) internal pure returns (uint256) {
return uint80(self >> 160);
}
// Pack 3 uint80s into a uint256
function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {
_ixs |= _ixf << 80;
_ixs |= _ixl << 160;
return _ixs;
}
}
/// @title Asn1Decode
/// @custom:security-contact security@taiko.xyz
library Asn1Decode {
using NodePtr for uint256;
using BytesUtils for bytes;
error NOT_TYPE_OCTET_STRING();
error NOT_A_CONSTRUCTED_TYPE();
/*
* @dev Get the root node. First step in traversing an ASN1 structure
* @param der The DER-encoded ASN1 structure
* @return A pointer to the outermost node
*/
function root(bytes memory der) internal pure returns (uint256) {
return _readNodeLength(der, 0);
}
/*
* @dev Get the root node of an ASN1 structure that's within an octet string value
* @param der The DER-encoded ASN1 structure
* @return A pointer to the outermost node
*/
function rootOfOctetStringAt(
bytes memory der,
uint256 ptr
)
internal
pure
returns (uint256)
{
require(der[ptr.ixs()] == 0x04, NOT_TYPE_OCTET_STRING());
return _readNodeLength(der, ptr.ixf());
}
/*
* @dev Get the next sibling node
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return A pointer to the next sibling node
*/
function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {
return _readNodeLength(der, ptr.ixl() + 1);
}
/*
* @dev Get the first child node of the current node
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return A pointer to the first child node
*/
function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {
require(der[ptr.ixs()] & 0x20 == 0x20, NOT_A_CONSTRUCTED_TYPE());
return _readNodeLength(der, ptr.ixf());
}
/*
* @dev Extract value of node from DER-encoded structure
* @param der The der-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return Value bytes of node
*/
function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {
return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());
}
/*
* @dev Extract entire node from DER-encoded structure
* @param der The DER-encoded ASN1 structure
* @param ptr Points to the indices of the current node
* @return All bytes of node
*/
function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {
return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());
}
function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {
return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());
}
function keccakOfAllBytesAt(
bytes memory der,
uint256 ptr
)
internal
pure
returns (bytes32)
{
return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());
}
function _readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {
uint256 length;
uint80 ixFirstContentByte;
uint80 ixLastContentByte;
if ((der[ix + 1] & 0x80) == 0) {
length = uint8(der[ix + 1]);
ixFirstContentByte = uint80(ix + 2);
ixLastContentByte = uint80(ixFirstContentByte + length - 1);
} else {
uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);
if (lengthbytesLength == 1) {
length = der.readUint8(ix + 2);
} else if (lengthbytesLength == 2) {
length = der.readUint16(ix + 2);
} else {
length = uint256(
der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8
);
}
ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);
ixLastContentByte = uint80(ixFirstContentByte + length - 1);
}
return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);
}
}
contracts/layer1/automata-attestation/utils/BytesUtils.sol
// SPDX-License-Identifier: BSD 2-Clause License
pragma solidity ^0.8.24;
// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license
// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol
/// @title BytesUtils
/// @custom:security-contact security@taiko.xyz
library BytesUtils {
error INVALID_OFFSET();
error INVALID_IDX();
error UNEXPECTED_LEN();
error UNEXPECTED_IDX();
error UNEXPECTED_OFFSET();
/*
* @dev Returns the keccak-256 hash of a byte range.
* @param self The byte string to hash.
* @param offset The position to start hashing at.
* @param len The number of bytes to hash.
* @return The hash of the byte range.
*/
function keccak(
bytes memory self,
uint256 offset,
uint256 len
)
internal
pure
returns (bytes32 ret)
{
require(offset + len <= self.length, INVALID_OFFSET());
assembly {
ret := keccak256(add(add(self, 32), offset), len)
}
}
/*
* @dev Returns true if the two byte ranges are equal.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @param otherOffset The offset into the second byte range.
* @param len The number of bytes to compare
* @return true if the byte ranges are equal, false otherwise.
*/
function equals(
bytes memory self,
uint256 offset,
bytes memory other,
uint256 otherOffset,
uint256 len
)
internal
pure
returns (bool)
{
return keccak(self, offset, len) == keccak(other, otherOffset, len);
}
/*
* @dev Returns the 8-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 8 bits of the string, interpreted as an integer.
*/
function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {
return uint8(self[idx]);
}
/*
* @dev Returns the 16-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 16 bits of the string, interpreted as an integer.
*/
function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {
require(idx + 2 <= self.length, INVALID_IDX());
assembly {
ret := and(mload(add(add(self, 2), idx)), 0xFFFF)
}
}
/*
* @dev Returns the n byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes.
* @param len The number of bytes.
* @return The specified 32 bytes of the string.
*/
function readBytesN(
bytes memory self,
uint256 idx,
uint256 len
)
internal
pure
returns (bytes32 ret)
{
require(len <= 32, UNEXPECTED_LEN());
require(idx + len <= self.length, UNEXPECTED_IDX());
assembly {
let mask := not(sub(exp(256, sub(32, len)), 1))
ret := and(mload(add(add(self, 32), idx)), mask)
}
}
function memcpy(uint256 dest, uint256 src, uint256 len) private pure {
assembly {
mcopy(dest, src, len)
}
}
/*
* @dev Copies a substring into a new byte string.
* @param self The byte string to copy from.
* @param offset The offset to start copying at.
* @param len The number of bytes to copy.
*/
function substring(
bytes memory self,
uint256 offset,
uint256 len
)
internal
pure
returns (bytes memory)
{
require(offset + len <= self.length, UNEXPECTED_OFFSET());
bytes memory ret = new bytes(len);
uint256 dest;
uint256 src;
assembly {
dest := add(ret, 32)
src := add(add(self, 32), offset)
}
memcpy(dest, src, len);
return ret;
}
function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {
return keccak256(a) == keccak256(b);
}
}
contracts/layer1/automata-attestation/utils/X509DateUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title X509DateUtils
/// @custom:security-contact security@taiko.xyz
library X509DateUtils {
function toTimestamp(bytes memory x509Time) internal pure returns (uint256) {
uint16 yrs;
uint8 mnths;
uint8 dys;
uint8 hrs;
uint8 mins;
uint8 secs;
uint8 offset;
if (x509Time.length == 13) {
if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;
else yrs += 1900;
} else {
yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;
offset = 2;
}
yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;
mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;
dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;
hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;
mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;
secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;
return toUnixTimestamp(yrs, mnths, dys, hrs, mins, secs);
}
function toUnixTimestamp(
uint16 year,
uint8 month,
uint8 day,
uint8 hour,
uint8 minute,
uint8 second
)
internal
pure
returns (uint256)
{
uint256 timestamp = 0;
for (uint16 i = 1970; i < year; ++i) {
if (isLeapYear(i)) {
timestamp += 31_622_400; // Leap year in seconds
} else {
timestamp += 31_536_000; // Normal year in seconds
}
}
uint8[12] memory monthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if (isLeapYear(year)) monthDays[1] = 29;
for (uint8 i = 1; i < month; ++i) {
timestamp += uint256(monthDays[i - 1]) * 86_400; // Days in seconds
}
timestamp += uint256(day - 1) * 86_400; // Days in seconds
timestamp += uint256(hour) * 3600; // Hours in seconds
timestamp += uint256(minute) * 60; // Minutes in seconds
timestamp += second;
return timestamp;
}
function isLeapYear(uint16 year) internal pure returns (bool) {
if (year % 4 != 0) return false;
if (year % 100 != 0) return true;
if (year % 400 != 0) return false;
return true;
}
}
node_modules/solady/src/utils/LibBytes.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for byte related operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol)
library LibBytes {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Goated bytes storage struct that totally MOGs, no cap, fr.
/// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af.
/// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
struct BytesStorage {
bytes32 _spacer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the bytes.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sets the value of the bytes storage `$` to `s`.
function set(BytesStorage storage $, bytes memory s) internal {
/// @solidity memory-safe-assembly
assembly {
let n := mload(s)
let packed := or(0xff, shl(8, n))
for { let i := 0 } 1 {} {
if iszero(gt(n, 0xfe)) {
i := 0x1f
packed := or(n, shl(8, mload(add(s, i))))
if iszero(gt(n, i)) { break }
}
let o := add(s, 0x20)
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
sstore(add(p, shr(5, i)), mload(add(o, i)))
i := add(i, 0x20)
if iszero(lt(i, n)) { break }
}
break
}
sstore($.slot, packed)
}
}
/// @dev Sets the value of the bytes storage `$` to `s`.
function setCalldata(BytesStorage storage $, bytes calldata s) internal {
/// @solidity memory-safe-assembly
assembly {
let packed := or(0xff, shl(8, s.length))
for { let i := 0 } 1 {} {
if iszero(gt(s.length, 0xfe)) {
i := 0x1f
packed := or(s.length, shl(8, shr(8, calldataload(s.offset))))
if iszero(gt(s.length, i)) { break }
}
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
sstore(add(p, shr(5, i)), calldataload(add(s.offset, i)))
i := add(i, 0x20)
if iszero(lt(i, s.length)) { break }
}
break
}
sstore($.slot, packed)
}
}
/// @dev Sets the value of the bytes storage `$` to the empty bytes.
function clear(BytesStorage storage $) internal {
delete $._spacer;
}
/// @dev Returns whether the value stored is `$` is the empty bytes "".
function isEmpty(BytesStorage storage $) internal view returns (bool) {
return uint256($._spacer) & 0xff == uint256(0);
}
/// @dev Returns the length of the value stored in `$`.
function length(BytesStorage storage $) internal view returns (uint256 result) {
result = uint256($._spacer);
/// @solidity memory-safe-assembly
assembly {
let n := and(0xff, result)
result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n))))
}
}
/// @dev Returns the value stored in `$`.
function get(BytesStorage storage $) internal view returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let o := add(result, 0x20)
let packed := sload($.slot)
let n := shr(8, packed)
for { let i := 0 } 1 {} {
if iszero(eq(or(packed, 0xff), packed)) {
mstore(o, packed)
n := and(0xff, packed)
i := 0x1f
if iszero(gt(n, i)) { break }
}
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
mstore(add(o, i), sload(add(p, shr(5, i))))
i := add(i, 0x20)
if iszero(lt(i, n)) { break }
}
break
}
mstore(result, n) // Store the length of the memory.
mstore(add(o, n), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(o, n), 0x20)) // Allocate memory.
}
}
/// @dev Returns the uint8 at index `i`. If out-of-bounds, returns 0.
function uint8At(BytesStorage storage $, uint256 i) internal view returns (uint8 result) {
/// @solidity memory-safe-assembly
assembly {
for { let packed := sload($.slot) } 1 {} {
if iszero(eq(or(packed, 0xff), packed)) {
if iszero(gt(i, 0x1e)) {
result := byte(i, packed)
break
}
if iszero(gt(i, and(0xff, packed))) {
mstore(0x00, $.slot)
let j := sub(i, 0x1f)
result := byte(and(j, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, j))))
}
break
}
if iszero(gt(i, shr(8, packed))) {
mstore(0x00, $.slot)
result := byte(and(i, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, i))))
}
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTES OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
function replace(bytes memory subject, bytes memory needle, bytes memory replacement)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let needleLen := mload(needle)
let replacementLen := mload(replacement)
let d := sub(result, subject) // Memory difference.
let i := add(subject, 0x20) // Subject bytes pointer.
mstore(0x00, add(i, mload(subject))) // End of subject.
if iszero(gt(needleLen, mload(subject))) {
let subjectSearchEnd := add(sub(mload(0x00), needleLen), 1)
let h := 0 // The hash of `needle`.
if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
let s := mload(add(needle, 0x20))
for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
let t := mload(i)
// Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(i, needleLen), h)) {
mstore(add(i, d), t)
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let j := 0 } 1 {} {
mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
j := add(j, 0x20)
if iszero(lt(j, replacementLen)) { break }
}
d := sub(add(d, replacementLen), needleLen)
if needleLen {
i := add(i, needleLen)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
mstore(add(i, d), t)
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
}
}
let end := mload(0x00)
let n := add(sub(d, add(result, 0x20)), end)
// Copy the rest of the bytes one word at a time.
for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
let o := add(i, d)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(bytes memory subject, bytes memory needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0) // Initialize to `NOT_FOUND`.
for { let subjectLen := mload(subject) } 1 {} {
if iszero(mload(needle)) {
result := from
if iszero(gt(from, subjectLen)) { break }
result := subjectLen
break
}
let needleLen := mload(needle)
let subjectStart := add(subject, 0x20)
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
let s := mload(add(needle, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLen))) { break }
if iszero(lt(needleLen, 0x20)) {
for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, needleLen), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`. Optimized for byte needles.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOfByte(bytes memory subject, bytes1 needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0) // Initialize to `NOT_FOUND`.
if gt(mload(subject), from) {
let start := add(subject, 0x20)
let end := add(start, mload(subject))
let m := div(not(0), 255) // `0x0101 ... `.
let h := mul(byte(0, needle), m) // Replicating needle mask.
m := not(shl(7, m)) // `0x7f7f ... `.
for { let i := add(start, from) } 1 {} {
let c := xor(mload(i), h) // Load 32-byte chunk and xor with mask.
c := not(or(or(add(and(c, m), m), c), m)) // Each needle byte will be `0x80`.
if c {
c := and(not(shr(shl(3, sub(end, i)), not(0))), c) // Truncate bytes past the end.
if c {
let r := shl(7, lt(0x8421084210842108cc6318c6db6d54be, c)) // Save bytecode.
r := or(shl(6, lt(0xffffffffffffffff, shr(r, c))), r)
// forgefmt: disable-next-item
result := add(sub(i, start), shr(3, xor(byte(and(0x1f, shr(byte(24,
mul(0x02040810204081, shr(r, c))), 0x8421084210842108cc6318c6db6d54be)),
0xc0c8c8d0c8e8d0d8c8e8e0e8d0d8e0f0c8d0e8d0e0e0d8f0d0d0e0d8f8f8f8f8), r)))
break
}
}
i := add(i, 0x20)
if iszero(lt(i, end)) { break }
}
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right. Optimized for byte needles.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOfByte(bytes memory subject, bytes1 needle)
internal
pure
returns (uint256 result)
{
return indexOfByte(subject, needle, 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) {
return indexOf(subject, needle, 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(bytes memory subject, bytes memory needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let needleLen := mload(needle)
if gt(needleLen, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), needleLen)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
if eq(keccak256(subject, needleLen), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(bytes memory subject, bytes memory needle)
internal
pure
returns (uint256)
{
return lastIndexOf(subject, needle, type(uint256).max);
}
/// @dev Returns true if `needle` is found in `subject`, false otherwise.
function contains(bytes memory subject, bytes memory needle) internal pure returns (bool) {
return indexOf(subject, needle) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `needle`.
function startsWith(bytes memory subject, bytes memory needle)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(needle)
// Just using keccak256 directly is actually cheaper.
let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n))
result := lt(gt(n, mload(subject)), t)
}
}
/// @dev Returns whether `subject` ends with `needle`.
function endsWith(bytes memory subject, bytes memory needle)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(needle)
let notInRange := gt(n, mload(subject))
// `subject + 0x20 + max(subject.length - needle.length, 0)`.
let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n)))
// Just using keccak256 directly is actually cheaper.
result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(bytes memory subject, uint256 times)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
let l := mload(subject) // Subject length.
if iszero(or(iszero(times), iszero(l))) {
result := mload(0x40)
subject := add(subject, 0x20)
let o := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let j := 0 } 1 {} {
mstore(add(o, j), mload(add(subject, j)))
j := add(j, 0x20)
if iszero(lt(j, l)) { break }
}
o := add(o, l)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(bytes memory subject, uint256 start, uint256 end)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
let l := mload(subject) // Subject length.
if iszero(gt(l, end)) { end := l }
if iszero(gt(l, start)) { start := l }
if lt(start, end) {
result := mload(0x40)
let n := sub(end, start)
let i := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let j := and(add(n, 0x1f), w) } 1 {} {
mstore(add(result, j), mload(add(i, j)))
j := add(j, w) // `sub(j, 0x20)`.
if iszero(j) { break }
}
let o := add(add(result, 0x20), n)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, n) // Store the length.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset.
function slice(bytes memory subject, uint256 start)
internal
pure
returns (bytes memory result)
{
result = slice(subject, start, type(uint256).max);
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start, uint256 end)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, end), sub(end, start))
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, subject.length), sub(subject.length, start))
}
}
/// @dev Reduces the size of `subject` to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncate(bytes memory subject, uint256 n)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := subject
mstore(mul(lt(n, mload(result)), result), n)
}
}
/// @dev Returns a copy of `subject`, with the length reduced to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncatedCalldata(bytes calldata subject, uint256 n)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
result.offset := subject.offset
result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n)))
}
}
/// @dev Returns all the indices of `needle` in `subject`.
/// The indices are byte offsets.
function indicesOf(bytes memory subject, bytes memory needle)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLen := mload(needle)
if iszero(gt(searchLen, mload(subject))) {
result := mload(0x40)
let i := add(subject, 0x20)
let o := add(result, 0x20)
let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
let h := 0 // The hash of `needle`.
if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
let s := mload(add(needle, 0x20))
for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
let t := mload(i)
// Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(i, searchLen), h)) {
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
o := add(o, 0x20)
i := add(i, searchLen) // Advance `i` by `searchLen`.
if searchLen {
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
}
mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(o, 0x20))
}
}
}
/// @dev Returns an arrays of bytess based on the `delimiter` inside of the `subject` bytes.
function split(bytes memory subject, bytes memory delimiter)
internal
pure
returns (bytes[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
for { let prevIndex := 0 } 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let l := sub(index, prevIndex)
mstore(element, l) // Store the length of the element.
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(l, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the bytes.
// Allocate memory for the length and the bytes, rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(l, 0x3f), w)))
mstore(indexPtr, element) // Store the `element` into the array.
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated bytes of `a` and `b`.
/// Cheaper than `bytes.concat()` and does not de-align the free memory pointer.
function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let w := not(0x1f)
let aLen := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLen, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLen := mload(b)
let output := add(result, aLen)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLen, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLen := add(aLen, bLen)
let last := add(add(result, 0x20), totalLen)
mstore(last, 0) // Zeroize the slot after the bytes.
mstore(result, totalLen) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate memory.
}
}
/// @dev Returns whether `a` equals `b`.
function eq(bytes memory a, bytes memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small bytes.
function eqs(bytes memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`.
/// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1.
function cmp(bytes memory a, bytes memory b) internal pure returns (int256 result) {
/// @solidity memory-safe-assembly
assembly {
let aLen := mload(a)
let bLen := mload(b)
let n := and(xor(aLen, mul(xor(aLen, bLen), lt(bLen, aLen))), not(0x1f))
if n {
for { let i := 0x20 } 1 {} {
let x := mload(add(a, i))
let y := mload(add(b, i))
if iszero(or(xor(x, y), eq(i, n))) {
i := add(i, 0x20)
continue
}
result := sub(gt(x, y), lt(x, y))
break
}
}
// forgefmt: disable-next-item
if iszero(result) {
let l := 0x201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201
let x := and(mload(add(add(a, 0x20), n)), shl(shl(3, byte(sub(aLen, n), l)), not(0)))
let y := and(mload(add(add(b, 0x20), n)), shl(shl(3, byte(sub(bLen, n), l)), not(0)))
result := sub(gt(x, y), lt(x, y))
if iszero(result) { result := sub(gt(aLen, bLen), lt(aLen, bLen)) }
}
}
}
/// @dev Directly returns `a` without copying.
function directReturn(bytes memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
// Assumes that the bytes does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the bytes is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20) // Store the return offset.
// End the transaction, returning the bytes.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
/// @dev Directly returns `a` with minimal copying.
function directReturn(bytes[] memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
let n := mload(a) // `a.length`.
let o := add(a, 0x20) // Start of elements in `a`.
let u := a // Highest memory slot.
let w := not(0x1f)
for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } {
let c := add(o, shl(5, i)) // Location of pointer to `a[i]`.
let s := mload(c) // `a[i]`.
let l := mload(s) // `a[i].length`.
let r := and(l, 0x1f) // `a[i].length % 32`.
let z := add(0x20, and(l, w)) // Offset of last word in `a[i]` from `s`.
// If `s` comes before `o`, or `s` is not zero right padded.
if iszero(lt(lt(s, o), or(iszero(r), iszero(shl(shl(3, r), mload(add(s, z))))))) {
let m := mload(0x40)
mstore(m, l) // Copy `a[i].length`.
for {} 1 {} {
mstore(add(m, z), mload(add(s, z))) // Copy `a[i]`, backwards.
z := add(z, w) // `sub(z, 0x20)`.
if iszero(z) { break }
}
let e := add(add(m, 0x20), l)
mstore(e, 0) // Zeroize the slot after the copied bytes.
mstore(0x40, add(e, 0x20)) // Allocate memory.
s := m
}
mstore(c, sub(s, o)) // Convert to calldata offset.
let t := add(l, add(s, 0x20))
if iszero(lt(t, u)) { u := t }
}
let retStart := add(a, w) // Assumes `a` doesn't start from scratch space.
mstore(retStart, 0x20) // Store the return offset.
return(retStart, add(0x40, sub(u, retStart))) // End the transaction.
}
}
/// @dev Returns the word at `offset`, without any bounds checks.
function load(bytes memory a, uint256 offset) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(add(add(a, 0x20), offset))
}
}
/// @dev Returns the word at `offset`, without any bounds checks.
function loadCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
result := calldataload(add(a.offset, offset))
}
}
/// @dev Returns a slice representing a static struct in the calldata. Performs bounds checks.
function staticStructInCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
let l := sub(a.length, 0x20)
result.offset := add(a.offset, offset)
result.length := sub(a.length, offset)
if or(shr(64, or(l, a.offset)), gt(offset, l)) { revert(l, 0x00) }
}
}
/// @dev Returns a slice representing a dynamic struct in the calldata. Performs bounds checks.
function dynamicStructInCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
let l := sub(a.length, 0x20)
let s := calldataload(add(a.offset, offset)) // Relative offset of `result` from `a.offset`.
result.offset := add(a.offset, s)
result.length := sub(a.length, s)
if or(shr(64, or(s, or(l, a.offset))), gt(offset, l)) { revert(l, 0x00) }
}
}
/// @dev Returns bytes in calldata. Performs bounds checks.
function bytesInCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
let l := sub(a.length, 0x20)
let s := calldataload(add(a.offset, offset)) // Relative offset of `result` from `a.offset`.
result.offset := add(add(a.offset, s), 0x20)
result.length := calldataload(add(a.offset, s))
// forgefmt: disable-next-item
if or(shr(64, or(result.length, or(s, or(l, a.offset)))),
or(gt(add(s, result.length), l), gt(offset, l))) { revert(l, 0x00) }
}
}
/// @dev Returns empty calldata bytes. For silencing the compiler.
function emptyCalldata() internal pure returns (bytes calldata result) {
/// @solidity memory-safe-assembly
assembly {
result.length := 0
}
}
}
node_modules/solady/src/utils/LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {LibBytes} from "./LibBytes.sol";
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Goated string storage struct that totally MOGs, no cap, fr.
/// Uses less gas and bytecode than Solidity's native string storage. It's meta af.
/// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
struct StringStorage {
bytes32 _spacer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/// @dev The input string must be a 7-bit ASCII.
error StringNot7BitASCII();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
/// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
/// @dev Lookup for '0123456789'.
uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
/// @dev Lookup for '0123456789abcdefABCDEF'.
uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
/// @dev Lookup for '01234567'.
uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
/// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRING STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sets the value of the string storage `$` to `s`.
function set(StringStorage storage $, string memory s) internal {
LibBytes.set(bytesStorage($), bytes(s));
}
/// @dev Sets the value of the string storage `$` to `s`.
function setCalldata(StringStorage storage $, string calldata s) internal {
LibBytes.setCalldata(bytesStorage($), bytes(s));
}
/// @dev Sets the value of the string storage `$` to the empty string.
function clear(StringStorage storage $) internal {
delete $._spacer;
}
/// @dev Returns whether the value stored is `$` is the empty string "".
function isEmpty(StringStorage storage $) internal view returns (bool) {
return uint256($._spacer) & 0xff == uint256(0);
}
/// @dev Returns the length of the value stored in `$`.
function length(StringStorage storage $) internal view returns (uint256) {
return LibBytes.length(bytesStorage($));
}
/// @dev Returns the value stored in `$`.
function get(StringStorage storage $) internal view returns (string memory) {
return string(LibBytes.get(bytesStorage($)));
}
/// @dev Returns the uint8 at index `i`. If out-of-bounds, returns 0.
function uint8At(StringStorage storage $, uint256 i) internal view returns (uint8) {
return LibBytes.uint8At(bytesStorage($), i);
}
/// @dev Helper to cast `$` to a `BytesStorage`.
function bytesStorage(StringStorage storage $)
internal
pure
returns (LibBytes.BytesStorage storage casted)
{
/// @solidity memory-safe-assembly
assembly {
casted.slot := $.slot
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
result := add(mload(0x40), 0x80)
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end of the memory to calculate the length later.
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
result := add(result, w) // `sub(result, 1)`.
// Store the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(result, add(48, mod(temp, 10)))
temp := div(temp, 10) // Keep dividing `temp` until zero.
if iszero(temp) { break }
}
let n := sub(end, result)
result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory result) {
if (value >= 0) return toString(uint256(value));
unchecked {
result = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let n := mload(result) // Load the string length.
mstore(result, 0x2d) // Store the '-' character.
result := sub(result, 1) // Move back the string pointer by a byte.
mstore(result, add(n, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `byteCount` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `byteCount * 2 + 2` bytes.
/// Reverts if `byteCount` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 byteCount)
internal
pure
returns (string memory result)
{
result = toHexStringNoPrefix(value, byteCount);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `byteCount` bytes.
/// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte,
/// giving a total length of `byteCount * 2` bytes.
/// Reverts if `byteCount` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 byteCount)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `byteCount * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
result := add(mload(0x40), and(add(shl(1, byteCount), 0x42), not(0x1f)))
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end to calculate the length later.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(result, add(byteCount, byteCount))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
result := add(result, w) // `sub(result, 2)`.
mstore8(add(result, 1), mload(and(temp, 15)))
mstore8(result, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(result, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
let n := sub(end, result)
result := sub(result, 0x20)
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
let n := add(mload(result), 2) // Compute the length.
mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero.
mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value)
internal
pure
returns (string memory result)
{
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
let n := mload(result) // Get the length.
result := add(result, o) // Move the pointer, accounting for leading zero.
mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
result := add(mload(0x40), 0x80)
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end to calculate the length later.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
result := add(result, w) // `sub(result, 2)`.
mstore8(add(result, 1), mload(and(temp, 15)))
mstore8(result, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
let n := sub(end, result)
result := sub(result, 0x20)
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory result) {
result = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(result, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
// Allocate memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(result, 0x80))
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
result := add(result, 2)
mstore(result, 40) // Store the length.
let o := add(result, 0x20)
mstore(add(o, 40), 0) // Zeroize the slot after the string.
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory result) {
result = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(raw)
result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(result, add(n, n)) // Store the length of the output.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let o := add(result, 0x20)
let end := add(raw, n)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
let mask := shl(7, div(not(0), 255))
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string,
/// AND all characters are in the `allowed` lookup.
/// Note: If `s` is empty, returns true regardless of `allowed`.
function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if mload(s) {
let allowed_ := shr(128, shl(128, allowed))
let o := add(s, 0x20)
for { let end := add(o, mload(s)) } 1 {} {
result := and(result, shr(byte(0, mload(o)), allowed_))
o := add(o, 1)
if iszero(and(result, lt(o, end))) { break }
}
}
}
}
/// @dev Converts the bytes in the 7-bit ASCII string `s` to
/// an allowed lookup for use in `is7BitASCII(s, allowed)`.
/// To save runtime gas, you can cache the result in an immutable variable.
function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
let o := add(s, 0x20)
for { let end := add(o, mload(s)) } 1 {} {
result := or(result, shl(byte(0, mload(o)), 1))
o := add(o, 1)
if iszero(lt(o, end)) { break }
}
if shr(128, result) {
mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
revert(0x1c, 0x04)
}
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
function replace(string memory subject, string memory needle, string memory replacement)
internal
pure
returns (string memory)
{
return string(LibBytes.replace(bytes(subject), bytes(needle), bytes(replacement)));
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(string memory subject, string memory needle, uint256 from)
internal
pure
returns (uint256)
{
return LibBytes.indexOf(bytes(subject), bytes(needle), from);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(string memory subject, string memory needle) internal pure returns (uint256) {
return LibBytes.indexOf(bytes(subject), bytes(needle), 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(string memory subject, string memory needle, uint256 from)
internal
pure
returns (uint256)
{
return LibBytes.lastIndexOf(bytes(subject), bytes(needle), from);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(string memory subject, string memory needle)
internal
pure
returns (uint256)
{
return LibBytes.lastIndexOf(bytes(subject), bytes(needle), type(uint256).max);
}
/// @dev Returns true if `needle` is found in `subject`, false otherwise.
function contains(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.contains(bytes(subject), bytes(needle));
}
/// @dev Returns whether `subject` starts with `needle`.
function startsWith(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.startsWith(bytes(subject), bytes(needle));
}
/// @dev Returns whether `subject` ends with `needle`.
function endsWith(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.endsWith(bytes(subject), bytes(needle));
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times) internal pure returns (string memory) {
return string(LibBytes.repeat(bytes(subject), times));
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory)
{
return string(LibBytes.slice(bytes(subject), start, end));
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start) internal pure returns (string memory) {
return string(LibBytes.slice(bytes(subject), start, type(uint256).max));
}
/// @dev Returns all the indices of `needle` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory needle)
internal
pure
returns (uint256[] memory)
{
return LibBytes.indicesOf(bytes(subject), bytes(needle));
}
/// @dev Returns an arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
bytes[] memory a = LibBytes.split(bytes(subject), bytes(delimiter));
/// @solidity memory-safe-assembly
assembly {
result := a
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b) internal pure returns (string memory) {
return string(LibBytes.concat(bytes(a), bytes(b)));
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(subject)
if n {
result := mload(0x40)
let o := add(result, 0x20)
let d := sub(subject, result)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
for { let end := add(o, n) } 1 {} {
let b := byte(0, mload(add(d, o)))
mstore8(o, xor(and(shr(b, flags), 0x20), b))
o := add(o, 1)
if eq(o, end) { break }
}
mstore(result, n) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n) // Store the length.
let o := add(result, 0x20)
mstore(o, s) // Store the bytes of the string.
mstore(add(o, n), 0) // Zeroize the slot after the string.
mstore(0x40, add(result, 0x40)) // Allocate memory.
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let end := add(s, mload(s))
let o := add(result, 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(o, c)
o := add(o, 1)
continue
}
let t := shr(248, mload(c))
mstore(o, mload(and(t, 0x1f)))
o := add(o, shr(5, t))
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let o := add(result, 0x20)
if addDoubleQuotes {
mstore8(o, 34)
o := add(1, o)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(o, c)
o := add(o, 1)
continue
}
mstore8(o, 0x5c) // "\\".
mstore8(add(o, 1), c)
o := add(o, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(o, mload(0x19)) // "\\u00XX".
o := add(o, 6)
continue
}
mstore8(o, 0x5c) // "\\".
mstore8(add(o, 1), mload(add(c, 8)))
o := add(o, 2)
}
if addDoubleQuotes {
mstore8(o, 34)
o := add(1, o)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Encodes `s` so that it can be safely used in a URI,
/// just like `encodeURIComponent` in JavaScript.
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
/// See: https://datatracker.ietf.org/doc/html/rfc2396
/// See: https://datatracker.ietf.org/doc/html/rfc3986
function encodeURIComponent(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
// Store "0123456789ABCDEF" in scratch space.
// Uppercased to be consistent with JavaScript's implementation.
mstore(0x0f, 0x30313233343536373839414243444546)
let o := add(result, 0x20)
for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// If not in `[0-9A-Z-a-z-_.!~*'()]`.
if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) {
mstore8(o, 0x25) // '%'.
mstore8(add(o, 1), mload(and(shr(4, c), 15)))
mstore8(add(o, 2), mload(and(c, 15)))
o := add(o, 3)
continue
}
mstore8(o, c)
o := add(o, 1)
}
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`.
/// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1.
function cmp(string memory a, string memory b) internal pure returns (int256) {
return LibBytes.cmp(bytes(a), bytes(b));
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40) // Grab the free memory pointer.
mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes).
mstore(result, 0) // Zeroize the length slot.
mstore(add(result, 0x1f), packed) // Store the length and bytes.
mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes.
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLen := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
or( // Load the length and the bytes of `a` and `b`.
shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))),
// `totalLen != 0 && totalLen < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLen, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
resultA := mload(0x40) // Grab the free memory pointer.
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20) // Store the return offset.
// End the transaction, returning the string.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
}
Compiler Settings
{"viaIR":true,"remappings":["openzeppelin/=node_modules/@openzeppelin/","@openzeppelin/=node_modules/@openzeppelin/","@openzeppelin-upgrades/contracts/=node_modules/@openzeppelin/contracts-upgradeable/","@risc0/contracts/=node_modules/risc0-ethereum/contracts/src/","@solady/=node_modules/solady/","solady/src/=node_modules/solady/src/","solady/utils/=node_modules/solady/src/utils/","@optimism/=node_modules/optimism/","@sp1-contracts/=node_modules/sp1-contracts/contracts/","forge-std/=node_modules/forge-std/","@p256-verifier/contracts/=node_modules/p256-verifier/src/","@eth-fabric/urc/=node_modules/urc/src/","ds-test/=node_modules/ds-test/","src/=contracts/","test/=test/","script/=script/","optimism/=node_modules/optimism/","p256-verifier/=node_modules/p256-verifier/","risc0-ethereum/=node_modules/risc0-ethereum/","sp1-contracts/=node_modules/sp1-contracts/","urc/=node_modules/urc/"],"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{"contracts/layer1/core/libs/LibInboxSetup.sol":{"LibInboxSetup":"0xf88Ef5437749A225621101BE8C1BE1A0cE967758"},"contracts/layer1/core/libs/LibForcedInclusion.sol":{"LibForcedInclusion":"0xd1a27F331c17eD8Cbb6DAbce67A42d6b8a6B0e14"}},"evmVersion":"prague"}
Contract ABI
[{"type":"error","name":"INVALID_IDX","inputs":[]},{"type":"error","name":"NOT_A_CONSTRUCTED_TYPE","inputs":[]},{"type":"error","name":"NOT_TYPE_OCTET_STRING","inputs":[]},{"type":"error","name":"UNEXPECTED_IDX","inputs":[]},{"type":"error","name":"UNEXPECTED_LEN","inputs":[]},{"type":"error","name":"UNEXPECTED_OFFSET","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"success","internalType":"bool"},{"type":"tuple","name":"cert","internalType":"struct IPEMCertChainLib.ECSha256Certificate","components":[{"type":"uint256","name":"notBefore","internalType":"uint256"},{"type":"uint256","name":"notAfter","internalType":"uint256"},{"type":"bytes","name":"serialNumber","internalType":"bytes"},{"type":"bytes","name":"tbsCertificate","internalType":"bytes"},{"type":"bytes","name":"pubKey","internalType":"bytes"},{"type":"bytes","name":"signature","internalType":"bytes"},{"type":"bool","name":"isPck","internalType":"bool"},{"type":"tuple","name":"pck","internalType":"struct IPEMCertChainLib.PCKCertificateField","components":[{"type":"string","name":"commonName","internalType":"string"},{"type":"string","name":"issuerName","internalType":"string"},{"type":"tuple","name":"sgxExtension","internalType":"struct IPEMCertChainLib.PCKTCBInfo","components":[{"type":"string","name":"pceid","internalType":"string"},{"type":"string","name":"fmspc","internalType":"string"},{"type":"uint256","name":"pcesvn","internalType":"uint256"},{"type":"uint256[]","name":"sgxTcbCompSvnArr","internalType":"uint256[]"}]}]}]}],"name":"decodeCert","inputs":[{"type":"bytes","name":"der","internalType":"bytes"},{"type":"bool","name":"isPckCert","internalType":"bool"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"success","internalType":"bool"},{"type":"bytes[]","name":"certs","internalType":"bytes[]"}],"name":"splitCertificateChain","inputs":[{"type":"bytes","name":"pemChain","internalType":"bytes"},{"type":"uint256","name":"size","internalType":"uint256"}]}]
Contract Creation Code
0x6080806040523460155761207f908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c8063b6e656fa146100345763c1c1d5c11461002f575f80fd5b6102b4565b346100e15760403660031901126100e15760043567ffffffffffffffff81116100e1576100686100719136906004016101a1565b60243590610480565b906040519182916040830190151583526040602084015281518091526060830190602060608260051b8601019301915f905b8282106100b257505050500390f35b919360019193955060206100d18192605f198a820301865288516101e7565b96019201920185949391926100a3565b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b610100810190811067ffffffffffffffff82111761011657604052565b6100e5565b6060810190811067ffffffffffffffff82111761011657604052565b6080810190811067ffffffffffffffff82111761011657604052565b90601f8019910116810190811067ffffffffffffffff82111761011657604052565b906101836040519283610153565b565b67ffffffffffffffff811161011657601f01601f191660200190565b81601f820112156100e1578035906101b882610185565b926101c66040519485610153565b828452602083830101116100e157815f926020809301838601378301015290565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90604061023661022484516060855260608501906101e7565b602085015184820360208601526101e7565b920151906040818403910152606061026c61025a83516080865260808601906101e7565b602084015185820360208701526101e7565b91604081015160408501520151916060818303910152602080835192838152019201905f5b81811061029e5750505090565b8251845260209384019390920191600101610291565b346100e15760403660031901126100e15760043567ffffffffffffffff81116100e1576102e59036906004016101a1565b60243580151581036100e1576102fa916106a9565b906103a96040519283921515835260406020840152805160408401526020810151606084015260e061038761037261035c610346604086015161010060808a01526101408901906101e7565b6060860151888203603f190160a08a01526101e7565b6080850151878203603f190160c08901526101e7565b60a0840151868203603f1901848801526101e7565b60c08301511515610100860152910151838203603f190161012085015261020b565b0390f35b67ffffffffffffffff81116101165760051b60200190565b634e487b7160e01b5f52601160045260245ffd5b90600382018092116103e757565b6103c5565b90601b82018092116103e757565b90601982018092116103e757565b90600182018092116103e757565b90600282018092116103e757565b906301e1338082018092116103e757565b906301e2850082018092116103e757565b919082018092116103e757565b634e487b7160e01b5f52603260045260245ffd5b805182101561047b5760209160051b010190565b610453565b9061048a816103ad565b6104976040519182610153565b818152601f196104a6836103ad565b015f5b818110610529575050905f83515f915b8383106104cb57505050509050600191565b8215610520576104ed6104e86104e18484610446565b83896115f6565b610b26565b9291906104fa8689610467565b52156105145760019161050c91610446565b9201916104b9565b505f9694955050505050565b6104ed86610b26565b8060606020809386010152016104a9565b60405190610547826100f9565b815f81525f60208201526060604082015260608082015260606080820152606060a08201525f60c082015260e0604051916105818361011b565b606083526060602084015260405161059881610137565b60608152606060208201525f604082015260608082015260408401520152565b604051906105c7604083610153565b601982527f496e74656c205347582050434b20506c6174666f726d204341000000000000006020830152565b60405190610602604083610153565b601a82527f496e74656c205347582050434b2050726f636573736f722043410000000000006020830152565b80511561047b5760200190565b80516001101561047b5760210190565b90815181101561047b570160200190565b6040519061066b604083610153565b601982527f496e74656c205347582050434b204365727469666963617465000000000000006020830152565b805191908290602001825e015f815290565b906106b261053a565b916106c56106bf8261166f565b82610c09565b91806106fb6106f56106e06106da8787610c09565b86610c51565b6106ea8187610c81565b604089015285610c51565b84610c51565b90610a54575b61070b9083610c51565b6107158184610c09565b61071f8185610c51565b906107546107466107406001600160501b0384165b6001600160501b031690565b8761064b565b516001600160f81b03191690565b61077261074661076c6001600160501b038616610734565b8861064b565b601760f81b6001600160f81b0319909216918214159182610a45575b508115610a15575b50610a0a57916107c16107b46107cc936107b96107b488978a610c81565b610e7d565b8a5287610c81565b602088015284610c51565b9061099f575b6107dc9083610c51565b906107e78284610c09565b6107f19084610c51565b6107fb8585610c51565b6108059085610c51565b6108176001600160501b0382166103d9565b9061082e6001600160501b03605083901c166103d9565b60501b90911769ffffffffffffffffffff60a01b909116176108509085610c09565b61085a8186610c81565b610863906111af565b9061086e9086610c51565b6108789086610c81565b610881906111af565b9161088c87876111fc565b606089015261089b9086610c81565b6108a4906111d8565b6080880152604051918291602083016108bc91610697565b6108c591610697565b03601f19810182526108d79082610153565b60a08601526108e9575b505050600191565b6108f39082610c51565b60a360f81b6001600160f81b031961091f6107466109196001600160501b038616610734565b8661064b565b160361099757906109376106bf61093d949383610c09565b906112ed565b919092931561098e5760209261096e6040936109799360e089019786808a5101510152606086895101510152611590565b838651015152611590565b925101510152600160c08201525f80806108e1565b505f9492505050565b505f93915050565b6109fc6109f86109d36109cd6109c76109c16109bb878a610c09565b89610c09565b88610c09565b87610c51565b86610c81565b60e088019081515251516109e561065c565b6020815191012090602081519101201490565b1590565b156107d257505f9492505050565b505f96945050505050565b6001600160f81b031916601760f81b811415915081610a36575b505f610796565b600360fb1b141590505f610a2f565b600360fb1b141591505f61078e565b610a82610a7c610a76610a70610a6a8588610c09565b87610c09565b86610c09565b85610c51565b84610c81565b60e0860190602082510152610a9e6020825101516109e56105b8565b908115610ab4575b5061070157505f9492505050565b5160200151610ac691506109e56105f3565b5f610aa6565b60405190610adb604083610153565b601982527f2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d000000000000006020830152565b60405190610b16604083610153565b60018252600560f91b6020830152565b90610b68604051610b38604082610153565b601b81527f2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0000000000602082015283611c77565b91610b7a610b74610acc565b82611c77565b925f1981148015610bff575b610bf35790610ba984610b9b610bae946103ec565b610ba3610b07565b936115f6565b611d4f565b9160609280515f915b818310610bd057505050610bca906103fa565b60019291565b909194610bea600191610be38885610467565b5190611e11565b95019190610bb7565b505f9250606091839150565b505f198414610b86565b600160fd1b80610c226001600160501b0385168461064b565b511603610c42576001600160501b03610c3f9260501c16906117aa565b90565b632347bfa760e01b5f5260045ffd5b906001600160501b039060a01c16600181018091116103e757610c3f916117aa565b5f198101919082116103e757565b906001600160501b03808260501c169160a01c169060018201918281116103e7578160019103019182116103e757610c3f926118b0565b60ff602f199116019060ff82116103e757565b60ff166020039060ff82116103e757565b60ff5f199116019060ff82116103e757565b61ffff6103e89116029061ffff82169182036103e757565b60ff60649116029060ff82169182036103e757565b60ff600a9116029060ff82169182036103e757565b60031b906107f860f88316921682036103e757565b61ffff169061ffff82116103e757565b61ffff61076c9116019061ffff82116103e757565b61ffff6107d09116019061ffff82116103e757565b9061ffff8091169116019061ffff82116103e757565b60ff169060ff82116103e757565b60ff60019116019060ff82116103e757565b60ff60029116019060ff82116103e757565b60ff60039116019060ff82116103e757565b60ff60049116019060ff82116103e757565b60ff60059116019060ff82116103e757565b60ff60069116019060ff82116103e757565b60ff60079116019060ff82116103e757565b60ff60089116019060ff82116103e757565b60ff60099116019060ff82116103e757565b60ff600a9116019060ff82116103e757565b60ff600b9116019060ff82116103e757565b9060ff8091169116019060ff82116103e757565b5f5f90600d8351145f1461115457600560ff610eac610ea7610ea16107468861062e565b60f81c90565b610cb8565b16101561114657610ebc90610d6a565b610ec582610d95565b60ff16610ed2908461064b565b51610edf9060f81c610cb8565b610ee890610d1b565b610ef183610da3565b60ff16610efe908561064b565b516001600160f81b03191660f81c610f1591610e69565b610f1e90610cb8565b60ff16610f2a91610d7f565b91610f3482610db5565b60ff16610f41908261064b565b51610f4e9060f81c610cb8565b610f5790610d1b565b610f6083610dc7565b60ff16610f6d908361064b565b516001600160f81b03191660f81c610f8491610e69565b610f8d90610cb8565b91610f9781610dd9565b60ff16610fa4908361064b565b51610fb19060f81c610cb8565b610fba90610d1b565b610fc382610deb565b60ff16610fd0908461064b565b516001600160f81b03191660f81c610fe791610e69565b610ff090610cb8565b610ff990610d95565b61100282610dfd565b60ff1661100f908461064b565b5161101c9060f81c610cb8565b61102590610d1b565b61102e83610e0f565b60ff1661103b908561064b565b516001600160f81b03191660f81c61105291610e69565b61105b90610cb8565b61106490610d95565b9161106e81610e21565b60ff1661107b908561064b565b516110889060f81c610cb8565b61109190610d1b565b61109a82610e33565b60ff166110a7908661064b565b516001600160f81b03191660f81c6110be91610e69565b6110c790610cb8565b6110d090610d95565b936110da82610e45565b60ff166110e7908261064b565b516110f49060f81c610cb8565b6110fd90610d1b565b9161110790610e57565b60ff166111139161064b565b516001600160f81b03191660f81c61112a91610e69565b61113390610cb8565b61113c90610d95565b93610c3f95611964565b61114f90610d55565b610ebc565b50506111a76111a261117960ff611173610ea7610ea16107468861062e565b16610cee565b61119c611196611191610ea7610ea16107468961063b565b610d06565b60ff1690565b90610d7f565b610d45565b600290610ebc565b80519060208211156111d357601f1982019182116103e757610c3f916020916118b0565b905090565b80519060408211156111d357603f1982019182116103e757610c3f916040916118b0565b906001600160501b038082169160a01c169060018201918281116103e7578160019103019182116103e757610c3f926118b0565b6040519061123f604083610153565b60098252682a864886f84d010d0160b81b6020830152565b604051906112648261011b565b5f6040838281528260208201520152565b60405190611284604083610153565b600a825269154324437c268086808160b11b6020830152565b604051906112ac604083610153565b600a8252692a864886f84d010d010360b01b6020830152565b604051906112d4604083610153565b600a8252690aa19221be134043404160b21b6020830152565b9092915f935f9360609384938386945b61130657505050565b6113108184610c09565b600360f91b61133b61132e6107466001600160501b0385168861064b565b6001600160f81b03191690565b036115875761136961134d8286610c81565b611355611230565b906020815191012090602081519101201490565b6113b1575061137e6107346107348360a01c90565b61138e6107346107348560a01c90565b11156113a85761139e9083610c51565b90815b90916112fd565b505f90816113a1565b9050826113cf93979598969499506113c99250610c51565b85611acb565b946113da8686610c09565b956113f36107346107346113ec611257565b9360a01c90565b965b815115158061157a575b8061156d575b611560576114138188610c09565b600360f91b61143161132e6107466001600160501b0385168c61064b565b0361154e5761144b611443828a610c81565b611355611275565b61152b575b61146561145d828a610c81565b61135561129d565b611509575b61147f611477828a610c81565b6113556112c5565b6114e8575b50876114966107346107348460a01c90565b10156114ab576114a69087610c51565b6113f5565b50919395509193505b8051151590816114d3575b816114c8575094565b604001511515905094565b90506114e26020820151151590565b906114bf565b6114fe9194506114f89088610c51565b87610c81565b60018252925f611484565b925061151e6115188489610c51565b88610c81565b600160208401529261146a565b9550611549945061153c8688611ba1565b9115156040850152969095565b611450565b505050929591945092505f9493929190565b50919395509193506114b4565b5060408201511515611405565b50602082015115156113ff565b505f9850505050565b90815191604051926022600285019482800186526f30313233343536373839616263646566600f5201908201915b8281036115d257505f815260200160405250565b60016002910191600f835116516001820153600f835160041c1651815301906115be565b805160609493929083811115611667575b8181111561165f575b5082811061161d57505050565b6040519450918290039101601f8201601f19165b8181015185820152601f190190811561164a5790611631565b505060408184015f6020820152016040528252565b90505f611610565b925082611607565b610c3f90600160ff1b61168d6107466116875f610408565b8461064b565b166116e757611196610ea16107466116ae936116a85f610408565b9061064b565b6001600160501b03806116da6107346116d56116cc6107345f610416565b95848716610446565b610c73565b1660a01b911660501b1790565b6001600160501b03806117496107346116d56116cc610734607f6117166107466117105f610408565b8b61064b565b60f81c16976001890361174e57611196611739916117335f610416565b90611efc565b975b6117445f610416565b610446565b6116da565b886002810361177b575061176e611775916117685f610416565b90611ecf565b61ffff1690565b9761173b565b611792906117759261178c5f610416565b90611e80565b6117a361179e8b610ccb565b610d30565b60ff161c90565b610c3f9190600160ff1b6117c361074661168785610408565b1661181657611196610ea16107466117de936116a886610408565b906001600160501b03806118066107346116d56117fd61073487610416565b96848816610446565b1692169160a01b9160501b171790565b906001600160501b038061186e6107346116d56117fd610734607f6118466107466118408b610408565b8c61064b565b60f81c169860018a0361187357611196611863916117338b610416565b985b61174489610416565b611806565b8960028103611893575061176e61188d916117688b610416565b98611865565b6118a49061188d9261178c8c610416565b6117a361179e8c610ccb565b9190918183018084116103e7578151106118fc5760206118cf83610185565b936118dd6040519586610153565b8385526118e984610185565b8583019390601f19013685370101905e90565b6343733a0960e11b5f5260045ffd5b90600c81101561047b5760051b0190565b90620151808202918083046201518014901517156103e757565b90610e10820291808304610e1014901517156103e757565b90603c820291808304603c14901517156103e757565b95949392915f916107b25b61ffff891661ffff821610611a9557506119e861198d610180610175565b601f815298601c60208b0152601f60408b0152601e60608b0152601f60808b0152601e60a08b0152601f60c08b0152601f60e08b0152601e6101008b0152601f6101208b0152601e6101408b0152601f6101608b0152611f0d565b611a89575b6001905b60ff811660ff831610611a4c575050610c3f95965060ff611a3d611a4494611a3083611a36819997611a30611a2b611196611a309a610cdc565b61191c565b90610446565b9216611936565b921661194e565b911690610446565b909260ff6001611a7e8293611a30611a2b6111968f611a7790611a716111968e610cdc565b9061190b565b5160ff1690565b9501169190506119f1565b601d60208901526119ed565b92600161ffff91611aa586611f0d565b15611abc57611ab390610435565b945b011661196f565b611ac590610424565b94611ab5565b600160fa1b6001600160f81b0319611aec6001600160501b0385168461064b565b511603611b09576001600160501b03610c3f9260501c16906117aa565b63a040ad4b60e01b5f5260045ffd5b6040516102209190611b2a8382610153565b6010815291601f1901366020840137565b80516020909101516001600160f01b0319811692919060028210611b5d575050565b6001600160f01b031960029290920360031b82901b16169150565b60405190611b87604083610153565b600b82526a2a864886f84d010d01021160a81b6020830152565b611baf6106bf5f9383610c51565b611bb7611b18565b915f6011915b828210611bcd5750505050600192565b90919294611bdb8683610c09565b90611c28611c20611bef6109cd8587610c51565b60028151105f14611c6557611c0f611c09611c1892611b3b565b60f01c90565b60081c60ff1690565b935b85610c81565b611355611b78565b15611c495750611c4061ffff60019216965b83610c51565b93920190611bbd565b95611c409061ffff60019316611c5f8689610467565b52611c3a565b611c09611c7191611b3b565b93611c1a565b805182515f199493909290821515828515611d4457856020602192019585010301946020601f8216810360031b93018051928787101615611d1d576020821015611cf6575050925b83815118821c15611ce3576001019284841015611cdc5792611cbf565b5050505050565b91909103601f1901945061018392505050565b90809295939120925b85815118821c15611d26575b6001019486861015611d1d5794611cff565b50505050505050565b8383822003611d0b5793909303601f19019650610183945050505050565b505f96505050505050565b919091611d5c8382611f46565b90602082018091600184510160051b840191602083019282519052600185510185525f5b81519060608352808203611dbc575b50602090885101910190838210611d80575050505081935115611db0575050565b90516001190181529150565b604051818303808252949091601f8601601f19165b82820181015184820152601f1901918215611dec5791611dd1565b9590506020939291505f848284010152603f601f199101168101604052835290611d8f565b6040518151909392909160208301601f19165b8181015186820152601f1901908115611e3d5790611e24565b505080519084830160208301601f19165b8281015182820152601f1901918215611e675791611e4e565b50505060409101808401905f6020830152845201604052565b919060208211611ec0578181018082116103e757835110611eb1576020915f199083036101000a0119920101511690565b634e0a27d960e01b5f5260045ffd5b63cbb7a73160e01b5f5260045ffd5b600282018083116103e757815110611eed57016002015161ffff1690565b63653582fb60e01b5f5260045ffd5b90611f069161064b565b5160f81c90565b60038116611f415761ffff60648183160616611f3b5761019061ffff8092160616611f3757600190565b5f90565b50600190565b505f90565b91909160609280519180519182841115611f61575b50505050565b9091929450604051946020830191602182602089019686010301935f91602084101561203b575b6020015193601f841660200360031b905b85815118821c15611fdc575b6001019486861015611fb8575b94611f99565b505050505050506020905b838103601f190160051c8452016040525f808080611f5b565b968484612011575b60208192601f19868c030181520198019015611fa55794868610611fb25750505050505050602090611fc3565b84908920036120205784611fe4565b9660010194868610611fb25750505050505050602090611fc3565b602081018490209250611f8856fea26469706673582212202108f8c9e1697ad9b588f12b49ee652dba223485b3e466f4e42f75f76a74a3f764736f6c634300081e0033
Deployed ByteCode
0x60806040526004361015610011575f80fd5b5f3560e01c8063b6e656fa146100345763c1c1d5c11461002f575f80fd5b6102b4565b346100e15760403660031901126100e15760043567ffffffffffffffff81116100e1576100686100719136906004016101a1565b60243590610480565b906040519182916040830190151583526040602084015281518091526060830190602060608260051b8601019301915f905b8282106100b257505050500390f35b919360019193955060206100d18192605f198a820301865288516101e7565b96019201920185949391926100a3565b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b610100810190811067ffffffffffffffff82111761011657604052565b6100e5565b6060810190811067ffffffffffffffff82111761011657604052565b6080810190811067ffffffffffffffff82111761011657604052565b90601f8019910116810190811067ffffffffffffffff82111761011657604052565b906101836040519283610153565b565b67ffffffffffffffff811161011657601f01601f191660200190565b81601f820112156100e1578035906101b882610185565b926101c66040519485610153565b828452602083830101116100e157815f926020809301838601378301015290565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90604061023661022484516060855260608501906101e7565b602085015184820360208601526101e7565b920151906040818403910152606061026c61025a83516080865260808601906101e7565b602084015185820360208701526101e7565b91604081015160408501520151916060818303910152602080835192838152019201905f5b81811061029e5750505090565b8251845260209384019390920191600101610291565b346100e15760403660031901126100e15760043567ffffffffffffffff81116100e1576102e59036906004016101a1565b60243580151581036100e1576102fa916106a9565b906103a96040519283921515835260406020840152805160408401526020810151606084015260e061038761037261035c610346604086015161010060808a01526101408901906101e7565b6060860151888203603f190160a08a01526101e7565b6080850151878203603f190160c08901526101e7565b60a0840151868203603f1901848801526101e7565b60c08301511515610100860152910151838203603f190161012085015261020b565b0390f35b67ffffffffffffffff81116101165760051b60200190565b634e487b7160e01b5f52601160045260245ffd5b90600382018092116103e757565b6103c5565b90601b82018092116103e757565b90601982018092116103e757565b90600182018092116103e757565b90600282018092116103e757565b906301e1338082018092116103e757565b906301e2850082018092116103e757565b919082018092116103e757565b634e487b7160e01b5f52603260045260245ffd5b805182101561047b5760209160051b010190565b610453565b9061048a816103ad565b6104976040519182610153565b818152601f196104a6836103ad565b015f5b818110610529575050905f83515f915b8383106104cb57505050509050600191565b8215610520576104ed6104e86104e18484610446565b83896115f6565b610b26565b9291906104fa8689610467565b52156105145760019161050c91610446565b9201916104b9565b505f9694955050505050565b6104ed86610b26565b8060606020809386010152016104a9565b60405190610547826100f9565b815f81525f60208201526060604082015260608082015260606080820152606060a08201525f60c082015260e0604051916105818361011b565b606083526060602084015260405161059881610137565b60608152606060208201525f604082015260608082015260408401520152565b604051906105c7604083610153565b601982527f496e74656c205347582050434b20506c6174666f726d204341000000000000006020830152565b60405190610602604083610153565b601a82527f496e74656c205347582050434b2050726f636573736f722043410000000000006020830152565b80511561047b5760200190565b80516001101561047b5760210190565b90815181101561047b570160200190565b6040519061066b604083610153565b601982527f496e74656c205347582050434b204365727469666963617465000000000000006020830152565b805191908290602001825e015f815290565b906106b261053a565b916106c56106bf8261166f565b82610c09565b91806106fb6106f56106e06106da8787610c09565b86610c51565b6106ea8187610c81565b604089015285610c51565b84610c51565b90610a54575b61070b9083610c51565b6107158184610c09565b61071f8185610c51565b906107546107466107406001600160501b0384165b6001600160501b031690565b8761064b565b516001600160f81b03191690565b61077261074661076c6001600160501b038616610734565b8861064b565b601760f81b6001600160f81b0319909216918214159182610a45575b508115610a15575b50610a0a57916107c16107b46107cc936107b96107b488978a610c81565b610e7d565b8a5287610c81565b602088015284610c51565b9061099f575b6107dc9083610c51565b906107e78284610c09565b6107f19084610c51565b6107fb8585610c51565b6108059085610c51565b6108176001600160501b0382166103d9565b9061082e6001600160501b03605083901c166103d9565b60501b90911769ffffffffffffffffffff60a01b909116176108509085610c09565b61085a8186610c81565b610863906111af565b9061086e9086610c51565b6108789086610c81565b610881906111af565b9161088c87876111fc565b606089015261089b9086610c81565b6108a4906111d8565b6080880152604051918291602083016108bc91610697565b6108c591610697565b03601f19810182526108d79082610153565b60a08601526108e9575b505050600191565b6108f39082610c51565b60a360f81b6001600160f81b031961091f6107466109196001600160501b038616610734565b8661064b565b160361099757906109376106bf61093d949383610c09565b906112ed565b919092931561098e5760209261096e6040936109799360e089019786808a5101510152606086895101510152611590565b838651015152611590565b925101510152600160c08201525f80806108e1565b505f9492505050565b505f93915050565b6109fc6109f86109d36109cd6109c76109c16109bb878a610c09565b89610c09565b88610c09565b87610c51565b86610c81565b60e088019081515251516109e561065c565b6020815191012090602081519101201490565b1590565b156107d257505f9492505050565b505f96945050505050565b6001600160f81b031916601760f81b811415915081610a36575b505f610796565b600360fb1b141590505f610a2f565b600360fb1b141591505f61078e565b610a82610a7c610a76610a70610a6a8588610c09565b87610c09565b86610c09565b85610c51565b84610c81565b60e0860190602082510152610a9e6020825101516109e56105b8565b908115610ab4575b5061070157505f9492505050565b5160200151610ac691506109e56105f3565b5f610aa6565b60405190610adb604083610153565b601982527f2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d000000000000006020830152565b60405190610b16604083610153565b60018252600560f91b6020830152565b90610b68604051610b38604082610153565b601b81527f2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0000000000602082015283611c77565b91610b7a610b74610acc565b82611c77565b925f1981148015610bff575b610bf35790610ba984610b9b610bae946103ec565b610ba3610b07565b936115f6565b611d4f565b9160609280515f915b818310610bd057505050610bca906103fa565b60019291565b909194610bea600191610be38885610467565b5190611e11565b95019190610bb7565b505f9250606091839150565b505f198414610b86565b600160fd1b80610c226001600160501b0385168461064b565b511603610c42576001600160501b03610c3f9260501c16906117aa565b90565b632347bfa760e01b5f5260045ffd5b906001600160501b039060a01c16600181018091116103e757610c3f916117aa565b5f198101919082116103e757565b906001600160501b03808260501c169160a01c169060018201918281116103e7578160019103019182116103e757610c3f926118b0565b60ff602f199116019060ff82116103e757565b60ff166020039060ff82116103e757565b60ff5f199116019060ff82116103e757565b61ffff6103e89116029061ffff82169182036103e757565b60ff60649116029060ff82169182036103e757565b60ff600a9116029060ff82169182036103e757565b60031b906107f860f88316921682036103e757565b61ffff169061ffff82116103e757565b61ffff61076c9116019061ffff82116103e757565b61ffff6107d09116019061ffff82116103e757565b9061ffff8091169116019061ffff82116103e757565b60ff169060ff82116103e757565b60ff60019116019060ff82116103e757565b60ff60029116019060ff82116103e757565b60ff60039116019060ff82116103e757565b60ff60049116019060ff82116103e757565b60ff60059116019060ff82116103e757565b60ff60069116019060ff82116103e757565b60ff60079116019060ff82116103e757565b60ff60089116019060ff82116103e757565b60ff60099116019060ff82116103e757565b60ff600a9116019060ff82116103e757565b60ff600b9116019060ff82116103e757565b9060ff8091169116019060ff82116103e757565b5f5f90600d8351145f1461115457600560ff610eac610ea7610ea16107468861062e565b60f81c90565b610cb8565b16101561114657610ebc90610d6a565b610ec582610d95565b60ff16610ed2908461064b565b51610edf9060f81c610cb8565b610ee890610d1b565b610ef183610da3565b60ff16610efe908561064b565b516001600160f81b03191660f81c610f1591610e69565b610f1e90610cb8565b60ff16610f2a91610d7f565b91610f3482610db5565b60ff16610f41908261064b565b51610f4e9060f81c610cb8565b610f5790610d1b565b610f6083610dc7565b60ff16610f6d908361064b565b516001600160f81b03191660f81c610f8491610e69565b610f8d90610cb8565b91610f9781610dd9565b60ff16610fa4908361064b565b51610fb19060f81c610cb8565b610fba90610d1b565b610fc382610deb565b60ff16610fd0908461064b565b516001600160f81b03191660f81c610fe791610e69565b610ff090610cb8565b610ff990610d95565b61100282610dfd565b60ff1661100f908461064b565b5161101c9060f81c610cb8565b61102590610d1b565b61102e83610e0f565b60ff1661103b908561064b565b516001600160f81b03191660f81c61105291610e69565b61105b90610cb8565b61106490610d95565b9161106e81610e21565b60ff1661107b908561064b565b516110889060f81c610cb8565b61109190610d1b565b61109a82610e33565b60ff166110a7908661064b565b516001600160f81b03191660f81c6110be91610e69565b6110c790610cb8565b6110d090610d95565b936110da82610e45565b60ff166110e7908261064b565b516110f49060f81c610cb8565b6110fd90610d1b565b9161110790610e57565b60ff166111139161064b565b516001600160f81b03191660f81c61112a91610e69565b61113390610cb8565b61113c90610d95565b93610c3f95611964565b61114f90610d55565b610ebc565b50506111a76111a261117960ff611173610ea7610ea16107468861062e565b16610cee565b61119c611196611191610ea7610ea16107468961063b565b610d06565b60ff1690565b90610d7f565b610d45565b600290610ebc565b80519060208211156111d357601f1982019182116103e757610c3f916020916118b0565b905090565b80519060408211156111d357603f1982019182116103e757610c3f916040916118b0565b906001600160501b038082169160a01c169060018201918281116103e7578160019103019182116103e757610c3f926118b0565b6040519061123f604083610153565b60098252682a864886f84d010d0160b81b6020830152565b604051906112648261011b565b5f6040838281528260208201520152565b60405190611284604083610153565b600a825269154324437c268086808160b11b6020830152565b604051906112ac604083610153565b600a8252692a864886f84d010d010360b01b6020830152565b604051906112d4604083610153565b600a8252690aa19221be134043404160b21b6020830152565b9092915f935f9360609384938386945b61130657505050565b6113108184610c09565b600360f91b61133b61132e6107466001600160501b0385168861064b565b6001600160f81b03191690565b036115875761136961134d8286610c81565b611355611230565b906020815191012090602081519101201490565b6113b1575061137e6107346107348360a01c90565b61138e6107346107348560a01c90565b11156113a85761139e9083610c51565b90815b90916112fd565b505f90816113a1565b9050826113cf93979598969499506113c99250610c51565b85611acb565b946113da8686610c09565b956113f36107346107346113ec611257565b9360a01c90565b965b815115158061157a575b8061156d575b611560576114138188610c09565b600360f91b61143161132e6107466001600160501b0385168c61064b565b0361154e5761144b611443828a610c81565b611355611275565b61152b575b61146561145d828a610c81565b61135561129d565b611509575b61147f611477828a610c81565b6113556112c5565b6114e8575b50876114966107346107348460a01c90565b10156114ab576114a69087610c51565b6113f5565b50919395509193505b8051151590816114d3575b816114c8575094565b604001511515905094565b90506114e26020820151151590565b906114bf565b6114fe9194506114f89088610c51565b87610c81565b60018252925f611484565b925061151e6115188489610c51565b88610c81565b600160208401529261146a565b9550611549945061153c8688611ba1565b9115156040850152969095565b611450565b505050929591945092505f9493929190565b50919395509193506114b4565b5060408201511515611405565b50602082015115156113ff565b505f9850505050565b90815191604051926022600285019482800186526f30313233343536373839616263646566600f5201908201915b8281036115d257505f815260200160405250565b60016002910191600f835116516001820153600f835160041c1651815301906115be565b805160609493929083811115611667575b8181111561165f575b5082811061161d57505050565b6040519450918290039101601f8201601f19165b8181015185820152601f190190811561164a5790611631565b505060408184015f6020820152016040528252565b90505f611610565b925082611607565b610c3f90600160ff1b61168d6107466116875f610408565b8461064b565b166116e757611196610ea16107466116ae936116a85f610408565b9061064b565b6001600160501b03806116da6107346116d56116cc6107345f610416565b95848716610446565b610c73565b1660a01b911660501b1790565b6001600160501b03806117496107346116d56116cc610734607f6117166107466117105f610408565b8b61064b565b60f81c16976001890361174e57611196611739916117335f610416565b90611efc565b975b6117445f610416565b610446565b6116da565b886002810361177b575061176e611775916117685f610416565b90611ecf565b61ffff1690565b9761173b565b611792906117759261178c5f610416565b90611e80565b6117a361179e8b610ccb565b610d30565b60ff161c90565b610c3f9190600160ff1b6117c361074661168785610408565b1661181657611196610ea16107466117de936116a886610408565b906001600160501b03806118066107346116d56117fd61073487610416565b96848816610446565b1692169160a01b9160501b171790565b906001600160501b038061186e6107346116d56117fd610734607f6118466107466118408b610408565b8c61064b565b60f81c169860018a0361187357611196611863916117338b610416565b985b61174489610416565b611806565b8960028103611893575061176e61188d916117688b610416565b98611865565b6118a49061188d9261178c8c610416565b6117a361179e8c610ccb565b9190918183018084116103e7578151106118fc5760206118cf83610185565b936118dd6040519586610153565b8385526118e984610185565b8583019390601f19013685370101905e90565b6343733a0960e11b5f5260045ffd5b90600c81101561047b5760051b0190565b90620151808202918083046201518014901517156103e757565b90610e10820291808304610e1014901517156103e757565b90603c820291808304603c14901517156103e757565b95949392915f916107b25b61ffff891661ffff821610611a9557506119e861198d610180610175565b601f815298601c60208b0152601f60408b0152601e60608b0152601f60808b0152601e60a08b0152601f60c08b0152601f60e08b0152601e6101008b0152601f6101208b0152601e6101408b0152601f6101608b0152611f0d565b611a89575b6001905b60ff811660ff831610611a4c575050610c3f95965060ff611a3d611a4494611a3083611a36819997611a30611a2b611196611a309a610cdc565b61191c565b90610446565b9216611936565b921661194e565b911690610446565b909260ff6001611a7e8293611a30611a2b6111968f611a7790611a716111968e610cdc565b9061190b565b5160ff1690565b9501169190506119f1565b601d60208901526119ed565b92600161ffff91611aa586611f0d565b15611abc57611ab390610435565b945b011661196f565b611ac590610424565b94611ab5565b600160fa1b6001600160f81b0319611aec6001600160501b0385168461064b565b511603611b09576001600160501b03610c3f9260501c16906117aa565b63a040ad4b60e01b5f5260045ffd5b6040516102209190611b2a8382610153565b6010815291601f1901366020840137565b80516020909101516001600160f01b0319811692919060028210611b5d575050565b6001600160f01b031960029290920360031b82901b16169150565b60405190611b87604083610153565b600b82526a2a864886f84d010d01021160a81b6020830152565b611baf6106bf5f9383610c51565b611bb7611b18565b915f6011915b828210611bcd5750505050600192565b90919294611bdb8683610c09565b90611c28611c20611bef6109cd8587610c51565b60028151105f14611c6557611c0f611c09611c1892611b3b565b60f01c90565b60081c60ff1690565b935b85610c81565b611355611b78565b15611c495750611c4061ffff60019216965b83610c51565b93920190611bbd565b95611c409061ffff60019316611c5f8689610467565b52611c3a565b611c09611c7191611b3b565b93611c1a565b805182515f199493909290821515828515611d4457856020602192019585010301946020601f8216810360031b93018051928787101615611d1d576020821015611cf6575050925b83815118821c15611ce3576001019284841015611cdc5792611cbf565b5050505050565b91909103601f1901945061018392505050565b90809295939120925b85815118821c15611d26575b6001019486861015611d1d5794611cff565b50505050505050565b8383822003611d0b5793909303601f19019650610183945050505050565b505f96505050505050565b919091611d5c8382611f46565b90602082018091600184510160051b840191602083019282519052600185510185525f5b81519060608352808203611dbc575b50602090885101910190838210611d80575050505081935115611db0575050565b90516001190181529150565b604051818303808252949091601f8601601f19165b82820181015184820152601f1901918215611dec5791611dd1565b9590506020939291505f848284010152603f601f199101168101604052835290611d8f565b6040518151909392909160208301601f19165b8181015186820152601f1901908115611e3d5790611e24565b505080519084830160208301601f19165b8281015182820152601f1901918215611e675791611e4e565b50505060409101808401905f6020830152845201604052565b919060208211611ec0578181018082116103e757835110611eb1576020915f199083036101000a0119920101511690565b634e0a27d960e01b5f5260045ffd5b63cbb7a73160e01b5f5260045ffd5b600282018083116103e757815110611eed57016002015161ffff1690565b63653582fb60e01b5f5260045ffd5b90611f069161064b565b5160f81c90565b60038116611f415761ffff60648183160616611f3b5761019061ffff8092160616611f3757600190565b5f90565b50600190565b505f90565b91909160609280519180519182841115611f61575b50505050565b9091929450604051946020830191602182602089019686010301935f91602084101561203b575b6020015193601f841660200360031b905b85815118821c15611fdc575b6001019486861015611fb8575b94611f99565b505050505050506020905b838103601f190160051c8452016040525f808080611f5b565b968484612011575b60208192601f19868c030181520198019015611fa55794868610611fb25750505050505050602090611fc3565b84908920036120205784611fe4565b9660010194868610611fb25750505050505050602090611fc3565b602081018490209250611f8856fea26469706673582212202108f8c9e1697ad9b588f12b49ee652dba223485b3e466f4e42f75f76a74a3f764736f6c634300081e0033
External libraries
LibForcedInclusion : 0xd1a27F331c17eD8Cbb6DAbce67A42d6b8a6B0e14
LibInboxSetup : 0xf88Ef5437749A225621101BE8C1BE1A0cE967758