Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- SecureSgxVerifier
- Optimization enabled
- true
- Compiler version
- v0.8.30+commit.73712a01
- Optimization runs
- 200
- EVM Version
- prague
- Verified at
- 2026-10-02T05:15:06.024716Z
Constructor Arguments
0x0000000000000000000000000000000000000000000000000000000000028c590000000000000000000000004779d18931b35540f84b0cd0e9633855b84df7b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015180
Arg [0] (uint64) : 167001
Arg [1] (address) : 0x4779d18931b35540f84b0cd0e9633855b84df7b8
Arg [2] (address) : 0x0000000000000000000000000000000000000000
Arg [3] (address) : 0x0000000000000000000000000000000000000000
Arg [4] (uint64) : 86400
contracts/layer1/verifiers/SecureSgxVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import { SgxVerifier } from "./SgxVerifier.sol";
import { TCBStatus } from "@automata-network/on-chain-pccs/helpers/FmspcTcbHelper.sol";
/// @title SecureSgxVerifier
/// @notice SGX verifier for mainnet/production: the strict TCB-status policy plus a per-MRENCLAVE
/// ATTRIBUTES pin. On top of the universal forbidden-attribute floor enforced by `SgxVerifier`
/// (DEBUG / PROVISION_KEY / EINITTOKEN_KEY), every allowlisted enclave measurement must declare the
/// exact ATTRIBUTES profile it is allowed to register with. Registration of an enclave with no
/// configured policy fails closed, so permissionless registration cannot admit an attribute
/// combination (e.g. a reserved bit, or a missing INIT/MODE64BIT) that the global deny-mask alone
/// would not catch.
/// @custom:security-contact security@taiko.xyz
contract SecureSgxVerifier is SgxVerifier {
/// @notice The ATTRIBUTES profile an allowlisted enclave measurement is pinned to. A
/// registering quote is accepted only when `quoteAttributes & mask == expected`. A zero `mask`
/// means no policy is configured and registration for that MRENCLAVE is rejected.
/// @param mask The ATTRIBUTES bits that are checked.
/// @param expected The required value of the checked bits (must have no bit set outside `mask`).
struct AttributePolicy {
bytes16 mask;
bytes16 expected;
}
/// @notice The ATTRIBUTES pin for each allowlisted application-enclave measurement.
mapping(bytes32 mrEnclave => AttributePolicy policy) public enclaveAttributePolicy;
/// @notice A security delay between a non-owner registration via `registerInstance` and the
/// instance becoming usable for proof verification. It gives off-chain monitoring a window to
/// evict a rogue self-registered instance (via `deleteInstances`) before it can prove. Owner
/// registrations — `addInstances`, or `registerInstance` called by the owner — are NOT delayed.
/// Set once at construction (mainnet/testnet deployments use 24 hours); it must be non-zero and
/// must not exceed `INSTANCE_EXPIRY`.
uint64 public immutable instanceValidityDelay;
/// @notice Emitted when an MRENCLAVE's ATTRIBUTES pin is set or updated.
/// @param mrEnclave The application-enclave measurement.
/// @param mask The checked ATTRIBUTES bits.
/// @param expected The required value of the checked bits.
/// @param version The new policy version; instances registered under this pin record it and are
/// revoked once it no longer matches.
event EnclaveAttributePolicySet(
bytes32 indexed mrEnclave, bytes16 mask, bytes16 expected, uint32 version
);
/// @notice Emitted when an MRENCLAVE's ATTRIBUTES pin is removed.
/// @param mrEnclave The application-enclave measurement.
event EnclaveAttributePolicyRemoved(bytes32 indexed mrEnclave);
constructor(
uint64 _taikoChainId,
address _owner,
address _automataDcapAttestation,
address _registrar,
uint64 _instanceValidityDelay
)
SgxVerifier(_taikoChainId, _owner, _automataDcapAttestation, _registrar)
{
// The delay must be positive (a zero delay defeats the monitoring window) and no longer than
// the validity window itself.
require(
_instanceValidityDelay > 0 && _instanceValidityDelay <= INSTANCE_EXPIRY,
SGX_INVALID_VALIDITY_DELAY()
);
instanceValidityDelay = _instanceValidityDelay;
}
/// @dev Restricts a call to the owner or `_addr` (used for `removeEnclaveAttributePolicy` with
/// the registrar).
/// @param _addr The additional address allowed alongside the owner.
modifier onlyOwnerOr(address _addr) {
require(msg.sender == owner() || msg.sender == _addr, SGX_NOT_AUTHORIZED());
_;
}
/// @notice Sets (or updates) the ATTRIBUTES pin for an allowlisted enclave measurement.
/// @dev The mask must cover every universally-forbidden bit and the expected value must clear
/// them, so a per-enclave pin can never re-admit a debug/provisioning/launch enclave; the
/// expected value must not assert any bit outside the mask.
/// @param _mrEnclave The application-enclave measurement to pin.
/// @param _mask The ATTRIBUTES bits to check (must be non-zero and cover the forbidden bits).
/// @param _expected The required value of the checked bits.
function setEnclaveAttributePolicy(
bytes32 _mrEnclave,
bytes16 _mask,
bytes16 _expected
)
external
onlyOwner
{
// A non-zero mask is what marks the policy as configured.
require(_mask != bytes16(0), SGX_INVALID_ATTRIBUTE_POLICY());
// The expected value must not assert any bit the mask does not check.
require(_expected & ~_mask == bytes16(0), SGX_INVALID_ATTRIBUTE_POLICY());
// The mask must check every universally-forbidden bit and the expected value must clear
// them: the per-enclave pin can never re-admit a debug/provisioning/launch enclave.
require(
_mask & SGX_FORBIDDEN_ATTRIBUTE_MASK == SGX_FORBIDDEN_ATTRIBUTE_MASK,
SGX_INVALID_ATTRIBUTE_POLICY()
);
require(
_expected & SGX_FORBIDDEN_ATTRIBUTE_MASK == bytes16(0), SGX_INVALID_ATTRIBUTE_POLICY()
);
// Bump the version on every set (including an in-place edit) so any change revokes instances
// registered under the previous pin. The counter is never reset, so a removed-then-re-added
// pin gets a brand-new version and cannot re-enable previously registered instances. The
// version shares the per-MRENCLAVE slot with the allowlist flag, so `verifyProof` reads both
// in one SLOAD.
uint32 version = mrEnclaveState[_mrEnclave].policyVersion + 1;
mrEnclaveState[_mrEnclave].policyVersion = version;
enclaveAttributePolicy[_mrEnclave] = AttributePolicy(_mask, _expected);
emit EnclaveAttributePolicySet(_mrEnclave, _mask, _expected, version);
}
/// @notice Returns the current per-MRENCLAVE policy version (the generation an instance must still
/// match to verify proofs). Zero means the pin was never set.
/// @param _mrEnclave The application-enclave measurement.
/// @return The current policy version.
function enclaveAttributePolicyVersion(bytes32 _mrEnclave) external view returns (uint32) {
return mrEnclaveState[_mrEnclave].policyVersion;
}
/// @notice Removes the ATTRIBUTES pin for an enclave measurement. Registration for that MRENCLAVE
/// then fails closed until a new pin is set, and — because `verifyProof` re-checks the current pin
/// — every instance already registered under it is revoked (invalidated, not deleted) and can no
/// longer verify proofs.
/// @dev Callable by the owner or the `registrar` (the SGX-instance registrar set at
/// construction); the registrar can only remove pins, so it can fail-close a compromised enclave
/// but cannot relax or re-admit one. When `registrar` is `address(0)`, removal is owner-only.
/// Removal bumps the monotonic policy version (so `verifyProof` needs only a single version
/// comparison to reject revoked instances) and the counter is never reset, so a later re-add gets
/// a fresh version and cannot re-enable the revoked instances.
/// @param _mrEnclave The application-enclave measurement whose pin is removed.
function removeEnclaveAttributePolicy(bytes32 _mrEnclave) external onlyOwnerOr(registrar) {
require(
enclaveAttributePolicy[_mrEnclave].mask != bytes16(0), SGX_ATTRIBUTE_POLICY_NOT_SET()
);
// Bump the version so every instance registered under this pin is revoked at proof time by a
// single version mismatch; no live instance can hold the bumped version because registration
// for this MRENCLAVE is now fail-closed until a new pin is set (which bumps again).
mrEnclaveState[_mrEnclave].policyVersion += 1;
delete enclaveAttributePolicy[_mrEnclave];
emit EnclaveAttributePolicyRemoved(_mrEnclave);
}
/// @inheritdoc SgxVerifier
/// @dev Strict policy: accept the TCB statuses whose platform microcode is up to date — `OK`,
/// `TCB_SW_HARDENING_NEEDED` and `TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED` (their mitigations
/// live in configuration / enclave software pinned by the MRENCLAVE allowlist, not in microcode).
/// The out-of-date statuses (`TCB_OUT_OF_DATE`, `TCB_OUT_OF_DATE_CONFIGURATION_NEEDED`) are
/// rejected, where the platform may be missing the microcode that patches SGX key-extraction
/// vulnerabilities (so the in-enclave signing key could be extractable); `TCB_CONFIGURATION_NEEDED`,
/// `TCB_REVOKED` and `TCB_UNRECOGNIZED` are rejected too. The policy is expressed against the
/// attestation's `TCBStatus` enum so an enum reorder is caught at compile time.
function isTcbStatusAccepted(uint8 _status) public pure override returns (bool) {
return _status == uint8(TCBStatus.OK) || _status == uint8(TCBStatus.TCB_SW_HARDENING_NEEDED)
|| _status == uint8(TCBStatus.TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED);
}
/// @inheritdoc SgxVerifier
/// @dev Fail-closed per-MRENCLAVE ATTRIBUTES pin: the enclave must have a configured policy and
/// its attested ATTRIBUTES must match the pinned profile over the checked bits. Returns the
/// current policy version so it is recorded on the instance for the `verifyProof` re-check.
function _validateEnclaveAttributes(
bytes32 _mrEnclave,
bytes16 _attributes
)
internal
view
override
returns (uint32 policyVersion_)
{
AttributePolicy memory policy = enclaveAttributePolicy[_mrEnclave];
require(policy.mask != bytes16(0), SGX_ATTRIBUTE_POLICY_NOT_SET());
require(_attributes & policy.mask == policy.expected, SGX_ATTRIBUTE_MISMATCH());
return mrEnclaveState[_mrEnclave].policyVersion;
}
/// @inheritdoc SgxVerifier
/// @dev Additionally requires the per-MRENCLAVE pin that gated registration to still be in force:
/// any edit, or a removal (which bumps the version too), changes the version so the recorded
/// version no longer matches and the instance is revoked. Owner-added instances (`mrEnclave == 0`)
/// are exempt, and the base trusted-MRENCLAVE/MRSIGNER allowlist re-check still applies on top.
/// Reads the per-MRENCLAVE slot once: it carries both the current version and the allowlist flag.
function _isEnclaveStillTrusted(Instance memory _instance)
internal
view
override
returns (bool)
{
if (_instance.mrEnclave == bytes32(0)) return true;
MrEnclaveState memory state = mrEnclaveState[_instance.mrEnclave];
if (state.policyVersion != _instance.policyVersion) return false;
if (!checkLocalEnclaveReport) return true;
return state.trusted && trustedUserMrSigner[_instance.mrSigner];
}
/// @inheritdoc SgxVerifier
function _validityDelay() internal view override returns (uint64) {
return instanceValidityDelay;
}
// ---------------------------------------------------------------
// Custom Errors
// ---------------------------------------------------------------
error SGX_ATTRIBUTE_POLICY_NOT_SET();
error SGX_ATTRIBUTE_MISMATCH();
error SGX_INVALID_ATTRIBUTE_POLICY();
error SGX_NOT_AUTHORIZED();
error SGX_INVALID_VALIDITY_DELAY();
}
contracts/layer1/verifiers/IDcapAttestation.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title IDcapAttestation
/// @notice Minimal interface for Automata's DCAP attestation entrypoint
/// (`AutomataDcapAttestationFee`), used by the SGX verifier to verify remote-attestation quotes
/// fully on-chain.
/// @dev The implementation is the pinned npm dependency
/// "automata-network/automata-dcap-attestation" v1.1.0 (latest stable release; Trail of Bits
/// audited the v1.0 baseline and v1.1.0 incorporates the audit remediation). It reads Intel
/// collateral from on-chain PCCS (the "automata-network/on-chain-pccs" package). Enclave-identity
/// policy (MRENCLAVE/MRSIGNER allowlist), TCB-status acceptance, and DEBUG-enclave rejection are
/// enforced by the SGX verifier, not here.
/// @custom:security-contact security@taiko.xyz
interface IDcapAttestation {
/// @notice Verifies an Intel DCAP quote fully on-chain.
/// @param rawQuote The Intel DCAP quote serialized as raw bytes.
/// @return success_ Whether the quote was successfully verified.
/// @return output_ The serialized verification output. On success this is the packed
/// `Output` struct: quoteVersion (2 bytes, BE), quoteBodyType (2 bytes, BE), tcbStatus
/// (1 byte), fmspc (6 bytes), followed by the quote body. On failure it is a UTF-8 reason
/// string.
function verifyAndAttestOnChain(bytes calldata rawQuote)
external
payable
returns (bool success_, bytes memory output_);
}
contracts/layer1/verifiers/IProofVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title IProofVerifier
/// @notice Interface for verifying validity proofs for state transitions
/// @custom:security-contact security@taiko.xyz
interface IProofVerifier {
/// @notice Verifies a validity proof for a state transition
/// @dev This function must revert if the proof is invalid
/// @param _proposalAge The age in seconds of the proposal being proven. Only set for
/// single-proposal proofs (calculated as block.timestamp - proposal.timestamp).
/// For multi-proposal batches, this is always 0, meaning "not applicable".
/// Verifiers should interpret _proposalAge == 0 as "not applicable" rather than
/// "instant proof". This parameter enables age-based verification logic, such as
/// detecting and handling prover-killer proposals differently.
/// @param _commitmentHash Hash of the last proposal hash and commitment data
/// @param _proof The proof data
function verifyProof(
uint256 _proposalAge,
bytes32 _commitmentHash,
bytes calldata _proof
)
external
view;
}
contracts/layer1/verifiers/LibPublicInput.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import { EfficientHashLib } from "solady/src/utils/EfficientHashLib.sol";
/// @title LibPublicInput
/// @notice A library for handling hashing the so-called public input hash, used by sgx and zk
/// proofs.
/// @custom:security-contact security@taiko.xyz
library LibPublicInput {
/// @notice Hashes the public input for the proof verification.
/// @param _aggregatedProvingHash The aggregated proving hash from the inbox.
/// @param _verifierContract The contract address which as current verifier.
/// @param _proofSigner The address of the instance that signed this proof. For SGX it is the
/// signer address, for ZK this variable is not used and must have value address(0).
/// @param _chainId The chain id.
/// @return The public input hash.
function hashPublicInputs(
bytes32 _aggregatedProvingHash,
address _verifierContract,
address _proofSigner,
uint64 _chainId
)
internal
pure
returns (bytes32)
{
require(_aggregatedProvingHash != bytes32(0), InvalidAggregatedProvingHash());
return EfficientHashLib.hash(
bytes32("VERIFY_PROOF"),
bytes32(uint256(_chainId)),
bytes32(uint256(uint160(_verifierContract))),
_aggregatedProvingHash,
bytes32(uint256(uint160(_proofSigner)))
);
}
/// @dev Hashes the public input for the ZK aggregation proof verification,
/// which contains the sub image id to be aggregated for security.
/// @param _blockProvingProgram The proving program identifier.
/// @param _aggregatedProvingHash The aggregated proving hash from the inbox.
/// @return The ZK aggregation public input hash.
function hashZKAggregationPublicInputs(
bytes32 _blockProvingProgram,
bytes32 _aggregatedProvingHash
)
internal
pure
returns (bytes32)
{
return EfficientHashLib.hash(_blockProvingProgram, _aggregatedProvingHash);
}
// ---------------------------------------------------------------
// Errors
// ---------------------------------------------------------------
error InvalidAggregatedProvingHash();
}
contracts/layer1/verifiers/SgxVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import { IDcapAttestation } from "./IDcapAttestation.sol";
import { IProofVerifier } from "./IProofVerifier.sol";
import { LibPublicInput } from "./LibPublicInput.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/// @title SgxVerifier
/// @notice Abstract base that verifies SGX signature proofs onchain using attested SGX instances.
/// Each instance is registered via remote attestation and can verify proofs until expiry. The
/// TCB-status acceptance policy is left abstract so that per-network subclasses define it (the
/// strict mainnet policy must remain the secure default).
/// @dev Side-channel protection is achieved through mandatory instance expiry (INSTANCE_EXPIRY),
/// requiring periodic re-attestation with new keypairs.
/// @custom:security-contact security@taiko.xyz
abstract contract SgxVerifier is IProofVerifier, Ownable2Step, ReentrancyGuard {
/// @dev Each public-private key pair (Ethereum address) is generated within
/// the SGX program when it boots up. The off-chain remote attestation
/// ensures the validity of the program hash and has the capability of
/// bootstrapping the network with trustworthy instances.
/// @dev `mrEnclave`, `mrSigner` and `policyVersion` let `verifyProof` re-check the *current*
/// enclave policy at proof time, so revoking trust in a measurement/signer (or changing/removing
/// its policy) also stops its already-registered instances — a registration-time-only check
/// cannot.
struct Instance {
// The instance's ECDSA address (SGX signing key); the unit of identity for proof signatures.
address addr;
// Unix time from which the instance may verify proofs (after the registration validity delay
// for non-owner registrations); it stays valid until `validSince + INSTANCE_EXPIRY`.
uint64 validSince;
// The per-MRENCLAVE policy version in force when this instance registered. `verifyProof`
// rejects the instance once the subclass's current version for `mrEnclave` no longer matches,
// so editing — or removing and re-adding — that policy revokes instances registered under the
// old policy. Zero for owner-added instances and subclasses with no versioned policy.
uint32 policyVersion;
// The attested application-enclave measurement (MRENCLAVE) this instance registered with, or
// `bytes32(0)` for an owner-added instance (`addInstances`) that carries no attestation and is
// therefore exempt from the proof-time policy re-check.
bytes32 mrEnclave;
// The attested enclave signer (MRSIGNER) this instance registered with (`bytes32(0)` for
// owner-added instances). Re-checked against the trusted-MRSIGNER allowlist at proof time
// while the local report check is enforced, so untrusting a signer revokes its instances.
bytes32 mrSigner;
}
/// @notice The expiry time for the SGX instance (3 months).
uint64 public constant INSTANCE_EXPIRY = 90 days;
/// @dev SGX ATTRIBUTES.FLAGS bits that a production application enclave must never set. In DCAP
/// quote bytes the 16-byte ATTRIBUTES field is FLAGS (low 8 bytes, little-endian) followed by
/// XFRM, so these FLAGS bits live in the first byte. Enforced uniformly on every network (it is
/// NOT part of the per-network policy): such an enclave must never be trusted on-chain.
/// DEBUG(0x02): the host can read/write enclave memory, so the in-enclave signing key is
/// extractable.
/// PROVISION_KEY(0x10): the enclave can derive platform-identifying provisioning keys.
/// EINITTOKEN_KEY(0x20): the enclave can derive the launch-token key, a launch-enclave-only
/// privilege an application enclave must never hold.
/// Subclasses may pin the remaining bits per-MRENCLAVE via `_validateEnclaveAttributes`.
bytes16 internal constant SGX_FORBIDDEN_ATTRIBUTE_MASK =
bytes16(0x32000000000000000000000000000000);
/// @dev DEBUG bit (bit 1) of the little-endian SGX ATTRIBUTES flags. A subset of
/// `SGX_FORBIDDEN_ATTRIBUTE_MASK`, checked separately so a debug enclave reverts with the
/// dedicated `SGX_DEBUG_ENCLAVE` error (the migration's headline security guard).
uint8 private constant SGX_FLAGS_DEBUG = 0x02;
/// @dev Field offsets within the Automata DCAP `Output` header: quoteVersion (BE uint16) at 0,
/// quoteBodyType (BE uint16) at 2, tcbStatus (1 byte) at 4, fmspc (6 bytes) at 5; the quote
/// body follows at offset 11 (= 2 + 2 + 1 + 6).
uint256 private constant OUTPUT_VERSION_OFFSET = 0;
uint256 private constant OUTPUT_BODY_TYPE_OFFSET = 2;
uint256 private constant OUTPUT_TCB_STATUS_OFFSET = 4;
uint256 private constant OUTPUT_BODY_OFFSET = 11;
/// @dev `quoteBodyType` value identifying an SGX Enclave Report body.
uint8 private constant SGX_QUOTE_BODY_TYPE = 1;
/// @dev Quote version handled by this verifier (Intel DCAP V3 / SGX).
uint8 private constant SGX_QUOTE_VERSION = 3;
/// @dev Length of an Intel SGX quote header.
uint256 private constant HEADER_LENGTH = 48;
/// @dev Length of an SGX Enclave Report body.
uint256 private constant ENCLAVE_REPORT_LENGTH = 384;
/// @dev MRENCLAVE offset within the raw quote (header + enclave-report offset 64).
uint256 private constant MRENCLAVE_OFFSET = HEADER_LENGTH + 64;
/// @dev MRSIGNER offset within the raw quote (header + enclave-report offset 128).
uint256 private constant MRSIGNER_OFFSET = HEADER_LENGTH + 128;
/// @dev reportData offset within the raw quote (header + enclave-report offset 320).
uint256 private constant REPORT_DATA_OFFSET = HEADER_LENGTH + 320;
/// @dev `attributes` offset within the raw quote (header + enclave-report offset 48).
uint256 private constant ATTRIBUTES_OFFSET = HEADER_LENGTH + 48;
uint64 public immutable taikoChainId;
address public immutable automataDcapAttestation;
/// @notice The address authorized to register SGX instances via `registerInstance`.
/// @dev If set to a non-zero address, only this address may call `registerInstance`.
/// If set to `address(0)`, `registerInstance` is permissionless and callable by anyone.
/// The registrar also selects the quote-freshness policy: permissionless registration requires
/// the attested quote to commit a recent L1 block (see `registerInstance`), while a trusted
/// registrar vouches for the provenance — and thus the age — of the quotes it submits, so its
/// registrations are exempt.
address public immutable registrar;
/// @dev For gas savings, we assign each SGX instance with an ID to minimize storage operations.
uint256 public nextInstanceId;
/// @dev One SGX instance is uniquely identified (on-chain) by its ECDSA public key
/// (or rather ethereum address). The instance address remains valid for INSTANCE_EXPIRY
/// duration (90 days) to protect against side-channel attacks through forced key expiry.
/// After expiry, the instance must be re-attested and registered with a new address.
mapping(uint256 instanceId => Instance instance) public instances;
/// @dev One address shall be registered (during attestation) only once, otherwise it could
/// bypass this contract's expiry check by always registering with the same attestation and
/// getting multiple valid instanceIds.
mapping(address instanceAddress => bool alreadyAttested) public addressRegistered;
/// @dev Per-MRENCLAVE state, packed into a single slot so `verifyProof` reads the allowlist flag
/// and the policy version in one SLOAD.
struct MrEnclaveState {
// Whether this MRENCLAVE is on the trusted allowlist (set via `setMrEnclave`, enforced at
// registration and proof time while `checkLocalEnclaveReport` is on).
bool trusted;
// The current per-MRENCLAVE policy generation, maintained by versioned subclasses
// (`SecureSgxVerifier`); always 0 in the base / non-versioned subclasses. `verifyProof`
// rejects an instance whose recorded `policyVersion` no longer equals this value.
uint32 policyVersion;
}
/// @dev Relocated from the replaced AutomataDcapV3Attestation contract. The new Automata DCAP
/// entrypoint verifies quote authenticity and TCB status but does NOT allowlist the application
/// enclave's identity, so the trusted MRENCLAVE/MRSIGNER policy is enforced here to preserve the
/// pre-migration security model. Enabled by default (set in the constructor); toggle off with
/// toggleLocalReportCheck().
bool public checkLocalEnclaveReport;
/// @dev Trusted-MRENCLAVE allowlist + policy version, co-located per measurement. Exposed via the
/// `trustedUserMrEnclave` view (allowlist flag) and, in versioned subclasses, a policy-version
/// view; `internal` so those subclasses can maintain the version in the same slot.
mapping(bytes32 mrEnclave => MrEnclaveState state) internal mrEnclaveState;
mapping(bytes32 mrSigner => bool trusted) public trustedUserMrSigner;
/// @dev Once an MRENCLAVE/MRSIGNER has been untrusted it is recorded here and can never be
/// re-trusted, so instances revoked by an allowlist removal can never be silently revived by
/// re-adding the same value. Set only on a trusted -> untrusted transition in
/// `setMrEnclave`/`setMrSigner`.
mapping(bytes32 mrEnclave => bool revoked) public revokedMrEnclave;
mapping(bytes32 mrSigner => bool revoked) public revokedMrSigner;
/// @notice Emitted when a new SGX instance is added to the registry.
/// @param id The ID of the SGX instance.
/// @param instance The address of the SGX instance.
/// @param replaced Reserved for future use (always zero address).
/// @param validSince The time since the instance is valid.
event InstanceAdded(
uint256 indexed id, address indexed instance, address indexed replaced, uint256 validSince
);
/// @notice Emitted when an SGX instance is deleted from the registry.
/// @param id The ID of the SGX instance.
/// @param instance The address of the SGX instance.
event InstanceDeleted(uint256 indexed id, address indexed instance);
/// @notice Emitted when a trusted MRENCLAVE value is updated.
/// @param mrEnclave The MRENCLAVE value.
/// @param trusted Whether the value is trusted.
event MrEnclaveUpdated(bytes32 indexed mrEnclave, bool trusted);
/// @notice Emitted when a trusted MRSIGNER value is updated.
/// @param mrSigner The MRSIGNER value.
/// @param trusted Whether the value is trusted.
event MrSignerUpdated(bytes32 indexed mrSigner, bool trusted);
/// @notice Emitted when a previously-trusted MRENCLAVE is permanently revoked (it can never be
/// re-trusted). Fires only on the trusted -> untrusted transition, alongside `MrEnclaveUpdated`.
/// @param mrEnclave The MRENCLAVE value.
event MrEnclaveRevoked(bytes32 indexed mrEnclave);
/// @notice Emitted when a previously-trusted MRSIGNER is permanently revoked (it can never be
/// re-trusted). Fires only on the trusted -> untrusted transition, alongside `MrSignerUpdated`.
/// @param mrSigner The MRSIGNER value.
event MrSignerRevoked(bytes32 indexed mrSigner);
/// @notice Emitted when enforcement of the local enclave identity allowlist is toggled.
/// @param checkLocalEnclaveReport Whether the allowlist is enforced.
event LocalReportCheckToggled(bool checkLocalEnclaveReport);
error SGX_ALREADY_ATTESTED();
error SGX_DEBUG_ENCLAVE();
error SGX_FORBIDDEN_ATTRIBUTES();
error SGX_INVALID_ATTESTATION();
error SGX_INVALID_INSTANCE();
error SGX_INVALID_PROOF();
error SGX_INSTANCE_ID_OVERFLOW();
error SGX_INVALID_CHAIN_ID();
error SGX_NOT_REGISTRAR();
error SGX_STALE_QUOTE();
error SGX_QUOTE_BLOCK_HASH_MISMATCH();
error SGX_MR_ENCLAVE_REVOKED();
error SGX_MR_SIGNER_REVOKED();
constructor(
uint64 _taikoChainId,
address _owner,
address _automataDcapAttestation,
address _registrar
) {
require(_taikoChainId != 0, SGX_INVALID_CHAIN_ID());
taikoChainId = _taikoChainId;
automataDcapAttestation = _automataDcapAttestation;
registrar = _registrar;
// Enforce the trusted MRENCLAVE/MRSIGNER allowlist by default (fail-closed): until the owner
// trusts at least one MRENCLAVE and MRSIGNER, no instance can register. Disable with
// toggleLocalReportCheck() if the Automata entrypoint alone is considered sufficient.
checkLocalEnclaveReport = true;
_transferOwnership(_owner);
}
/// @notice Adds trusted SGX instances to the registry.
/// @param _instances The address array of trusted SGX instances.
/// @return The respective instanceId array per addresses.
function addInstances(address[] calldata _instances)
external
onlyOwner
returns (uint256[] memory)
{
// Owner-added instances carry no attested measurement, so they record no MRENCLAVE/MRSIGNER/
// policy version and are exempt from the proof-time enclave-policy re-check.
return _addInstances(_instances, true, bytes32(0), bytes32(0), 0);
}
/// @notice Deletes SGX instances from the registry.
/// @param _ids The ids array of SGX instances.
function deleteInstances(uint256[] calldata _ids) external onlyOwner {
uint256 size = _ids.length;
for (uint256 i; i < size; ++i) {
uint256 idx = _ids[i];
require(instances[idx].addr != address(0), SGX_INVALID_INSTANCE());
emit InstanceDeleted(idx, instances[idx].addr);
delete instances[idx];
}
}
/// @notice Sets whether a given MRENCLAVE is trusted for instance registration.
/// @dev Untrusting a currently-trusted MRENCLAVE is permanent: the value is recorded as revoked
/// and can never be re-trusted. Without this, untrusting a measurement (to revoke its fleet) and
/// later re-trusting the same value — e.g. to onboard a new fleet under it — would silently
/// revive every previously-revoked instance, because the proof-time re-check keys only off the
/// current boolean. Onboard a new enclave build under a fresh MRENCLAVE instead. Concrete
/// compromised instances are revoked irreversibly with `deleteInstances`.
/// @param _mrEnclave The MRENCLAVE value.
/// @param _trusted Whether the value is trusted.
function setMrEnclave(bytes32 _mrEnclave, bool _trusted) external onlyOwner {
if (_trusted) {
require(!revokedMrEnclave[_mrEnclave], SGX_MR_ENCLAVE_REVOKED());
} else if (mrEnclaveState[_mrEnclave].trusted) {
revokedMrEnclave[_mrEnclave] = true;
// Distinct from `MrEnclaveUpdated(_, false)` so off-chain monitoring can detect the
// permanent trusted -> revoked transition without diffing `revokedMrEnclave`.
emit MrEnclaveRevoked(_mrEnclave);
}
mrEnclaveState[_mrEnclave].trusted = _trusted;
emit MrEnclaveUpdated(_mrEnclave, _trusted);
}
/// @notice Returns whether a given MRENCLAVE is on the trusted allowlist.
/// @param _mrEnclave The MRENCLAVE value.
/// @return Whether the MRENCLAVE is trusted.
function trustedUserMrEnclave(bytes32 _mrEnclave) external view returns (bool) {
return mrEnclaveState[_mrEnclave].trusted;
}
/// @notice Sets whether a given MRSIGNER is trusted for instance registration.
/// @dev Untrusting a currently-trusted MRSIGNER is permanent (see `setMrEnclave` for the
/// revival hazard this closes): the value is recorded as revoked and can never be re-trusted.
/// @param _mrSigner The MRSIGNER value.
/// @param _trusted Whether the value is trusted.
function setMrSigner(bytes32 _mrSigner, bool _trusted) external onlyOwner {
if (_trusted) {
require(!revokedMrSigner[_mrSigner], SGX_MR_SIGNER_REVOKED());
} else if (trustedUserMrSigner[_mrSigner]) {
revokedMrSigner[_mrSigner] = true;
// Distinct from `MrSignerUpdated(_, false)` so off-chain monitoring can detect the
// permanent trusted -> revoked transition without diffing `revokedMrSigner`.
emit MrSignerRevoked(_mrSigner);
}
trustedUserMrSigner[_mrSigner] = _trusted;
emit MrSignerUpdated(_mrSigner, _trusted);
}
/// @notice Toggles enforcement of the trusted MRENCLAVE/MRSIGNER allowlist.
function toggleLocalReportCheck() external onlyOwner {
checkLocalEnclaveReport = !checkLocalEnclaveReport;
emit LocalReportCheckToggled(checkLocalEnclaveReport);
}
/// @notice Adds an SGX instance after remote attestation is verified fully on-chain.
/// @dev Migrated to the Automata DCAP attestation entrypoint
/// (`IDcapAttestation.verifyAndAttestOnChain`), which consumes a raw quote and reads Intel
/// collateral from on-chain PCCS. The trusted MRENCLAVE/MRSIGNER allowlist and the TCB-status
/// acceptance policy are enforced here (previously in AutomataDcapV3Attestation).
/// @dev A non-owner (permissionless or registrar) registration is subject to the validity
/// delay; an owner-submitted registration is as trusted as `addInstances` and takes effect
/// immediately.
/// @dev When registration is permissionless (`registrar == address(0)`), the quote must also
/// commit a recent L1 block in reportData — block number (8 bytes, big-endian) then that
/// block's hash (32 bytes), right after the 20-byte instance address — proving the quote was
/// generated within the last 256 blocks (see the freshness gate below).
/// @param _rawQuote The raw Intel DCAP v3 (SGX) attestation quote.
/// @return The respective instanceId.
function registerInstance(bytes calldata _rawQuote) external nonReentrant returns (uint256) {
// When a registrar is configured, only it may register instances; otherwise registration
// is permissionless.
require(registrar == address(0) || msg.sender == registrar, SGX_NOT_REGISTRAR());
// Fail fast with a clear error if this verifier was deployed without an attestation
// entrypoint (e.g. a dummy-verifier deployment).
require(automataDcapAttestation != address(0), SGX_INVALID_ATTESTATION());
// Reject anything too short to hold a header + SGX enclave report body before the
// expensive attestation call. This also guarantees every fixed-offset slice below
// (attributes, MRENCLAVE, MRSIGNER, reportData) is in bounds.
require(
_rawQuote.length >= HEADER_LENGTH + ENCLAVE_REPORT_LENGTH, SGX_INVALID_ATTESTATION()
);
// The Taiko-owned attestation entrypoint runs feeless, so forward zero value; this function
// is non-payable, so stray ETH can never be sent here or trapped in the verifier.
(bool verified, bytes memory output) =
IDcapAttestation(automataDcapAttestation).verifyAndAttestOnChain{ value: 0 }(_rawQuote);
require(verified, SGX_INVALID_ATTESTATION());
// `output` is the serialized Automata `Output`; require a full SGX enclave report body.
require(
output.length >= OUTPUT_BODY_OFFSET + ENCLAVE_REPORT_LENGTH, SGX_INVALID_ATTESTATION()
);
// quoteVersion is a big-endian uint16 at output[0:2]; this verifier handles V3 only.
require(
uint8(output[OUTPUT_VERSION_OFFSET]) == 0
&& uint8(output[OUTPUT_VERSION_OFFSET + 1]) == SGX_QUOTE_VERSION,
SGX_INVALID_ATTESTATION()
);
// quoteBodyType is a big-endian uint16 at output[2:4]; 1 == SGX Enclave Report.
require(
uint8(output[OUTPUT_BODY_TYPE_OFFSET]) == 0
&& uint8(output[OUTPUT_BODY_TYPE_OFFSET + 1]) == SGX_QUOTE_BODY_TYPE,
SGX_INVALID_ATTESTATION()
);
// Reject quotes whose platform TCB is not up to date (see isTcbStatusAccepted).
require(
isTcbStatusAccepted(uint8(output[OUTPUT_TCB_STATUS_OFFSET])), SGX_INVALID_ATTESTATION()
);
// Bind the fields read from the raw quote below (DEBUG attributes, MRENCLAVE/MRSIGNER,
// reportData) to the enclave report the entrypoint actually authenticated. Automata's
// verifier copies the raw enclave report into the Output body verbatim
// (output[OUTPUT_BODY_OFFSET : +ENCLAVE_REPORT_LENGTH] == _rawQuote enclave report) and
// verifies its integrity, so requiring byte-equality proves those fields come from verified
// bytes — not attacker-controlled data outside the authenticated region. Reading the body
// from `output` (memory) needs assembly; the prior output.length check makes the region
// safe to hash.
bytes32 verifiedBodyHash;
assembly {
verifiedBodyHash := keccak256(
add(add(output, 0x20), OUTPUT_BODY_OFFSET),
ENCLAVE_REPORT_LENGTH
)
}
require(
verifiedBodyHash
== keccak256(_rawQuote[HEADER_LENGTH:HEADER_LENGTH + ENCLAVE_REPORT_LENGTH]),
SGX_INVALID_ATTESTATION()
);
// Reject DEBUG-mode enclaves: a debug enclave's memory (including the in-enclave signing
// key recorded in reportData) is readable and writable by the host, so its quotes must
// never be trusted on-chain. SECURITY-CRITICAL: omitting this DEBUG-attribute check lets a
// host-controlled debug enclave forge SGX proofs (a gap previously exploited in production);
// this guard must never be removed or weakened. DEBUG is bit 1 of the SGX ATTRIBUTES flags;
// the flags are little-endian, so the bit lives in the low byte of the 16-byte `attributes`
// field at enclave-report offset 48 (raw-quote offset HEADER_LENGTH + 48).
require((uint8(_rawQuote[ATTRIBUTES_OFFSET]) & SGX_FLAGS_DEBUG) == 0, SGX_DEBUG_ENCLAVE());
// Read the authenticated MRENCLAVE, MRSIGNER and full 16-byte ATTRIBUTES (FLAGS || XFRM) from
// the verified enclave report for the attribute policies below; all are bound to the report by
// the body-hash check above. MRENCLAVE and MRSIGNER are recorded on the instance so
// `verifyProof` can re-check the current allowlist/policy at proof time.
bytes32 mrEnclave = bytes32(_rawQuote[MRENCLAVE_OFFSET:MRENCLAVE_OFFSET + 32]);
bytes32 mrSigner = bytes32(_rawQuote[MRSIGNER_OFFSET:MRSIGNER_OFFSET + 32]);
bytes16 attributes = bytes16(_rawQuote[ATTRIBUTES_OFFSET:ATTRIBUTES_OFFSET + 16]);
// Universal forbidden-attribute floor, enforced on every network (DEBUG / PROVISION_KEY /
// EINITTOKEN_KEY). DEBUG is also rejected above with a dedicated error; the remaining bits
// are caught here so even the lenient devnet verifier can never admit a provisioning or
// launch enclave.
require(attributes & SGX_FORBIDDEN_ATTRIBUTE_MASK == bytes16(0), SGX_FORBIDDEN_ATTRIBUTES());
// Per-network enclave-identity policy on top of the universal floor. The strict mainnet
// subclass pins the full ATTRIBUTES profile per allowlisted MRENCLAVE; the base/devnet
// implementation is a no-op. It returns the policy version to bind to the instance so
// `verifyProof` can revoke the instance if that policy is later changed or removed (0 when the
// subclass has no versioned policy).
uint32 policyVersion = _validateEnclaveAttributes(mrEnclave, attributes);
if (checkLocalEnclaveReport) {
require(
mrEnclaveState[mrEnclave].trusted && trustedUserMrSigner[mrSigner],
SGX_INVALID_ATTESTATION()
);
}
// The SGX program embeds its freshly generated instance address in the first 20 bytes of
// the report's reportData; we trust the off-chain prover to do so (unchanged from the
// pre-migration design). A zero address is rejected by _addInstances, and the value is
// bound to the verified enclave report by the body-hash check above.
address[] memory addresses = new address[](1);
addresses[0] = address(bytes20(_rawQuote[REPORT_DATA_OFFSET:REPORT_DATA_OFFSET + 20]));
// Quote-freshness gate, derived from the registration trust model rather than stored
// config. INSTANCE_EXPIRY only bounds key exposure *after* registration; nothing otherwise
// bounds how old the attested quote itself is, so a quote (or a slowly side-channel-
// extracted key) from long ago could be registered today and trusted for a fresh 90-day
// window. Permissionless registration (no registrar) therefore fails closed: the enclave
// must have committed a recent L1 block into reportData right after the instance address —
// block number (8 bytes, BE) then that block's hash (32 bytes) — and the hash must match
// on-chain. `blockhash` returns zero outside the most recent 256 blocks, so a non-zero match
// bounds the quote's age to that window (the prover embeds the commitment and the
// registration must land within that window). With a registrar, the trusted registrar
// vouches for the provenance — and thus the age — of the quotes it submits, and registrar
// flows (e.g. a multisig) are typically slower than 256 blocks, so the gate is skipped.
// Both fields are bound to the verified enclave report by the body-hash check above.
if (registrar == address(0)) {
uint64 quoteBlock =
uint64(bytes8(_rawQuote[REPORT_DATA_OFFSET + 20:REPORT_DATA_OFFSET + 28]));
bytes32 quoteBlockHash =
bytes32(_rawQuote[REPORT_DATA_OFFSET + 28:REPORT_DATA_OFFSET + 60]);
bytes32 actualBlockHash = blockhash(quoteBlock);
// A zero `blockhash` means the committed block is outside the most recent 256 (too old,
// or the current/a future block) — the quote is stale. A non-zero hash that does not
// match means the commitment is wrong. Split for on-chain diagnosability.
require(actualBlockHash != bytes32(0), SGX_STALE_QUOTE());
require(actualBlockHash == quoteBlockHash, SGX_QUOTE_BLOCK_HASH_MISMATCH());
}
// An owner-submitted registration is as trusted as `addInstances`, so it skips the validity
// delay; permissionless (and registrar) registrations remain delayed. The attested MRENCLAVE,
// MRSIGNER and policy version are recorded so `verifyProof` can re-check the current enclave
// policy and allowlist.
return
_addInstances(addresses, msg.sender == owner(), mrEnclave, mrSigner, policyVersion)[0];
}
/// @inheritdoc IProofVerifier
function verifyProof(
uint256, /* _proposalAge */
bytes32 _aggregatedProvingHash,
bytes calldata _proof
)
external
view
{
require(_proof.length == 89, SGX_INVALID_PROOF());
uint32 id = uint32(bytes4(_proof[:4]));
address instance = address(bytes20(_proof[4:24]));
require(_isInstanceValid(id, instance), SGX_INVALID_INSTANCE());
bytes32 signatureHash = LibPublicInput.hashPublicInputs(
_aggregatedProvingHash, address(this), instance, taikoChainId
);
// Verify the signature was created by the registered instance
bytes memory signature = _proof[24:];
require(instance == ECDSA.recover(signatureHash, signature), SGX_INVALID_PROOF());
}
/// @notice Returns whether a platform TCB status is accepted by this verifier's network policy.
/// @dev The TCB-status acceptance policy is defined by per-network subclasses. Each subclass
/// expresses its policy against Automata's `TCBStatus` enum (the same pinned on-chain-pccs
/// package the attestation entrypoint uses to produce `tcbStatus`), so the on-chain policy and
/// the entrypoint cannot diverge and a dependency bump that reorders the enum is caught at
/// compile time. The strict mainnet policy must remain the secure default.
/// @param _status The TCB status code from the attestation output.
/// @return Whether the status is accepted.
function isTcbStatusAccepted(uint8 _status) public pure virtual returns (bool);
/// @dev Hook for an additional, per-network enclave-identity policy enforced during
/// `registerInstance`, run after the universal forbidden-attribute floor. The base
/// implementation is a no-op (the floor is the only attribute check) and is intended only for
/// non-production (devnet) verifiers; production subclasses MUST override this to pin the full
/// ATTRIBUTES profile per allowlisted MRENCLAVE. An override MUST revert to reject a
/// registration. Parameters are the attested application-enclave measurement and its 16-byte
/// ATTRIBUTES (FLAGS || XFRM) field, both authenticated by the attestation.
/// @return policyVersion_ The version of the per-MRENCLAVE policy that admitted this registration.
/// It is recorded on the instance and re-checked in `verifyProof` (via `_isEnclaveStillTrusted`)
/// so a later policy change or removal revokes the instance. The base returns 0 (no versioned
/// policy).
function _validateEnclaveAttributes(
bytes32,
bytes16
)
internal
view
virtual
returns (uint32 policyVersion_)
{
return 0;
}
/// @dev Re-evaluates at proof time whether a stored instance's attested enclave is still trusted
/// under the verifier's *current* policy, so revoking trust in a measurement/signer also stops its
/// already-registered instances (a registration-time-only check cannot). Owner-added instances
/// (`addInstances`) carry no attested measurement (`mrEnclave == 0`) and are always trusted; the
/// owner revokes them with `deleteInstances`. The base re-checks the trusted-MRENCLAVE and
/// trusted-MRSIGNER allowlist exactly as `registerInstance` did, and only while it is enforced.
/// Production subclasses override this to additionally require the per-MRENCLAVE policy that gated
/// registration to still be configured and unchanged (matched by version). Takes the whole stored
/// `Instance` so subclasses can consult any recorded field without a signature change.
/// @param _instance The stored instance record being verified.
/// @return Whether the instance may still verify proofs.
function _isEnclaveStillTrusted(Instance memory _instance)
internal
view
virtual
returns (bool)
{
if (_instance.mrEnclave == bytes32(0)) return true;
if (!checkLocalEnclaveReport) return true;
return
mrEnclaveState[_instance.mrEnclave].trusted && trustedUserMrSigner[_instance.mrSigner];
}
/// @dev The delay applied to a non-owner `registerInstance` registration before the instance
/// becomes usable, giving off-chain monitoring a window to evict a rogue self-registered instance
/// (via `deleteInstances`) before it can prove. Owner registrations — `addInstances`, or
/// `registerInstance` called by the owner — are never delayed. The base applies no delay;
/// production subclasses override this to return their configured delay (which must not exceed
/// `INSTANCE_EXPIRY`).
/// @return The registration validity delay, in seconds.
function _validityDelay() internal view virtual returns (uint64) {
return 0;
}
function _addInstances(
address[] memory _instances,
bool instantValid,
bytes32 _mrEnclave,
bytes32 _mrSigner,
uint32 _policyVersion
)
private
returns (uint256[] memory ids)
{
uint256 size = _instances.length;
ids = new uint256[](size);
uint64 validSince = uint64(block.timestamp);
if (!instantValid) {
validSince += _validityDelay();
}
for (uint256 i; i < size; ++i) {
require(!addressRegistered[_instances[i]], SGX_ALREADY_ATTESTED());
addressRegistered[_instances[i]] = true;
require(_instances[i] != address(0), SGX_INVALID_INSTANCE());
// `verifyProof` references an instance by a uint32 id decoded from the proof, while ids
// are assigned from the uint256 `nextInstanceId`. Reject any id that would not survive
// that truncation, so a registered instance is always reachable from a proof (a
// reachable-instance invariant made explicit rather than left to a silent wrap).
require(nextInstanceId <= type(uint32).max, SGX_INSTANCE_ID_OVERFLOW());
instances[nextInstanceId] =
Instance(_instances[i], validSince, _policyVersion, _mrEnclave, _mrSigner);
ids[i] = nextInstanceId;
emit InstanceAdded(nextInstanceId, _instances[i], address(0), validSince);
++nextInstanceId;
}
}
function _isInstanceValid(uint256 id, address instance) private view returns (bool) {
require(instance != address(0), SGX_INVALID_INSTANCE());
Instance memory inst = instances[id];
require(instance == inst.addr, SGX_INVALID_INSTANCE());
// Re-check the current enclave policy: an instance whose MRENCLAVE/MRSIGNER trust or policy
// has since been revoked or changed is rejected even before expiry (revocation, not deletion).
if (!_isEnclaveStillTrusted(inst)) return false;
return
inst.validSince <= block.timestamp
&& block.timestamp <= inst.validSince + INSTANCE_EXPIRY;
}
}
node_modules/@automata-network/on-chain-pccs/src/helpers/FmspcTcbHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JSONParserLib} from "solady/utils/JSONParserLib.sol";
import {LibString} from "solady/utils/LibString.sol";
import {DateTimeUtils} from "../utils/DateTimeUtils.sol";
import {BytesUtils} from "../utils/BytesUtils.sol";
// https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/e7604e02331b3377f3766ed3653250e03af72d45/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/X509Constants.h#L64
uint256 constant TCB_CPUSVN_SIZE = 16;
enum TcbId {
/// the "id" field is absent from TCBInfo V2
/// which defaults TcbId to SGX
/// since TDX TCBInfos are only included in V3 or above
SGX,
TDX
}
/**
* @dev This is a simple representation of the TCBInfo.json in string as a Solidity object.
* @param tcbInfo: tcbInfoJson.tcbInfo string object body
* @param signature The signature to be passed as bytes array
*/
struct TcbInfoJsonObj {
string tcbInfoStr;
bytes signature;
}
/// @dev Solidity object representing TCBInfo.json excluding TCBLevels
struct TcbInfoBasic {
/// the name "tcbType" can be confusing/misleading
/// as the tcbType referred here in this struct is the type
/// of TCB level composition that determines TCB level comparison logic
/// It is not the same as the "type" parameter passed as an argument to the
/// getTcbInfo() API method described in Section 4.2.3 of the Intel PCCS Design Document
/// Instead, getTcbInfo() "type" argument should be checked against the "id" value of this struct
/// which represents the TEE type for the given TCBInfo
uint8 tcbType;
TcbId id;
uint32 version;
uint64 issueDate;
uint64 nextUpdate;
uint32 evaluationDataNumber;
bytes6 fmspc;
bytes2 pceid;
}
struct TCBLevelsObj {
uint16 pcesvn;
uint8[] sgxComponentCpuSvns;
uint8[] tdxComponentCpuSvns;
uint64 tcbDateTimestamp;
TCBStatus status;
string[] advisoryIDs;
}
struct TDXModule {
bytes mrsigner; // 48 bytes
bytes8 attributes;
bytes8 attributesMask;
}
struct TDXModuleIdentity {
string id;
bytes8 attributes;
bytes8 attributesMask;
bytes mrsigner; // 48 bytes
TDXModuleTCBLevelsObj[] tcbLevels;
}
struct TDXModuleTCBLevelsObj {
uint8 isvsvn;
uint64 tcbDateTimestamp;
TCBStatus status;
}
enum TCBStatus {
OK,
TCB_SW_HARDENING_NEEDED,
TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED,
TCB_CONFIGURATION_NEEDED,
TCB_OUT_OF_DATE,
TCB_OUT_OF_DATE_CONFIGURATION_NEEDED,
TCB_REVOKED,
TCB_UNRECOGNIZED
}
/**
* @title FMSPC TCB Helper Contract
* @notice This is a standalone contract that can be used by off-chain applications and smart contracts
* to parse TCBInfo data
*/
contract FmspcTcbHelper {
using JSONParserLib for JSONParserLib.Item;
using LibString for string;
using BytesUtils for bytes;
error TCBInfo_Invalid();
error TCB_TDX_Version_Invalid();
error TCB_TDX_ID_Invalid();
/**
* @notice this method generates content-specific hash
* @notice in other words, we omit the "issueDate" and "nextUpdate" fields from the preimage
* @notice of the hash.
* @notice hence, this allows us to keep track of the changes made ONLY to the TCBInfo content
* @notice regardless of when the collateral is being issued and expires
*/
function generateFmspcTcbContentHash(
TcbInfoBasic memory tcbInfoContent,
string memory tcbLevelsString,
string memory tdxModuleString,
string memory tdxModuleIdentitiesString
) external pure returns (bytes32 contentHash) {
bytes memory content = abi.encodePacked(
tcbInfoContent.tcbType,
tcbInfoContent.id,
tcbInfoContent.version,
tcbInfoContent.evaluationDataNumber,
tcbInfoContent.fmspc,
tcbInfoContent.pceid,
bytes(tcbLevelsString)
);
if (bytes(tdxModuleString).length > 0) {
content = abi.encodePacked(content, bytes(tdxModuleString));
}
if (bytes(tdxModuleIdentitiesString).length > 0) {
content = abi.encodePacked(content, bytes(tdxModuleIdentitiesString));
}
contentHash = keccak256(content);
}
function tcbLevelsObjToBytes(TCBLevelsObj calldata obj) external pure returns (bytes memory serialized) {
// first slot = (uint64, uint64, uint64)
uint256 firstSlot = uint256(obj.pcesvn) << (2 * 64) | uint256(obj.tcbDateTimestamp) << 64 | uint8(obj.status);
// second slot = (padded uint16 sgxCpuSvns (16 bytes) + padded uint16 tdxCpuSvns (16 bytes))
uint256 secondSlot;
uint256 n = obj.sgxComponentCpuSvns.length;
for (uint256 i = 0; i < n;) {
uint256 v1Shift = 8 * ((2 * n) - i - 1);
secondSlot |= uint256(obj.sgxComponentCpuSvns[i]) << v1Shift;
unchecked {
i++;
}
}
if (obj.tdxComponentCpuSvns.length > 0) {
for (uint256 i = 0; i < n;) {
uint256 v2Shift = 8 * (n - i - 1);
secondSlot |= uint256(obj.tdxComponentCpuSvns[i]) << v2Shift;
unchecked {
i++;
}
}
}
// string slot = padding all advisory IDs together using '\n' as a delimiter
bytes memory stringSlot;
if (obj.advisoryIDs.length > 0) {
string memory concat = obj.advisoryIDs[0];
for (uint256 j = 1; j < obj.advisoryIDs.length; j++) {
concat = string.concat(concat, "\n", obj.advisoryIDs[j]);
}
stringSlot = bytes(concat);
}
serialized = abi.encodePacked(firstSlot, secondSlot, stringSlot);
}
function tcbLevelsObjFromBytes(bytes calldata encoded) external pure returns (TCBLevelsObj memory parsed) {
// Step 1: decode first slot
parsed.pcesvn = uint16(bytes2(encoded[14:16]));
parsed.tcbDateTimestamp = uint64(bytes8(encoded[16:24]));
parsed.status = TCBStatus(uint8(bytes1(encoded[31:32])));
// Step 2: decode second slot
parsed.sgxComponentCpuSvns = new uint8[](16);
parsed.tdxComponentCpuSvns = new uint8[](16);
bytes32 encodedSlot2 = bytes32(encoded[32:64]);
for (uint256 i = 0; i < 16;) {
if (encodedSlot2[i] != 0) {
parsed.sgxComponentCpuSvns[i] = uint8(bytes1(encodedSlot2[i]));
}
if (encodedSlot2[i + 16] != 0) {
parsed.tdxComponentCpuSvns[i] = uint8(bytes1(encodedSlot2[i + 16]));
}
unchecked {
i++;
}
}
// Step 3: decode the string
if (encoded.length > 64) {
parsed.advisoryIDs = LibString.split(string(encoded[64:encoded.length]), "\n");
}
}
function tdxModuleIdentityToBytes(TDXModuleIdentity calldata tdxModuleIdentity)
external
pure
returns (bytes memory packedTdxModuleIdentity)
{
bytes32 slot1 = LibString.packOne(tdxModuleIdentity.id);
// mrsigner is split into two slots
// first slot: contains the first 32 bytes of mrsigner
// second slot: contains the remaining 16 bytes, followed by 16 zero bytes
bytes32 slot2 = bytes32(tdxModuleIdentity.mrsigner);
bytes32 slot3 = bytes32(abi.encodePacked(slot2, tdxModuleIdentity.mrsigner.substring(32, 16)));
// Slot 4 is occupied by packing both the attributes and attributes mask
// Slot 4 = (attributes, attributesMask)
bytes32 slot4 = bytes32(tdxModuleIdentity.attributes) | bytes32(tdxModuleIdentity.attributesMask) >> 128;
// encode the tdx module array
uint256 n = tdxModuleIdentity.tcbLevels.length;
uint256[] memory tdxTcbSlots = new uint256[](n);
for (uint256 i = 0; i < n;) {
tdxTcbSlots[i] = _tdxModuleTcbLevelsObjToSlot(tdxModuleIdentity.tcbLevels[i]);
unchecked {
i++;
}
}
// total slots = 4 + n
packedTdxModuleIdentity = abi.encodePacked(slot1, slot2, slot3, slot4, abi.encodePacked(tdxTcbSlots));
}
function tdxModuleIdentityFromBytes(bytes calldata packedTdxModuleIdentity)
external
pure
returns (TDXModuleIdentity memory tdxModuleIdentity)
{
// decode slot 1
tdxModuleIdentity.id = LibString.unpackOne(bytes32(packedTdxModuleIdentity[0:32]));
// decode slots 2 and 3 to get mrsigner
tdxModuleIdentity.mrsigner = packedTdxModuleIdentity[32:80];
// decode tdx module identity tcb level array
tdxModuleIdentity.attributes = bytes8(packedTdxModuleIdentity[96:104]);
tdxModuleIdentity.attributesMask = bytes8(packedTdxModuleIdentity[112:120]);
uint256 offset = 128;
uint256 n = (packedTdxModuleIdentity.length - offset) / 32;
tdxModuleIdentity.tcbLevels = new TDXModuleTCBLevelsObj[](n);
for (uint256 i = 0; i < n;) {
uint256 end = offset + 32;
uint256 slot = uint256(bytes32(packedTdxModuleIdentity[offset:end]));
tdxModuleIdentity.tcbLevels[i] = _tdxModuleTcbLevelsObjFromSlot(slot);
offset = end;
unchecked {
i++;
}
}
}
// use bitmaps to represent the keys found in TCBInfo
// all tcb types regardless of version and tee types should have these keys described below:
// [version, issueDate, nextUpdate, fmspc, pceId, tcbType, tcbEvaluationDataNumber, tcbLevels]
// Bits are sorted in the order of the keys above from LSB to MSB
// e.g. if version is found, the bytes would look like 00000001
// e.g. if both version and fmspc were found, the bytes would look like 00001001
// the next byte contains the keys only found for V3, and TDX TCBInfos
// [id, tdxModule, tdxModuleIdentities]
uint8 constant TCB_VERSION_BIT = 1;
uint8 constant TCB_ISSUE_DATE_BIT = 2;
uint8 constant TCB_NEXT_UPDATE_BIT = 4;
uint8 constant TCB_FMSPC_BIT = 8;
uint8 constant TCB_PCEID_BIT = 16;
uint8 constant TCB_TYPE_BIT = 32;
uint8 constant TCB_EVALUATION_DATA_NUMBER_BIT = 64;
uint8 constant TCB_LEVELS_BIT = 128;
uint16 constant TCB_ID_BIT = 256;
uint16 constant TCB_TDX_MODULE_BIT = 512;
uint16 constant TCB_TDX_MODULE_IDENTITIES_BIT = 1024;
function parseTcbString(string calldata tcbInfoStr)
external
pure
returns (
TcbInfoBasic memory tcbInfo,
string memory tcbLevelsString,
string memory tdxModuleString,
string memory tdxModuleIdentitiesString
)
{
JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);
JSONParserLib.Item[] memory tcbInfoObj = root.children();
uint256 f;
bool isTdx;
uint256 n = root.size();
for (uint256 i = 0; i < n;) {
JSONParserLib.Item memory current = tcbInfoObj[i];
string memory decodedKey = JSONParserLib.decodeString(current.key());
string memory val = current.value();
if (f & TCB_ID_BIT == 0 && decodedKey.eq("id")) {
string memory idStr = JSONParserLib.decodeString(val);
f |= TCB_ID_BIT;
if (idStr.eq("TDX")) {
tcbInfo.id = TcbId.TDX;
isTdx = true;
} else if (!idStr.eq("SGX")) {
revert TCBInfo_Invalid();
}
} else if (f & TCB_VERSION_BIT == 0 && decodedKey.eq("version")) {
tcbInfo.version = uint32(JSONParserLib.parseUint(val));
f |= TCB_VERSION_BIT;
if (tcbInfo.version < 3) {
f |= TCB_ID_BIT;
}
} else if (f & TCB_ISSUE_DATE_BIT == 0 && decodedKey.eq("issueDate")) {
tcbInfo.issueDate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));
f |= TCB_ISSUE_DATE_BIT;
} else if (f & TCB_NEXT_UPDATE_BIT == 0 && decodedKey.eq("nextUpdate")) {
tcbInfo.nextUpdate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));
f |= TCB_NEXT_UPDATE_BIT;
} else if (f & TCB_FMSPC_BIT == 0 && decodedKey.eq("fmspc")) {
tcbInfo.fmspc = bytes6(uint48(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));
f |= TCB_FMSPC_BIT;
} else if (f & TCB_PCEID_BIT == 0 && decodedKey.eq("pceId")) {
tcbInfo.pceid = bytes2(uint16(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));
f |= TCB_PCEID_BIT;
} else if (f & TCB_TYPE_BIT == 0 && decodedKey.eq("tcbType")) {
tcbInfo.tcbType = uint8(JSONParserLib.parseUint(val));
f |= TCB_TYPE_BIT;
} else if (f & TCB_EVALUATION_DATA_NUMBER_BIT == 0 && decodedKey.eq("tcbEvaluationDataNumber")) {
tcbInfo.evaluationDataNumber = uint32(JSONParserLib.parseUint(val));
f |= TCB_EVALUATION_DATA_NUMBER_BIT;
} else if (
tcbInfo.version > 2 && isTdx && (f & TCB_TDX_MODULE_BIT == 0 || f & TCB_TDX_MODULE_IDENTITIES_BIT == 0)
) {
if (f & TCB_TDX_MODULE_BIT == 0 && decodedKey.eq("tdxModule")) {
tdxModuleString = val;
f |= TCB_TDX_MODULE_BIT;
} else if (f & TCB_TDX_MODULE_IDENTITIES_BIT == 0 && decodedKey.eq("tdxModuleIdentities")) {
tdxModuleIdentitiesString = val;
f |= TCB_TDX_MODULE_IDENTITIES_BIT;
}
} else if (f & TCB_LEVELS_BIT == 0 && decodedKey.eq("tcbLevels")) {
tcbLevelsString = val;
f |= TCB_LEVELS_BIT;
}
unchecked {
i++;
}
}
// v2 tcbinfo does not explicitly have the "id" field
// but we set the bit to 1 anyway to save gas by skipping the check
// incrementing n prevents from the "id" bit to be set to 0 by masking
if (tcbInfo.version < 3) {
n++;
}
bool allFound = f == (2 ** n) - 1;
if (!allFound) {
revert TCBInfo_Invalid();
}
}
function parseTcbLevels(uint256 version, string calldata tcbLevelsString)
external
pure
returns (TCBLevelsObj[] memory tcbLevels)
{
JSONParserLib.Item memory root = JSONParserLib.parse(tcbLevelsString);
JSONParserLib.Item[] memory tcbLevelsObj = root.children();
uint256 tcbLevelsSize = tcbLevelsObj.length;
tcbLevels = new TCBLevelsObj[](tcbLevelsSize);
// iterating through the array
for (uint256 i = 0; i < tcbLevelsSize; i++) {
JSONParserLib.Item[] memory tcbObj = tcbLevelsObj[i].children();
// iterating through individual tcb objects
for (uint256 j = 0; j < tcbLevelsObj[i].size(); j++) {
string memory tcbKey = JSONParserLib.decodeString(tcbObj[j].key());
if (tcbKey.eq("tcb")) {
string memory tcbStr = tcbObj[j].value();
JSONParserLib.Item memory tcbParent = JSONParserLib.parse(tcbStr);
JSONParserLib.Item[] memory tcbComponents = tcbParent.children();
if (version == 2) {
(tcbLevels[i].sgxComponentCpuSvns, tcbLevels[i].pcesvn) = _parseV2Tcb(tcbComponents);
} else if (version == 3) {
(tcbLevels[i].sgxComponentCpuSvns, tcbLevels[i].tdxComponentCpuSvns, tcbLevels[i].pcesvn) =
_parseV3Tcb(tcbComponents);
} else {
revert TCBInfo_Invalid();
}
} else if (tcbKey.eq("tcbDate")) {
tcbLevels[i].tcbDateTimestamp =
uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcbObj[j].value())));
} else if (tcbKey.eq("tcbStatus")) {
tcbLevels[i].status = _getTcbStatus(JSONParserLib.decodeString(tcbObj[j].value()));
} else if (tcbKey.eq("advisoryIDs")) {
JSONParserLib.Item[] memory advisoryArr = tcbObj[j].children();
uint256 n = tcbObj[j].size();
tcbLevels[i].advisoryIDs = new string[](n);
for (uint256 k = 0; k < n; k++) {
tcbLevels[i].advisoryIDs[k] = JSONParserLib.decodeString(advisoryArr[k].value());
}
}
}
}
}
function parseTcbTdxModules(string calldata tdxModuleString, string calldata tdxModuleIdentitiesString)
external
pure
returns (TDXModule memory module, TDXModuleIdentity[] memory moduleIdentities)
{
JSONParserLib.Item memory tdxModuleRoot = JSONParserLib.parse(tdxModuleString);
JSONParserLib.Item[] memory tdxModuleItems = tdxModuleRoot.children();
JSONParserLib.Item memory tdxModuleIdentitiesRoot = JSONParserLib.parse(tdxModuleIdentitiesString);
JSONParserLib.Item[] memory tdxModuleIdentitiesItems = tdxModuleIdentitiesRoot.children();
module = _parseTdxModule(tdxModuleItems);
moduleIdentities = _parseTdxModuleIdentities(tdxModuleIdentitiesItems);
}
/// ====== INTERNAL METHODS BELOW ======
function _tdxModuleTcbLevelsObjToSlot(TDXModuleTCBLevelsObj memory tdxModuleTcbLevelsObj)
private
pure
returns (uint256 tdxTcbPacked)
{
// tcb levels within tdx module can be packed into a single slot
// (uint64 packedIsvsvn, uint64 packedTcbDateTimestamp, uint64 packedStatus)
tdxTcbPacked = uint256(tdxModuleTcbLevelsObj.isvsvn) << (2 * 64)
| uint256(tdxModuleTcbLevelsObj.tcbDateTimestamp) << 64 | uint8(tdxModuleTcbLevelsObj.status);
}
function _tdxModuleTcbLevelsObjFromSlot(uint256 tdxTcbPacked)
private
pure
returns (TDXModuleTCBLevelsObj memory tdxModuleTcbLevelsObj)
{
uint64 mask = 0xFFFFFFFFFFFFFFFF;
tdxModuleTcbLevelsObj.status = TCBStatus(uint8(uint64(tdxTcbPacked & mask)));
tdxModuleTcbLevelsObj.tcbDateTimestamp = uint64((tdxTcbPacked >> 64) & mask);
tdxModuleTcbLevelsObj.isvsvn = uint8(uint64((tdxTcbPacked >> 128) & mask));
}
function _getTcbStatus(string memory statusStr) private pure returns (TCBStatus status) {
if (statusStr.eq("UpToDate")) {
status = TCBStatus.OK;
} else if (statusStr.eq("OutOfDate")) {
status = TCBStatus.TCB_OUT_OF_DATE;
} else if (statusStr.eq("OutOfDateConfigurationNeeded")) {
status = TCBStatus.TCB_OUT_OF_DATE_CONFIGURATION_NEEDED;
} else if (statusStr.eq("ConfigurationNeeded")) {
status = TCBStatus.TCB_CONFIGURATION_NEEDED;
} else if (statusStr.eq("ConfigurationAndSWHardeningNeeded")) {
status = TCBStatus.TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED;
} else if (statusStr.eq("SWHardeningNeeded")) {
status = TCBStatus.TCB_SW_HARDENING_NEEDED;
} else if (statusStr.eq("Revoked")) {
status = TCBStatus.TCB_REVOKED;
} else {
status = TCBStatus.TCB_UNRECOGNIZED;
}
}
function _parseV2Tcb(JSONParserLib.Item[] memory tcbComponents)
private
pure
returns (uint8[] memory sgxComponentCpuSvns, uint16 pcesvn)
{
sgxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);
uint256 cpusvnCounter = 0;
for (uint256 i = 0; i < tcbComponents.length; i++) {
string memory key = JSONParserLib.decodeString(tcbComponents[i].key());
uint256 value = JSONParserLib.parseUint(tcbComponents[i].value());
if (key.eq("pcesvn")) {
pcesvn = uint16(value);
} else {
sgxComponentCpuSvns[cpusvnCounter++] = uint8(value);
}
}
if (cpusvnCounter != TCB_CPUSVN_SIZE) {
revert TCBInfo_Invalid();
}
}
function _parseV3Tcb(JSONParserLib.Item[] memory tcbComponents)
private
pure
returns (uint8[] memory sgxComponentCpuSvns, uint8[] memory tdxComponentCpuSvns, uint16 pcesvn)
{
sgxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);
tdxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);
for (uint256 i = 0; i < tcbComponents.length; i++) {
string memory key = JSONParserLib.decodeString(tcbComponents[i].key());
if (key.eq("pcesvn")) {
pcesvn = uint16(JSONParserLib.parseUint(tcbComponents[i].value()));
} else {
string memory componentKey = key;
JSONParserLib.Item[] memory componentArr = tcbComponents[i].children();
uint256 cpusvnCounter = 0;
for (uint256 j = 0; j < tcbComponents[i].size(); j++) {
JSONParserLib.Item[] memory component = componentArr[j].children();
for (uint256 k = 0; k < componentArr[j].size(); k++) {
key = JSONParserLib.decodeString(component[k].key());
if (key.eq("svn")) {
if (componentKey.eq("tdxtcbcomponents")) {
tdxComponentCpuSvns[cpusvnCounter++] =
uint8(JSONParserLib.parseUint(component[k].value()));
} else {
sgxComponentCpuSvns[cpusvnCounter++] =
uint8(JSONParserLib.parseUint(component[k].value()));
}
}
}
}
if (cpusvnCounter != TCB_CPUSVN_SIZE) {
revert TCBInfo_Invalid();
}
}
}
}
function _parseTdxModule(JSONParserLib.Item[] memory tdxModuleObj) private pure returns (TDXModule memory module) {
for (uint256 i = 0; i < tdxModuleObj.length; i++) {
string memory key = JSONParserLib.decodeString(tdxModuleObj[i].key());
string memory val = JSONParserLib.decodeString(tdxModuleObj[i].value());
if (key.eq("attributes")) {
module.attributes = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));
}
if (key.eq("attributesMask")) {
module.attributesMask = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));
}
if (key.eq("mrsigner")) {
module.mrsigner = _getMrSignerHex(val);
}
}
}
function _parseTdxModuleIdentities(JSONParserLib.Item[] memory tdxModuleIdentitiesArr)
private
pure
returns (TDXModuleIdentity[] memory identities)
{
uint256 n = tdxModuleIdentitiesArr.length;
identities = new TDXModuleIdentity[](n);
for (uint256 i = 0; i < n; i++) {
JSONParserLib.Item[] memory currIdentity = tdxModuleIdentitiesArr[i].children();
for (uint256 j = 0; j < tdxModuleIdentitiesArr[i].size(); j++) {
string memory key = JSONParserLib.decodeString(currIdentity[j].key());
if (key.eq("id")) {
string memory val = JSONParserLib.decodeString(currIdentity[j].value());
identities[i].id = val;
}
if (key.eq("mrsigner")) {
string memory val = JSONParserLib.decodeString(currIdentity[j].value());
identities[i].mrsigner = _getMrSignerHex(val);
}
if (key.eq("attributes")) {
string memory val = JSONParserLib.decodeString(currIdentity[j].value());
identities[i].attributes = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));
}
if (key.eq("attributesMask")) {
string memory val = JSONParserLib.decodeString(currIdentity[j].value());
identities[i].attributesMask = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));
}
if (key.eq("tcbLevels")) {
JSONParserLib.Item[] memory tcbLevelsArr = currIdentity[j].children();
uint256 x = tcbLevelsArr.length;
identities[i].tcbLevels = new TDXModuleTCBLevelsObj[](x);
for (uint256 k = 0; k < x; k++) {
JSONParserLib.Item[] memory tcb = tcbLevelsArr[k].children();
for (uint256 l = 0; l < tcb.length; l++) {
key = JSONParserLib.decodeString(tcb[l].key());
if (key.eq("tcb")) {
JSONParserLib.Item[] memory isvsvnObj = tcb[l].children();
key = JSONParserLib.decodeString(isvsvnObj[0].key());
if (key.eq("isvsvn")) {
identities[i].tcbLevels[k].isvsvn =
uint8(JSONParserLib.parseUint(isvsvnObj[0].value()));
} else {
revert TCBInfo_Invalid();
}
}
if (key.eq("tcbDate")) {
identities[i].tcbLevels[k].tcbDateTimestamp =
uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcb[l].value())));
}
if (key.eq("tcbStatus")) {
identities[i].tcbLevels[k].status =
_getTcbStatus(JSONParserLib.decodeString(tcb[l].value()));
}
}
}
}
}
}
}
function _getMrSignerHex(string memory mrSignerStr) private pure returns (bytes memory mrSignerBytes) {
string memory mrSignerUpper16BytesStr = mrSignerStr.slice(0, 16);
string memory mrSignerLower32BytesStr = mrSignerStr.slice(16, 48);
uint256 mrSignerUpperBytes = JSONParserLib.parseUintFromHex(mrSignerUpper16BytesStr);
uint256 mrSignerLowerBytes = JSONParserLib.parseUintFromHex(mrSignerLower32BytesStr);
mrSignerBytes = abi.encodePacked(uint128(mrSignerUpperBytes), mrSignerLowerBytes);
}
}
node_modules/@automata-network/on-chain-pccs/src/utils/BytesUtils.sol
// SPDX-License-Identifier: BSD 2-Clause License
pragma solidity ^0.8.0;
// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license
// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol
library BytesUtils {
/*
* @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);
assembly {
ret := keccak256(add(add(self, 32), offset), len)
}
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two bytes are equal.
* @param self The first bytes to compare.
* @param other The second bytes to compare.
* @return The result of the comparison.
*/
function compare(bytes memory self, bytes memory other) internal pure returns (int256) {
return compare(self, 0, self.length, other, 0, other.length);
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two bytes are equal. Comparison is done per-rune,
* on unicode codepoints.
* @param self The first bytes to compare.
* @param offset The offset of self.
* @param len The length of self.
* @param other The second bytes to compare.
* @param otheroffset The offset of the other string.
* @param otherlen The length of the other string.
* @return The result of the comparison.
*/
function compare(
bytes memory self,
uint256 offset,
uint256 len,
bytes memory other,
uint256 otheroffset,
uint256 otherlen
) internal pure returns (int256) {
uint256 shortest = len;
if (otherlen < len) {
shortest = otherlen;
}
uint256 selfptr;
uint256 otherptr;
assembly {
selfptr := add(self, add(offset, 32))
otherptr := add(other, add(otheroffset, 32))
}
for (uint256 idx = 0; idx < shortest; idx += 32) {
uint256 a;
uint256 b;
assembly {
a := mload(selfptr)
b := mload(otherptr)
}
if (a != b) {
// Mask out irrelevant bytes and check again
uint256 mask;
if (shortest > 32) {
mask = type(uint256).max; // aka 0xffffff....
} else {
mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
}
uint256 diff = (a & mask) - (b & mask);
if (diff != 0) {
return int256(diff);
}
}
selfptr += 32;
otherptr += 32;
}
return int256(len) - int256(otherlen);
}
/*
* @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 true if the two byte ranges are equal with offsets.
* @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.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset)
internal
pure
returns (bool)
{
return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);
}
/*
* @dev Compares a range of 'self' to all of 'other' and returns True iff
* they 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.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint256 offset, bytes memory other) internal pure returns (bool) {
return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);
}
/*
* @dev Returns true if the two byte ranges are equal.
* @param self The first byte range to compare.
* @param other The second byte range to compare.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, bytes memory other) internal pure returns (bool) {
return self.length == other.length && equals(self, 0, other, 0, self.length);
}
/*
* @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);
assembly {
ret := and(mload(add(add(self, 2), idx)), 0xFFFF)
}
}
/*
* @dev Returns the 32-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bits of the string, interpreted as an integer.
*/
function readUint32(bytes memory self, uint256 idx) internal pure returns (uint32 ret) {
require(idx + 4 <= self.length);
assembly {
ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)
}
}
/*
* @dev Returns the 32 byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bytes of the string.
*/
function readBytes32(bytes memory self, uint256 idx) internal pure returns (bytes32 ret) {
require(idx + 32 <= self.length);
assembly {
ret := mload(add(add(self, 32), idx))
}
}
/*
* @dev Returns the 32 byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bytes of the string.
*/
function readBytes20(bytes memory self, uint256 idx) internal pure returns (bytes20 ret) {
require(idx + 20 <= self.length);
assembly {
ret :=
and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)
}
}
/*
* @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);
require(idx + len <= self.length);
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 {
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
uint256 mask;
if (len == 0) {
mask = type(uint256).max; // Set to maximum value of uint256
} else {
mask = 256 ** (32 - len) - 1;
}
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
/*
* @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);
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;
}
// Maps characters from 0x30 to 0x7A to their base32 values.
// 0xFF represents invalid characters in that range.
bytes constant base32HexTable =
hex"00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F";
/**
* @dev Decodes unpadded base32 data of up to one word in length.
* @param self The data to decode.
* @param off Offset into the string to start at.
* @param len Number of characters to decode.
* @return The decoded data, left aligned.
*/
function base32HexDecodeWord(bytes memory self, uint256 off, uint256 len) internal pure returns (bytes32) {
require(len <= 52);
uint256 ret = 0;
uint8 decoded;
for (uint256 i = 0; i < len; i++) {
bytes1 char = self[off + i];
require(char >= 0x30 && char <= 0x7A);
decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);
require(decoded <= 0x20);
if (i == len - 1) {
break;
}
ret = (ret << 5) | decoded;
}
uint256 bitlen = len * 5;
if (len % 8 == 0) {
// Multiple of 8 characters, no padding
ret = (ret << 5) | decoded;
} else if (len % 8 == 2) {
// Two extra characters - 1 byte
ret = (ret << 3) | (decoded >> 2);
bitlen -= 2;
} else if (len % 8 == 4) {
// Four extra characters - 2 bytes
ret = (ret << 1) | (decoded >> 4);
bitlen -= 4;
} else if (len % 8 == 5) {
// Five extra characters - 3 bytes
ret = (ret << 4) | (decoded >> 1);
bitlen -= 1;
} else if (len % 8 == 7) {
// Seven extra characters - 4 bytes
ret = (ret << 2) | (decoded >> 3);
bitlen -= 3;
} else {
revert();
}
return bytes32(ret << (256 - bitlen));
}
function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {
if (a.length != b.length) {
return false;
}
for (uint256 i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
}
node_modules/@automata-network/on-chain-pccs/src/utils/DateTimeUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {DateTimeLib} from "solady/utils/DateTimeLib.sol";
import {LibString} from "solady/utils/LibString.sol";
library DateTimeUtils {
using LibString for string;
/*
* @dev Convert a DER-encoded time to a unix timestamp
* @param x509Time The DER-encoded time
* @return The unix timestamp
*/
function fromDERToTimestamp(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 DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);
}
/// @dev iso follows pattern: "YYYY-MM-DDTHH:mm:ssZ"
function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {
require(bytes(iso).length == 20, "invalid iso string length");
uint256 y = stringToUint(iso.slice(0, 4));
uint256 m = stringToUint(iso.slice(5, 7));
uint256 d = stringToUint(iso.slice(8, 10));
uint256 h = stringToUint(iso.slice(11, 13));
uint256 min = stringToUint(iso.slice(14, 16));
uint256 s = stringToUint(iso.slice(17, 19));
return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);
}
// https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int
function stringToUint(string memory s) private pure returns (uint256 result) {
bytes memory b = bytes(s);
result = 0;
for (uint256 i = 0; i < b.length; i++) {
uint256 c = uint256(uint8(b[i]));
if (c >= 48 && c <= 57) {
result = result * 10 + (c - 48);
}
}
}
}
node_modules/@openzeppelin/contracts/access/Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
node_modules/@openzeppelin/contracts/access/Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
node_modules/@openzeppelin/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
node_modules/@openzeppelin/contracts/utils/Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}
node_modules/@openzeppelin/contracts/utils/math/Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
node_modules/@openzeppelin/contracts/utils/math/SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
node_modules/solady/src/utils/DateTimeLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for date time operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)
/// @author Modified from BokkyPooBahsDateTimeLibrary (https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary)
/// @dev
/// Conventions:
/// --------------------------------------------------------------------+
/// Unit | Range | Notes |
/// --------------------------------------------------------------------|
/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |
/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |
/// year | 1970..0xffffffff | Gregorian calendar year. |
/// month | 1..12 | Gregorian calendar month. |
/// day | 1..31 | Gregorian calendar day of month. |
/// weekday | 1..7 | The day of the week (1-indexed). |
/// --------------------------------------------------------------------+
/// All timestamps of days are rounded down to 00:00:00 UTC.
library DateTimeLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Weekdays are 1-indexed, adhering to ISO 8601.
uint256 internal constant MON = 1;
uint256 internal constant TUE = 2;
uint256 internal constant WED = 3;
uint256 internal constant THU = 4;
uint256 internal constant FRI = 5;
uint256 internal constant SAT = 6;
uint256 internal constant SUN = 7;
// Months and days of months are 1-indexed, adhering to ISO 8601.
uint256 internal constant JAN = 1;
uint256 internal constant FEB = 2;
uint256 internal constant MAR = 3;
uint256 internal constant APR = 4;
uint256 internal constant MAY = 5;
uint256 internal constant JUN = 6;
uint256 internal constant JUL = 7;
uint256 internal constant AUG = 8;
uint256 internal constant SEP = 9;
uint256 internal constant OCT = 10;
uint256 internal constant NOV = 11;
uint256 internal constant DEC = 12;
// These limits are large enough for most practical purposes.
// Inputs that exceed these limits result in undefined behavior.
uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;
uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;
uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATE TIME OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).
/// See: https://howardhinnant.github.io/date_algorithms.html
/// Note: Inputs outside the supported ranges result in undefined behavior.
/// Use {isSupportedDate} to check if the inputs are supported.
function dateToEpochDay(uint256 year, uint256 month, uint256 day)
internal
pure
returns (uint256 epochDay)
{
/// @solidity memory-safe-assembly
assembly {
year := sub(year, lt(month, 3))
let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)
let yoe := mod(year, 400)
let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))
epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)
}
}
/// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.
/// Note: Inputs outside the supported ranges result in undefined behavior.
/// Use {isSupportedDays} to check if the inputs is supported.
function epochDayToDate(uint256 epochDay)
internal
pure
returns (uint256 year, uint256 month, uint256 day)
{
/// @solidity memory-safe-assembly
assembly {
epochDay := add(epochDay, 719468)
let doe := mod(epochDay, 146097)
let yoe :=
div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)
let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))
let mp := div(add(mul(5, doy), 2), 153)
day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)
month := byte(mp, shl(160, 0x030405060708090a0b0c0102))
year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))
}
}
/// @dev Returns the unix timestamp from (`year`,`month`,`day`).
/// Note: Inputs outside the supported ranges result in undefined behavior.
/// Use {isSupportedDate} to check if the inputs are supported.
function dateToTimestamp(uint256 year, uint256 month, uint256 day)
internal
pure
returns (uint256 result)
{
unchecked {
result = dateToEpochDay(year, month, day) * 86400;
}
}
/// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.
/// Note: Inputs outside the supported ranges result in undefined behavior.
/// Use {isSupportedTimestamp} to check if the inputs are supported.
function timestampToDate(uint256 timestamp)
internal
pure
returns (uint256 year, uint256 month, uint256 day)
{
(year, month, day) = epochDayToDate(timestamp / 86400);
}
/// @dev Returns the unix timestamp from
/// (`year`,`month`,`day`,`hour`,`minute`,`second`).
/// Note: Inputs outside the supported ranges result in undefined behavior.
/// Use {isSupportedDateTime} to check if the inputs are supported.
function dateTimeToTimestamp(
uint256 year,
uint256 month,
uint256 day,
uint256 hour,
uint256 minute,
uint256 second
) internal pure returns (uint256 result) {
unchecked {
result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;
}
}
/// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)
/// from the given unix timestamp.
/// Note: Inputs outside the supported ranges result in undefined behavior.
/// Use {isSupportedTimestamp} to check if the inputs are supported.
function timestampToDateTime(uint256 timestamp)
internal
pure
returns (
uint256 year,
uint256 month,
uint256 day,
uint256 hour,
uint256 minute,
uint256 second
)
{
unchecked {
(year, month, day) = epochDayToDate(timestamp / 86400);
uint256 secs = timestamp % 86400;
hour = secs / 3600;
secs = secs % 3600;
minute = secs / 60;
second = secs % 60;
}
}
/// @dev Returns if the `year` is leap.
function isLeapYear(uint256 year) internal pure returns (bool leap) {
/// @solidity memory-safe-assembly
assembly {
leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))
}
}
/// @dev Returns number of days in given `month` of `year`.
function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {
bool flag = isLeapYear(year);
/// @solidity memory-safe-assembly
assembly {
// `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.
// `result = daysInMonths[month - 1] + isLeapYear(year)`.
result :=
add(byte(month, shl(152, 0x1f1c1f1e1f1e1f1f1e1f1e1f)), and(eq(month, 2), flag))
}
}
/// @dev Returns the weekday from the unix timestamp.
/// Monday: 1, Tuesday: 2, ....., Sunday: 7.
function weekday(uint256 timestamp) internal pure returns (uint256 result) {
unchecked {
result = ((timestamp / 86400 + 3) % 7) + 1;
}
}
/// @dev Returns if (`year`,`month`,`day`) is a supported date.
/// - `1970 <= year <= MAX_SUPPORTED_YEAR`.
/// - `1 <= month <= 12`.
/// - `1 <= day <= daysInMonth(year, month)`.
function isSupportedDate(uint256 year, uint256 month, uint256 day)
internal
pure
returns (bool result)
{
uint256 md = daysInMonth(year, month);
/// @solidity memory-safe-assembly
assembly {
result :=
and(
lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),
and(lt(sub(month, 1), 12), lt(sub(day, 1), md))
)
}
}
/// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.
/// - `1970 <= year <= MAX_SUPPORTED_YEAR`.
/// - `1 <= month <= 12`.
/// - `1 <= day <= daysInMonth(year, month)`.
/// - `hour < 24`.
/// - `minute < 60`.
/// - `second < 60`.
function isSupportedDateTime(
uint256 year,
uint256 month,
uint256 day,
uint256 hour,
uint256 minute,
uint256 second
) internal pure returns (bool result) {
if (isSupportedDate(year, month, day)) {
/// @solidity memory-safe-assembly
assembly {
result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))
}
}
}
/// @dev Returns if `epochDay` is a supported unix epoch day.
function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {
unchecked {
result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;
}
}
/// @dev Returns if `timestamp` is a supported unix timestamp.
function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {
unchecked {
result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;
}
}
/// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.
/// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`
/// Note: `n` is 1-indexed for traditional consistency.
/// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.
function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)
internal
pure
returns (uint256 result)
{
uint256 d = dateToEpochDay(year, month, 1);
uint256 md = daysInMonth(year, month);
/// @solidity memory-safe-assembly
assembly {
let diff := sub(wd, add(mod(add(d, 3), 7), 1))
let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))
result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))
}
}
/// @dev Returns the unix timestamp of the most recent Monday.
function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {
uint256 t = timestamp;
/// @solidity memory-safe-assembly
assembly {
let day := div(t, 86400)
result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))
}
}
/// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.
/// To check whether it is a week day, just take the negation of the result.
function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {
result = weekday(timestamp) > FRI;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATE TIME ARITHMETIC OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Adds `numYears` to the unix timestamp, and returns the result.
/// Note: The result will share the same Gregorian calendar month,
/// but different Gregorian calendar years for non-zero `numYears`.
/// If the Gregorian calendar month of the result has less days
/// than the Gregorian calendar month day of the `timestamp`,
/// the result's month day will be the maximum possible value for the month.
/// (e.g. from 29th Feb to 28th Feb)
function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {
(uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);
result = _offsetted(year + numYears, month, day, timestamp);
}
/// @dev Adds `numMonths` to the unix timestamp, and returns the result.
/// Note: If the Gregorian calendar month of the result has less days
/// than the Gregorian calendar month day of the `timestamp`,
/// the result's month day will be the maximum possible value for the month.
/// (e.g. from 29th Feb to 28th Feb)
function addMonths(uint256 timestamp, uint256 numMonths)
internal
pure
returns (uint256 result)
{
(uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);
month = _sub(month + numMonths, 1);
result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);
}
/// @dev Adds `numDays` to the unix timestamp, and returns the result.
function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {
result = timestamp + numDays * 86400;
}
/// @dev Adds `numHours` to the unix timestamp, and returns the result.
function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {
result = timestamp + numHours * 3600;
}
/// @dev Adds `numMinutes` to the unix timestamp, and returns the result.
function addMinutes(uint256 timestamp, uint256 numMinutes)
internal
pure
returns (uint256 result)
{
result = timestamp + numMinutes * 60;
}
/// @dev Adds `numSeconds` to the unix timestamp, and returns the result.
function addSeconds(uint256 timestamp, uint256 numSeconds)
internal
pure
returns (uint256 result)
{
result = timestamp + numSeconds;
}
/// @dev Subtracts `numYears` from the unix timestamp, and returns the result.
/// Note: The result will share the same Gregorian calendar month,
/// but different Gregorian calendar years for non-zero `numYears`.
/// If the Gregorian calendar month of the result has less days
/// than the Gregorian calendar month day of the `timestamp`,
/// the result's month day will be the maximum possible value for the month.
/// (e.g. from 29th Feb to 28th Feb)
function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {
(uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);
result = _offsetted(year - numYears, month, day, timestamp);
}
/// @dev Subtracts `numYears` from the unix timestamp, and returns the result.
/// Note: If the Gregorian calendar month of the result has less days
/// than the Gregorian calendar month day of the `timestamp`,
/// the result's month day will be the maximum possible value for the month.
/// (e.g. from 29th Feb to 28th Feb)
function subMonths(uint256 timestamp, uint256 numMonths)
internal
pure
returns (uint256 result)
{
(uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);
uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);
result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);
}
/// @dev Subtracts `numDays` from the unix timestamp, and returns the result.
function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {
result = timestamp - numDays * 86400;
}
/// @dev Subtracts `numHours` from the unix timestamp, and returns the result.
function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {
result = timestamp - numHours * 3600;
}
/// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.
function subMinutes(uint256 timestamp, uint256 numMinutes)
internal
pure
returns (uint256 result)
{
result = timestamp - numMinutes * 60;
}
/// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.
function subSeconds(uint256 timestamp, uint256 numSeconds)
internal
pure
returns (uint256 result)
{
result = timestamp - numSeconds;
}
/// @dev Returns the difference in Gregorian calendar years
/// between `fromTimestamp` and `toTimestamp`.
/// Note: Even if the true time difference is less than a year,
/// the difference can be non-zero is the timestamps are
/// from different Gregorian calendar years
function diffYears(uint256 fromTimestamp, uint256 toTimestamp)
internal
pure
returns (uint256 result)
{
toTimestamp - fromTimestamp;
(uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);
(uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);
result = _sub(toYear, fromYear);
}
/// @dev Returns the difference in Gregorian calendar months
/// between `fromTimestamp` and `toTimestamp`.
/// Note: Even if the true time difference is less than a month,
/// the difference can be non-zero is the timestamps are
/// from different Gregorian calendar months.
function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)
internal
pure
returns (uint256 result)
{
toTimestamp - fromTimestamp;
(uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);
(uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);
result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));
}
/// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.
function diffDays(uint256 fromTimestamp, uint256 toTimestamp)
internal
pure
returns (uint256 result)
{
result = (toTimestamp - fromTimestamp) / 86400;
}
/// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.
function diffHours(uint256 fromTimestamp, uint256 toTimestamp)
internal
pure
returns (uint256 result)
{
result = (toTimestamp - fromTimestamp) / 3600;
}
/// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.
function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)
internal
pure
returns (uint256 result)
{
result = (toTimestamp - fromTimestamp) / 60;
}
/// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.
function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)
internal
pure
returns (uint256 result)
{
result = toTimestamp - fromTimestamp;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unchecked arithmetic for computing the total number of months.
function _totalMonths(uint256 numYears, uint256 numMonths)
private
pure
returns (uint256 total)
{
unchecked {
total = numYears * 12 + numMonths;
}
}
/// @dev Unchecked arithmetic for adding two numbers.
function _add(uint256 a, uint256 b) private pure returns (uint256 c) {
unchecked {
c = a + b;
}
}
/// @dev Unchecked arithmetic for subtracting two numbers.
function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {
unchecked {
c = a - b;
}
}
/// @dev Returns the offsetted timestamp.
function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)
private
pure
returns (uint256 result)
{
uint256 dm = daysInMonth(year, month);
if (day >= dm) {
day = dm;
}
result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);
}
}
node_modules/solady/src/utils/EfficientHashLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for efficiently performing keccak256 hashes.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EfficientHashLib.sol)
/// @dev To avoid stack-too-deep, you can use:
/// ```
/// bytes32[] memory buffer = EfficientHashLib.malloc(10);
/// EfficientHashLib.set(buffer, 0, value0);
/// ..
/// EfficientHashLib.set(buffer, 9, value9);
/// bytes32 finalHash = EfficientHashLib.hash(buffer);
/// ```
library EfficientHashLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MALLOC-LESS HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `keccak256(abi.encode(v0))`.
function hash(bytes32 v0) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
result := keccak256(0x00, 0x20)
}
}
/// @dev Returns `keccak256(abi.encode(v0))`.
function hash(uint256 v0) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
result := keccak256(0x00, 0x20)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1))`.
function hash(bytes32 v0, bytes32 v1) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
mstore(0x20, v1)
result := keccak256(0x00, 0x40)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1))`.
function hash(uint256 v0, uint256 v1) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
mstore(0x20, v1)
result := keccak256(0x00, 0x40)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
result := keccak256(m, 0x60)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
function hash(uint256 v0, uint256 v1, uint256 v2) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
result := keccak256(m, 0x60)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
result := keccak256(m, 0x80)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
result := keccak256(m, 0x80)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
result := keccak256(m, 0xa0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
result := keccak256(m, 0xa0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4, bytes32 v5)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
result := keccak256(m, 0xc0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4, uint256 v5)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
result := keccak256(m, 0xc0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
result := keccak256(m, 0xe0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
result := keccak256(m, 0xe0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
result := keccak256(m, 0x100)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
result := keccak256(m, 0x100)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
result := keccak256(m, 0x120)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
result := keccak256(m, 0x120)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
result := keccak256(m, 0x140)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
result := keccak256(m, 0x140)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
result := keccak256(m, 0x160)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
result := keccak256(m, 0x160)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
result := keccak256(m, 0x180)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
result := keccak256(m, 0x180)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11,
bytes32 v12
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
result := keccak256(m, 0x1a0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11,
uint256 v12
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
result := keccak256(m, 0x1a0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11,
bytes32 v12,
bytes32 v13
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
mstore(add(m, 0x1a0), v13)
result := keccak256(m, 0x1c0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11,
uint256 v12,
uint256 v13
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
mstore(add(m, 0x1a0), v13)
result := keccak256(m, 0x1c0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTES32 BUFFER HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `keccak256(abi.encode(buffer[0], .., buffer[buffer.length - 1]))`.
function hash(bytes32[] memory buffer) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := keccak256(add(buffer, 0x20), shl(5, mload(buffer)))
}
}
/// @dev Sets `buffer[i]` to `value`, without a bounds check.
/// Returns the `buffer` for function chaining.
function set(bytes32[] memory buffer, uint256 i, bytes32 value)
internal
pure
returns (bytes32[] memory)
{
/// @solidity memory-safe-assembly
assembly {
mstore(add(buffer, shl(5, add(1, i))), value)
}
return buffer;
}
/// @dev Sets `buffer[i]` to `value`, without a bounds check.
/// Returns the `buffer` for function chaining.
function set(bytes32[] memory buffer, uint256 i, uint256 value)
internal
pure
returns (bytes32[] memory)
{
/// @solidity memory-safe-assembly
assembly {
mstore(add(buffer, shl(5, add(1, i))), value)
}
return buffer;
}
/// @dev Returns `new bytes32[](n)`, without zeroing out the memory.
function malloc(uint256 n) internal pure returns (bytes32[] memory buffer) {
/// @solidity memory-safe-assembly
assembly {
buffer := mload(0x40)
mstore(buffer, n)
mstore(0x40, add(shl(5, add(1, n)), buffer))
}
}
/// @dev Frees memory that has been allocated for `buffer`.
/// No-op if `buffer.length` is zero, or if new memory has been allocated after `buffer`.
function free(bytes32[] memory buffer) internal pure {
/// @solidity memory-safe-assembly
assembly {
let n := mload(buffer)
mstore(shl(6, lt(iszero(n), eq(add(shl(5, add(1, n)), buffer), mload(0x40)))), buffer)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EQUALITY CHECKS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `a == abi.decode(b, (bytes32))`.
function eq(bytes32 a, bytes memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(eq(0x20, mload(b)), eq(a, mload(add(b, 0x20))))
}
}
/// @dev Returns `abi.decode(a, (bytes32)) == a`.
function eq(bytes memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(eq(0x20, mload(a)), eq(b, mload(add(a, 0x20))))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE SLICE HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function hash(bytes memory b, uint256 start, uint256 end)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
end := xor(end, mul(xor(end, n), lt(n, end)))
start := xor(start, mul(xor(start, n), lt(n, start)))
result := keccak256(add(add(b, 0x20), start), mul(gt(end, start), sub(end, start)))
}
}
/// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
function hash(bytes memory b, uint256 start) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
start := xor(start, mul(xor(start, n), lt(n, start)))
result := keccak256(add(add(b, 0x20), start), mul(gt(n, start), sub(n, start)))
}
}
/// @dev Returns the keccak256 of the bytes.
function hash(bytes memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := keccak256(add(b, 0x20), mload(b))
}
}
/// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function hashCalldata(bytes calldata b, uint256 start, uint256 end)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(end, start), sub(end, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := keccak256(mload(0x40), n)
}
}
/// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
function hashCalldata(bytes calldata b, uint256 start) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(b.length, start), sub(b.length, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := keccak256(mload(0x40), n)
}
}
/// @dev Returns the keccak256 of the bytes.
function hashCalldata(bytes calldata b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
calldatacopy(mload(0x40), b.offset, b.length)
result := keccak256(mload(0x40), b.length)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SHA2-256 HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `sha256(abi.encode(b))`. Yes, it's more efficient.
function sha2(bytes32 b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, b)
result := mload(staticcall(gas(), 2, 0x00, 0x20, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function sha2(bytes memory b, uint256 start, uint256 end)
internal
view
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
end := xor(end, mul(xor(end, n), lt(n, end)))
start := xor(start, mul(xor(start, n), lt(n, start)))
// forgefmt: disable-next-item
result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
mul(gt(end, start), sub(end, start)), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
function sha2(bytes memory b, uint256 start) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
start := xor(start, mul(xor(start, n), lt(n, start)))
// forgefmt: disable-next-item
result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
mul(gt(n, start), sub(n, start)), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the bytes.
function sha2(bytes memory b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(staticcall(gas(), 2, add(b, 0x20), mload(b), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function sha2Calldata(bytes calldata b, uint256 start, uint256 end)
internal
view
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(end, start), sub(end, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
function sha2Calldata(bytes calldata b, uint256 start) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(b.length, start), sub(b.length, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the bytes.
function sha2Calldata(bytes calldata b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
calldatacopy(mload(0x40), b.offset, b.length)
result := mload(staticcall(gas(), 2, mload(0x40), b.length, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
}
node_modules/solady/src/utils/JSONParserLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for parsing JSONs.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/JSONParserLib.sol)
library JSONParserLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The input is invalid.
error ParsingFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// There are 6 types of variables in JSON (excluding undefined).
/// @dev For denoting that an item has not been initialized.
/// A item returned from `parse` will never be of an undefined type.
/// Parsing an invalid JSON string will simply revert.
uint8 internal constant TYPE_UNDEFINED = 0;
/// @dev Type representing an array (e.g. `[1,2,3]`).
uint8 internal constant TYPE_ARRAY = 1;
/// @dev Type representing an object (e.g. `{"a":"A","b":"B"}`).
uint8 internal constant TYPE_OBJECT = 2;
/// @dev Type representing a number (e.g. `-1.23e+21`).
uint8 internal constant TYPE_NUMBER = 3;
/// @dev Type representing a string (e.g. `"hello"`).
uint8 internal constant TYPE_STRING = 4;
/// @dev Type representing a boolean (i.e. `true` or `false`).
uint8 internal constant TYPE_BOOLEAN = 5;
/// @dev Type representing null (i.e. `null`).
uint8 internal constant TYPE_NULL = 6;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A pointer to a parsed JSON node.
struct Item {
// Do NOT modify the `_data` directly.
uint256 _data;
}
// Private constants for packing `_data`.
uint256 private constant _BITPOS_STRING = 32 * 7 - 8;
uint256 private constant _BITPOS_KEY_LENGTH = 32 * 6 - 8;
uint256 private constant _BITPOS_KEY = 32 * 5 - 8;
uint256 private constant _BITPOS_VALUE_LENGTH = 32 * 4 - 8;
uint256 private constant _BITPOS_VALUE = 32 * 3 - 8;
uint256 private constant _BITPOS_CHILD = 32 * 2 - 8;
uint256 private constant _BITPOS_SIBLING_OR_PARENT = 32 * 1 - 8;
uint256 private constant _BITMASK_POINTER = 0xffffffff;
uint256 private constant _BITMASK_TYPE = 7;
uint256 private constant _KEY_INITED = 1 << 3;
uint256 private constant _VALUE_INITED = 1 << 4;
uint256 private constant _CHILDREN_INITED = 1 << 5;
uint256 private constant _PARENT_IS_ARRAY = 1 << 6;
uint256 private constant _PARENT_IS_OBJECT = 1 << 7;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* JSON PARSING OPERATION */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Parses the JSON string `s`, and returns the root.
/// Reverts if `s` is not a valid JSON as specified in RFC 8259.
/// Object items WILL simply contain all their children, inclusive of repeated keys,
/// in the same order which they appear in the JSON string.
///
/// Note: For efficiency, this function WILL NOT make a copy of `s`.
/// The parsed tree WILL contain offsets to `s`.
/// Do NOT pass in a string that WILL be modified later on.
function parse(string memory s) internal pure returns (Item memory result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x40, result) // We will use our own allocation instead.
}
bytes32 r = _query(_toInput(s), 255);
/// @solidity memory-safe-assembly
assembly {
result := r
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* JSON ITEM OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note:
// - An item is a node in the JSON tree.
// - The value of a string item WILL be double-quoted, JSON encoded.
// - We make a distinction between `index` and `key`.
// - Items in arrays are located by `index` (uint256).
// - Items in objects are located by `key` (string).
// - Keys are always strings, double-quoted, JSON encoded.
//
// These design choices are made to balance between efficiency and ease-of-use.
/// @dev Returns the string value of the item.
/// This is its exact string representation in the original JSON string.
/// The returned string WILL have leading and trailing whitespace trimmed.
/// All inner whitespace WILL be preserved, exactly as it is in the original JSON string.
/// If the item's type is string, the returned string WILL be double-quoted, JSON encoded.
///
/// Note: This function lazily instantiates and caches the returned string.
/// Do NOT modify the returned string.
function value(Item memory item) internal pure returns (string memory result) {
bytes32 r = _query(_toInput(item), 0);
/// @solidity memory-safe-assembly
assembly {
result := r
}
}
/// @dev Returns the index of the item in the array.
/// It the item's parent is not an array, returns 0.
function index(Item memory item) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if and(mload(item), _PARENT_IS_ARRAY) {
result := and(_BITMASK_POINTER, shr(_BITPOS_KEY, mload(item)))
}
}
}
/// @dev Returns the key of the item in the object.
/// It the item's parent is not an object, returns an empty string.
/// The returned string WILL be double-quoted, JSON encoded.
///
/// Note: This function lazily instantiates and caches the returned string.
/// Do NOT modify the returned string.
function key(Item memory item) internal pure returns (string memory result) {
if (item._data & _PARENT_IS_OBJECT != 0) {
bytes32 r = _query(_toInput(item), 1);
/// @solidity memory-safe-assembly
assembly {
result := r
}
}
}
/// @dev Returns the key of the item in the object.
/// It the item is neither an array nor object, returns an empty array.
///
/// Note: This function lazily instantiates and caches the returned array.
/// Do NOT modify the returned array.
function children(Item memory item) internal pure returns (Item[] memory result) {
bytes32 r = _query(_toInput(item), 3);
/// @solidity memory-safe-assembly
assembly {
result := r
}
}
/// @dev Returns the number of children.
/// It the item is neither an array nor object, returns zero.
function size(Item memory item) internal pure returns (uint256 result) {
bytes32 r = _query(_toInput(item), 3);
/// @solidity memory-safe-assembly
assembly {
result := mload(r)
}
}
/// @dev Returns the item at index `i` for (array).
/// If `item` is not an array, the result's type WILL be undefined.
/// If there is no item with the index, the result's type WILL be undefined.
function at(Item memory item, uint256 i) internal pure returns (Item memory result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x40, result) // Free the default allocation. We'll allocate manually.
}
bytes32 r = _query(_toInput(item), 3);
/// @solidity memory-safe-assembly
assembly {
result := mload(add(add(r, 0x20), shl(5, i)))
if iszero(and(lt(i, mload(r)), eq(and(mload(item), _BITMASK_TYPE), TYPE_ARRAY))) {
result := 0x60 // Reset to the zero pointer.
}
}
}
/// @dev Returns the item at key `k` for (object).
/// If `item` is not an object, the result's type WILL be undefined.
/// The key MUST be double-quoted, JSON encoded. This is for efficiency reasons.
/// - Correct : `item.at('"k"')`.
/// - Wrong : `item.at("k")`.
/// For duplicated keys, the last item with the key WILL be returned.
/// If there is no item with the key, the result's type WILL be undefined.
function at(Item memory item, string memory k) internal pure returns (Item memory result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x40, result) // Free the default allocation. We'll allocate manually.
result := 0x60 // Initialize to the zero pointer.
}
if (isObject(item)) {
bytes32 kHash = keccak256(bytes(k));
Item[] memory r = children(item);
// We'll just do a linear search. The alternatives are very bloated.
for (uint256 i = r.length << 5; i != 0;) {
/// @solidity memory-safe-assembly
assembly {
item := mload(add(r, i))
i := sub(i, 0x20)
}
if (keccak256(bytes(key(item))) != kHash) continue;
result = item;
break;
}
}
}
/// @dev Returns the item's type.
function getType(Item memory item) internal pure returns (uint8 result) {
result = uint8(item._data & _BITMASK_TYPE);
}
/// Note: All types are mutually exclusive.
/// @dev Returns whether the item is of type undefined.
function isUndefined(Item memory item) internal pure returns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_UNDEFINED;
}
/// @dev Returns whether the item is of type array.
function isArray(Item memory item) internal pure returns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_ARRAY;
}
/// @dev Returns whether the item is of type object.
function isObject(Item memory item) internal pure returns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_OBJECT;
}
/// @dev Returns whether the item is of type number.
function isNumber(Item memory item) internal pure returns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_NUMBER;
}
/// @dev Returns whether the item is of type string.
function isString(Item memory item) internal pure returns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_STRING;
}
/// @dev Returns whether the item is of type boolean.
function isBoolean(Item memory item) internal pure returns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_BOOLEAN;
}
/// @dev Returns whether the item is of type null.
function isNull(Item memory item) internal pure returns (bool result) {
result = item._data & _BITMASK_TYPE == TYPE_NULL;
}
/// @dev Returns the item's parent.
/// If the item does not have a parent, the result's type will be undefined.
function parent(Item memory item) internal pure returns (Item memory result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x40, result) // Free the default allocation. We've already allocated.
result := and(shr(_BITPOS_SIBLING_OR_PARENT, mload(item)), _BITMASK_POINTER)
if iszero(result) { result := 0x60 } // Reset to the zero pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UTILITY FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Parses an unsigned integer from a string (in decimal, i.e. base 10).
/// Reverts if `s` is not a valid uint256 string matching the RegEx `^[0-9]+$`,
/// or if the parsed number is too big for a uint256.
function parseUint(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(s)
let preMulOverflowThres := div(not(0), 10)
for { let i := 0 } 1 {} {
i := add(i, 1)
let digit := sub(and(mload(add(s, i)), 0xff), 48)
let mulOverflowed := gt(result, preMulOverflowThres)
let product := mul(10, result)
result := add(product, digit)
n := mul(n, iszero(or(or(mulOverflowed, lt(result, product)), gt(digit, 9))))
if iszero(lt(i, n)) { break }
}
if iszero(n) {
mstore(0x00, 0x10182796) // `ParsingFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Parses a signed integer from a string (in decimal, i.e. base 10).
/// Reverts if `s` is not a valid int256 string matching the RegEx `^[+-]?[0-9]+$`,
/// or if the parsed number cannot fit within `[-2**255 .. 2**255 - 1]`.
function parseInt(string memory s) internal pure returns (int256 result) {
uint256 n = bytes(s).length;
uint256 sign;
uint256 isNegative;
/// @solidity memory-safe-assembly
assembly {
if n {
let c := and(mload(add(s, 1)), 0xff)
isNegative := eq(c, 45)
if or(eq(c, 43), isNegative) {
sign := c
s := add(s, 1)
mstore(s, sub(n, 1))
}
if iszero(or(sign, lt(sub(c, 48), 10))) { s := 0x60 }
}
}
uint256 x = parseUint(s);
/// @solidity memory-safe-assembly
assembly {
if iszero(lt(x, add(shl(255, 1), isNegative))) {
mstore(0x00, 0x10182796) // `ParsingFailed()`.
revert(0x1c, 0x04)
}
if sign {
mstore(s, sign)
s := sub(s, 1)
mstore(s, n)
}
result := xor(x, mul(xor(x, add(not(x), 1)), isNegative))
}
}
/// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).
/// Reverts if `s` is not a valid uint256 hex string matching the RegEx
/// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number cannot fit within `[0 .. 2**256 - 1]`.
function parseUintFromHex(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(s)
// Skip two if starts with '0x' or '0X'.
let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))
for {} 1 {} {
i := add(i, 1)
let c :=
byte(
and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),
0x3010a071000000b0104040208000c05090d060e0f
)
n := mul(n, iszero(or(iszero(c), shr(252, result))))
result := add(shl(4, result), sub(c, 1))
if iszero(lt(i, n)) { break }
}
if iszero(n) {
mstore(0x00, 0x10182796) // `ParsingFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Decodes a JSON encoded string.
/// The string MUST be double-quoted, JSON encoded.
/// Reverts if the string is invalid.
/// As you can see, it's pretty complex for a deceptively simple looking task.
function decodeString(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
function fail() {
mstore(0x00, 0x10182796) // `ParsingFailed()`.
revert(0x1c, 0x04)
}
function decodeUnicodeEscapeSequence(pIn_, end_) -> _unicode, _pOut {
_pOut := add(pIn_, 4)
let b_ := iszero(gt(_pOut, end_))
let t_ := mload(pIn_) // Load the whole word.
for { let i_ := 0 } iszero(eq(i_, 4)) { i_ := add(i_, 1) } {
let c_ := sub(byte(i_, t_), 48)
if iszero(and(shr(c_, 0x7e0000007e03ff), b_)) { fail() } // Not hexadecimal.
c_ := sub(c_, add(mul(gt(c_, 16), 7), shl(5, gt(c_, 48))))
_unicode := add(shl(4, _unicode), c_)
}
}
function decodeUnicodeCodePoint(pIn_, end_) -> _unicode, _pOut {
_unicode, _pOut := decodeUnicodeEscapeSequence(pIn_, end_)
if iszero(or(lt(_unicode, 0xd800), gt(_unicode, 0xdbff))) {
let t_ := mload(_pOut) // Load the whole word.
end_ := mul(end_, eq(shr(240, t_), 0x5c75)) // Fail if not starting with '\\u'.
t_, _pOut := decodeUnicodeEscapeSequence(add(_pOut, 2), end_)
_unicode := add(0x10000, add(shl(10, and(0x3ff, _unicode)), and(0x3ff, t_)))
}
}
function appendCodePointAsUTF8(pIn_, c_) -> _pOut {
if iszero(gt(c_, 0x7f)) {
mstore8(pIn_, c_)
_pOut := add(pIn_, 1)
leave
}
mstore8(0x1f, c_)
mstore8(0x1e, shr(6, c_))
if iszero(gt(c_, 0x7ff)) {
mstore(pIn_, shl(240, or(0xc080, and(0x1f3f, mload(0x00)))))
_pOut := add(pIn_, 2)
leave
}
mstore8(0x1d, shr(12, c_))
if iszero(gt(c_, 0xffff)) {
mstore(pIn_, shl(232, or(0xe08080, and(0x0f3f3f, mload(0x00)))))
_pOut := add(pIn_, 3)
leave
}
mstore8(0x1c, shr(18, c_))
mstore(pIn_, shl(224, or(0xf0808080, and(0x073f3f3f, mload(0x00)))))
_pOut := add(pIn_, shl(2, lt(c_, 0x110000)))
}
function chr(p_) -> _c {
_c := byte(0, mload(p_))
}
let n := mload(s)
let end := add(add(s, n), 0x1f)
if iszero(and(gt(n, 1), eq(0x2222, or(and(0xff00, mload(add(s, 2))), chr(end))))) {
fail() // Fail if not double-quoted.
}
let out := add(mload(0x40), 0x20)
for { let curr := add(s, 0x21) } iszero(eq(curr, end)) {} {
let c := chr(curr)
curr := add(curr, 1)
// Not '\\'.
if iszero(eq(c, 92)) {
// Not '"'.
if iszero(eq(c, 34)) {
mstore8(out, c)
out := add(out, 1)
continue
}
curr := end
}
if iszero(eq(curr, end)) {
let escape := chr(curr)
curr := add(curr, 1)
// '"', '/', '\\'.
if and(shr(escape, 0x100000000000800400000000), 1) {
mstore8(out, escape)
out := add(out, 1)
continue
}
// 'u'.
if eq(escape, 117) {
escape, curr := decodeUnicodeCodePoint(curr, end)
out := appendCodePointAsUTF8(out, escape)
continue
}
// `{'b':'\b', 'f':'\f', 'n':'\n', 'r':'\r', 't':'\t'}`.
escape := byte(sub(escape, 85), 0x080000000c000000000000000a0000000d0009)
if escape {
mstore8(out, escape)
out := add(out, 1)
continue
}
}
fail()
break
}
mstore(out, 0) // Zeroize the last slot.
result := mload(0x40)
mstore(result, sub(out, add(result, 0x20))) // Store the length.
mstore(0x40, add(out, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Performs a query on the input with the given mode.
function _query(bytes32 input, uint256 mode) private pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
function fail() {
mstore(0x00, 0x10182796) // `ParsingFailed()`.
revert(0x1c, 0x04)
}
function chr(p_) -> _c {
_c := byte(0, mload(p_))
}
function skipWhitespace(pIn_, end_) -> _pOut {
for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {
if iszero(and(shr(chr(_pOut), 0x100002600), 1)) { leave } // Not in ' \n\r\t'.
}
}
function setP(packed_, bitpos_, p_) -> _packed {
// Perform an out-of-gas revert if `p_` exceeds `_BITMASK_POINTER`.
returndatacopy(returndatasize(), returndatasize(), gt(p_, _BITMASK_POINTER))
_packed := or(and(not(shl(bitpos_, _BITMASK_POINTER)), packed_), shl(bitpos_, p_))
}
function getP(packed_, bitpos_) -> _p {
_p := and(_BITMASK_POINTER, shr(bitpos_, packed_))
}
function mallocItem(s_, packed_, pStart_, pCurr_, type_) -> _item {
_item := mload(0x40)
// forgefmt: disable-next-item
packed_ := setP(setP(packed_, _BITPOS_VALUE, sub(pStart_, add(s_, 0x20))),
_BITPOS_VALUE_LENGTH, sub(pCurr_, pStart_))
mstore(_item, or(packed_, type_))
mstore(0x40, add(_item, 0x20)) // Allocate memory.
}
function parseValue(s_, sibling_, pIn_, end_) -> _item, _pOut {
let packed_ := setP(mload(0x00), _BITPOS_SIBLING_OR_PARENT, sibling_)
_pOut := skipWhitespace(pIn_, end_)
if iszero(lt(_pOut, end_)) { leave }
for { let c_ := chr(_pOut) } 1 {} {
// If starts with '"'.
if eq(c_, 34) {
let pStart_ := _pOut
_pOut := parseStringSub(s_, packed_, _pOut, end_)
_item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_STRING)
break
}
// If starts with '['.
if eq(c_, 91) {
_item, _pOut := parseArray(s_, packed_, _pOut, end_)
break
}
// If starts with '{'.
if eq(c_, 123) {
_item, _pOut := parseObject(s_, packed_, _pOut, end_)
break
}
// If starts with any in '0123456789-'.
if and(shr(c_, shl(45, 0x1ff9)), 1) {
_item, _pOut := parseNumber(s_, packed_, _pOut, end_)
break
}
if iszero(gt(add(_pOut, 4), end_)) {
let pStart_ := _pOut
let w_ := shr(224, mload(_pOut))
// 'true' in hex format.
if eq(w_, 0x74727565) {
_pOut := add(_pOut, 4)
_item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)
break
}
// 'null' in hex format.
if eq(w_, 0x6e756c6c) {
_pOut := add(_pOut, 4)
_item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_NULL)
break
}
}
if iszero(gt(add(_pOut, 5), end_)) {
let pStart_ := _pOut
let w_ := shr(216, mload(_pOut))
// 'false' in hex format.
if eq(w_, 0x66616c7365) {
_pOut := add(_pOut, 5)
_item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)
break
}
}
fail()
break
}
_pOut := skipWhitespace(_pOut, end_)
}
function parseArray(s_, packed_, pIn_, end_) -> _item, _pOut {
let j_ := 0
for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {
if iszero(lt(_pOut, end_)) { fail() }
if iszero(_item) {
_pOut := skipWhitespace(_pOut, end_)
if eq(chr(_pOut), 93) { break } // ']'.
}
_item, _pOut := parseValue(s_, _item, _pOut, end_)
if _item {
// forgefmt: disable-next-item
mstore(_item, setP(or(_PARENT_IS_ARRAY, mload(_item)),
_BITPOS_KEY, j_))
j_ := add(j_, 1)
let c_ := chr(_pOut)
if eq(c_, 93) { break } // ']'.
if eq(c_, 44) { continue } // ','.
}
_pOut := end_
}
_pOut := add(_pOut, 1)
packed_ := setP(packed_, _BITPOS_CHILD, _item)
_item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_ARRAY)
}
function parseObject(s_, packed_, pIn_, end_) -> _item, _pOut {
for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {
if iszero(lt(_pOut, end_)) { fail() }
if iszero(_item) {
_pOut := skipWhitespace(_pOut, end_)
if eq(chr(_pOut), 125) { break } // '}'.
}
_pOut := skipWhitespace(_pOut, end_)
let pKeyStart_ := _pOut
let pKeyEnd_ := parseStringSub(s_, _item, _pOut, end_)
_pOut := skipWhitespace(pKeyEnd_, end_)
// If ':'.
if eq(chr(_pOut), 58) {
_item, _pOut := parseValue(s_, _item, add(_pOut, 1), end_)
if _item {
// forgefmt: disable-next-item
mstore(_item, setP(setP(or(_PARENT_IS_OBJECT, mload(_item)),
_BITPOS_KEY_LENGTH, sub(pKeyEnd_, pKeyStart_)),
_BITPOS_KEY, sub(pKeyStart_, add(s_, 0x20))))
let c_ := chr(_pOut)
if eq(c_, 125) { break } // '}'.
if eq(c_, 44) { continue } // ','.
}
}
_pOut := end_
}
_pOut := add(_pOut, 1)
packed_ := setP(packed_, _BITPOS_CHILD, _item)
_item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_OBJECT)
}
function checkStringU(p_, o_) {
// If not in '0123456789abcdefABCDEF', revert.
if iszero(and(shr(sub(chr(add(p_, o_)), 48), 0x7e0000007e03ff), 1)) { fail() }
if iszero(eq(o_, 5)) { checkStringU(p_, add(o_, 1)) }
}
function parseStringSub(s_, packed_, pIn_, end_) -> _pOut {
if iszero(lt(pIn_, end_)) { fail() }
for { _pOut := add(pIn_, 1) } 1 {} {
let c_ := chr(_pOut)
if eq(c_, 34) { break } // '"'.
// Not '\'.
if iszero(eq(c_, 92)) {
_pOut := add(_pOut, 1)
continue
}
c_ := chr(add(_pOut, 1))
// '"', '\', '//', 'b', 'f', 'n', 'r', 't'.
if and(shr(sub(c_, 34), 0x510110400000000002001), 1) {
_pOut := add(_pOut, 2)
continue
}
// 'u'.
if eq(c_, 117) {
checkStringU(_pOut, 2)
_pOut := add(_pOut, 6)
continue
}
_pOut := end_
break
}
if iszero(lt(_pOut, end_)) { fail() }
_pOut := add(_pOut, 1)
}
function skip0To9s(pIn_, end_, atLeastOne_) -> _pOut {
for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {
if iszero(lt(sub(chr(_pOut), 48), 10)) { break } // Not '0'..'9'.
}
if and(atLeastOne_, eq(pIn_, _pOut)) { fail() }
}
function parseNumber(s_, packed_, pIn_, end_) -> _item, _pOut {
_pOut := pIn_
if eq(chr(_pOut), 45) { _pOut := add(_pOut, 1) } // '-'.
if iszero(lt(sub(chr(_pOut), 48), 10)) { fail() } // Not '0'..'9'.
let c_ := chr(_pOut)
_pOut := add(_pOut, 1)
if iszero(eq(c_, 48)) { _pOut := skip0To9s(_pOut, end_, 0) } // Not '0'.
if eq(chr(_pOut), 46) { _pOut := skip0To9s(add(_pOut, 1), end_, 1) } // '.'.
let t_ := mload(_pOut)
// 'E', 'e'.
if eq(or(0x20, byte(0, t_)), 101) {
// forgefmt: disable-next-item
_pOut := skip0To9s(add(byte(sub(byte(1, t_), 14), 0x010001), // '+', '-'.
add(_pOut, 1)), end_, 1)
}
_item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_NUMBER)
}
function copyStr(s_, offset_, len_) -> _sCopy {
_sCopy := mload(0x40)
s_ := add(s_, offset_)
let w_ := not(0x1f)
for { let i_ := and(add(len_, 0x1f), w_) } 1 {} {
mstore(add(_sCopy, i_), mload(add(s_, i_)))
i_ := add(i_, w_) // `sub(i_, 0x20)`.
if iszero(i_) { break }
}
mstore(_sCopy, len_) // Copy the length.
mstore(add(add(_sCopy, 0x20), len_), 0) // Zeroize the last slot.
mstore(0x40, add(add(_sCopy, 0x40), len_)) // Allocate memory.
}
function value(item_) -> _value {
let packed_ := mload(item_)
_value := getP(packed_, _BITPOS_VALUE) // The offset in the string.
if iszero(and(_VALUE_INITED, packed_)) {
let s_ := getP(packed_, _BITPOS_STRING)
_value := copyStr(s_, _value, getP(packed_, _BITPOS_VALUE_LENGTH))
packed_ := setP(packed_, _BITPOS_VALUE, _value)
mstore(s_, or(_VALUE_INITED, packed_))
}
}
function children(item_) -> _arr {
_arr := 0x60 // Initialize to the zero pointer.
let packed_ := mload(item_)
for {} iszero(gt(and(_BITMASK_TYPE, packed_), TYPE_OBJECT)) {} {
if or(iszero(packed_), iszero(item_)) { break }
if and(packed_, _CHILDREN_INITED) {
_arr := getP(packed_, _BITPOS_CHILD)
break
}
_arr := mload(0x40)
let o_ := add(_arr, 0x20)
for { let h_ := getP(packed_, _BITPOS_CHILD) } h_ {} {
mstore(o_, h_)
let q_ := mload(h_)
let y_ := getP(q_, _BITPOS_SIBLING_OR_PARENT)
mstore(h_, setP(q_, _BITPOS_SIBLING_OR_PARENT, item_))
h_ := y_
o_ := add(o_, 0x20)
}
let w_ := not(0x1f)
let n_ := add(w_, sub(o_, _arr))
mstore(_arr, shr(5, n_))
mstore(0x40, o_) // Allocate memory.
packed_ := setP(packed_, _BITPOS_CHILD, _arr)
mstore(item_, or(_CHILDREN_INITED, packed_))
// Reverse the array.
if iszero(lt(n_, 0x40)) {
let lo_ := add(_arr, 0x20)
let hi_ := add(_arr, n_)
for {} 1 {} {
let temp_ := mload(lo_)
mstore(lo_, mload(hi_))
mstore(hi_, temp_)
hi_ := add(hi_, w_)
lo_ := add(lo_, 0x20)
if iszero(lt(lo_, hi_)) { break }
}
}
break
}
}
function getStr(item_, bitpos_, bitposLength_, bitmaskInited_) -> _result {
_result := 0x60 // Initialize to the zero pointer.
let packed_ := mload(item_)
if or(iszero(item_), iszero(packed_)) { leave }
_result := getP(packed_, bitpos_)
if iszero(and(bitmaskInited_, packed_)) {
let s_ := getP(packed_, _BITPOS_STRING)
_result := copyStr(s_, _result, getP(packed_, bitposLength_))
mstore(item_, or(bitmaskInited_, setP(packed_, bitpos_, _result)))
}
}
switch mode
// Get value.
case 0 { result := getStr(input, _BITPOS_VALUE, _BITPOS_VALUE_LENGTH, _VALUE_INITED) }
// Get key.
case 1 { result := getStr(input, _BITPOS_KEY, _BITPOS_KEY_LENGTH, _KEY_INITED) }
// Get children.
case 3 { result := children(input) }
// Parse.
default {
let p := add(input, 0x20)
let e := add(p, mload(input))
if iszero(eq(p, e)) {
let c := chr(e)
mstore8(e, 34) // Place a '"' at the end to speed up parsing.
// The `34 << 248` makes `mallocItem` preserve '"' at the end.
mstore(0x00, setP(shl(248, 34), _BITPOS_STRING, input))
result, p := parseValue(input, 0, p, e)
mstore8(e, c) // Restore the original char at the end.
}
if or(lt(p, e), iszero(result)) { fail() }
}
}
}
/// @dev Casts the input to a bytes32.
function _toInput(string memory input) private pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := input
}
}
/// @dev Casts the input to a bytes32.
function _toInput(Item memory input) private pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := input
}
}
}
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 Checks if `x` is in `a`. Assumes `a` has been checked.
function checkInCalldata(bytes calldata x, bytes calldata a) internal pure {
/// @solidity memory-safe-assembly
assembly {
if or(
or(lt(x.offset, a.offset), gt(add(x.offset, x.length), add(a.length, a.offset))),
shr(64, or(x.length, x.offset))
) { revert(0x00, 0x00) }
}
}
/// @dev Checks if `x` is in `a`. Assumes `a` has been checked.
function checkInCalldata(bytes[] calldata x, bytes calldata a) internal pure {
/// @solidity memory-safe-assembly
assembly {
let e := sub(add(a.length, a.offset), 0x20)
if or(lt(x.offset, a.offset), shr(64, x.offset)) { revert(0x00, 0x00) }
for { let i := 0 } iszero(eq(x.length, i)) { i := add(i, 1) } {
let o := calldataload(add(x.offset, shl(5, i)))
let t := add(o, x.offset)
let l := calldataload(t)
if or(shr(64, or(l, o)), gt(add(t, l), e)) { revert(0x00, 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
}
}
/// @dev Returns the most significant 20 bytes as an address.
function msbToAddress(bytes32 x) internal pure returns (address) {
return address(bytes20(x));
}
/// @dev Returns the least significant 20 bytes as an address.
function lsbToAddress(bytes32 x) internal pure returns (address) {
return address(uint160(uint256(x)));
}
}
node_modules/solady/src/utils/LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {LibBytes} from "./LibBytes.sol";
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Goated string storage struct that totally MOGs, no cap, fr.
/// Uses less gas and bytecode than Solidity's native string storage. It's meta af.
/// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
struct StringStorage {
bytes32 _spacer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/// @dev The input string must be a 7-bit ASCII.
error StringNot7BitASCII();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
/// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
/// @dev Lookup for '0123456789'.
uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
/// @dev Lookup for '0123456789abcdefABCDEF'.
uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
/// @dev Lookup for '01234567'.
uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
/// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRING STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sets the value of the string storage `$` to `s`.
function set(StringStorage storage $, string memory s) internal {
LibBytes.set(bytesStorage($), bytes(s));
}
/// @dev Sets the value of the string storage `$` to `s`.
function setCalldata(StringStorage storage $, string calldata s) internal {
LibBytes.setCalldata(bytesStorage($), bytes(s));
}
/// @dev Sets the value of the string storage `$` to the empty string.
function clear(StringStorage storage $) internal {
delete $._spacer;
}
/// @dev Returns whether the value stored is `$` is the empty string "".
function isEmpty(StringStorage storage $) internal view returns (bool) {
return uint256($._spacer) & 0xff == uint256(0);
}
/// @dev Returns the length of the value stored in `$`.
function length(StringStorage storage $) internal view returns (uint256) {
return LibBytes.length(bytesStorage($));
}
/// @dev Returns the value stored in `$`.
function get(StringStorage storage $) internal view returns (string memory) {
return string(LibBytes.get(bytesStorage($)));
}
/// @dev Returns the uint8 at index `i`. If out-of-bounds, returns 0.
function uint8At(StringStorage storage $, uint256 i) internal view returns (uint8) {
return LibBytes.uint8At(bytesStorage($), i);
}
/// @dev Helper to cast `$` to a `BytesStorage`.
function bytesStorage(StringStorage storage $)
internal
pure
returns (LibBytes.BytesStorage storage casted)
{
/// @solidity memory-safe-assembly
assembly {
casted.slot := $.slot
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
result := add(mload(0x40), 0x80)
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end of the memory to calculate the length later.
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
result := add(result, w) // `sub(result, 1)`.
// Store the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(result, add(48, mod(temp, 10)))
temp := div(temp, 10) // Keep dividing `temp` until zero.
if iszero(temp) { break }
}
let n := sub(end, result)
result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory result) {
if (value >= 0) return toString(uint256(value));
unchecked {
result = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let n := mload(result) // Load the string length.
mstore(result, 0x2d) // Store the '-' character.
result := sub(result, 1) // Move back the string pointer by a byte.
mstore(result, add(n, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `byteCount` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `byteCount * 2 + 2` bytes.
/// Reverts if `byteCount` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 byteCount)
internal
pure
returns (string memory result)
{
result = toHexStringNoPrefix(value, byteCount);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `byteCount` bytes.
/// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte,
/// giving a total length of `byteCount * 2` bytes.
/// Reverts if `byteCount` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 byteCount)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `byteCount * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
result := add(mload(0x40), and(add(shl(1, byteCount), 0x42), not(0x1f)))
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end to calculate the length later.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(result, add(byteCount, byteCount))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
result := add(result, w) // `sub(result, 2)`.
mstore8(add(result, 1), mload(and(temp, 15)))
mstore8(result, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(result, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
let n := sub(end, result)
result := sub(result, 0x20)
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
let n := add(mload(result), 2) // Compute the length.
mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero.
mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value)
internal
pure
returns (string memory result)
{
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
let n := mload(result) // Get the length.
result := add(result, o) // Move the pointer, accounting for leading zero.
mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
result := add(mload(0x40), 0x80)
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end to calculate the length later.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
result := add(result, w) // `sub(result, 2)`.
mstore8(add(result, 1), mload(and(temp, 15)))
mstore8(result, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
let n := sub(end, result)
result := sub(result, 0x20)
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory result) {
result = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(result, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
// Allocate memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(result, 0x80))
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
result := add(result, 2)
mstore(result, 40) // Store the length.
let o := add(result, 0x20)
mstore(add(o, 40), 0) // Zeroize the slot after the string.
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory result) {
result = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(raw)
result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(result, add(n, n)) // Store the length of the output.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let o := add(result, 0x20)
let end := add(raw, n)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
let mask := shl(7, div(not(0), 255))
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string,
/// AND all characters are in the `allowed` lookup.
/// Note: If `s` is empty, returns true regardless of `allowed`.
function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if mload(s) {
let allowed_ := shr(128, shl(128, allowed))
let o := add(s, 0x20)
for { let end := add(o, mload(s)) } 1 {} {
result := and(result, shr(byte(0, mload(o)), allowed_))
o := add(o, 1)
if iszero(and(result, lt(o, end))) { break }
}
}
}
}
/// @dev Converts the bytes in the 7-bit ASCII string `s` to
/// an allowed lookup for use in `is7BitASCII(s, allowed)`.
/// To save runtime gas, you can cache the result in an immutable variable.
function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
let o := add(s, 0x20)
for { let end := add(o, mload(s)) } 1 {} {
result := or(result, shl(byte(0, mload(o)), 1))
o := add(o, 1)
if iszero(lt(o, end)) { break }
}
if shr(128, result) {
mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
revert(0x1c, 0x04)
}
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
function replace(string memory subject, string memory needle, string memory replacement)
internal
pure
returns (string memory)
{
return string(LibBytes.replace(bytes(subject), bytes(needle), bytes(replacement)));
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(string memory subject, string memory needle, uint256 from)
internal
pure
returns (uint256)
{
return LibBytes.indexOf(bytes(subject), bytes(needle), from);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(string memory subject, string memory needle) internal pure returns (uint256) {
return LibBytes.indexOf(bytes(subject), bytes(needle), 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(string memory subject, string memory needle, uint256 from)
internal
pure
returns (uint256)
{
return LibBytes.lastIndexOf(bytes(subject), bytes(needle), from);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(string memory subject, string memory needle)
internal
pure
returns (uint256)
{
return LibBytes.lastIndexOf(bytes(subject), bytes(needle), type(uint256).max);
}
/// @dev Returns true if `needle` is found in `subject`, false otherwise.
function contains(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.contains(bytes(subject), bytes(needle));
}
/// @dev Returns whether `subject` starts with `needle`.
function startsWith(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.startsWith(bytes(subject), bytes(needle));
}
/// @dev Returns whether `subject` ends with `needle`.
function endsWith(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.endsWith(bytes(subject), bytes(needle));
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times) internal pure returns (string memory) {
return string(LibBytes.repeat(bytes(subject), times));
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory)
{
return string(LibBytes.slice(bytes(subject), start, end));
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start) internal pure returns (string memory) {
return string(LibBytes.slice(bytes(subject), start, type(uint256).max));
}
/// @dev Returns all the indices of `needle` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory needle)
internal
pure
returns (uint256[] memory)
{
return LibBytes.indicesOf(bytes(subject), bytes(needle));
}
/// @dev Returns an arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
bytes[] memory a = LibBytes.split(bytes(subject), bytes(delimiter));
/// @solidity memory-safe-assembly
assembly {
result := a
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b) internal pure returns (string memory) {
return string(LibBytes.concat(bytes(a), bytes(b)));
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(subject)
if n {
result := mload(0x40)
let o := add(result, 0x20)
let d := sub(subject, result)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
for { let end := add(o, n) } 1 {} {
let b := byte(0, mload(add(d, o)))
mstore8(o, xor(and(shr(b, flags), 0x20), b))
o := add(o, 1)
if eq(o, end) { break }
}
mstore(result, n) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n) // Store the length.
let o := add(result, 0x20)
mstore(o, s) // Store the bytes of the string.
mstore(add(o, n), 0) // Zeroize the slot after the string.
mstore(0x40, add(result, 0x40)) // Allocate memory.
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let end := add(s, mload(s))
let o := add(result, 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(o, c)
o := add(o, 1)
continue
}
let t := shr(248, mload(c))
mstore(o, mload(and(t, 0x1f)))
o := add(o, shr(5, t))
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let o := add(result, 0x20)
if addDoubleQuotes {
mstore8(o, 34)
o := add(1, o)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(o, c)
o := add(o, 1)
continue
}
mstore8(o, 0x5c) // "\\".
mstore8(add(o, 1), c)
o := add(o, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(o, mload(0x19)) // "\\u00XX".
o := add(o, 6)
continue
}
mstore8(o, 0x5c) // "\\".
mstore8(add(o, 1), mload(add(c, 8)))
o := add(o, 2)
}
if addDoubleQuotes {
mstore8(o, 34)
o := add(1, o)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Encodes `s` so that it can be safely used in a URI,
/// just like `encodeURIComponent` in JavaScript.
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
/// See: https://datatracker.ietf.org/doc/html/rfc2396
/// See: https://datatracker.ietf.org/doc/html/rfc3986
function encodeURIComponent(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
// Store "0123456789ABCDEF" in scratch space.
// Uppercased to be consistent with JavaScript's implementation.
mstore(0x0f, 0x30313233343536373839414243444546)
let o := add(result, 0x20)
for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// If not in `[0-9A-Z-a-z-_.!~*'()]`.
if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) {
mstore8(o, 0x25) // '%'.
mstore8(add(o, 1), mload(and(shr(4, c), 15)))
mstore8(add(o, 2), mload(and(c, 15)))
o := add(o, 3)
continue
}
mstore8(o, c)
o := add(o, 1)
}
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`.
/// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1.
function cmp(string memory a, string memory b) internal pure returns (int256) {
return LibBytes.cmp(bytes(a), bytes(b));
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40) // Grab the free memory pointer.
mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes).
mstore(result, 0) // Zeroize the length slot.
mstore(add(result, 0x1f), packed) // Store the length and bytes.
mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes.
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLen := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
or( // Load the length and the bytes of `a` and `b`.
shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))),
// `totalLen != 0 && totalLen < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLen, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
resultA := mload(0x40) // Grab the free memory pointer.
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20) // Store the return offset.
// End the transaction, returning the string.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
}
Compiler Settings
{"viaIR":false,"remappings":["openzeppelin/=node_modules/@openzeppelin/","@openzeppelin/=node_modules/@openzeppelin/","@openzeppelin-upgrades/contracts/=node_modules/@openzeppelin/contracts-upgradeable/","@risc0/contracts/=node_modules/risc0-ethereum/contracts/src/","@solady/=node_modules/solady/","solady/src/=node_modules/solady/src/","solady/utils/=node_modules/solady/src/utils/","solady/auth/=node_modules/solady/src/auth/","@optimism/=node_modules/optimism/","@sp1-contracts/=node_modules/sp1-contracts/contracts/src/","forge-std/=node_modules/forge-std/","@p256-verifier/contracts/=node_modules/p256-verifier/src/","@eth-fabric/urc/=node_modules/urc/src/","@automata-network/on-chain-pccs/=node_modules/@automata-network/on-chain-pccs/src/","@automata-network/automata-dcap-attestation/=node_modules/@automata-network/automata-dcap-attestation/","risc0/=node_modules/risc0-ethereum/contracts/src/","ds-test/=node_modules/ds-test/","src/=contracts/","test/=test/","script/=script/","optimism/=node_modules/optimism/","p256-verifier/=node_modules/p256-verifier/","risc0-ethereum/=node_modules/risc0-ethereum/","sp1-contracts/=node_modules/sp1-contracts/","urc/=node_modules/urc/"],"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{},"evmVersion":"prague"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"uint64","name":"_taikoChainId","internalType":"uint64"},{"type":"address","name":"_owner","internalType":"address"},{"type":"address","name":"_automataDcapAttestation","internalType":"address"},{"type":"address","name":"_registrar","internalType":"address"},{"type":"uint64","name":"_instanceValidityDelay","internalType":"uint64"}]},{"type":"error","name":"InvalidAggregatedProvingHash","inputs":[]},{"type":"error","name":"SGX_ALREADY_ATTESTED","inputs":[]},{"type":"error","name":"SGX_ATTRIBUTE_MISMATCH","inputs":[]},{"type":"error","name":"SGX_ATTRIBUTE_POLICY_NOT_SET","inputs":[]},{"type":"error","name":"SGX_DEBUG_ENCLAVE","inputs":[]},{"type":"error","name":"SGX_FORBIDDEN_ATTRIBUTES","inputs":[]},{"type":"error","name":"SGX_INSTANCE_ID_OVERFLOW","inputs":[]},{"type":"error","name":"SGX_INVALID_ATTESTATION","inputs":[]},{"type":"error","name":"SGX_INVALID_ATTRIBUTE_POLICY","inputs":[]},{"type":"error","name":"SGX_INVALID_CHAIN_ID","inputs":[]},{"type":"error","name":"SGX_INVALID_INSTANCE","inputs":[]},{"type":"error","name":"SGX_INVALID_PROOF","inputs":[]},{"type":"error","name":"SGX_INVALID_VALIDITY_DELAY","inputs":[]},{"type":"error","name":"SGX_MR_ENCLAVE_REVOKED","inputs":[]},{"type":"error","name":"SGX_MR_SIGNER_REVOKED","inputs":[]},{"type":"error","name":"SGX_NOT_AUTHORIZED","inputs":[]},{"type":"error","name":"SGX_NOT_REGISTRAR","inputs":[]},{"type":"error","name":"SGX_QUOTE_BLOCK_HASH_MISMATCH","inputs":[]},{"type":"error","name":"SGX_STALE_QUOTE","inputs":[]},{"type":"event","name":"EnclaveAttributePolicyRemoved","inputs":[{"type":"bytes32","name":"mrEnclave","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"EnclaveAttributePolicySet","inputs":[{"type":"bytes32","name":"mrEnclave","internalType":"bytes32","indexed":true},{"type":"bytes16","name":"mask","internalType":"bytes16","indexed":false},{"type":"bytes16","name":"expected","internalType":"bytes16","indexed":false},{"type":"uint32","name":"version","internalType":"uint32","indexed":false}],"anonymous":false},{"type":"event","name":"InstanceAdded","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true},{"type":"address","name":"instance","internalType":"address","indexed":true},{"type":"address","name":"replaced","internalType":"address","indexed":true},{"type":"uint256","name":"validSince","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"InstanceDeleted","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true},{"type":"address","name":"instance","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"LocalReportCheckToggled","inputs":[{"type":"bool","name":"checkLocalEnclaveReport","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"MrEnclaveRevoked","inputs":[{"type":"bytes32","name":"mrEnclave","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"MrEnclaveUpdated","inputs":[{"type":"bytes32","name":"mrEnclave","internalType":"bytes32","indexed":true},{"type":"bool","name":"trusted","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"MrSignerRevoked","inputs":[{"type":"bytes32","name":"mrSigner","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"MrSignerUpdated","inputs":[{"type":"bytes32","name":"mrSigner","internalType":"bytes32","indexed":true},{"type":"bool","name":"trusted","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferStarted","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"INSTANCE_EXPIRY","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"acceptOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"addInstances","inputs":[{"type":"address[]","name":"_instances","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"alreadyAttested","internalType":"bool"}],"name":"addressRegistered","inputs":[{"type":"address","name":"instanceAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"automataDcapAttestation","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"checkLocalEnclaveReport","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deleteInstances","inputs":[{"type":"uint256[]","name":"_ids","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes16","name":"mask","internalType":"bytes16"},{"type":"bytes16","name":"expected","internalType":"bytes16"}],"name":"enclaveAttributePolicy","inputs":[{"type":"bytes32","name":"mrEnclave","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"enclaveAttributePolicyVersion","inputs":[{"type":"bytes32","name":"_mrEnclave","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"instanceValidityDelay","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"addr","internalType":"address"},{"type":"uint64","name":"validSince","internalType":"uint64"},{"type":"uint32","name":"policyVersion","internalType":"uint32"},{"type":"bytes32","name":"mrEnclave","internalType":"bytes32"},{"type":"bytes32","name":"mrSigner","internalType":"bytes32"}],"name":"instances","inputs":[{"type":"uint256","name":"instanceId","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTcbStatusAccepted","inputs":[{"type":"uint8","name":"_status","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nextInstanceId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pendingOwner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"registerInstance","inputs":[{"type":"bytes","name":"_rawQuote","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"registrar","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeEnclaveAttributePolicy","inputs":[{"type":"bytes32","name":"_mrEnclave","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"revoked","internalType":"bool"}],"name":"revokedMrEnclave","inputs":[{"type":"bytes32","name":"mrEnclave","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"revoked","internalType":"bool"}],"name":"revokedMrSigner","inputs":[{"type":"bytes32","name":"mrSigner","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setEnclaveAttributePolicy","inputs":[{"type":"bytes32","name":"_mrEnclave","internalType":"bytes32"},{"type":"bytes16","name":"_mask","internalType":"bytes16"},{"type":"bytes16","name":"_expected","internalType":"bytes16"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMrEnclave","inputs":[{"type":"bytes32","name":"_mrEnclave","internalType":"bytes32"},{"type":"bool","name":"_trusted","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMrSigner","inputs":[{"type":"bytes32","name":"_mrSigner","internalType":"bytes32"},{"type":"bool","name":"_trusted","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"taikoChainId","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"toggleLocalReportCheck","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"trustedUserMrEnclave","inputs":[{"type":"bytes32","name":"_mrEnclave","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"trusted","internalType":"bool"}],"name":"trustedUserMrSigner","inputs":[{"type":"bytes32","name":"mrSigner","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[],"name":"verifyProof","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes32","name":"_aggregatedProvingHash","internalType":"bytes32"},{"type":"bytes","name":"_proof","internalType":"bytes"}]}]
Contract Creation Code
0x610100604052348015610010575f5ffd5b5060405161265838038061265883398101604081905261002f91610197565b8484848461003c336100fb565b60016002556001600160401b0384165f0361006a5760405163759159ef60e11b815260040160405180910390fd5b6001600160401b0384166080526001600160a01b0380831660a052811660c0526006805460ff191660011790556100a0836100fb565b505050505f816001600160401b03161180156100c857506276a7006001600160401b03821611155b6100e55760405163019eb09b60e01b815260040160405180910390fd5b6001600160401b031660e052506101f892505050565b600180546001600160a01b031916905561011481610117565b50565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160401b038116811461017c575f5ffd5b919050565b80516001600160a01b038116811461017c575f5ffd5b5f5f5f5f5f60a086880312156101ab575f5ffd5b6101b486610166565b94506101c260208701610181565b93506101d060408701610181565b92506101de60608701610181565b91506101ec60808701610166565b90509295509295909350565b60805160a05160c05160e0516123f56102635f395f8181610266015261174601525f81816102a50152818161075301528181610dd101528181610e0b015261132901525f818161034501528181610e4c0152610ec201525f8181610498015261064c01526123f55ff3fe608060405234801561000f575f5ffd5b50600436106101d1575f3560e01c806383801580116100fe578063d990f9e51161009e578063e30c39781161006e578063e30c39781461056e578063e7d809b81461057f578063ee45abb0146105a0578063f2fde38b146105a9575f5ffd5b8063d990f9e5146104e6578063e16e821814610535578063e1bc02ba14610548578063e2e282941461055b575f5ffd5b8063a2f7b3a5116100d9578063a2f7b3a5146103f9578063a5a1d0c514610493578063d31845a3146104ba578063d632cf35146104dc575f5ffd5b806383801580146103bf5780638da5cb5b146103c75780639d7809b5146103d7575f5ffd5b80632f5f4a29116101745780634ef36a56116101445780634ef36a561461037a57806354e219131461038d578063715018a6146103af57806379ba5097146103b7575f5ffd5b80632f5f4a29146102df57806332f555ec1461031e57806336383dc7146103405780633a34301414610367575f5ffd5b806316107290116101af578063161072901461022e5780631cc2f0561461024e57806322084f01146102615780632b20e397146102a0575f5ffd5b80630570e1fc146101d55780630bf71381146101f757806314bcf3dd14610219575b5f5ffd5b6006546101e29060ff1681565b60405190151581526020015b60405180910390f35b6101e2610205366004611e76565b60096020525f908152604090205460ff1681565b61022c610227366004611eca565b6105bc565b005b61024161023c366004611f58565b6106fe565b6040516101ee9190611f96565b61022c61025c366004611e76565b610751565b6102887f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160401b0390911681526020016101ee565b6102c77f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101ee565b6103096102ed366004611e76565b5f90815260076020526040902054610100900463ffffffff1690565b60405163ffffffff90911681526020016101ee565b6101e261032c366004611e76565b60086020525f908152604090205460ff1681565b6102c77f000000000000000000000000000000000000000000000000000000000000000081565b61022c610375366004611fe5565b610885565b61022c610388366004611f58565b610974565b6101e261039b366004611e76565b5f9081526007602052604090205460ff1690565b61022c610a4d565b61022c610a60565b61022c610adf565b5f546001600160a01b03166102c7565b6101e26103e5366004612013565b60056020525f908152604090205460ff1681565b61044e610407366004611e76565b60046020525f90815260409020805460018201546002909201546001600160a01b03821692600160a01b83046001600160401b031692600160e01b900463ffffffff169185565b604080516001600160a01b0390961686526001600160401b03909416602086015263ffffffff909216928401929092526060830191909152608082015260a0016101ee565b6102887f000000000000000000000000000000000000000000000000000000000000000081565b6101e26104c8366004611e76565b600a6020525f908152604090205460ff1681565b6102886276a70081565b6105146104f4366004611e76565b600b6020525f9081526040902054608081811b91600160801b9004901b82565b604080516001600160801b03199384168152929091166020830152016101ee565b6101e2610543366004612039565b610b34565b61022c610556366004612075565b610b58565b61022c610569366004611fe5565b610cdf565b6001546001600160a01b03166102c7565b61059261058d3660046120ae565b610dc6565b6040519081526020016101ee565b61059260035481565b61022c6105b7366004612013565b611493565b605981146105dd57604051637bb2c12960e01b815260040160405180910390fd5b5f6105eb60048284866120e0565b6105f491612107565b60e01c90505f6106086018600485876120e0565b6106119161213f565b60601c90506106268263ffffffff1682611503565b61064357604051630c3bd7cd60e11b815260040160405180910390fd5b5f6106708630847f000000000000000000000000000000000000000000000000000000000000000061160c565b90505f61068085601881896120e0565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509293506106c39250849150839050611677565b6001600160a01b0316836001600160a01b0316146106f457604051637bb2c12960e01b815260040160405180910390fd5b5050505050505050565b6060610708611699565b6107488383808060200260200160405190810160405280939291908181526020018383602002808284375f920182905250600193509150819050806116f2565b90505b92915050565b7f00000000000000000000000000000000000000000000000000000000000000006107835f546001600160a01b031690565b6001600160a01b0316336001600160a01b031614806107aa5750336001600160a01b038216145b6107c757604051633b39fa5f60e21b815260040160405180910390fd5b5f828152600b602052604090205460801b6001600160801b0319166107ff57604051633431803b60e11b815260040160405180910390fd5b5f828152600760205260409020805460019190829061082a908290610100900463ffffffff16612193565b825463ffffffff9182166101009390930a9283029190920219909116179055505f828152600b60205260408082208290555183917fa62134757a627c2ebc44fca5f94dc8f94d9ab32f419274e3c04852dd9726492091a25050565b61088d611699565b80156108c7575f8281526009602052604090205460ff16156108c257604051635066639f60e11b815260040160405180910390fd5b61091d565b5f8281526007602052604090205460ff161561091d575f82815260096020526040808220805460ff191660011790555183917f3c0015566335c0372b3fbb16742a6a311d32703117d70d50bf0c3ec59337f7be91a25b5f82815260076020908152604091829020805460ff1916841515908117909155915191825283917fe9c8da9c89154486636f96dbbf87f6cdf819637dda597383e5b1533b446b51d291015b60405180910390a25050565b61097c611699565b805f5b81811015610a47575f84848381811061099a5761099a6121af565b602090810292909201355f8181526004909352604090922054919250506001600160a01b03166109dd57604051630c3bd7cd60e11b815260040160405180910390fd5b5f818152600460205260408082205490516001600160a01b039091169183917f89d0dca869ffe08b709ca9ff5adfd5ee8d9de2750d0561e15df614c7a2596d8e9190a35f90815260046020526040812081815560018082018390556002909101919091550161097f565b50505050565b610a55611699565b610a5e5f611a24565b565b60015433906001600160a01b03168114610ad35760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b60648201526084015b60405180910390fd5b610adc81611a24565b50565b610ae7611699565b6006805460ff8082161560ff1990921682179092556040519116151581527fcafd71daf69ac558b30d115a7b5a9751259ac2352764cdf5b7038c74dc43ccb49060200160405180910390a1565b5f60ff82161580610b48575060ff82166001145b8061074b57505060ff1660021490565b610b60611699565b6001600160801b03198216610b88576040516327f7e63b60e21b815260040160405180910390fd5b811981166001600160801b03191615610bb4576040516327f7e63b60e21b815260040160405180910390fd5b601960f91b82811614610bda576040516327f7e63b60e21b815260040160405180910390fd5b601960f91b811615610bff576040516327f7e63b60e21b815260040160405180910390fd5b5f83815260076020526040812054610c2390610100900463ffffffff166001612193565b5f858152600760209081526040808320805464ffffffff00191661010063ffffffff871690810291909117909155815180830183526001600160801b03198981168083529089168286018181528c8852600b87529685902092519651608097881c600160801b9190981c029690961790915582519081529283019390935281019190915290915084907f07325639c976c5a9ab549ece8a5f2eaa4961bd1a7dbb49e9a1507625424c913f9060600160405180910390a250505050565b610ce7611699565b8015610d21575f828152600a602052604090205460ff1615610d1c57604051630a46dccd60e01b815260040160405180910390fd5b610d77565b5f8281526008602052604090205460ff1615610d77575f828152600a6020526040808220805460ff191660011790555183917f8668a255c112add925bb4b60e48a3a9c5d174a6a2114497478dde8cd82f6847391a25b5f82815260086020908152604091829020805460ff1916841515908117909155915191825283917fa8796d5584281f38b5f5c7f2ff1ee978acc17db51930ab666a66712c2d9f93ac9101610968565b5f610dcf611a3d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161580610e2d5750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b610e4a5760405163ae79e77d60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610e9157604051631cbfe78f60e21b815260040160405180910390fd5b610e9e61018060306121d7565b821015610ebe57604051631cbfe78f60e21b815260040160405180910390fd5b5f5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166338d8480a5f87876040518463ffffffff1660e01b8152600401610f0f9291906121ea565b5f6040518083038185885af1158015610f2a573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f19168201604052610f52919081019061222c565b9150915081610f7457604051631cbfe78f60e21b815260040160405180910390fd5b610f81610180600b6121d7565b81511015610fa257604051631cbfe78f60e21b815260040160405180910390fd5b805f81518110610fb457610fb46121af565b016020015160f81c158015610fec5750600381610fd25f60016121d7565b81518110610fe257610fe26121af565b016020015160f81c145b61100957604051631cbfe78f60e21b815260040160405180910390fd5b8060028151811061101c5761101c6121af565b016020015160f81c158015611054575060018161103a6002836121d7565b8151811061104a5761104a6121af565b016020015160f81c145b61107157604051631cbfe78f60e21b815260040160405180910390fd5b61109481600481518110611087576110876121af565b016020015160f81c610b34565b6110b157604051631cbfe78f60e21b815260040160405180910390fd5b610180602b820181902090869060309087906110cd90836121d7565b926110da939291906120e0565b6040516110e89291906122f0565b6040518091039020811461110f57604051631cbfe78f60e21b815260040160405180910390fd5b6002868661111e6030806121d7565b81811061112d5761112d6121af565b9050013560f81c60f81b60f81c1660ff165f1461115d576040516318ad149760e31b815260040160405180910390fd5b5f868661116c603060406121d7565b90611179603060406121d7565b6111849060206121d7565b92611191939291906120e0565b61119a916122ff565b90505f87876111ab603060806121d7565b906111b8603060806121d7565b6111c39060206121d7565b926111d0939291906120e0565b6111d9916122ff565b90505f88886111e96030806121d7565b906111f56030806121d7565b6112009060106121d7565b9261120d939291906120e0565b6112169161231c565b9050601960f91b81161561123d57604051631e9271af60e31b815260040160405180910390fd5b5f6112488483611a94565b60065490915060ff161561129d575f8481526007602052604090205460ff16801561128057505f8381526008602052604090205460ff165b61129d57604051631cbfe78f60e21b815260040160405180910390fd5b6040805160018082528183019092525f91602080830190803683370190505090508a8a6112cd60306101406121d7565b906112db60306101406121d7565b6112e69060146121d7565b926112f3939291906120e0565b6112fc9161213f565b60601c815f81518110611311576113116121af565b6001600160a01b0392831660209182029290920101527f000000000000000000000000000000000000000000000000000000000000000016611435575f8b8b61135d60306101406121d7565b6113689060146121d7565b9061137660306101406121d7565b61138190601c6121d7565b9261138e939291906120e0565b61139791612352565b60c01c90505f8c8c6113ac60306101406121d7565b6113b790601c6121d7565b906113c560306101406121d7565b6113d090603c6121d7565b926113dd939291906120e0565b6113e6916122ff565b90506001600160401b038216408061141157604051630754f1cb60e01b815260040160405180910390fd5b818114611431576040516341648d1160e01b815260040160405180910390fd5b5050505b6114668161144a5f546001600160a01b031690565b6001600160a01b0316336001600160a01b0316148787866116f2565b5f81518110611477576114776121af565b60200260200101519850505050505050505061074b6001600255565b61149b611699565b600180546001600160a01b0383166001600160a01b031990911681179091556114cb5f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f6001600160a01b03821661152b57604051630c3bd7cd60e11b815260040160405180910390fd5b5f83815260046020908152604091829020825160a08101845281546001600160a01b03808216808452600160a01b83046001600160401b031695840195909552600160e01b90910463ffffffff1694820194909452600182015460608201526002909101546080820152918416146115b657604051630c3bd7cd60e11b815260040160405180910390fd5b6115bf81611b44565b6115cc575f91505061074b565b4281602001516001600160401b03161115801561160457506276a70081602001516115f79190612388565b6001600160401b03164211155b949350505050565b5f8461162b576040516318e48a7560e21b815260040160405180910390fd5b50604080516b2b22a924a32cafa82927a7a360a11b81526001600160401b039290921660208301526001600160a01b0393841690820152606081019390935216608082015260a0902090565b5f5f5f6116848585611bdf565b9150915061169181611c21565b509392505050565b5f546001600160a01b03163314610a5e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610aca565b8451606090806001600160401b0381111561170f5761170f612218565b604051908082528060200260200182016040528015611738578160200160208202803683370190505b509150428661176e5761176b7f000000000000000000000000000000000000000000000000000000000000000082612388565b90505b5f5b82811015611a185760055f8a838151811061178d5761178d6121af565b6020908102919091018101516001600160a01b031682528101919091526040015f205460ff16156117d15760405163a239527960e01b815260040160405180910390fd5b600160055f8b84815181106117e8576117e86121af565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548160ff0219169083151502179055505f6001600160a01b0316898281518110611841576118416121af565b60200260200101516001600160a01b03160361187057604051630c3bd7cd60e11b815260040160405180910390fd5b60035463ffffffff10156118975760405163890d54a160e01b815260040160405180910390fd5b6040518060a001604052808a83815181106118b4576118b46121af565b6020908102919091018101516001600160a01b0390811683526001600160401b038681168484015263ffffffff8a811660408087019190915260608087018f905260809687018e9052600380545f9081526004885283902089518154988b0151948b015197166001600160e01b031990981697909717600160a01b9390951692909202939093176001600160e01b0316600160e01b94909216939093021783558401516001830155929091015160029091015554845185908390811061197c5761197c6121af565b6020026020010181815250505f6001600160a01b03168982815181106119a4576119a46121af565b60200260200101516001600160a01b03166003547fbbe529d240965181270c1e2e32a80761e8807dda1ee9765e326178bd6804a9cb856040516119f691906001600160401b0391909116815260200190565b60405180910390a460035f8154611a0c906123a7565b90915550600101611770565b50505095945050505050565b600180546001600160a01b0319169055610adc81611d6a565b6002805403611a8e5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610aca565b60028055565b5f828152600b602090815260408083208151808301909252546001600160801b0319608082811b8216808552600160801b909304901b169282019290925290611af057604051633431803b60e11b815260040160405180910390fd5b6020810151815184166001600160801b0319908116911614611b25576040516372c6bfb560e01b815260040160405180910390fd5b5050505f90815260076020526040902054610100900463ffffffff1690565b60608101515f90611b5757506001919050565b60608201515f9081526007602090815260409182902082518084018452905460ff81161515825263ffffffff61010090910481169282018390529285015190921614611ba557505f92915050565b60065460ff16611bb85750600192915050565b80518015611bd8575060808301515f9081526008602052604090205460ff165b9392505050565b5f5f8251604103611c13576020830151604084015160608501515f1a611c0787828585611db9565b94509450505050611c1a565b505f905060025b9250929050565b5f816004811115611c3457611c346121c3565b03611c3c5750565b6001816004811115611c5057611c506121c3565b03611c9d5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610aca565b6002816004811115611cb157611cb16121c3565b03611cfe5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610aca565b6003816004811115611d1257611d126121c3565b03610adc5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610aca565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611dee57505f90506003611e6d565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611e3f573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b038116611e67575f60019250925050611e6d565b91505f90505b94509492505050565b5f60208284031215611e86575f5ffd5b5035919050565b5f5f83601f840112611e9d575f5ffd5b5081356001600160401b03811115611eb3575f5ffd5b602083019150836020828501011115611c1a575f5ffd5b5f5f5f5f60608587031215611edd575f5ffd5b843593506020850135925060408501356001600160401b03811115611f00575f5ffd5b611f0c87828801611e8d565b95989497509550505050565b5f5f83601f840112611f28575f5ffd5b5081356001600160401b03811115611f3e575f5ffd5b6020830191508360208260051b8501011115611c1a575f5ffd5b5f5f60208385031215611f69575f5ffd5b82356001600160401b03811115611f7e575f5ffd5b611f8a85828601611f18565b90969095509350505050565b602080825282518282018190525f918401906040840190835b81811015611fcd578351835260209384019390920191600101611faf565b509095945050505050565b8015158114610adc575f5ffd5b5f5f60408385031215611ff6575f5ffd5b82359150602083013561200881611fd8565b809150509250929050565b5f60208284031215612023575f5ffd5b81356001600160a01b0381168114611bd8575f5ffd5b5f60208284031215612049575f5ffd5b813560ff81168114611bd8575f5ffd5b80356001600160801b031981168114612070575f5ffd5b919050565b5f5f5f60608486031215612087575f5ffd5b8335925061209760208501612059565b91506120a560408501612059565b90509250925092565b5f5f602083850312156120bf575f5ffd5b82356001600160401b038111156120d4575f5ffd5b611f8a85828601611e8d565b5f5f858511156120ee575f5ffd5b838611156120fa575f5ffd5b5050820193919092039150565b80356001600160e01b03198116906004841015612138576001600160e01b0319600485900360031b81901b82161691505b5092915050565b80356bffffffffffffffffffffffff198116906014841015612138576bffffffffffffffffffffffff1960149490940360031b84901b1690921692915050565b634e487b7160e01b5f52601160045260245ffd5b63ffffffff818116838216019081111561074b5761074b61217f565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b8082018082111561074b5761074b61217f565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52604160045260245ffd5b5f5f6040838503121561223d575f5ffd5b825161224881611fd8565b60208401519092506001600160401b03811115612263575f5ffd5b8301601f81018513612273575f5ffd5b80516001600160401b0381111561228c5761228c612218565b604051601f8201601f19908116603f011681016001600160401b03811182821017156122ba576122ba612218565b6040528181528282016020018710156122d1575f5ffd5b8160208401602083015e5f602083830101528093505050509250929050565b818382375f9101908152919050565b8035602083101561074b575f19602084900360031b1b1692915050565b80356001600160801b03198116906010841015612138576001600160801b031960109490940360031b84901b1690921692915050565b80356001600160c01b03198116906008841015612138576001600160c01b031960089490940360031b84901b1690921692915050565b6001600160401b03818116838216019081111561074b5761074b61217f565b5f600182016123b8576123b861217f565b506001019056fea2646970667358221220a907390674a74931d8b20fdad665e71de95e786c2512dd401578931fd6218bcd64736f6c634300081e00330000000000000000000000000000000000000000000000000000000000028c590000000000000000000000004779d18931b35540f84b0cd0e9633855b84df7b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015180
Deployed ByteCode
0x608060405234801561000f575f5ffd5b50600436106101d1575f3560e01c806383801580116100fe578063d990f9e51161009e578063e30c39781161006e578063e30c39781461056e578063e7d809b81461057f578063ee45abb0146105a0578063f2fde38b146105a9575f5ffd5b8063d990f9e5146104e6578063e16e821814610535578063e1bc02ba14610548578063e2e282941461055b575f5ffd5b8063a2f7b3a5116100d9578063a2f7b3a5146103f9578063a5a1d0c514610493578063d31845a3146104ba578063d632cf35146104dc575f5ffd5b806383801580146103bf5780638da5cb5b146103c75780639d7809b5146103d7575f5ffd5b80632f5f4a29116101745780634ef36a56116101445780634ef36a561461037a57806354e219131461038d578063715018a6146103af57806379ba5097146103b7575f5ffd5b80632f5f4a29146102df57806332f555ec1461031e57806336383dc7146103405780633a34301414610367575f5ffd5b806316107290116101af578063161072901461022e5780631cc2f0561461024e57806322084f01146102615780632b20e397146102a0575f5ffd5b80630570e1fc146101d55780630bf71381146101f757806314bcf3dd14610219575b5f5ffd5b6006546101e29060ff1681565b60405190151581526020015b60405180910390f35b6101e2610205366004611e76565b60096020525f908152604090205460ff1681565b61022c610227366004611eca565b6105bc565b005b61024161023c366004611f58565b6106fe565b6040516101ee9190611f96565b61022c61025c366004611e76565b610751565b6102887f000000000000000000000000000000000000000000000000000000000001518081565b6040516001600160401b0390911681526020016101ee565b6102c77f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101ee565b6103096102ed366004611e76565b5f90815260076020526040902054610100900463ffffffff1690565b60405163ffffffff90911681526020016101ee565b6101e261032c366004611e76565b60086020525f908152604090205460ff1681565b6102c77f000000000000000000000000000000000000000000000000000000000000000081565b61022c610375366004611fe5565b610885565b61022c610388366004611f58565b610974565b6101e261039b366004611e76565b5f9081526007602052604090205460ff1690565b61022c610a4d565b61022c610a60565b61022c610adf565b5f546001600160a01b03166102c7565b6101e26103e5366004612013565b60056020525f908152604090205460ff1681565b61044e610407366004611e76565b60046020525f90815260409020805460018201546002909201546001600160a01b03821692600160a01b83046001600160401b031692600160e01b900463ffffffff169185565b604080516001600160a01b0390961686526001600160401b03909416602086015263ffffffff909216928401929092526060830191909152608082015260a0016101ee565b6102887f0000000000000000000000000000000000000000000000000000000000028c5981565b6101e26104c8366004611e76565b600a6020525f908152604090205460ff1681565b6102886276a70081565b6105146104f4366004611e76565b600b6020525f9081526040902054608081811b91600160801b9004901b82565b604080516001600160801b03199384168152929091166020830152016101ee565b6101e2610543366004612039565b610b34565b61022c610556366004612075565b610b58565b61022c610569366004611fe5565b610cdf565b6001546001600160a01b03166102c7565b61059261058d3660046120ae565b610dc6565b6040519081526020016101ee565b61059260035481565b61022c6105b7366004612013565b611493565b605981146105dd57604051637bb2c12960e01b815260040160405180910390fd5b5f6105eb60048284866120e0565b6105f491612107565b60e01c90505f6106086018600485876120e0565b6106119161213f565b60601c90506106268263ffffffff1682611503565b61064357604051630c3bd7cd60e11b815260040160405180910390fd5b5f6106708630847f0000000000000000000000000000000000000000000000000000000000028c5961160c565b90505f61068085601881896120e0565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509293506106c39250849150839050611677565b6001600160a01b0316836001600160a01b0316146106f457604051637bb2c12960e01b815260040160405180910390fd5b5050505050505050565b6060610708611699565b6107488383808060200260200160405190810160405280939291908181526020018383602002808284375f920182905250600193509150819050806116f2565b90505b92915050565b7f00000000000000000000000000000000000000000000000000000000000000006107835f546001600160a01b031690565b6001600160a01b0316336001600160a01b031614806107aa5750336001600160a01b038216145b6107c757604051633b39fa5f60e21b815260040160405180910390fd5b5f828152600b602052604090205460801b6001600160801b0319166107ff57604051633431803b60e11b815260040160405180910390fd5b5f828152600760205260409020805460019190829061082a908290610100900463ffffffff16612193565b825463ffffffff9182166101009390930a9283029190920219909116179055505f828152600b60205260408082208290555183917fa62134757a627c2ebc44fca5f94dc8f94d9ab32f419274e3c04852dd9726492091a25050565b61088d611699565b80156108c7575f8281526009602052604090205460ff16156108c257604051635066639f60e11b815260040160405180910390fd5b61091d565b5f8281526007602052604090205460ff161561091d575f82815260096020526040808220805460ff191660011790555183917f3c0015566335c0372b3fbb16742a6a311d32703117d70d50bf0c3ec59337f7be91a25b5f82815260076020908152604091829020805460ff1916841515908117909155915191825283917fe9c8da9c89154486636f96dbbf87f6cdf819637dda597383e5b1533b446b51d291015b60405180910390a25050565b61097c611699565b805f5b81811015610a47575f84848381811061099a5761099a6121af565b602090810292909201355f8181526004909352604090922054919250506001600160a01b03166109dd57604051630c3bd7cd60e11b815260040160405180910390fd5b5f818152600460205260408082205490516001600160a01b039091169183917f89d0dca869ffe08b709ca9ff5adfd5ee8d9de2750d0561e15df614c7a2596d8e9190a35f90815260046020526040812081815560018082018390556002909101919091550161097f565b50505050565b610a55611699565b610a5e5f611a24565b565b60015433906001600160a01b03168114610ad35760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b60648201526084015b60405180910390fd5b610adc81611a24565b50565b610ae7611699565b6006805460ff8082161560ff1990921682179092556040519116151581527fcafd71daf69ac558b30d115a7b5a9751259ac2352764cdf5b7038c74dc43ccb49060200160405180910390a1565b5f60ff82161580610b48575060ff82166001145b8061074b57505060ff1660021490565b610b60611699565b6001600160801b03198216610b88576040516327f7e63b60e21b815260040160405180910390fd5b811981166001600160801b03191615610bb4576040516327f7e63b60e21b815260040160405180910390fd5b601960f91b82811614610bda576040516327f7e63b60e21b815260040160405180910390fd5b601960f91b811615610bff576040516327f7e63b60e21b815260040160405180910390fd5b5f83815260076020526040812054610c2390610100900463ffffffff166001612193565b5f858152600760209081526040808320805464ffffffff00191661010063ffffffff871690810291909117909155815180830183526001600160801b03198981168083529089168286018181528c8852600b87529685902092519651608097881c600160801b9190981c029690961790915582519081529283019390935281019190915290915084907f07325639c976c5a9ab549ece8a5f2eaa4961bd1a7dbb49e9a1507625424c913f9060600160405180910390a250505050565b610ce7611699565b8015610d21575f828152600a602052604090205460ff1615610d1c57604051630a46dccd60e01b815260040160405180910390fd5b610d77565b5f8281526008602052604090205460ff1615610d77575f828152600a6020526040808220805460ff191660011790555183917f8668a255c112add925bb4b60e48a3a9c5d174a6a2114497478dde8cd82f6847391a25b5f82815260086020908152604091829020805460ff1916841515908117909155915191825283917fa8796d5584281f38b5f5c7f2ff1ee978acc17db51930ab666a66712c2d9f93ac9101610968565b5f610dcf611a3d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161580610e2d5750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b610e4a5760405163ae79e77d60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610e9157604051631cbfe78f60e21b815260040160405180910390fd5b610e9e61018060306121d7565b821015610ebe57604051631cbfe78f60e21b815260040160405180910390fd5b5f5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166338d8480a5f87876040518463ffffffff1660e01b8152600401610f0f9291906121ea565b5f6040518083038185885af1158015610f2a573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f19168201604052610f52919081019061222c565b9150915081610f7457604051631cbfe78f60e21b815260040160405180910390fd5b610f81610180600b6121d7565b81511015610fa257604051631cbfe78f60e21b815260040160405180910390fd5b805f81518110610fb457610fb46121af565b016020015160f81c158015610fec5750600381610fd25f60016121d7565b81518110610fe257610fe26121af565b016020015160f81c145b61100957604051631cbfe78f60e21b815260040160405180910390fd5b8060028151811061101c5761101c6121af565b016020015160f81c158015611054575060018161103a6002836121d7565b8151811061104a5761104a6121af565b016020015160f81c145b61107157604051631cbfe78f60e21b815260040160405180910390fd5b61109481600481518110611087576110876121af565b016020015160f81c610b34565b6110b157604051631cbfe78f60e21b815260040160405180910390fd5b610180602b820181902090869060309087906110cd90836121d7565b926110da939291906120e0565b6040516110e89291906122f0565b6040518091039020811461110f57604051631cbfe78f60e21b815260040160405180910390fd5b6002868661111e6030806121d7565b81811061112d5761112d6121af565b9050013560f81c60f81b60f81c1660ff165f1461115d576040516318ad149760e31b815260040160405180910390fd5b5f868661116c603060406121d7565b90611179603060406121d7565b6111849060206121d7565b92611191939291906120e0565b61119a916122ff565b90505f87876111ab603060806121d7565b906111b8603060806121d7565b6111c39060206121d7565b926111d0939291906120e0565b6111d9916122ff565b90505f88886111e96030806121d7565b906111f56030806121d7565b6112009060106121d7565b9261120d939291906120e0565b6112169161231c565b9050601960f91b81161561123d57604051631e9271af60e31b815260040160405180910390fd5b5f6112488483611a94565b60065490915060ff161561129d575f8481526007602052604090205460ff16801561128057505f8381526008602052604090205460ff165b61129d57604051631cbfe78f60e21b815260040160405180910390fd5b6040805160018082528183019092525f91602080830190803683370190505090508a8a6112cd60306101406121d7565b906112db60306101406121d7565b6112e69060146121d7565b926112f3939291906120e0565b6112fc9161213f565b60601c815f81518110611311576113116121af565b6001600160a01b0392831660209182029290920101527f000000000000000000000000000000000000000000000000000000000000000016611435575f8b8b61135d60306101406121d7565b6113689060146121d7565b9061137660306101406121d7565b61138190601c6121d7565b9261138e939291906120e0565b61139791612352565b60c01c90505f8c8c6113ac60306101406121d7565b6113b790601c6121d7565b906113c560306101406121d7565b6113d090603c6121d7565b926113dd939291906120e0565b6113e6916122ff565b90506001600160401b038216408061141157604051630754f1cb60e01b815260040160405180910390fd5b818114611431576040516341648d1160e01b815260040160405180910390fd5b5050505b6114668161144a5f546001600160a01b031690565b6001600160a01b0316336001600160a01b0316148787866116f2565b5f81518110611477576114776121af565b60200260200101519850505050505050505061074b6001600255565b61149b611699565b600180546001600160a01b0383166001600160a01b031990911681179091556114cb5f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f6001600160a01b03821661152b57604051630c3bd7cd60e11b815260040160405180910390fd5b5f83815260046020908152604091829020825160a08101845281546001600160a01b03808216808452600160a01b83046001600160401b031695840195909552600160e01b90910463ffffffff1694820194909452600182015460608201526002909101546080820152918416146115b657604051630c3bd7cd60e11b815260040160405180910390fd5b6115bf81611b44565b6115cc575f91505061074b565b4281602001516001600160401b03161115801561160457506276a70081602001516115f79190612388565b6001600160401b03164211155b949350505050565b5f8461162b576040516318e48a7560e21b815260040160405180910390fd5b50604080516b2b22a924a32cafa82927a7a360a11b81526001600160401b039290921660208301526001600160a01b0393841690820152606081019390935216608082015260a0902090565b5f5f5f6116848585611bdf565b9150915061169181611c21565b509392505050565b5f546001600160a01b03163314610a5e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610aca565b8451606090806001600160401b0381111561170f5761170f612218565b604051908082528060200260200182016040528015611738578160200160208202803683370190505b509150428661176e5761176b7f000000000000000000000000000000000000000000000000000000000001518082612388565b90505b5f5b82811015611a185760055f8a838151811061178d5761178d6121af565b6020908102919091018101516001600160a01b031682528101919091526040015f205460ff16156117d15760405163a239527960e01b815260040160405180910390fd5b600160055f8b84815181106117e8576117e86121af565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548160ff0219169083151502179055505f6001600160a01b0316898281518110611841576118416121af565b60200260200101516001600160a01b03160361187057604051630c3bd7cd60e11b815260040160405180910390fd5b60035463ffffffff10156118975760405163890d54a160e01b815260040160405180910390fd5b6040518060a001604052808a83815181106118b4576118b46121af565b6020908102919091018101516001600160a01b0390811683526001600160401b038681168484015263ffffffff8a811660408087019190915260608087018f905260809687018e9052600380545f9081526004885283902089518154988b0151948b015197166001600160e01b031990981697909717600160a01b9390951692909202939093176001600160e01b0316600160e01b94909216939093021783558401516001830155929091015160029091015554845185908390811061197c5761197c6121af565b6020026020010181815250505f6001600160a01b03168982815181106119a4576119a46121af565b60200260200101516001600160a01b03166003547fbbe529d240965181270c1e2e32a80761e8807dda1ee9765e326178bd6804a9cb856040516119f691906001600160401b0391909116815260200190565b60405180910390a460035f8154611a0c906123a7565b90915550600101611770565b50505095945050505050565b600180546001600160a01b0319169055610adc81611d6a565b6002805403611a8e5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610aca565b60028055565b5f828152600b602090815260408083208151808301909252546001600160801b0319608082811b8216808552600160801b909304901b169282019290925290611af057604051633431803b60e11b815260040160405180910390fd5b6020810151815184166001600160801b0319908116911614611b25576040516372c6bfb560e01b815260040160405180910390fd5b5050505f90815260076020526040902054610100900463ffffffff1690565b60608101515f90611b5757506001919050565b60608201515f9081526007602090815260409182902082518084018452905460ff81161515825263ffffffff61010090910481169282018390529285015190921614611ba557505f92915050565b60065460ff16611bb85750600192915050565b80518015611bd8575060808301515f9081526008602052604090205460ff165b9392505050565b5f5f8251604103611c13576020830151604084015160608501515f1a611c0787828585611db9565b94509450505050611c1a565b505f905060025b9250929050565b5f816004811115611c3457611c346121c3565b03611c3c5750565b6001816004811115611c5057611c506121c3565b03611c9d5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610aca565b6002816004811115611cb157611cb16121c3565b03611cfe5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610aca565b6003816004811115611d1257611d126121c3565b03610adc5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610aca565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611dee57505f90506003611e6d565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611e3f573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b038116611e67575f60019250925050611e6d565b91505f90505b94509492505050565b5f60208284031215611e86575f5ffd5b5035919050565b5f5f83601f840112611e9d575f5ffd5b5081356001600160401b03811115611eb3575f5ffd5b602083019150836020828501011115611c1a575f5ffd5b5f5f5f5f60608587031215611edd575f5ffd5b843593506020850135925060408501356001600160401b03811115611f00575f5ffd5b611f0c87828801611e8d565b95989497509550505050565b5f5f83601f840112611f28575f5ffd5b5081356001600160401b03811115611f3e575f5ffd5b6020830191508360208260051b8501011115611c1a575f5ffd5b5f5f60208385031215611f69575f5ffd5b82356001600160401b03811115611f7e575f5ffd5b611f8a85828601611f18565b90969095509350505050565b602080825282518282018190525f918401906040840190835b81811015611fcd578351835260209384019390920191600101611faf565b509095945050505050565b8015158114610adc575f5ffd5b5f5f60408385031215611ff6575f5ffd5b82359150602083013561200881611fd8565b809150509250929050565b5f60208284031215612023575f5ffd5b81356001600160a01b0381168114611bd8575f5ffd5b5f60208284031215612049575f5ffd5b813560ff81168114611bd8575f5ffd5b80356001600160801b031981168114612070575f5ffd5b919050565b5f5f5f60608486031215612087575f5ffd5b8335925061209760208501612059565b91506120a560408501612059565b90509250925092565b5f5f602083850312156120bf575f5ffd5b82356001600160401b038111156120d4575f5ffd5b611f8a85828601611e8d565b5f5f858511156120ee575f5ffd5b838611156120fa575f5ffd5b5050820193919092039150565b80356001600160e01b03198116906004841015612138576001600160e01b0319600485900360031b81901b82161691505b5092915050565b80356bffffffffffffffffffffffff198116906014841015612138576bffffffffffffffffffffffff1960149490940360031b84901b1690921692915050565b634e487b7160e01b5f52601160045260245ffd5b63ffffffff818116838216019081111561074b5761074b61217f565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b8082018082111561074b5761074b61217f565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52604160045260245ffd5b5f5f6040838503121561223d575f5ffd5b825161224881611fd8565b60208401519092506001600160401b03811115612263575f5ffd5b8301601f81018513612273575f5ffd5b80516001600160401b0381111561228c5761228c612218565b604051601f8201601f19908116603f011681016001600160401b03811182821017156122ba576122ba612218565b6040528181528282016020018710156122d1575f5ffd5b8160208401602083015e5f602083830101528093505050509250929050565b818382375f9101908152919050565b8035602083101561074b575f19602084900360031b1b1692915050565b80356001600160801b03198116906010841015612138576001600160801b031960109490940360031b84901b1690921692915050565b80356001600160c01b03198116906008841015612138576001600160c01b031960089490940360031b84901b1690921692915050565b6001600160401b03818116838216019081111561074b5761074b61217f565b5f600182016123b8576123b861217f565b506001019056fea2646970667358221220a907390674a74931d8b20fdad665e71de95e786c2512dd401578931fd6218bcd64736f6c634300081e0033