Contract
0x70b4f3c06e5d93d695129f1255c55c01e7be13bf
- Address
- 0x70b4f3c06e5d93d695129f1255c55c01e7be13bf
- Kind
- verified contract FinalIdentityRegistry
- Balance
- 0 vETH
- Nonce
- 1
- Code
- 25,865 bytes codehash 0x4a3fe8ecf743a798c8a19c8dbd0bf927e549721145108e20fb8adfffdc20b058
account tree
- Tree
- 1 · accounts
- Present
- no leaf
- Key
- 0xb78bf56e1cbe256a715defd818c5657a6d68c2b7fe629c97121856d34831aec8
- Live root
- 0x0a08b5139d5865ad7350389e6fee558ae5579447b1094084d4afdfd3e993e984
This address holds no leaf in the account tree. Every Final Wallet — service identities included — has one, so an absent leaf means an ordinary account rather than a wallet.
source verified
- Contract
- FinalIdentityRegistry exact match
- Compiler
- v0.8.33+commit.64118f21
- Optimizer
- enabled · 200 runs
- EVM version
- prague
- Verified
- 2026-09-10T07:09:52.752Z
- Provenance
- preverify-final-chain (forge artifact, bytecode compared against live code)
contracts/finalchain/FinalCertificate.sol
// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2024-2026 Final DeFi
// Licensed under the Business Source License 1.1 (the "License")
//
// Change Date: 2029-01-01
// Change License: GPL-2.0-or-later
//
// Additional Use Grant:
// 1. Any person or entity may link against and call this certificate reader,
// and may encode certificates that it accepts, as part of the Final DeFi
// Protocol.
// 2. Operators, integrators, and end users may have their certificates parsed,
// self-checked, and verified through any Final DeFi surface that links it.
// 3. For the avoidance of doubt, this Grant does NOT permit the commercial
// deployment of a Fork of this certificate reader or a competing identity
// certificate format derived from it without permission prior to the
// Change Date.
//
// @author Final DeFi
// @version 1.0.0
pragma solidity ^0.8.20;
import {FinalChainPrecompiles} from "./FinalChainPrecompiles.sol";
import {FinalChainTime} from "./FinalChainTime.sol";
/**
* @title Final Certificate
* @notice Reads a Final Certificate on chain and self-checks it, so a certificate's keys can never be
* anything other than the keys it declares.
* @dev Deployed only as part of this project's own reth-based state plane, and only on the reth-based chains
* that carry the precompiles it calls: SHA3-256 at `0x0202`, ML-DSA-87 at `0x0204` and
* SLH-DSA-SHAKE-256s at `0x0205`, each address being that primitive's FIPS number. The contracts it is
* linked into probe those precompiles at construction and refuse to exist where they are absent, so
* this library never runs somewhere its verdicts would be meaningless. It takes part in no CREATE2
* derivation, and nothing outside this directory imports it.
*
* The SHA3 precompile is not a convenience: the certificate format hashes with FIPS-202 SHA3 and the
* EVM's `keccak256` is a DIFFERENT function, so a digest computed with the wrong one matches no
* certificate any issuer ever wrote.
*
* ## Why the chain parses this at all
*
* The alternative is taking the TBS bytes and the public keys as separate arguments and deriving
* `certHash` from the bytes. That looks like verification and is not: nothing compares the keys to the
* certificate, so a registrar could bind any certificate to any keypair, the registry would hold a key
* the certificate does not contain, and every signature that key produced would verify against a
* certificate that never authorised it.
*
* So the keys are read OUT of the certificate. There is one input, and no pair of arguments that can
* disagree.
*
* Gas is deliberately not a design constraint on the chain this runs on and must not be optimised for.
* Parsing and re-hashing on chain costs more than trusting a parse done elsewhere and buys a verdict
* that is re-derivable from public state, which is the trade this whole plane is built on.
*
* ## The key-identifier check
*
* A certificate declares `SubjectKeyId` as the SHA3-256 digest of its `PublicKeyBlock`. Having parsed
* that block, {parse} recomputes the digest and compares. The field sits inside the TBS, so it is
* covered by the issuer's signatures — which makes the check a statement about what the issuer
* attested, not merely about internal consistency of bytes the caller supplied.
*
* ## Deploy-linked, not inlined
*
* {parseLive}, {parseRecovery}, {parseCa} and {verifyIssuerSignatures} are `external`, so the identity
* registry calls them across a link boundary rather than carrying them in its own bytecode, which it
* has no room for. The link target is fixed at deployment: a linked library is code, not a pointer
* anyone can move afterwards.
*
* ## What this library deliberately does not do
*
* It does not verify an issuer's signatures over the TBS as part of parsing, and it does not walk a
* certificate chain to the root. On the registration path there is nothing to walk — a chain-attested
* certificate is admitted by this chain against pinned issuer constants and the holder's own proof of
* possession, so an issuer signature is not what makes it valid. {verifyIssuerSignatures} is here for
* callers verifying an off-chain issuance, and it verifies exactly what it is handed.
*
* It also does not check an encapsulation key's length or structure. Those are checked where they are
* REGISTERED, by the precompiles that own the answer, because two checks of one thing in two shapes is
* how one of them ends up weaker and nobody notices which.
*/
library FinalCertificate {
/// @notice The four magic bytes every certificate opens with, `"PQCF"`.
uint32 internal constant MAGIC = 0x50514346;
/// @notice The current wire generation, which encoders write.
/// @dev A generation this parser does not know fails to parse rather than being reinterpreted: the
/// folded key commitment, and therefore every wallet address, derives from this exact layout, so a
/// layout read under the wrong generation would produce a self-consistent digest that matches
/// nothing.
uint32 internal constant VERSION = 2;
/// @notice The previous wire generation, still accepted on parse.
/// @dev Reading an older artifact is not the same as admitting it. Whether such a certificate may be
/// REGISTERED is settled at admission, by the holder's proof of possession and the chain-issuer
/// pins, rather than by refusing to decode it.
uint32 internal constant VERSION_V4 = 1;
/// @notice The institution identity extension, which carries an issuer's legal name, registration
/// number and jurisdiction.
uint16 internal constant EXT_INSTITUTION = 0x0102;
/// @notice ML-KEM-1024 (FIPS 203), the lattice half of the encapsulation pair.
/// @dev Algorithm identifiers ARE the FIPS numbers, in one space shared by signatures and encapsulation
/// — the same identifiers the quorum wire format uses, and the numbers the precompile addresses end
/// in. One space rather than two means an identifier can never be read against the wrong table.
uint16 internal constant ALG_ML_KEM_1024 = 0x0003;
/// @notice ML-DSA-87 (FIPS 204). Transaction class.
uint16 internal constant ALG_ML_DSA_87 = 0x0004;
/// @notice SLH-DSA-SHAKE-256s (FIPS 205). Access class, and the seal.
uint16 internal constant ALG_SLH_DSA_SHAKE_256S = 0x0005;
/// @notice FN-DSA (FIPS 206). Reserved: there is no implementation behind it and it is never accepted in
/// a slot.
uint16 internal constant ALG_FN_DSA = 0x0006;
/// @notice HQC-5 (FIPS 207), the code-based half of the encapsulation pair.
uint16 internal constant ALG_HQC_5 = 0x0007;
/// @notice Certificate signing, for both of an issuer's keys.
/// @dev Says which key to verify WITH; it grants nothing on its own — capability to issue comes from the
/// depth pair.
uint16 internal constant PURPOSE_CERT_SIGNING = 0x0004;
/// @notice The live stage's transaction-class slot, ML-DSA-87.
/// @dev A wallet holds four slots in two stages of two, and a certificate carries ONE stage, never all
/// four. The stage is what is issued, rotated and revoked as a unit, and a holder presenting a live
/// certificate presents both of that stage's keys or neither — splitting them per slot would let
/// half a stage be presented as if it were whole.
/// @dev This applies to services exactly as it applies to a user's wallet. A co-signer is a Final
/// Wallet: same four slots, same split, same algorithms. There is no second kind of identity in
/// this system.
uint16 internal constant PURPOSE_ACTIVE_TX = 0x0010;
/// @notice The live stage's access-class slot, SLH-DSA-SHAKE-256s.
uint16 internal constant PURPOSE_ACTIVE_ACCESS = 0x0011;
/// @notice The recovery stage's transaction-class slot, ML-DSA-87.
uint16 internal constant PURPOSE_RECOVERY_TX = 0x0012;
/// @notice The recovery stage's access-class slot, SLH-DSA-SHAKE-256s.
uint16 internal constant PURPOSE_RECOVERY_ACCESS = 0x0013;
/// @notice The live stage's encapsulation slot.
/// @dev Each stage's encapsulation pair is resolved alongside its signing pair, and the identity
/// registry stores both halves, so a sender can encapsulate to a registered party without a second
/// lookup somewhere less authoritative. Both halves sit under ONE purpose and are told apart by
/// algorithm, which is why the key loop matches on the `(purpose, algorithm)` pair.
uint16 internal constant PURPOSE_ACTIVE_KEM = 0x0014;
/// @notice The recovery stage's encapsulation slot, carrying the same two algorithms.
uint16 internal constant PURPOSE_RECOVERY_KEM = 0x0015;
/// @notice The seal purpose: a second SLH-DSA-SHAKE-256s key that co-signs execution-class quorum
/// decisions.
/// @dev Distinct from the access key, and carried by SERVICE certificates only — a user's wallet never
/// seals. Optional in the format, so a certificate without it parses unchanged.
/// @dev Outside the folded key commitment: a seal is operational, rotated by issuing a new live
/// certificate, and it must not move a wallet address it plays no part in deriving.
uint16 internal constant PURPOSE_ACTIVE_SEAL = 0x0016;
/// @notice A sentinel purpose no certificate can carry.
/// @dev Lets {parse} be told "this stage has no encapsulation slot" without a second boolean argument.
/// `0xffff` is outside the purpose registry and is reserved by being used here.
uint16 internal constant NO_KEM_PURPOSE = 0xffff;
/// @notice Nanoseconds per millisecond, the conversion from a certificate's validity fields to this
/// chain's clock.
/// @dev A certificate stamps validity in NANOseconds and this chain's clock is MILLIseconds, so the
/// parser divides by 1e6 on the way in and nothing downstream ever compares across units. Getting
/// the divisor wrong does not fail loudly: it shifts every window by three orders of magnitude, so
/// every certificate reads as already valid, including one issued for the future.
uint64 internal constant NS_PER_MILLISECOND = FinalChainTime.NS_PER_MILLISECOND;
/**
* @title Parsed
* @notice What the chain keeps out of one certificate.
* @dev Every field is read OUT of the TBS. Nothing here can be supplied alongside the bytes, which is
* what makes it impossible for a caller to bind a certificate to material the certificate does not
* contain.
*/
struct Parsed {
/// `SHA3-256` of the TBS bytes: the certificate's own identity, and the handle revocation is keyed
/// on.
bytes32 certHash;
/// The certificate's 32-byte serial. A serial is per certificate SET, so the two stages of one
/// wallet share it and two stages that disagree are two different wallets.
bytes32 serial;
/// keccak256 of the issuer-name bytes, for the chain-issuer pin: a chain-attested certificate
/// carries the chain's own constant issuer name, and the registry compares one hash rather than two
/// strings.
bytes32 issuerDnHash;
/// The subject-name bytes verbatim. Kept whole rather than hashed because the jurisdiction rule
/// reads its country component at issuer registration.
bytes subjectDn;
/// The institution extension's VALUE, when present; empty otherwise. Issuer registration parses
/// the declared jurisdiction out of it and requires it to match the subject name's country.
bytes institutionExt;
/// SHA3-256 of the ISSUER's public key block. Zero-length — and so
/// `bytes32(0)` here — for exactly one certificate in the hierarchy,
/// which is what terminates chain validation.
bytes32 authorityKeyId;
/// SHA3-256 of this certificate's own public key block. The child's
/// `authorityKeyId` must equal it, which is what links the two.
bytes32 subjectKeyId;
/// Position on the delegation axis; 0 is the chain's own root.
uint8 depth;
/// Deepest level this key may issue to. `== depth` means it signs no certificates at all, which is
/// every end entity. The pair is immutable per certificate, which is why consumers discriminate
/// record kinds by it rather than by a role bit.
uint8 maxDelegationDepth;
/// MILLISECONDS, converted from the schema's nanoseconds — this chain's clock.
uint64 notBefore;
/// Milliseconds. Zero means never expires, which the schema allows.
uint64 notAfter;
/// The stage's transaction-class key. ML-DSA-87 — spending, and every
/// high-cadence protocol action.
bytes transactionKey;
/// The stage's access-class key. SLH-DSA-SHAKE-256s — identity,
/// rotation, recovery-pair promotion. A different hardness assumption,
/// so a lattice break leaves the key that governs identity standing.
bytes accessKey;
/// The stage's ML-KEM-1024 encapsulation key. Empty on a CA, which has
/// no encapsulation stage, and on any v4 certificate issued without
/// one — see `parse` for why that is tolerated rather than refused.
bytes kemMlKem;
/// The stage's HQC-5 encapsulation key. Carried under the SAME purpose
/// as the lattice half and distinguished only by algorithm, which is
/// why the parser matches on the `(purpose, algorithm)` pair.
bytes kemHqc;
/// The service's seal key (`PURPOSE_ACTIVE_SEAL`, SLH-DSA-SHAKE-256s).
/// Empty on every certificate that does not carry one — a user wallet,
/// a recovery stage, a CA.
bytes sealKey;
/// Where the TBS ends, so a caller holding the whole certificate can
/// find the `SignatureBlock` without parsing forward again.
uint256 tbsLength;
}
/// @notice The bytes do not open with the certificate magic, so they are not a certificate at all.
/// @param got The four bytes that were present.
error BadMagic(uint32 got);
/// @notice The wire generation is one this parser does not read.
/// @param got The generation the certificate declares.
error BadVersion(uint32 got);
/// @notice The TBS ends before a field the parser was about to read.
/// @param needed The offset the read required.
/// @param got The length actually supplied.
error Truncated(uint256 needed, uint256 got);
/// @notice The recomputed key-block digest does not equal the one the certificate declares, so the keys
/// present are not the keys the issuer attested.
/// @param derived The digest recomputed from the key block.
/// @param declared The digest the certificate carries.
error SubjectKeyIdMismatch(bytes32 derived, bytes32 declared);
/// @notice A stage is missing a key it must carry, or carries half of a pair that is issued whole.
/// @param purpose The purpose whose slot is unfilled.
error MissingSlot(uint16 purpose);
/// @notice A slot carries a key of the wrong scheme. It would verify cryptographically and mean
/// something else entirely, which is exactly what splitting the classes exists to prevent.
/// @param purpose The slot's purpose.
/// @param algorithm The algorithm identifier that was present.
error WrongAlgorithmForSlot(uint16 purpose, uint16 algorithm);
/// @notice Two key entries share one `(purpose, algorithm)` pair, so one would silently shadow the
/// other.
/// @param purpose The repeated purpose.
/// @param algorithm The repeated algorithm identifier.
error DuplicateKey(uint16 purpose, uint16 algorithm);
/// @notice The key entries are not in ascending `(purpose, algorithm)` order. The schema requires that
/// order so `certHash` is reproducible across implementations.
error KeysNotSorted();
/// @notice A signing key whose length is not the one its algorithm defines.
/// @param algorithm The algorithm identifier the entry declares.
/// @param length The key length that was present.
error BadKeyLength(uint16 algorithm, uint256 length);
/// @notice A delegation bound shallower than the certificate's own depth, which admits nothing.
/// @param depth The certificate's position on the delegation axis.
/// @param maxDelegationDepth The deepest level it claims to issue to.
error InvalidDepth(uint8 depth, uint8 maxDelegationDepth);
/// @notice A certificate that expires no later than it begins.
/// @param notBefore The declared start, in the schema's nanoseconds.
/// @param notAfter The declared end, in the schema's nanoseconds.
error ValidityInverted(uint64 notBefore, uint64 notAfter);
/**
* @notice Parse and self-check a `TBSCertificate`.
* @dev Checking for a CAPABILITY rather than a type is the certificate schema's own rule, and the reason
* there is no type field to check instead. Passing the LIVE purposes to a recovery certificate
* finds neither key and reverts — which is what stops a recovery certificate being registered as a
* live one and handing the recovery pair everyday authority.
*
* Self-check means the declared `SubjectKeyId` is recomputed from the key block that follows it and
* compared. That field is inside the TBS and therefore covered by the issuer's signatures, so the
* comparison turns "these bytes decode" into "the issuer attested these exact keys". Doing it on
* chain costs one precompile call and buys a verdict any reader can recompute; gas is not a design
* constraint on the chain this runs on, and must not be traded for a check that would then have to
* be taken on trust from whichever process ran it.
*
* A stage is issued as a unit, so both of a stage's signing keys must be present, and its
* encapsulation pair must be present in full or absent in full.
* @param tbs the TBS bytes, verbatim. Not the whole certificate.
* @param txPurpose the transaction-class purpose this stage should carry.
* @param accessPurpose the access-class purpose for the same stage.
* @param kemPurpose the encapsulation purpose for the same stage, or {NO_KEM_PURPOSE} for a stage that
* has none.
* @return out The parsed certificate: digest, serial, names, key identifiers, depth pair, validity
* window, and every key slot the stage carries.
*/
function parse(bytes calldata tbs, uint16 txPurpose, uint16 accessPurpose, uint16 kemPurpose)
internal
view
returns (Parsed memory out)
{
_need(tbs, 58);
if (uint32(bytes4(tbs[0:4])) != MAGIC) revert BadMagic(uint32(bytes4(tbs[0:4])));
// Both live wire generations parse. An artifact issued under the older one is read rather than
// refused; whether it may be ADMITTED is a separate question, settled at registration by the
// holder's proof of possession and the chain-issuer pins.
uint32 wireVersion = uint32(bytes4(tbs[4:8]));
if (wireVersion != VERSION && wireVersion != VERSION_V4) revert BadVersion(wireVersion);
out.certHash = FinalChainPrecompiles.sha3_256(tbs);
out.serial = bytes32(tbs[8:40]);
out.depth = uint8(tbs[40]);
out.maxDelegationDepth = uint8(tbs[41]);
uint64 notBeforeNs = uint64(bytes8(tbs[42:50]));
uint64 notAfterNs = uint64(bytes8(tbs[50:58]));
if (out.maxDelegationDepth < out.depth) {
revert InvalidDepth(out.depth, out.maxDelegationDepth);
}
if (notAfterNs != 0 && notAfterNs <= notBeforeNs) {
revert ValidityInverted(notBeforeNs, notAfterNs);
}
out.notBefore = notBeforeNs / NS_PER_MILLISECOND;
out.notAfter = notAfterNs == 0 ? 0 : notAfterNs / NS_PER_MILLISECOND;
// Four length-prefixed fields: IssuerDN, SubjectDN, AuthorityKeyId,
// SubjectKeyId. Every field before them is fixed width, which is the
// whole reason the schema orders them this way.
uint256 p = 58;
uint256 issuerDnLen;
(p, issuerDnLen) = _skipLengthPrefixed(tbs, p);
out.issuerDnHash = keccak256(tbs[p - issuerDnLen:p]);
uint256 subjectDnLen;
(p, subjectDnLen) = _skipLengthPrefixed(tbs, p);
out.subjectDn = tbs[p - subjectDnLen:p];
uint256 akidLen;
(p, akidLen) = _skipLengthPrefixed(tbs, p);
out.authorityKeyId = _bytes32At(tbs, p - akidLen, akidLen);
uint256 skidLen;
(p, skidLen) = _skipLengthPrefixed(tbs, p);
uint256 skidStart = p - skidLen;
_need(tbs, p + 2);
uint16 keyCount = uint16(bytes2(tbs[p:p + 2]));
p += 2;
// AFTER the count word. `SubjectKeyId` is SHA3-256 of the KeyEntry
// array alone — `encodeTbs` writes `PublicKeyCount` as its own field and
// `encodePublicKeyBlock` returns only the entries. Hashing the count in
// produces a digest that is self-consistent and matches no certificate
// any issuer ever wrote.
uint256 blockStart = p;
uint32 previousSort = 0;
for (uint256 i = 0; i < keyCount; i++) {
_need(tbs, p + 8);
uint16 alg = uint16(bytes2(tbs[p:p + 2]));
uint16 purpose = uint16(bytes2(tbs[p + 2:p + 4]));
uint32 keyLen = uint32(bytes4(tbs[p + 4:p + 8]));
p += 8;
_need(tbs, p + keyLen);
// Ascending by (purpose, algorithm), duplicates invalid. The schema
// requires the order so `certHash` is reproducible across
// implementations; enforcing it here also means a second entry for
// one slot cannot quietly shadow the first.
uint32 sortKey = (uint32(purpose) << 16) | uint32(alg);
if (i > 0) {
if (sortKey == previousSort) revert DuplicateKey(purpose, alg);
if (sortKey < previousSort) revert KeysNotSorted();
}
previousSort = sortKey;
// The algorithm is pinned per CLASS, not merely recorded. A
// transaction slot carrying an access-class key would verify
// cryptographically and mean something entirely different — an
// identity key must never authorize a transaction, or splitting the
// classes buys nothing.
// Matched on the PAIR, not on the purpose alone. A CA carries two
// keys under one purpose (`0x0004`) distinguished only by
// algorithm, so matching on purpose first would find the first of
// them twice and the second never.
if (purpose == txPurpose && alg == ALG_ML_DSA_87) {
if (keyLen != FinalChainPrecompiles.ML_DSA_87_PUBLIC_KEY_LEN) {
revert BadKeyLength(alg, keyLen);
}
out.transactionKey = tbs[p:p + keyLen];
} else if (purpose == accessPurpose && alg == ALG_SLH_DSA_SHAKE_256S) {
if (keyLen != FinalChainPrecompiles.SLH_DSA_SHAKE_256S_PUBLIC_KEY_LEN) {
revert BadKeyLength(alg, keyLen);
}
out.accessKey = tbs[p:p + keyLen];
} else if (purpose == kemPurpose && alg == ALG_ML_KEM_1024) {
out.kemMlKem = tbs[p:p + keyLen];
} else if (purpose == kemPurpose && alg == ALG_HQC_5) {
out.kemHqc = tbs[p:p + keyLen];
} else if (purpose == PURPOSE_ACTIVE_SEAL && alg == ALG_SLH_DSA_SHAKE_256S) {
if (keyLen != FinalChainPrecompiles.SLH_DSA_SHAKE_256S_PUBLIC_KEY_LEN) {
revert BadKeyLength(alg, keyLen);
}
out.sealKey = tbs[p:p + keyLen];
} else if (purpose == PURPOSE_ACTIVE_SEAL) {
// The seal is hash-based by definition — it exists to stand on
// the OTHER assumption from the transaction key it co-signs
// with. A lattice seal would be two signatures on one bet.
revert WrongAlgorithmForSlot(purpose, alg);
} else if (purpose == txPurpose || purpose == accessPurpose) {
// A slot the caller asked for, carrying the wrong scheme. It
// would verify cryptographically and mean something else
// entirely — an identity key must never authorize a
// transaction, or splitting the classes buys nothing.
revert WrongAlgorithmForSlot(purpose, alg);
} else if (purpose == kemPurpose) {
// Same rule for the encapsulation slot. A third KEM appearing
// under this purpose is a hybrid whose second family nobody
// agreed on, and admitting it silently is how a pair becomes a
// trio that one reader honours and another ignores.
revert WrongAlgorithmForSlot(purpose, alg);
}
// NO length check on the KEM keys here, and that is deliberate.
// The signing slots are checked against a constant because the
// parser's own callers depend on the length; an encapsulation key
// is checked by `0x0203` / `0x0207` at the moment it is REGISTERED,
// where the answer is a well-formedness verdict rather than a
// parse failure. Two checks of the same thing in two shapes is how
// one of them ends up weaker and nobody notices which.
p += keyLen;
}
// `SubjectKeyId` is SHA3-256 of the KeyEntry array, count word
// EXCLUDED — `blockStart` is taken after the count is consumed, for the
// reason given where it is set. Recomputing it is what turns "these
// bytes decode" into "the CA signed these exact keys"; the field is
// inside the TBS, so it is covered by the signatures.
out.subjectKeyId = FinalChainPrecompiles.sha3_256(tbs[blockStart:p]);
bytes32 declared = _bytes32At(tbs, skidStart, skidLen);
if (out.subjectKeyId != declared) revert SubjectKeyIdMismatch(out.subjectKeyId, declared);
// Both or neither. A stage is issued as a unit, so a certificate
// carrying one of its two keys is not a partial certificate — it is a
// certificate for a stage that does not exist.
if (out.transactionKey.length == 0) revert MissingSlot(txPurpose);
if (out.accessKey.length == 0) revert MissingSlot(accessPurpose);
// The encapsulation pair is both-or-neither for the same reason, and
// the reason is louder here: a hybrid quietly reduced to one family is
// identical on the wire, so a certificate carrying only the lattice
// half would seal successfully and silently drop the code-based hedge.
// Neither is the CA case and the pre-v4 case, both legitimate.
if ((out.kemMlKem.length == 0) != (out.kemHqc.length == 0)) {
revert MissingSlot(kemPurpose);
}
_need(tbs, p + 2);
uint16 extCount = uint16(bytes2(tbs[p:p + 2]));
p += 2;
for (uint256 i = 0; i < extCount; i++) {
_need(tbs, p + 7);
uint16 extType = uint16(bytes2(tbs[p:p + 2]));
uint32 valueLen = uint32(bytes4(tbs[p + 3:p + 7]));
p += 7;
_need(tbs, p + valueLen);
// The Institution extension's VALUE, kept for the issuer
// profile's jurisdiction rule. Everything else is skipped as
// before — extensions are structural to certHash, semantic to
// whichever consumer knows them.
if (extType == EXT_INSTITUTION) out.institutionExt = tbs[p:p + valueLen];
p += valueLen;
}
out.tbsLength = p;
}
/// @notice Parse a LIVE-stage certificate: the live transaction and access keys.
/// @dev `external`, like the other three entry points below. The identity registry sits against the
/// deployed-code ceiling and this parser is its single largest inlined dependency, so the four doors
/// it calls are DEPLOY-LINKED: the library is one more contract in the state plane's fixed deploy
/// order, and its address is baked immutably into the registry's bytecode. A linked library is code,
/// not a key — nothing can repoint it after deployment, so the split costs a call boundary and no
/// trust.
/// @param tbs The TBS bytes, verbatim.
/// @return The parsed and self-checked certificate.
function parseLive(bytes calldata tbs) external view returns (Parsed memory) {
return parse(tbs, PURPOSE_ACTIVE_TX, PURPOSE_ACTIVE_ACCESS, PURPOSE_ACTIVE_KEM);
}
/// @notice Parse a RECOVERY-stage certificate.
/// @dev The recovery pair authorizes rotating the wallet's own credentials and NOTHING else. Acting as a
/// guardian is an ordinary action for that account and uses the live access key, so keeping the two
/// stages in separate certificates is what makes that boundary something a verifier can see.
/// @param tbs The TBS bytes, verbatim.
/// @return The parsed and self-checked certificate.
function parseRecovery(bytes calldata tbs) external view returns (Parsed memory) {
return parse(tbs, PURPOSE_RECOVERY_TX, PURPOSE_RECOVERY_ACCESS, PURPOSE_RECOVERY_KEM);
}
/// @notice Parse a certificate authority's certificate, whose two keys are both cert-signing.
/// @dev Both classes resolve to the same purpose, which is why {parse} matches on the
/// `(purpose, algorithm)` PAIR: an authority carries two keys under one purpose and matching on the
/// purpose alone would find the first of them twice and the second never.
/// @dev No encapsulation purpose. An authority signs and is never sealed to, so {NO_KEM_PURPOSE} is
/// passed as a value the key loop can never match. An authority certificate carrying encapsulation
/// keys would parse them into slots the registry then discards, which is a shape worth refusing to
/// have at all.
/// @param tbs The TBS bytes, verbatim.
/// @return The parsed and self-checked certificate.
function parseCa(bytes calldata tbs) external view returns (Parsed memory) {
return parse(tbs, PURPOSE_CERT_SIGNING, PURPOSE_CERT_SIGNING, NO_KEM_PURPOSE);
}
/**
* @notice Verify an issuer's dual signature over a TBS.
* @dev Both must verify, not either. Two signatures under two different hardness assumptions is the
* entire reason a certificate carries two, and accepting one would collapse that to whichever
* family breaks first.
*
* Provided for callers that verify an off-chain issuance against keys they already trust. The
* caller supplies the issuer's keys, so it is the caller's job to have taken them from a registered
* record rather than from its own calldata — a key handed in with the signature proves nothing.
* @param tbs The signed TBS bytes.
* @param issuerMlDsaKey The issuer's registered ML-DSA-87 cert-signing key.
* @param issuerSlhDsaKey The issuer's registered SLH-DSA-SHAKE-256s cert-signing key.
* @param mlDsaSignature The lattice signature over `tbs`.
* @param slhDsaSignature The hash-based signature over `tbs`.
* @return Whether both signatures verify.
*/
function verifyIssuerSignatures(
bytes memory tbs,
bytes memory issuerMlDsaKey,
bytes memory issuerSlhDsaKey,
bytes memory mlDsaSignature,
bytes memory slhDsaSignature
) external view returns (bool) {
return FinalChainPrecompiles.verifyMlDsa87(issuerMlDsaKey, tbs, mlDsaSignature)
&& FinalChainPrecompiles.verifySlhDsa(issuerSlhDsaKey, tbs, slhDsaSignature);
}
/// @notice Refuse a TBS that is shorter than the parser is about to read.
/// @dev Called before every read rather than once at the top, because the layout is variable-length: a
/// certificate can be well-formed up to its key block and truncated inside it, and a parser that
/// only checked the fixed header would read whatever calldata followed.
/// @param tbs The TBS bytes.
/// @param upto The offset the next read needs to be valid.
function _need(bytes calldata tbs, uint256 upto) private pure {
if (tbs.length < upto) revert Truncated(upto, tbs.length);
}
/// @notice Step over one four-byte-length-prefixed field and report where it was.
/// @dev Bounds-checks the prefix before reading it and the value before returning, so a truncated
/// certificate cannot make the cursor run past the end of calldata. The caller recovers the value's
/// slice as `tbs[next - length:next]`.
/// @param tbs The TBS bytes.
/// @param p Offset of the length prefix.
/// @return next Offset just past the field's value.
/// @return length The field's declared length.
function _skipLengthPrefixed(bytes calldata tbs, uint256 p)
private
pure
returns (uint256 next, uint256 length)
{
_need(tbs, p + 4);
length = uint32(bytes4(tbs[p:p + 4]));
next = p + 4 + length;
_need(tbs, next);
}
/// @notice Read a key identifier out of the TBS as one word.
/// @dev Answers `bytes32(0)` for any length other than 32 rather than reverting. A key identifier that
/// is not 32 bytes is not a SHA3-256 digest, so it cannot match the value it is compared against,
/// and the comparison at the call site produces the correct refusal with no separate error to
/// define. The one legitimate short case is a zero-length authority key identifier, which the
/// caller must reject on its own terms.
/// @param tbs The TBS bytes.
/// @param start Offset of the field's value.
/// @param length The field's declared length.
/// @return The 32-byte value, or zero when the field is not 32 bytes long.
function _bytes32At(bytes calldata tbs, uint256 start, uint256 length)
private
pure
returns (bytes32)
{
// A SubjectKeyId that is not 32 bytes is not a SHA3-256 digest, so it
// cannot match and the comparison will fail — which is the correct
// outcome and needs no separate error.
if (length != 32) return bytes32(0);
return bytes32(tbs[start:start + 32]);
}
}
contracts/finalchain/FinalChainPrecompiles.sol
// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2024-2026 Final DeFi
// Licensed under the Business Source License 1.1 (the "License")
//
// Change Date: 2029-01-01
// Change License: GPL-2.0-or-later
//
// Additional Use Grant:
// 1. Any person or entity may link this library into contracts deployed on a
// Final DeFi Protocol chain in order to reach that chain's hash and
// post-quantum signature-verification precompiles.
// 2. Integrators, node operators, and auditors may use it to reproduce and
// independently re-verify any verdict those precompiles produced, as part of
// their integration with the Final DeFi Protocol.
// 3. For the avoidance of doubt, this Grant does NOT permit the commercial
// deployment of a Fork of this library or a competing state plane derived
// from it without permission prior to the Change Date.
//
// @author Final DeFi
// @version 1.0.0
pragma solidity ^0.8.20;
/**
* @title Final Chain Precompiles
* @notice The three primitives Final Chain adds to the EVM, and the only
* supported way to reach them.
*
* @dev **These exist ONLY on Final Chain (chain id 48359).** They are provided
* by this chain's own node binary, and
* nothing at these addresses on Ethereum, Optimism or any other chain will
* answer. A contract that calls them must be one that only ever runs here;
* `assertAvailable` below is the cheap way to fail loudly rather than treat an
* empty return as a verified signature.
*
* The addresses are the FIPS numbers, which is the whole allocation rule —
* there is no local registry to consult and no way for two implementations to
* disagree about where a primitive lives:
*
* | address | primitive | FIPS |
* |---|---|---|
* | `0x…0202` | SHA3-256 | 202 |
* | `0x…0203` | ML-KEM-1024 key validation | 203 |
* | `0x…0204` | ML-DSA-87 verify | 204 |
* | `0x…0205` | SLH-DSA-SHAKE-256s verify | 205 |
* | `0x…0207` | HQC-5 key validation | 207 |
*
* The two KEM addresses VALIDATE keys and do nothing else, for one reason:
* encapsulation is a SENDER operation and decapsulation needs the secret key,
* so neither belongs on a chain at all. Checking that a registered public key
* is well-formed is hardening rather than a dependency, and nothing in this
* system waits on it.
*
* HQC's number is 207. It had none when the KEM pair was chosen, which was the
* one thing separating it from ML-KEM here — a primitive with no standard
* number has no address under this rule, and inventing one would have been a
* local convention masquerading as the global one.
*
* **No AEAD precompile, at any number.** The chain must never be able to
* decrypt an intent, and checking a revealed body against its commitment is a
* hash compare that `0x0202` already serves.
*
* ## Why this library refuses to take a public key from its caller
*
* It does take one — the primitives are pure functions and cannot do otherwise.
* The rule lives one level up, in `FinalPqQuorum`: a key passed as an argument
* proves nothing, because anyone holding a keypair can produce a valid
* signature under it. Only a key read from `FinalIdentityRegistry` is evidence
* about WHO signed. Every call site here must be able to answer "where did this
* key come from" with "storage", never "calldata".
*
* ## `success` is not the answer
*
* A `staticcall` to a verifier returns two things and both matter. `success`
* false means the call was malformed — usually a length bug in the caller — and
* `success` true with a zero word means the signature did not verify. The
* helpers below collapse both to `false` for the caller's convenience, which is
* safe in that direction and only in that direction: treating a failed call as
* a valid signature would be the whole security of the system.
*/
library FinalChainPrecompiles {
/// @notice SHA3-256 (FIPS 202). NOT `keccak256`, which is the
/// pre-standardisation padding and produces a different digest.
address internal constant SHA3_256 = address(0x0202);
/// @notice ML-DSA-87 verification (FIPS 204). Transaction-class keys.
address internal constant ML_DSA_87 = address(0x0204);
/// @notice SLH-DSA-SHAKE-256s verification (FIPS 205). Access-class keys.
address internal constant SLH_DSA_SHAKE_256S = address(0x0205);
/// @notice ML-KEM-1024 encapsulation-key validation (FIPS 203).
/// @dev VALIDATES; it does not encapsulate. Runs FIPS 203 §7.2's own
/// encapsulation-key check — the type check and the modulus check — and
/// nothing else. Encapsulation is a sender operation and decapsulation
/// needs the secret key, so neither belongs on a chain.
address internal constant ML_KEM_1024 = address(0x0203);
/// @notice HQC-5 public-key validation (FIPS 207).
/// @dev Structural only: the length, and the three padding bits the
/// encoding leaves beyond `n = 57637`. HQC has no cheap key-validity
/// predicate and this does not pretend to one.
address internal constant HQC_5 = address(0x0207);
/// @notice ML-DSA-87 public key length. Round-3 Dilithium5 shares it.
uint256 internal constant ML_DSA_87_PUBLIC_KEY_LEN = 2592;
/// @notice ML-DSA-87 signature length. Round-3 Dilithium5 is 4595.
uint256 internal constant ML_DSA_87_SIGNATURE_LEN = 4627;
/// @notice SLH-DSA-SHAKE-256s public key length (`PK.seed ‖ PK.root`).
uint256 internal constant SLH_DSA_SHAKE_256S_PUBLIC_KEY_LEN = 64;
/// @notice SLH-DSA-SHAKE-256s signature length. The `f` set is 49,856.
uint256 internal constant SLH_DSA_SHAKE_256S_SIGNATURE_LEN = 29792;
/// @notice Thrown when a precompile is absent, i.e. this is not Final Chain
/// or the node is stock reth rather than `final-reth`.
error PrecompileUnavailable(address precompile);
/**
* @notice Reverts unless all five precompiles answer.
* @dev Call this from a constructor. A contract whose security rests on PQ
* verification must not deploy onto a chain that cannot perform it — the
* failure mode otherwise is a quorum that reaches threshold with zero valid
* signatures, discovered at the worst possible moment.
*
* The probe is SHA3-256 of the empty string, whose value is a published
* FIPS 202 constant. It cannot be produced by an address with no code
* (which returns empty) nor by `keccak256` (which gives a different digest
* for the same input), so it distinguishes "the right precompile" from both
* "nothing here" and "the wrong hash function".
*/
function assertAvailable() internal view {
bytes32 expected = 0xa7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a;
(bool ok, bytes memory out) = SHA3_256.staticcall("");
if (!ok || out.length != 32 || bytes32(out) != expected) {
revert PrecompileUnavailable(SHA3_256);
}
// The two signature verifiers are probed by shape rather than by a
// known-answer vector: a KAT here would put a 29,792-byte signature in
// this contract's bytecode. A deliberately short input is a
// *precompile error* by contract, so a FAILED call is the pass and a
// silent success would mean something else is answering at the address.
_probeRejectsShortInput(ML_DSA_87);
_probeRejectsShortInput(SLH_DSA_SHAKE_256S);
// The two KEM validators are probed the other way round, because they
// are total by contract: a wrong length is a malformed KEY, which is
// the question being asked, so they ANSWER rather than error. A
// one-byte input must therefore come back as a well-formed `false`, and
// a failed call means nothing is there.
_probeAnswersFalse(ML_KEM_1024);
_probeAnswersFalse(HQC_5);
}
/**
* @dev A short input must make the precompile ERROR. The gas budget is the
* whole subtlety.
*
* A reverting CONTRACT refunds the gas it did not use. A precompile that
* returns an error consumes **everything forwarded to it** — and Solidity
* forwards 63/64 of what is left by default. Two such probes in a
* constructor therefore burn all but 1/4096 of the deployment's gas, and
* the deploy fails with no revert data at all.
*
* That is not hypothetical: it is what happened the first time this ran
* against a real `final-reth`, and no Foundry test could have caught it.
* A mocked precompile is a contract, and a contract's `require` hands the
* gas back.
*
* 5,000 is generous for a call that fails on a length check before any
* cryptography runs, and small enough that both probes together are noise
* against a deployment.
*/
function _probeRejectsShortInput(address precompile) private view {
bool ok;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore8(ptr, 0x00)
ok := staticcall(5000, precompile, ptr, 0x01, 0x00, 0x00)
}
if (ok) revert PrecompileUnavailable(precompile);
}
/**
* @dev A one-byte input must come back as a well-formed zero word.
*
* The inverse of `_probeRejectsShortInput`, and the inversion is the point:
* these two precompiles are TOTAL. Every byte string has an answer to "is
* this a well-formed key", and for one byte the answer is no. A precompile
* that errored here would be one that treats a malformed key as a caller
* bug, which is the opposite of what a registry wants.
*
* Gas is bounded for the same reason as the other probe — an erroring
* precompile consumes everything forwarded — even though the pass case
* returns normally and refunds.
*/
function _probeAnswersFalse(address precompile) private view {
bool ok;
bytes32 answer;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore8(ptr, 0x00)
ok := staticcall(5000, precompile, ptr, 0x01, ptr, 0x20)
answer := mload(ptr)
}
if (!ok || answer != bytes32(0)) revert PrecompileUnavailable(precompile);
}
/**
* @notice Is `encapsulationKey` a well-formed ML-KEM-1024 key?
*
* @dev The check a registry owes a sender. A malformed encapsulation key
* stored on chain is an account whose intents cannot be sealed, and the
* discovery happens at the first attempt to seal one — on the hybrid path,
* as a pair silently reduced to one family, which is the failure with no
* error attached.
*
* False rather than reverting on any shape, including the wrong length,
* because the caller is asking a question and every input has an answer.
*/
function isWellFormedMlKem1024(bytes memory encapsulationKey) internal view returns (bool) {
return _validatesKey(ML_KEM_1024, encapsulationKey);
}
/// @notice Is `publicKey` a well-formed HQC-5 key?
/// @dev Structural, and honestly partial — see the precompile. It catches a
/// truncated key, a key from the wrong parameter set, and a tail carrying
/// smuggled bytes, which are the three ways this goes wrong in practice.
function isWellFormedHqc5(bytes memory publicKey) internal view returns (bool) {
return _validatesKey(HQC_5, publicKey);
}
/// @dev A failed CALL is not a false answer. It means nothing is at the
/// address — this is not Final Chain, or the node is stock reth — and
/// reading it as "the key is malformed" would silently disable the check on
/// exactly the deployment where it cannot run.
function _validatesKey(address precompile, bytes memory key) private view returns (bool) {
(bool ok, bytes memory out) = precompile.staticcall(key);
if (!ok || out.length != 32) revert PrecompileUnavailable(precompile);
return bytes32(out) != bytes32(0);
}
/// @notice FIPS 202 SHA3-256 over `data`.
/// @dev The certificate schema hashes `TBSCertificate`, `SubjectKeyId` and
/// `AuthorityKeyId` with this, so it is the only function that can check a
/// `certHash` against the bytes it claims to summarise.
function sha3_256(bytes memory data) internal view returns (bytes32 digest) {
(bool ok, bytes memory out) = SHA3_256.staticcall(data);
if (!ok || out.length != 32) revert PrecompileUnavailable(SHA3_256);
digest = bytes32(out);
}
/// @notice Verify an ML-DSA-87 signature. False on any failure, including
/// a malformed call.
function verifyMlDsa87(bytes memory publicKey, bytes memory message, bytes memory signature)
internal
view
returns (bool)
{
if (
publicKey.length != ML_DSA_87_PUBLIC_KEY_LEN
|| signature.length != ML_DSA_87_SIGNATURE_LEN
) return false;
return _verify(ML_DSA_87, publicKey, signature, message);
}
/// @notice Verify an SLH-DSA-SHAKE-256s signature. False on any failure.
function verifySlhDsa(bytes memory publicKey, bytes memory message, bytes memory signature)
internal
view
returns (bool)
{
if (
publicKey.length != SLH_DSA_SHAKE_256S_PUBLIC_KEY_LEN
|| signature.length != SLH_DSA_SHAKE_256S_SIGNATURE_LEN
) return false;
return _verify(SLH_DSA_SHAKE_256S, publicKey, signature, message);
}
/// @dev `publicKey ‖ signature ‖ message`, in that order. Both fixed-length
/// fields come first so the message is unambiguously the remainder — the
/// same reason the precompile takes no length prefix.
function _verify(
address precompile,
bytes memory publicKey,
bytes memory signature,
bytes memory message
) private view returns (bool) {
(bool ok, bytes memory out) =
precompile.staticcall(abi.encodePacked(publicKey, signature, message));
return ok && out.length == 32 && bytes32(out) != bytes32(0);
}
}
contracts/finalchain/FinalChainTime.sol
// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2024-2026 Final DeFi
// Licensed under the Business Source License 1.1 (the "License")
//
// Change Date: 2029-01-01
// Change License: GPL-2.0-or-later
//
// Additional Use Grant:
// 1. Any person or entity may link this time library into contracts deployed on
// a Final DeFi Protocol chain, and may read its constants to interpret the
// timestamps and durations that chain publishes.
// 2. Integrators, indexers, and operators may use it to convert between this
// chain's clock and the units their own systems keep, as part of their
// integration with the Final DeFi Protocol.
// 3. For the avoidance of doubt, this Grant does NOT permit the commercial
// deployment of a Fork of this library or a competing state plane derived
// from it without permission prior to the Change Date.
//
// @author Final DeFi
// @version 1.0.0
pragma solidity ^0.8.20;
/**
* @title Final Chain Time
* @notice **On this chain, `block.timestamp` is MILLISECONDS, not seconds.**
* @dev Every other EVM chain stamps seconds. This one cannot. It mints a block every 100 ms, and the protocol
* requires block timestamps to strictly increase, so a second-denominated clock would exhaust its distinct
* values ten times over per second. Milliseconds is the deliberate consequence, and it is a property of the
* CHAIN itself rather than of any contract here — nothing in this library can change it, and nothing deployed
* beside this library may assume otherwise.
*
* Every duration and every instant on this chain is therefore in milliseconds. This library exists so that fact
* is stated in one place and converted in one place, instead of being assumed independently everywhere a
* deadline or a delay is written.
*
* ## The naming rule, which is a safety rule
*
* A field or constant carrying a duration or an instant on this chain ends in `Ms`. This is not decoration. A
* delay field named for seconds while holding milliseconds elapses a thousand times too fast: a one-day
* recovery delay would mature in about eighty-six seconds, and a two-year dormancy threshold in under a day.
* Those delays are the whole of what stands between a stolen credential and an account, so a name that states
* the wrong unit is not a cosmetic defect — it is the defect, wearing a disguise. `Seconds`-suffixed names do
* not appear in this directory and must not be introduced.
*
* A test harness is not a check on this. Standard EVM tooling stamps `block.timestamp` in seconds, so a suite
* can agree with the contracts under test and both be wrong about the chain they deploy to. The unit has to be
* carried by the names.
*
* Solidity's `hours` and `days` suffixes remain the clearest way to write a duration, so durations are written
* as `24 hours * MS_PER_SECOND` rather than as a bare literal: the intent stays readable and the unit stays
* explicit at the point of use.
*/
library FinalChainTime {
/// @notice Milliseconds per second — the whole conversion between this chain's clock and ordinary time,
/// named once.
/// @dev Multiply a `seconds`-denominated Solidity duration literal by this to express it in this chain's
/// units. It is deliberately the only place the factor appears.
uint64 internal constant MS_PER_SECOND = 1_000;
/// @notice Nanoseconds per millisecond — the divisor for values that arrive stamped in nanoseconds.
/// @dev The certificate schema stamps validity windows in nanoseconds, so a certificate converts DOWN to
/// this chain's clock. Dividing rather than multiplying is the direction that cannot overflow, and it
/// truncates toward the past, which for a validity window is the conservative rounding.
uint64 internal constant NS_PER_MILLISECOND = 1_000_000;
/// @notice This chain's current time, in milliseconds.
/// @dev A function rather than a bare `block.timestamp` read so the unit is visible at every call site.
/// It performs no arithmetic and exists purely so that reading the clock is self-describing, where
/// `block.timestamp` on this chain is silently a thousand times what a reader would assume.
/// @return nowInMs The current block's timestamp, in milliseconds.
function nowMs() internal view returns (uint64) {
return uint64(block.timestamp);
}
}
contracts/finalchain/FinalIdentityRegistry.sol
// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2024-2026 Final DeFi
// Licensed under the Business Source License 1.1 (the "License")
//
// Change Date: 2029-01-01
// Change License: GPL-2.0-or-later
//
// Additional Use Grant:
// 1. Any person or entity may deploy this identity registry as part of a Final
// DeFi Protocol state plane, and may register, rotate, and revoke identity
// records in it under the authority this contract enforces.
// 2. Operators, integrators, and end users may read the certificates, public
// keys, role bits, and signer bindings it holds, and may call its views to
// resolve an identity, a sender, or a quorum roster.
// 3. For the avoidance of doubt, this Grant does NOT permit the commercial
// deployment of a Fork of this identity registry or a competing certificate
// authority derived from it without permission prior to the Change Date.
//
// @author Final DeFi
// @version 1.0.0
pragma solidity ^0.8.20;
import {FinalCertificate} from "./FinalCertificate.sol";
import {FinalChainTime} from "./FinalChainTime.sol";
import {FinalChainPrecompiles} from "./FinalChainPrecompiles.sol";
import {FinalPqQuorum} from "./FinalPqQuorum.sol";
import {FinalSweep} from "../utils/FinalSweep.sol";
/// @dev Commitment space for one stage's encapsulation pair.
/// Byte-equal to `FinalWalletFactory.DOMAIN_KEM_BUNDLE` and to the certificate issuer's own preimage
/// constant. Three independent derivations of one word: a mismatch in any of them is a certificate that
/// verifies nowhere, so the value is pinned by test against the other two rather than imported.
bytes32 constant DOMAIN_KEM_BUNDLE = keccak256("FINAL_KEM_BUNDLE_v01");
/// @dev Commitment space for the identity tree's wallet leaf.
/// Byte-equal to `IdentityRootModule.DOMAIN_IDENTITY_LEAF` on every execution chain. Restated rather
/// than imported because that module lives on other chains and no import would make the two one value; a
/// cross-contract parity test pins the pair. The spelling is FROZEN: the premined certificates were mined
/// against this exact constant, and the leaf it derives is the `certHash` inside a wallet's address
/// derivation, so changing a byte here moves addresses that already exist.
bytes32 constant DOMAIN_IDENTITY_LEAF = keccak256("FINAL_IDENTITY_LEAF_PQ_v01");
/// @dev Commitment space for the identity tree's ISSUER leaf.
/// An issuer projects under its own domain — `DOMAIN_ISSUER_LEAF ‖ certHash ‖ version ‖
/// issuerTreeRoot` — so an issuer record is stapleable for offline licence verification while the distinct
/// domain keeps it out of wallet admission: an execution chain's gateway folds with the wallet domain, so an
/// issuer leaf can never satisfy an identity-certificate check there. `issuerTreeRoot` is a RESERVED word,
/// zero until an issuer's own certificate-tree anchor is wired — the only clean path to offline licence
/// revocation, since fixed-depth insertion-ordered state trees cannot prove non-inclusion.
bytes32 constant DOMAIN_ISSUER_LEAF = keccak256("FINAL_ISSUER_LEAF_v01");
/// @dev The issuer name every chain-attested certificate carries, as a keccak digest.
/// The chain is the issuer but holds no keypair, so a chain-attested certificate carries this named
/// value in its issuer field: required by the wire format, verifying nothing on its own, and covered by
/// `certHash`. The name is deliberately environment-agnostic and jurisdiction-silent — the issuer is the
/// worldwide network rather than a legal entity, and an environment-specific name would fork `certHash` per
/// environment. Compared as a hash rather than as a string, so the check costs one word.
bytes32 constant CHAIN_ISSUER_DN_HASH = keccak256("CN=Final Chain,O=Final DeFi");
/// @dev The authority key identifier every chain-attested certificate names.
/// `SHA3-256(utf8("FINAL_CHAIN_AUTHORITY_v01"))` — a DOMAIN constant rather than the digest of a key,
/// because the chain issues certificates and holds no public key block to hash. Precomputed rather than
/// derived at construction: the harness the unit tests run under does not implement the real SHA3 function,
/// and the literal is pinned by test against a reference implementation. A zero-length authority key
/// identifier is reserved and is admitted nowhere.
bytes32 constant CHAIN_AUTHORITY_KEY_ID =
0x9a6a5d8139ad2d28957698330aaa691017dba7dc80eb7cbec585239fb680bbab;
/**
* @title Identity Leaf Sink
* @notice The identity tree's projection door on the state-trees contract.
* @dev A narrow interface rather than an import, because the trees contract imports THIS file — the
* dependency runs that way, and this is the one call that runs the other. Declaring the single method
* here keeps the cycle away from the compiler without duplicating either contract's surface.
*/
interface IIdentityLeafSink {
/// @notice Recompute and store the identity-tree leaf for each named account.
/// @dev Called inside the same transaction as every identity mutation, so an execution chain's admission
/// set sees a registration, rotation or revocation the moment this chain does. The leaf VALUE is
/// derived by the trees contract from the registry's post-mutation state, so the caller supplies
/// accounts and never a leaf.
/// @param accounts The accounts whose leaves are stale.
function syncIdentityLeaves(address[] calldata accounts) external;
}
/**
* @title Revocation Recorder
* @notice The revocation log's recording door.
* @dev Same narrow-interface reasoning as the leaf sink above. `recorded` is read first, so a fingerprint
* somebody already recorded through the log's permissionless door cannot revert the registry mutation
* that feeds it.
*/
interface IRevocationRecorder {
/// @notice Fold a permanently retired signer fingerprint into the revocation log.
/// @dev The log applies its own permanence gate, reading this registry back; the call states nothing the
/// registry has not already decided.
/// @param signerId The fingerprint that has lost standing for good.
function record(bytes32 signerId) external;
/// @notice Whether the log already holds `signerId`.
/// @param signerId The fingerprint to look up.
/// @return Whether a leaf for it exists.
function recorded(bytes32 signerId) external view returns (bool);
}
/**
* @title Final Identity Registry
* @notice Who every party in the system is, on chain: one record per party, carrying its certificate and its
* actual public keys.
* @dev Every service, every co-signer, every certificate authority and every operator has one record here.
* The record holds the party's public keys in full rather than commitments to them, and this contract is
* the certificate authority as well as the roster.
*
* ## Where this runs
*
* Only on this project's own reth-based chains. Verification happens inside precompiles that exist
* nowhere else: SHA3-256 at `0x0202`, ML-DSA-87 at `0x0204` and SLH-DSA-SHAKE-256s at `0x0205`, each
* address being that primitive's FIPS number. The constructor probes them and refuses to deploy where
* they are absent, so a registry of keys the chain cannot check never comes into existence. This
* contract takes part in no CREATE2 derivation — its address is per chain, and nothing derives an
* address from it — and nothing outside this directory imports it.
*
* Gas is deliberately NOT a design constraint on that chain and must not be optimised for. Where a
* choice below trades gas for a verdict that is re-derivable from public state, the verdict wins: a
* signature checked in a precompile is a fact anyone can recompute, where the same check run in a
* library by whichever process happened to hold the keys is only a claim.
*
* ## Keys are read from STORAGE, never from calldata
*
* A commitment would be a quarter of the storage and would be enough to CHECK a key someone hands you.
* It is not enough to VERIFY A SIGNATURE, because verification needs the key itself — and a key that
* arrives in calldata proves nothing, since anyone holding a keypair can produce a valid signature under
* it. A quorum built on caller-supplied keys is a quorum of one: whoever built the calldata.
*
* So the keys live here in full. `FinalPqQuorum` resolves a member through this registry and reads that
* member's key from this registry's storage, and "which key is co-signer three" has exactly one answer,
* in exactly one place. That is the load-bearing rule of every quorum on the chain, not an optimisation.
*
* ## The certificate is the record, not a pointer to one
*
* `certHash` is `SHA3-256(TBSCertificate)`: the certificate's own identity, and the handle revocation is
* keyed on. {registerWallet} and {registerIssuer} take the certificate's TBS bytes and read everything
* out of them — the digest, the serial, the key identifiers, the depth pair, the validity window and
* every public key. Neither takes a key argument, so no two arguments can disagree and no registrar can
* bind a certificate to a keypair that certificate does not contain.
*
* ## The root is the first record here, not a self-signed file
*
* This chain is the only root certificate authority, and the root is pinned as an entry in this registry
* rather than distributed as a self-signed certificate somebody has to install. Chain validation
* terminates here BY IDENTITY. Everything registered after the root is verified on chain, inside the
* precompiles, against what this registry already holds: the holder's own two signatures over the
* admission digest, the pinned chain-issuer constants, and — for a nested issuer — lineage to a
* registered parent whose depth admits it. There is no path by which a key enters this registry
* unattested; a registrar cannot register anything else.
*
* ## Roles are a bitmask
*
* One party is legitimately several things: a co-signer that also publishes, an operator that is also a
* guardian. A single enum would force either duplicate records for one key, which is two sources of
* truth about one party, or a role hierarchy nobody agrees on. A mask has neither problem, and a quorum
* asks whether an account CARRIES a capability rather than whether it IS a type.
*
* ## Membership is hybrid-gated
*
* Who is in this registry, and with which roles, is the root of every quorum on the chain, so it is the
* one thing no single key may decide. Once bootstrap is sealed, every membership mutation — register,
* roles, revoke, a hash-based signing key, the registrar threshold itself — and every state-plane
* configuration change routed through {requireRegistrarQuorum} takes a `ROLE_REGISTRAR` quorum whose
* approvals carry BOTH families: the ML-DSA-87 vote and the SLH-DSA seal. A lattice break cannot then
* rewrite the roster, and neither can a hash-function break; only both at once.
*
* The bootstrap window is the only exception. While it is open the bootstrap admin writes alone, because
* every roster has to be installed by someone before it can install itself. {sealBootstrap} closes it
* irreversibly, and refuses to close it onto a registrar quorum that cannot be met.
*
* ## The sender is not the account
*
* Transactions on this chain are signed by ML-DSA-87, and the node derives `msg.sender` from the key as
* `keccak256(0x04 ‖ publicKey)[12:]`. That address pays gas and holds no authority. {accountOfSender}
* binds it to the identity whose live transaction key it derives from, so a `msg.sender` gate anywhere
* on this chain asks {senderHasRole} and resolves to the identity — and a key rotation moves the binding
* instead of the roster.
*
* ## What this contract deliberately does not do
*
* It never un-revokes: a revoked certificate is finished, and reversing that would reopen every past
* verification. It never enumerates a mapping inside a mutation — the registrars supply the chain list a
* revocation touches, and a fingerprint an incomplete list missed stays permanently recordable through
* the revocation log's own permissionless door. It holds no funds, exposes no payable entrypoint, and
* reserves nothing against a sweep. And it grants no capability by parsing one: a certificate says which
* keys a party holds, `roles` says what the party may do, and the two arrive as different arguments on
* purpose.
*/
contract FinalIdentityRegistry is FinalSweep {
// ---------------------------------------------------------------- roles
/// @notice May co-sign account-state rounds (tree 1).
uint256 public constant ROLE_ACCOUNT_COSIGNER = 1 << 0;
/// @notice May co-sign MMR / bundle-log advances.
uint256 public constant ROLE_MMR_COSIGNER = 1 << 1;
/// @notice May publish PHI ledger state (tree 2).
uint256 public constant ROLE_PHI_PUBLISHER = 1 << 2;
/// @notice May publish vAsset state (tree 3).
uint256 public constant ROLE_VASSET_PUBLISHER = 1 << 3;
/// @notice May publish oracle data (tree 4).
uint256 public constant ROLE_ORACLE_PUBLISHER = 1 << 4;
/// @notice May publish settlement / asset registry roots (trees 5 and 6).
uint256 public constant ROLE_REGISTRY_PUBLISHER = 1 << 5;
/// @notice May act as a wallet guardian.
uint256 public constant ROLE_GUARDIAN = 1 << 6;
/// @notice May submit transactions on behalf of the protocol.
uint256 public constant ROLE_RELAYER = 1 << 7;
/// @notice May register and revoke identities once bootstrap is sealed.
uint256 public constant ROLE_REGISTRAR = 1 << 8;
/// @notice A certificate authority — the root, or an intermediate under it.
uint256 public constant ROLE_CERTIFICATE_AUTHORITY = 1 << 9;
/// @notice May co-sign `FinalSettlementLog` appends — the cross-chain
/// settlement quorum, the same members whose LMS keys satisfy the
/// execution chains' settlement set. A role of its own rather than a
/// second use of `ROLE_REGISTRY_PUBLISHER`: the registries (trees 5/6)
/// change on listing cadence and settlement leaves release custody, and
/// one role for both would put the value plane behind the listing roster.
uint256 public constant ROLE_SETTLEMENT_COSIGNER = 1 << 10;
// ----------------------------------------------------- action domains
/// @notice Action domain for registering or rotating a wallet identity.
/// @dev One domain per membership mutation, so an approval to grant a role can never be replayed as one
/// to revoke. This registry is its own verifying contract for all of these, and the digest also
/// binds a per-contract counter, so an approval authorises exactly one action once.
bytes32 public constant DOMAIN_REGISTER_WALLET = keccak256("FINAL_REGISTRY_REGISTER_WALLET_v01");
/// @notice Action domain for registering or rotating an issuer.
bytes32 public constant DOMAIN_REGISTER_ISSUER = keccak256("FINAL_REGISTRY_REGISTER_ISSUER_v01");
/// @notice The admission proof-of-possession digest domain.
/// @dev The HOLDER signs `keccak256(abi.encode(domain, chainid, registry, certHash, recoveryCertHash,
/// gateNonce))` with the live transaction key (ML-DSA-87) AND the live access key
/// (SLH-DSA-SHAKE-256s) — both families, in the admission transaction, verified by the precompiles.
/// Possession lives in the TRANSACTION, never in the artifact, so holding a copy of somebody's
/// public certificate admits nothing.
bytes32 public constant DOMAIN_IDENTITY_ADMISSION = keccak256("FINAL_IDENTITY_ADMISSION_v01");
/// @notice Action domain for root-plane global certificate revocation, by handle.
bytes32 public constant DOMAIN_REVOKE_CERTIFICATE =
keccak256("FINAL_REGISTRY_REVOKE_CERTIFICATE_v01");
/// @notice Digest domain for an issuer revoking a certificate it signed off chain.
/// @dev Signed by the issuer's own registered cert-signing keys rather than approved by a quorum, and
/// bound to the issuer's own gate nonce, so one issuer's revocations cannot be replayed as
/// another's.
bytes32 public constant DOMAIN_ISSUER_CERT_REVOCATION =
keccak256("FINAL_ISSUER_CERT_REVOCATION_v01");
/// @notice Action domain for recording an account's hash-based signing key.
bytes32 public constant DOMAIN_REGISTER_LMS_KEY = keccak256("FINAL_REGISTRY_REGISTER_LMS_KEY_v01");
/// @notice Action domain for replacing an identity's capability bitmask.
bytes32 public constant DOMAIN_SET_ROLES = keccak256("FINAL_REGISTRY_SET_ROLES_v01");
/// @notice Action domain for retiring an identity.
bytes32 public constant DOMAIN_REVOKE = keccak256("FINAL_REGISTRY_REVOKE_v01");
/// @notice Action domain for moving the registrar threshold itself.
bytes32 public constant DOMAIN_SET_REGISTRAR_THRESHOLD =
keccak256("FINAL_REGISTRY_SET_REGISTRAR_THRESHOLD_v01");
/// @notice The algorithm identifier the sender derivation is domain-separated by.
/// @dev ML-DSA-87, FIPS 204 — the only algorithm this chain's transaction envelope admits. Prefixing it
/// means a key of another family can never derive the same sender address.
uint8 private constant ENVELOPE_ALG_ML_DSA_87 = 4;
// ------------------------------------------------------------- storage
/**
* @title Identity
* @notice One party's on-chain identity.
* @dev `version` increments on every mutation, and that increment is what a rotation IS: the record is
* replaced rather than appended to, and the version is how a reader on another chain knows which of
* two copies it has seen is newer.
*/
struct Identity {
/// SHA3-256 of the LIVE certificate's TBS bytes. The revocation handle.
bytes32 certHash;
/// SHA3-256 of the RECOVERY certificate's TBS bytes.
bytes32 recoveryCertHash;
/// The certificate's 32-byte serial, `16 B entropy ‖ 16 B counter`.
bytes32 serial;
/// SHA3-256 of this certificate's public key block. A child names it in
/// its own `AuthorityKeyId`, which is how the chain links the two.
bytes32 subjectKeyId;
/// Capability bitmask. Zero for a registered-but-idle party.
uint256 roles;
/// Position on the delegation axis; 0 is the Final Chain root.
uint8 depth;
/// Deepest level this key may issue to. `== depth` means it signs no
/// certificates at all, which is every end entity.
uint8 maxDelegationDepth;
/// Milliseconds since the epoch, on this chain's clock. The certificate schema stamps validity in
/// nanoseconds and the parser converts on the way in, so nothing here ever compares across units.
uint64 notBefore;
/// Milliseconds since the epoch, or 0 for "never expires" — which the certificate schema allows and
/// personal identity certificates use. The bound is exclusive.
uint64 notAfter;
/// Monotonic. A rotation that does not advance it is refused.
uint64 version;
/// Set by `revoke`. Never unset: a revoked certificate is finished, and
/// an un-revoke would make every past verification re-openable.
bool revoked;
/// Distinguishes "no record" from "a record whose fields are all zero".
bool registered;
}
/**
* @title Lms Key
* @notice A hash-based (LMS) signing key held by a registered account.
* @dev The execution chains' quorums verify LMS rather than ML-DSA, because those chains have no
* post-quantum precompiles and check a keccak hash chain instead. Those keys are the authority over
* the post-quantum anchor, and therefore over post-quantum execution — which makes "who holds this
* fingerprint?" a question the state plane has to be able to answer, exactly as it answers it for
* every other key.
*
* Recorded against an account that is ALREADY registered, so an LMS key is a capability of a known
* identity rather than a standalone credential. It inherits that identity's revocation: a revoked
* account's signer is a revoked signer, with nothing extra to remember to do.
*/
struct LmsKey {
/// `I`, hashed into every step of the signature.
bytes16 keyId;
/// Merkle tree height. Bound into the fingerprint, because the leaf
/// commits to node `2^h + q` and a signer who could vary it could vary
/// the numbering.
uint8 height;
/// `T[1]`, the LMS public key.
bytes32 root;
/// Monotonic. A rotation that does not advance it is refused, so a
/// replayed registration cannot reinstate a superseded key.
uint64 version;
/// Distinguishes "no key" from "a key whose fields are all zero".
bool registered;
}
/// @notice The hash-based (LMS) signing key an account holds, per chain.
/// @dev One slot per account AND chain. A single-use hash-based counter is a complete defence only while
/// the key it names signs for ONE chain, so the roster is stored the way it is armed: the same
/// operator is a different signer on every chain, and a rotation on one says nothing about another.
mapping(address account => mapping(uint64 chainId => LmsKey)) private _lmsKey;
/**
* @title Lms Binding
* @notice What a signer fingerprint is bound to: the account holding it and the chain it signs for.
* @dev Two fields in one slot, deliberately. This contract sits within a few bytes of the deployed-code
* ceiling, so anything added to this surface has to pay for itself in bytecode first — which is why
* checks that no authority consults, such as refusing a zero chain identifier, are left to the
* publisher off chain rather than spent here.
*/
struct LmsBinding {
/// The account that registered the fingerprint. Zero means no account ever did.
address account;
/// The chain that registration was for. Zero alongside a zero account, for a fingerprint never
/// registered.
uint64 chainId;
}
/// @notice Which account a signer fingerprint belongs to, and which chain it signs for.
/// @dev The lookup the whole LMS record exists for: an execution chain's roster names fingerprints and
/// nothing else, so without this the keys behind those names are unattributable. Written once at
/// registration and left in place when the key is superseded, because attribution is history — a
/// signature made under a retired key was still made by that operator.
///
/// The chain it names is what selects the slot {lmsSignerIsLive} resolves the fingerprint against.
mapping(bytes32 signerId => LmsBinding) private _lmsBinding;
/// @notice The identity record for an account.
mapping(address account => Identity) private _identity;
/// @notice The live transaction key, ML-DSA-87: spending, and every high-cadence protocol action.
/// @dev All four key slots are stored in FULL rather than as commitments, because the precompiles verify
/// against a KEY and a key that arrived in calldata proves nothing about who signed. This is the
/// rule every quorum on this chain rests on.
/// @dev A certificate authority has two keys rather than four, and they live in the two active slots.
/// One storage shape rather than two, because every reader would otherwise have to know which kind
/// of party it was looking at before it could look.
mapping(address account => bytes) private _activeTransactionKey;
/// @notice The live access key, SLH-DSA-SHAKE-256s: identity, rotation and guardianship.
mapping(address account => bytes) private _activeAccessKey;
/// @notice The pre-committed recovery transaction key, ML-DSA-87. Empty for a certificate authority.
mapping(address account => bytes) private _recoveryTransactionKey;
/// @notice The pre-committed recovery access key, SLH-DSA-SHAKE-256s. Empty for a certificate
/// authority.
mapping(address account => bytes) private _recoveryAccessKey;
/// @notice The seal key: a service's second SLH-DSA-SHAKE-256s key, which co-signs execution-class
/// quorum decisions.
/// @dev Empty for every identity whose certificate carries no seal slot, which is every user wallet and
/// every certificate authority. An identity with no seal can never contribute to a sealed quorum,
/// so {sealableMemberCount} counts this rather than counting role bits.
mapping(address account => bytes) private _activeSealKey;
/// @notice The live stage's ML-KEM-1024 encapsulation key, the lattice half of the pair.
/// @dev Two algorithms per stage — ML-KEM-1024 and HQC-5 — so a break in either family leaves the other
/// standing, the same reasoning that pairs the two signature families. The pair is written and
/// cleared together, so an account holds both or neither.
/// @dev Stored as the RAW keys, like the signing keys, because a registry that held only commitments
/// could not answer "encapsulate to this party" without a second lookup somewhere less
/// authoritative.
mapping(address account => bytes) private _activeKemMlKem;
/// @notice The live stage's HQC-5 encapsulation key, the code-based half of the pair.
mapping(address account => bytes) private _activeKemHqc;
/// @notice The recovery stage's ML-KEM-1024 encapsulation key. Empty when the account has no recovery
/// stage.
mapping(address account => bytes) private _recoveryKemMlKem;
/// @notice The recovery stage's HQC-5 encapsulation key. Empty when the account has no recovery stage.
mapping(address account => bytes) private _recoveryKemHqc;
/// @notice Reverse index. A certificate identifies exactly one account, so
/// presenting a `certHash` is enough to find who it belongs to.
mapping(bytes32 certHash => address account) public accountOfCertificate;
/// @notice Revocation by certificate, independent of the account record.
/// A certificate stays revoked even if its account is later re-registered
/// under a new one.
mapping(bytes32 certHash => bool) public certificateRevoked;
/// @notice Who revoked a certificate through the ISSUER half of the lane.
/// Scoped by the verifier: the entry binds only when the recorded revoker
/// is the certificate's own issuer. Never gates registration.
mapping(bytes32 certHash => address) public certificateRevokedBy;
/// @notice Every registered account, in registration order. Small by
/// construction — this is services and co-signers, not wallets.
address[] private _accounts;
/// @notice Bootstrap authority. Zero once `sealBootstrap` has run.
address public bootstrapAdmin;
/// @notice Whether registration still accepts the bootstrap admin.
bool public bootstrapSealed;
/// @notice Where identity mutations project the tree-8 leaf, same-tx.
/// Zero only before {wireStatePlane} — the deploy tooling wires it before
/// the first registration, and the projection is skipped while unset so
/// the wiring transaction itself can be ordered freely in the bootstrap
/// window.
address public stateTrees;
/// @notice Where the PERMANENT standing losses — revocation and LMS-key
/// supersession — are recorded, same-tx. Zero only before {wireStatePlane}.
address public revocationLog;
/// @notice Sealed `ROLE_REGISTRAR` approvals a membership mutation needs.
/// @dev Zero until set, and bootstrap cannot be sealed while it is zero or
/// unreachable: a registry sealed behind a threshold nobody can meet is a
/// registry nobody can ever write to again.
uint256 public registrarThreshold;
/// @notice Replay counter per verifying contract — this registry for its
/// own mutations, each state-plane contract for its configuration. Bound
/// into every registrar digest, so an approval is for exactly one action.
mapping(address caller => uint64) private _gateNonce;
/// @notice The identity a Final Chain sender belongs to. See the contract
/// notes: a sender is derived from the `activeTransaction` key and is not
/// the account.
mapping(address sender => address account) public accountOfSender;
// -------------------------------------------------------------- events
/// @notice An identity was registered, or an existing one rotated onto a new certificate set.
/// @param account The identity written.
/// @param certHash The live certificate's handle.
/// @param roles The capability bitmask now in force.
/// @param version The record's monotonic version.
event IdentityRegistered(
address indexed account, bytes32 indexed certHash, uint256 roles, uint64 version
);
/// @notice An identity's capability bitmask was replaced.
/// @param account The identity whose roles changed.
/// @param previousRoles The mask before the change.
/// @param newRoles The mask now in force.
event IdentityRolesChanged(address indexed account, uint256 previousRoles, uint256 newRoles);
/// @notice An account's hash-based signing key for one chain was recorded or rotated.
/// @param account The identity that holds the key.
/// @param signerId The fingerprint an execution chain's roster names.
/// @param chainId The chain the key is armed for.
/// @param keyId The LMS key identifier.
/// @param height The Merkle tree height.
/// @param root The LMS public key.
/// @param version The lineage counter for this account and chain.
event LmsKeyRegistered(
address indexed account,
bytes32 indexed signerId,
uint64 indexed chainId,
bytes16 keyId,
uint8 height,
bytes32 root,
uint64 version
);
/// @notice An identity was retired. Irreversible, and its roles are cleared in the same transaction.
/// @param account The identity that was revoked.
/// @param certHash The certificate it held at the time.
event IdentityRevoked(address indexed account, bytes32 indexed certHash);
/// @notice One revocation-lane entry.
/// @param certHash The certificate that was revoked.
/// @param revoker Zero for a root-plane revocation, the issuing identity for an issuer's own.
event CertificateRevoked(bytes32 indexed certHash, address indexed revoker);
/// @notice The bootstrap window closed. After this there is no single-caller write path left.
/// @param sealedBy The bootstrap admin that closed it, immediately before being cleared.
event BootstrapSealed(address indexed sealedBy);
/// @notice The one-shot state-plane wiring landed. Emitted at most once in this contract's lifetime.
/// @param stateTrees The state-trees contract that owns the identity tree.
/// @param revocationLog The append-only log of retired signer fingerprints.
event StatePlaneWired(address stateTrees, address revocationLog);
/// @notice The number of sealed registrar approvals a membership mutation needs was set.
/// @param threshold The new threshold.
event RegistrarThresholdSet(uint256 threshold);
/// @notice A registrar quorum authorized an action.
/// @param verifyingContract The contract the approvals were collected for, and whose counter was burned.
/// @param actionDomain The action domain the approvals bound.
/// @param nonce The counter value the approvals were made over; the next action needs the next one.
/// @param valid How many approvals verified.
event RegistrarQuorumApproved(
address indexed verifyingContract, bytes32 indexed actionDomain, uint64 nonce, uint256 valid
);
// -------------------------------------------------------------- errors
/// @notice The caller holds none of the authority the entry point requires.
/// @param caller The address that called.
error NotAuthorized(address caller);
/// @notice The bootstrap window is already closed. Closing it is irreversible.
error BootstrapAlreadySealed();
/// @notice No record claims this account, or a zero address was offered as one.
/// @param account The address that was named.
error UnknownAccount(address account);
/// @notice A certificate's encapsulation key failed the chain's own well-formedness check.
/// @dev Names the algorithm, because the pair is stored together and "one of these two" is not an
/// actionable answer.
/// @param account The account being registered.
/// @param algorithmId The algorithm whose key was malformed.
error MalformedEncapsulationKey(address account, uint16 algorithmId);
/// @notice The certificate is already bound to a different account. One certificate identifies exactly
/// one party.
/// @param certHash The certificate's handle.
/// @param boundTo The account that already holds it.
error CertificateAlreadyBound(bytes32 certHash, address boundTo);
/// @notice The certificate has been revoked, or the account's own certificate has. Revocation is never
/// undone, so this is terminal for that handle.
/// @param certHash The revoked certificate's handle.
error CertificateIsRevoked(bytes32 certHash);
/// @notice A registration or rotation did not advance the record's version. Monotonicity is what stops a
/// replayed transaction reinstating credentials their holder has moved off.
/// @param current The version on record.
/// @param offered The version the caller presented.
error VersionNotNewer(uint64 current, uint64 offered);
/// @notice The named account does not carry `ROLE_CERTIFICATE_AUTHORITY`, or does not currently stand.
/// @param issuer The account that was named.
error IssuerNotACertificateAuthority(address issuer);
/// @notice The named parent has reached its own delegation bound and may issue nothing further.
/// @param issuer The parent account.
/// @param depth The parent's depth.
/// @param maxDelegationDepth The deepest level the parent may issue to.
error IssuerMayNotSign(address issuer, uint8 depth, uint8 maxDelegationDepth);
/// @notice A certificate sits at a depth its lineage does not put it at. Levels cannot be skipped,
/// because skipping one is how an issuer escapes its own delegation bound.
/// @param got The depth the certificate declares.
/// @param want The depth its lineage requires.
error WrongDepth(uint8 got, uint8 want);
/// @notice A child certificate claims a deeper delegation bound than the parent that admits it.
/// @param child The child's `maxDelegationDepth`.
/// @param issuer The parent's `maxDelegationDepth`.
error DelegationWidened(uint8 child, uint8 issuer);
/// @notice The certificate names an authority key that is not its declared parent's subject key.
/// @param got The authority key identifier the certificate carries.
/// @param want The parent's subject key identifier.
error AuthorityKeyIdMismatch(bytes32 got, bytes32 want);
/// @notice The live and recovery certificates carry different serials, so they describe two different
/// certificate sets rather than two stages of one.
/// @param liveSerial The live certificate's serial.
/// @param recoverySerial The recovery certificate's serial.
error StagesDisagree(bytes32 liveSerial, bytes32 recoverySerial);
/// @notice An LMS tree height outside 1 through 24, the range the verifier admits.
/// @param height The height offered.
error LmsHeightOutOfRange(uint8 height);
/// @notice A zero LMS root commits to no tree and is refused.
error LmsRootIsZero();
/// @notice This signer fingerprint already belongs to a different account.
/// @param signerId The fingerprint offered.
/// @param boundTo The account that already holds it.
error LmsKeyAlreadyBound(bytes32 signerId, address boundTo);
/// @notice Two identities cannot share a transaction key: the sender it derives would be attributable to
/// both.
/// @param sender The derived sender address.
/// @param boundTo The account that already claims it.
error SenderAlreadyBound(address sender, address boundTo);
/// @notice Fewer registrars able to seal than the threshold asks for.
/// @param sealable How many standing registrars hold a seal key.
/// @param threshold How many approvals a membership mutation needs.
error RegistrarThresholdUnreachable(uint256 sealable, uint256 threshold);
/// @notice A zero registrar threshold was offered, or a quorum was demanded before one was set. A zero
/// threshold is a registry with no authority behind its membership.
error RegistrarThresholdIsZero();
/// @notice {wireStatePlane} has already run. Both pointers are trust topology and are written once.
error StatePlaneAlreadyWired();
/// @notice {wireStatePlane} was handed a zero address for the trees or for the revocation log.
error ZeroStatePlane();
/// @notice The holder's proof of possession did not verify: one family failed, or the digest was built
/// over the wrong nonce.
/// @param account The account the admission was for.
error AdmissionProofInvalid(address account);
/// @notice The certificate does not name the chain's authority key, so it is not chain-attested.
/// @param authorityKeyId The authority key identifier that was presented.
error NotChainAttested(bytes32 authorityKeyId);
/// @notice The certificate's issuer name is not the chain's own.
/// @param issuerDnHash The digest of the name that was presented.
error WrongIssuerDn(bytes32 issuerDnHash);
/// @notice A chain-attested end entity sits at depth 1 with `maxDelegationDepth == depth`; anything else
/// is not an end entity.
/// @param depth The certificate's position on the delegation axis.
/// @param maxDelegationDepth The deepest level it may issue to.
error NotAnEndEntity(uint8 depth, uint8 maxDelegationDepth);
/// @notice An issuer that cannot sign is an end entity wearing an issuer profile, and belongs in
/// {registerWallet}.
/// @param depth The certificate's position on the delegation axis.
/// @param maxDelegationDepth The deepest level it may issue to.
error IssuerCannotSign(uint8 depth, uint8 maxDelegationDepth);
/// @notice A registered issuer's certificate never expires.
/// @dev Expiry is the passive half of an issuer's lifecycle, so a zero `NotAfter` is refused here even
/// though the certificate schema allows one for an end entity.
error IssuerMustExpire();
/// @notice An issuer validity window past {MAX_ISSUER_VALIDITY_MS}.
/// @param notBefore The certificate's start, in this chain's milliseconds.
/// @param notAfter The certificate's end, in this chain's milliseconds.
error IssuerValidityTooLong(uint64 notBefore, uint64 notAfter);
/// @notice An institution registration whose subject name carries no ISO 3166 country component, or
/// whose institution extension is too short to hold one.
/// @dev Only the trust root is jurisdiction-silent; a registered institution names where it answers for
/// itself.
error JurisdictionMissing();
/// @notice The subject name's country and the institution extension's `jurisdiction` field disagree, or
/// the extension's jurisdiction is not a two-byte country code.
error JurisdictionMismatch();
// --------------------------------------------------------- constructor
/**
* @notice Deploy the registry with a bootstrap registrar in place.
* @dev The precompile probe is the point of the constructor. This contract is meaningless on a chain
* that cannot verify post-quantum signatures, and deploying it there would produce a registry full
* of keys nothing on that chain can check — so it refuses to exist where the precompiles are
* absent rather than existing and being trusted.
*
* The admin is the whole authority until {sealBootstrap} runs, because every roster has to be
* installed by someone before it can install itself.
* @param admin The bootstrap registrar. Genesis names the chain deployer.
*/
constructor(address admin) {
FinalChainPrecompiles.assertAvailable();
bootstrapAdmin = admin;
}
// ----------------------------------------------------------- authority
/**
* @notice The authority gate on every membership mutation this registry performs.
* @dev Bootstrap is a real window, not a formality: every roster in this system has to be installed by
* someone before it can install itself, and a design that pretends otherwise ends up with a roster
* that cannot be brought into existence at all. It is closed by {sealBootstrap}, irreversibly.
*
* While the window is open the admin writes alone. Once it is closed there is no single-caller path
* left — not for a registrar, not for anyone — and every mutation goes through the sealed registrar
* quorum, whose approvals carry both signature families.
* @param actionDomain One of the `DOMAIN_*` constants naming the mutation.
* @param payloadDigest The mutation's own arguments, folded.
* @param anchorBlock The block the registrars read the roster at. Ignored while bootstrap is open.
* @param approvals The sealed registrar quorum. Empty while bootstrap is open.
*/
function _requireMembershipAuthority(
bytes32 actionDomain,
bytes32 payloadDigest,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) private {
if (!bootstrapSealed && msg.sender == bootstrapAdmin) return;
_requireRegistrarQuorum(address(this), actionDomain, payloadDigest, anchorBlock, approvals);
}
/**
* @notice The sealed registrar quorum, for the other contracts in the state plane.
* @dev `msg.sender` — the calling contract — is the verifying contract the digest binds and the counter
* it burns, so an approval collected for one contract's configuration cannot be spent on another's.
* The caller decides its own bootstrap exemption before calling; this function knows no caller's
* admin and applies none.
*
* Anyone may SUBMIT such a transaction. Authority is the approvals, not the sender, which is the
* whole point of a quorum.
* @param actionDomain The caller's own action domain for the change being authorised.
* @param payloadDigest The change's arguments, folded by the caller.
* @param anchorBlock The block the registrars read the roster at.
* @param approvals The registrar approvals, each carrying both families.
*/
function requireRegistrarQuorum(
bytes32 actionDomain,
bytes32 payloadDigest,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
_requireRegistrarQuorum(msg.sender, actionDomain, payloadDigest, anchorBlock, approvals);
}
/// @notice Burn one gate nonce and require a sealed registrar quorum over the action.
/// @dev The digest is `FinalPqQuorum.digest(verifyingContract, actionDomain, anchorBlock,
/// keccak256(abi.encode(nonce, payloadDigest)))`. The counter is burned BEFORE verification, so an
/// approval set is spent whether or not it turns out to be sufficient.
///
/// The seal is required rather than optional: membership is the hybrid class, and an approval
/// carrying only the lattice vote is not an approval here.
/// @param verifyingContract The contract the approvals are for, and whose counter is burned.
/// @param actionDomain One of the `DOMAIN_*` constants, so an approval to grant cannot be replayed to
/// revoke.
/// @param payloadDigest The action's own arguments, folded.
/// @param anchorBlock The block the registrars read the roster at.
/// @param approvals The registrar approvals, each carrying both families.
function _requireRegistrarQuorum(
address verifyingContract,
bytes32 actionDomain,
bytes32 payloadDigest,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) private {
if (registrarThreshold == 0) revert RegistrarThresholdIsZero();
uint64 nonce = _gateNonce[verifyingContract];
_gateNonce[verifyingContract] = nonce + 1;
bytes32 quorumDigest = FinalPqQuorum.digest(
verifyingContract, actionDomain, anchorBlock, keccak256(abi.encode(nonce, payloadDigest))
);
uint256 valid = FinalPqQuorum.require_(
this,
approvals,
quorumDigest,
ROLE_REGISTRAR,
registrarThreshold,
FinalPqQuorum.ALG_ML_DSA_87,
anchorBlock,
true
);
emit RegistrarQuorumApproved(verifyingContract, actionDomain, nonce, valid);
}
/**
* @notice Set how many sealed registrar approvals a membership mutation needs.
* @dev The bootstrap admin while the window is open; the current registrar quorum afterwards, so a
* registrar set that grows or shrinks can move the threshold to match itself.
*
* Refuses a threshold the sealable registrars cannot meet, and refuses zero. Both are a registry
* that can never be written to again, and the way that presents is every membership mutation
* reverting forever with nothing naming the threshold as the cause.
* @param threshold How many sealed approvals a mutation needs. Must be reachable and non-zero.
* @param anchorBlock The block the registrars read the roster at. Ignored while bootstrap is open.
* @param approvals The sealed registrar quorum. Empty while bootstrap is open.
*/
function setRegistrarThreshold(
uint256 threshold,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
_requireMembershipAuthority(
DOMAIN_SET_REGISTRAR_THRESHOLD, keccak256(abi.encode(threshold)), anchorBlock, approvals
);
if (threshold == 0) revert RegistrarThresholdIsZero();
uint256 sealable = sealableMemberCount(ROLE_REGISTRAR);
if (sealable < threshold) revert RegistrarThresholdUnreachable(sealable, threshold);
registrarThreshold = threshold;
emit RegistrarThresholdSet(threshold);
}
/// @notice The replay counter the next registrar approval for `caller` must be made over.
/// @dev One counter per verifying contract, so an approval collected for one contract's configuration
/// cannot be spent on another's. A caller reads this to build the digest its registrars will sign.
/// @param caller The verifying contract the approvals will name — this registry for its own mutations.
/// @return The value the next approval must bind.
function gateNonceOf(address caller) external view returns (uint64) {
return _gateNonce[caller];
}
// -------------------------------------------------------- LMS signers
/**
* @notice The roster identity of an LMS public key.
* @dev Byte-identical to `FinalRootAuthority.signerId` on the execution chains. Restated rather than
* imported because the two live on different chains and no import would make them one value —
* which is precisely why a test pins them together. A drift here would make every lookup miss while
* looking perfectly well-formed.
*
* The height is bound into the fingerprint as well as the root, because a leaf commits to a node
* number derived from it, so a signer free to vary the height could vary the numbering.
* @param keyId The LMS key identifier.
* @param height The Merkle tree height.
* @param root The LMS public key.
* @return The fingerprint an execution chain's roster names.
*/
function lmsSignerId(bytes16 keyId, uint8 height, bytes32 root) public pure returns (bytes32) {
return keccak256(abi.encode(keyId, height, root));
}
/**
* @notice Record the hash-based (LMS) signing key an already-registered account holds for one chain.
* @dev Membership-gated, like every other write here.
*
* Deliberately NOT a certificate: an LMS key is a capability of an existing identity, not an
* identity of its own. Binding it to an account means it inherits that account's revocation, so
* retiring a compromised operator is one action rather than one action per key they hold.
*
* A rotation records the SUPERSEDED fingerprint into the revocation log in the same transaction, so
* the execution chains' suspension lane never depends on someone noticing. The superseded
* fingerprint is left BOUND to this account rather than cleared, because attribution is history.
*
* A zero `chainId` is a tooling mistake rather than an attack — the slot it occupies is
* self-consistent and no authority consults it — so the publisher refuses it off chain and this
* contract spends no bytecode on the check.
* @param account Must already be registered and not revoked.
* @param chainId The execution chain this key is armed for.
* @param keyId The LMS key identifier, hashed into every step of a signature under it.
* @param height The Merkle tree height, 1 through 24.
* @param root The LMS public key. Zero commits to no tree and is refused.
* @param version Strictly increasing per account and chain. A rotation that does not advance it is
* refused, so a replayed registration cannot reinstate a key the operator has moved off.
* @param anchorBlock The block the registrars read the roster at. Ignored while bootstrap is open.
* @param approvals The sealed registrar quorum. Empty while bootstrap is open.
*/
function registerLmsKey(
address account,
uint64 chainId,
bytes16 keyId,
uint8 height,
bytes32 root,
uint64 version,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
_requireMembershipAuthority(
DOMAIN_REGISTER_LMS_KEY,
keccak256(abi.encode(account, chainId, keyId, height, root, version)),
anchorBlock,
approvals
);
Identity storage id = _identity[account];
if (!id.registered) revert UnknownAccount(account);
if (id.revoked) revert CertificateIsRevoked(id.certHash);
// A zero chain id is a tooling mistake, not an attack: the slot it
// would occupy is self-consistent and no authority consults it. The
// publisher refuses it; EIP-170 pressure keeps the check off-chain.
if (height == 0 || height > 24) revert LmsHeightOutOfRange(height);
if (root == bytes32(0)) revert LmsRootIsZero();
// Version lineage is PER account and chain: the same operator is a different signer on every chain,
// so one chain starting at version 1 says nothing about another already being at version 3.
LmsKey storage existing = _lmsKey[account][chainId];
// An empty slot holds version 0, so this alone also refuses a version-0
// registration — versions start at 1.
if (version <= existing.version) {
revert VersionNotNewer(existing.version, version);
}
bytes32 signerId = lmsSignerId(keyId, height, root);
address boundTo = _lmsBinding[signerId].account;
if (boundTo != address(0) && boundTo != account) {
revert LmsKeyAlreadyBound(signerId, boundTo);
}
// The fingerprint being superseded, captured before the slot moves —
// `existing` is a storage pointer and reads the NEW key afterwards.
bytes32 superseded = existing.registered
? lmsSignerId(existing.keyId, existing.height, existing.root)
: bytes32(0);
// The superseded fingerprint is left bound to this account rather than
// cleared. It is history: a signature made under the old key was made
// by this operator, and a lookup that stopped resolving would make that
// unprovable after the fact.
_lmsKey[account][chainId] = LmsKey(keyId, height, root, version, true);
_lmsBinding[signerId] = LmsBinding(account, chainId);
emit LmsKeyRegistered(account, signerId, chainId, keyId, height, root, version);
// Supersession is a PERMANENT transition — the old fingerprint stops
// being this slot's current key and nothing re-registers it (a
// re-registration of the same material is the same fingerprint, which
// the guard below leaves alone). Recorded same-tx so the execution
// chains' suspension lane never depends on someone noticing.
if (superseded != bytes32(0) && superseded != signerId) {
_recordRevokedSigner(superseded);
}
_projectIdentity(account);
}
/// @notice The LMS key an account holds for one chain, if any.
/// @dev Keyed per account AND per chain, because a single-use hash-based counter is only complete while
/// the key it names signs for one chain. `registered` is the field to branch on; the zero struct
/// means no key rather than a key of zeroes.
/// @param account The identity to read.
/// @param chainId The chain the key is armed for.
/// @return The stored key, copied to memory.
function lmsKeyOf(address account, uint64 chainId) external view returns (LmsKey memory) {
return _lmsKey[account][chainId];
}
/// @notice What a fingerprint is bound to: the account that registered it and the chain it signs for.
/// @dev The binding survives supersession, because attribution is history: a signature made under a
/// retired key was still made by that operator, and a lookup that stopped resolving would make that
/// unprovable after the fact. Standing is a separate question, answered by {lmsSignerIsLive}.
///
/// The revocation log's permanence gate reads this to find the slot a fingerprint belongs to; that
/// slot's current key is what separates a superseded fingerprint, which is permanent and
/// recordable, from a merely lapsed one, which renewal undoes.
/// @param signerId The fingerprint to resolve.
/// @return account The account that registered it, or zero for a fingerprint never registered.
/// @return chainId The chain that registration was for, or zero alongside a zero account.
function lmsBindingOf(bytes32 signerId) external view returns (address account, uint64 chainId) {
LmsBinding storage binding = _lmsBinding[signerId];
return (binding.account, binding.chainId);
}
/**
* @notice Whether a signer fingerprint is held by a standing, unrevoked account.
* @dev The question a verifier actually has. An execution chain's authority roster names fingerprints
* and learns nothing else about them, so without this the keys behind those names are
* unanswerable from the state plane.
*
* Standing is asked through {isActive} rather than by spelling the conditions out again, because a
* second spelling is how two answers drift: an expired identity already holds no role, and a signer
* lookup that disagreed would leave a roster satisfiable by an operator the rest of the registry
* has stopped honouring.
*
* Live means the CURRENT key of the fingerprint's own account-and-chain slot, not merely one this
* account ever held. A superseded fingerprint stays attributable but stops being live, and a
* rotation on one chain says nothing about the same operator's key on another.
* @param signerId The fingerprint an authority roster names.
* @return live Whether the fingerprint is that slot's current key and the account still stands.
* @return account The account the fingerprint is bound to, or zero when none ever registered it.
*/
function lmsSignerIsLive(bytes32 signerId) external view returns (bool live, address account) {
LmsBinding storage binding = _lmsBinding[signerId];
account = binding.account;
if (account == address(0)) return (false, address(0));
// `isActive`, not a registered/revoked pair spelled out here. The
// certificate validity window is part of standing: an expired identity
// already holds no role, and a signer lookup that disagreed would leave
// a roster satisfiable by an operator the rest of the registry has
// stopped honouring. Spelling the condition out a second time is how
// the two drift apart.
if (!isActive(account)) return (false, account);
// The CURRENT key of the fingerprint's own (account, chain) slot, not
// merely one this account ever held: a superseded fingerprint stays
// attributable but stops being live, and a rotation on one chain says
// nothing about the same operator's key on another.
LmsKey storage k = _lmsKey[account][binding.chainId];
live = k.registered && lmsSignerId(k.keyId, k.height, k.root) == signerId;
}
/// @notice Close the bootstrap window. Irreversible.
/// @dev Refuses while the registrar quorum is unset or unreachable, because sealing then would leave a
/// registry nobody can ever write to again — including to fix the threshold that locked it. The
/// count is of registrars that can SEAL: a certificate authority carrying the registrar role is
/// registered from a certificate with no seal slot and can never contribute an approval, so
/// counting role bits alone would seal onto a quorum that looks reachable and is not.
///
/// Clears the admin as well as setting the flag, so no single-caller path survives the seal.
function sealBootstrap() external {
if (msg.sender != bootstrapAdmin) revert NotAuthorized(msg.sender);
if (bootstrapSealed) revert BootstrapAlreadySealed();
if (registrarThreshold == 0) revert RegistrarThresholdIsZero();
uint256 sealable = sealableMemberCount(ROLE_REGISTRAR);
if (sealable < registrarThreshold) {
revert RegistrarThresholdUnreachable(sealable, registrarThreshold);
}
bootstrapSealed = true;
bootstrapAdmin = address(0);
emit BootstrapSealed(msg.sender);
}
// ------------------------------------------------- state-plane wiring
/**
* @notice Wire the state trees and the revocation log, once, inside the bootstrap window.
* @dev One-shot because both pointers are TRUST TOPOLOGY: the trees pointer decides where the
* wallet-creation admission set is written, and the log pointer decides where permanent standing
* losses are recorded. A re-wireable pointer would be a key over both.
*
* It cannot be a constructor argument, because both of those contracts take THIS registry as one of
* theirs. The deploy tooling calls it in the same nonce-fixed block that deploys them, before any
* identity is registered, which is why the projection is silently skipped while the pointers are
* zero rather than reverting.
* @param stateTrees_ The state-trees contract that owns tree 8. Zero is refused.
* @param revocationLog_ The append-only log of retired signer fingerprints. Zero is refused.
*/
function wireStatePlane(address stateTrees_, address revocationLog_) external {
if (bootstrapSealed || msg.sender != bootstrapAdmin) revert NotAuthorized(msg.sender);
if (stateTrees != address(0) || revocationLog != address(0)) revert StatePlaneAlreadyWired();
if (stateTrees_ == address(0) || revocationLog_ == address(0)) revert ZeroStatePlane();
stateTrees = stateTrees_;
revocationLog = revocationLog_;
emit StatePlaneWired(stateTrees_, revocationLog_);
}
/// @notice Refresh `account`'s tree-8 leaf in the state trees, same transaction.
/// @dev Skipped while the plane is unwired, which is a bootstrap-window state the deploy tooling closes
/// before the first registration, and never otherwise. The leaf VALUE is derived by the trees
/// contract from this registry's post-mutation state, so there is nothing here to get wrong beyond
/// forgetting to call it — which is why every mutation calls it, including the one that cannot
/// change the leaf.
/// @param account The identity whose leaf is stale.
function _projectIdentity(address account) private {
address trees = stateTrees;
if (trees == address(0)) return;
address[] memory one = new address[](1);
one[0] = account;
IIdentityLeafSink(trees).syncIdentityLeaves(one);
}
/// @notice Record a permanently retired signer fingerprint into the revocation log, same transaction.
/// @dev Skipped while the log is unwired, and skipped when somebody already recorded the fingerprint
/// through the log's permissionless door — the log refuses a duplicate, and a membership mutation
/// must not be revertible by a stranger who front-ran its bookkeeping.
/// @param signerId The fingerprint that has lost standing for good.
function _recordRevokedSigner(bytes32 signerId) private {
address log = revocationLog;
if (log == address(0)) return;
if (IRevocationRecorder(log).recorded(signerId)) return;
IRevocationRecorder(log).record(signerId);
}
// -------------------------------------------------------- registration
/**
* @title Admission Proof
* @notice The holder's proof of possession at admission: both live-stage families over the admission
* digest.
* @dev There is no root keypair and no issuer signature on this path. The chain admits, and the two
* signatures presented at creation are the HOLDER's, verified by the precompiles inside the same
* transaction that writes the record. Possession lives in the TRANSACTION, never in the artifact:
* a public certificate is a document anyone may hold, so presenting one proves nothing.
*/
struct AdmissionProof {
/// The holder's ML-DSA-87 signature under the live TRANSACTION key, over the admission digest.
bytes mlDsaSignature;
/// The holder's SLH-DSA-SHAKE-256s signature under the live ACCESS key, over the same digest. Two
/// families over one message, so neither a lattice break nor a hash-function break alone admits an
/// identity.
bytes slhDsaSignature;
}
/**
* @notice Register or rotate a Final Wallet identity from its two public certificates.
* @dev **Both stages, together.** A wallet has four keys in two stages and the recovery pair is
* PRE-COMMITTED — written at wallet initialization from the same certificate set that determined
* the wallet's address, which is why enabling post-quantum mode later takes no key arguments. The
* two certificates must share a serial: a serial is per certificate SET, so two stages that
* disagree about it are two different wallets.
*
* **Chain-attested means pinned, per stage:** the chain's issuer name and authority key, depth
* exactly 1 so the certificate hangs directly under the chain, and `maxDelegationDepth == depth` so
* the holder issues nothing. That immutable pair is what {identityTreeLeafOf} discriminates record
* kinds by.
*
* Issuance authority is the registrar quorum and possession is the holder's own proof; there is no
* root keypair anywhere and no certificate-authority signature over this admission.
* @param account The wallet address the certificate set derives.
* @param liveTbs The live certificate's TBS bytes: the live transaction and access keys.
* @param recoveryTbs The recovery certificate's TBS bytes: the pre-committed recovery pair.
* @param proof The holder's two signatures over the admission digest — the live transaction key
* (ML-DSA-87) and the live access key (SLH-DSA-SHAKE-256s), both verified in the precompiles
* inside this transaction.
* @param roles Capability bitmask. The one thing the certificates do not say, because capability is this
* system's decision rather than the certificate's.
* @param version Monotonic. A rotation that does not advance it is refused.
* @param anchorBlock The block the registrars read the roster at. Ignored while bootstrap is open.
* @param approvals The sealed registrar quorum. Empty while bootstrap is open. The digest binds the
* account, both certificates' bytes, the roles and the version.
* @return certHash The handle the live certificate is now known by.
*/
function registerWallet(
address account,
bytes calldata liveTbs,
bytes calldata recoveryTbs,
AdmissionProof calldata proof,
uint256 roles,
uint64 version,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external returns (bytes32 certHash) {
// Read BEFORE the authority check: the quorum path burns this counter
// inside `_requireRegistrarQuorum`, and the proof must bind the value
// the round was built over. The bootstrap path burns it explicitly in
// `_requireAdmissionProof`, so an admission is one-shot in both regimes.
uint64 admissionNonce = _gateNonce[address(this)];
_requireMembershipAuthority(
DOMAIN_REGISTER_WALLET,
keccak256(
abi.encode(account, keccak256(liveTbs), keccak256(recoveryTbs), roles, version)
),
anchorBlock,
approvals
);
FinalCertificate.Parsed memory l = FinalCertificate.parseLive(liveTbs);
FinalCertificate.Parsed memory r = FinalCertificate.parseRecovery(recoveryTbs);
if (l.serial != r.serial) revert StagesDisagree(l.serial, r.serial);
_requireChainAttestedEndEntity(l);
_requireChainAttestedEndEntity(r);
_requireAdmissionProof(account, l, r.certHash, proof, admissionNonce);
certHash = l.certHash;
_write(account, l, r, roles, version, false);
}
/**
* @notice Register or rotate an ISSUER: a third party, or one of this system's own intermediates, that
* signs certificates off chain with the keys registered here.
* @dev Admission is chain-native like any identity — the registrar quorum authorises, and the holder's
* own proof of possession establishes that the party controls the keys it is claiming. The
* delegation rules survive as LINEAGE: a nested issuer's depth, delegation bound and
* `AuthorityKeyId` must chain to its registered parent. No parent signs anything; this chain's
* admission IS the issuance.
*
* A registered issuer always expires, and its window is bounded by {MAX_ISSUER_VALIDITY_MS}.
*
* An institution must carry its real ISO 3166 country in its subject name, matching the
* `jurisdiction` field of its institution extension. That is enforced at the door because a
* verifier's legal recourse starts with knowing where an issuer answers for itself.
*
* `ROLE_CERTIFICATE_AUTHORITY` is added to whatever `roles` asks for, rather than being required in
* it: the capability is what this entry point means, so it cannot be forgotten in an argument.
* @param account The issuer's account on this chain.
* @param tbs The issuer certificate's TBS bytes: two cert-signing keys, ML-DSA-87 and
* SLH-DSA-SHAKE-256s, and no recovery stage — renewing an issuer is re-issuing, a governance act
* rather than a key rotation.
* @param parent The registered parent issuer for a nested intermediate; zero for an issuer hanging
* directly under the chain.
* @param proof The issuer's own two cert-signing keys over the admission digest. The recovery-handle
* slot in that digest is zero, because there is no recovery stage to bind.
* @param roles Capability bitmask, over and above the certificate-authority bit this call adds.
* @param version Monotonic. A rotation that does not advance it is refused.
* @param anchorBlock The block the registrars read the roster at. Ignored while bootstrap is open.
* @param approvals The sealed registrar quorum. Empty while bootstrap is open. The digest binds the
* account, the certificate bytes, the parent, the roles and the version.
* @return certHash The handle the registered certificate is now known by.
*/
function registerIssuer(
address account,
bytes calldata tbs,
address parent,
AdmissionProof calldata proof,
uint256 roles,
uint64 version,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external returns (bytes32 certHash) {
uint64 admissionNonce = _gateNonce[address(this)];
_requireMembershipAuthority(
DOMAIN_REGISTER_ISSUER,
keccak256(abi.encode(account, keccak256(tbs), parent, roles, version)),
anchorBlock,
approvals
);
FinalCertificate.Parsed memory c = FinalCertificate.parseCa(tbs);
// An issuer that cannot sign is an end entity wearing a profile —
// and an end entity belongs in `registerWallet`.
if (c.depth == 0 || c.maxDelegationDepth <= c.depth) {
revert IssuerCannotSign(c.depth, c.maxDelegationDepth);
}
if (c.notAfter == 0) revert IssuerMustExpire();
if (c.notAfter - c.notBefore > MAX_ISSUER_VALIDITY_MS) {
revert IssuerValidityTooLong(c.notBefore, c.notAfter);
}
if (c.issuerDnHash != CHAIN_ISSUER_DN_HASH) revert WrongIssuerDn(c.issuerDnHash);
_requireLineage(parent, c);
_requireJurisdiction(c);
_requireAdmissionProof(account, c, bytes32(0), proof, admissionNonce);
certHash = c.certHash;
_write(account, c, c, roles | ROLE_CERTIFICATE_AUTHORITY, version, true);
}
/// @notice The validity ceiling a registered issuer's certificate may not exceed, in this chain's
/// milliseconds: two 366-day years.
/// @dev Expiry is the passive half of an issuer's lifecycle — the touchpoint that proves an issuer is
/// still there without anyone having to act — so a registered issuer always carries a real
/// `NotAfter` and a bounded window. Renewal re-issues under the same registered keys with a version
/// bump rather than extending a certificate in place.
uint64 public constant MAX_ISSUER_VALIDITY_MS = 2 * 366 days * 1000;
/// @notice Pin one stage of a chain-attested end-entity certificate.
/// @dev Three checks, run once per stage: the certificate names the chain's authority key, it carries the
/// chain's issuer name, and its depth pair is exactly that of an end entity — depth 1, directly
/// under the chain, issuing nothing. The depth pair is immutable per version, which is why
/// {identityTreeLeafOf} discriminates record kinds by it rather than by a role bit.
/// @param c The parsed certificate stage.
function _requireChainAttestedEndEntity(FinalCertificate.Parsed memory c) private pure {
if (c.authorityKeyId != CHAIN_AUTHORITY_KEY_ID) revert NotChainAttested(c.authorityKeyId);
if (c.issuerDnHash != CHAIN_ISSUER_DN_HASH) revert WrongIssuerDn(c.issuerDnHash);
if (c.depth != 1 || c.maxDelegationDepth != c.depth) {
revert NotAnEndEntity(c.depth, c.maxDelegationDepth);
}
}
/// @notice Check a nested issuer's lineage to its registered parent.
/// @dev Delegation is governed by DEPTH, not by a boolean: a parent may sign only while
/// `depth < maxDelegationDepth`, a child sits exactly one level down so it cannot skip levels to
/// escape that bound, and its own bound may never widen past its parent's. The child's
/// `AuthorityKeyId` must equal the parent's `SubjectKeyId`, which is the link the chain follows.
///
/// A zero `parent` means the issuer hangs directly under the chain: it must then name the chain's
/// own authority key and sit at depth 1. No parent SIGNS anything here — admission by this chain is
/// the issuance, and lineage is what keeps the delegation bounds honest across it.
/// @param parent The registered parent issuer, or zero for one directly under the chain.
/// @param c The parsed issuer certificate.
function _requireLineage(address parent, FinalCertificate.Parsed memory c) private view {
if (parent == address(0)) {
if (c.authorityKeyId != CHAIN_AUTHORITY_KEY_ID) {
revert NotChainAttested(c.authorityKeyId);
}
if (c.depth != 1) revert WrongDepth(c.depth, 1);
return;
}
Identity storage ca = _identity[parent];
if (!hasRole(parent, ROLE_CERTIFICATE_AUTHORITY)) {
revert IssuerNotACertificateAuthority(parent);
}
// Delegation is governed by depth, not by a boolean. `Depth <
// MaxDelegationDepth` permits signing, and a child sits exactly one
// level down — an issuer cannot skip levels to escape its own bound.
if (ca.depth >= ca.maxDelegationDepth) {
revert IssuerMayNotSign(parent, ca.depth, ca.maxDelegationDepth);
}
if (c.depth != ca.depth + 1) revert WrongDepth(c.depth, ca.depth + 1);
if (c.maxDelegationDepth > ca.maxDelegationDepth) {
revert DelegationWidened(c.maxDelegationDepth, ca.maxDelegationDepth);
}
if (c.authorityKeyId != ca.subjectKeyId) {
revert AuthorityKeyIdMismatch(c.authorityKeyId, ca.subjectKeyId);
}
}
/// @notice Refuse an issuer whose subject name carries no jurisdiction, or one that disagrees with its
/// institution extension.
/// @dev An issuer that answers for itself somewhere is an issuer a verifier has recourse against, so a
/// registered institution must name its jurisdiction and must name it once. Only the trust root is
/// jurisdiction-silent, because the root is the worldwide network rather than a legal entity.
///
/// The rule is a real ISO 3166 alpha-2 `C=` component in the subject name, equal to the
/// `jurisdiction` field of the certificate's institution extension. The name is in canonical
/// comma-separated form, so `C=` matches at the start or immediately after a comma, and the
/// component value is exactly two bytes — a longer one is a different component that happens to
/// start with the same letter.
/// @param c The parsed issuer certificate.
function _requireJurisdiction(FinalCertificate.Parsed memory c) private pure {
bytes memory dn = c.subjectDn;
bytes2 country;
bool found = false;
for (uint256 i = 0; i + 4 <= dn.length; i++) {
if ((i == 0 || dn[i - 1] == ",") && dn[i] == "C" && dn[i + 1] == "=") {
// Exactly two bytes, then end-of-DN or the next component.
if (i + 4 < dn.length && dn[i + 4] != ",") revert JurisdictionMissing();
country = bytes2(bytes.concat(dn[i + 2], dn[i + 3]));
found = true;
break;
}
}
if (!found) revert JurisdictionMissing();
// Institution extension: legalNameLength ‖ legalName ‖
// registrationNoLength ‖ registrationNo ‖ jurisdictionLength ‖
// jurisdiction. The jurisdiction must EQUAL the DN's country.
bytes memory ext = c.institutionExt;
if (ext.length < 6) revert JurisdictionMissing();
uint256 q = 2 + (uint256(uint8(ext[0])) << 8 | uint256(uint8(ext[1])));
if (ext.length < q + 2) revert JurisdictionMissing();
q += 2 + (uint256(uint8(ext[q])) << 8 | uint256(uint8(ext[q + 1])));
if (ext.length < q + 2) revert JurisdictionMissing();
uint256 jLen = uint256(uint8(ext[q])) << 8 | uint256(uint8(ext[q + 1]));
q += 2;
if (jLen != 2 || ext.length < q + 2) revert JurisdictionMismatch();
if (bytes2(bytes.concat(ext[q], ext[q + 1])) != country) revert JurisdictionMismatch();
}
/// @notice Verify the holder's proof of possession over the admission digest.
/// @dev Both live-stage families, in the precompiles, inside this transaction: an ML-DSA-87 signature
/// under the certificate's transaction key and an SLH-DSA-SHAKE-256s signature under its access
/// key. Possession lives in the TRANSACTION rather than in the artifact, so holding a copy of
/// somebody's public certificate proves nothing.
///
/// The keys come out of the certificate being admitted, not out of calldata, which is what makes
/// this a proof rather than a self-signed assertion.
///
/// Burns the gate nonce on the bootstrap path — the quorum path burned it already — so an admission
/// is one-shot in both regimes and a captured proof cannot be replayed into a second registration.
/// @param account The account being admitted; named in the revert so a failure is attributable.
/// @param live The parsed live-stage certificate whose keys verify the proof.
/// @param recoveryCertHash The recovery certificate's handle, bound into the digest; zero for an issuer.
/// @param proof The holder's two signatures.
/// @param admissionNonce The gate-nonce value the digest was built over.
function _requireAdmissionProof(
address account,
FinalCertificate.Parsed memory live,
bytes32 recoveryCertHash,
AdmissionProof calldata proof,
uint64 admissionNonce
) private {
bytes memory message = abi.encodePacked(
keccak256(
abi.encode(
DOMAIN_IDENTITY_ADMISSION,
block.chainid,
address(this),
live.certHash,
recoveryCertHash,
admissionNonce
)
)
);
if (
!FinalChainPrecompiles.verifyMlDsa87(live.transactionKey, message, proof.mlDsaSignature)
|| !FinalChainPrecompiles.verifySlhDsa(live.accessKey, message, proof.slhDsaSignature)
) revert AdmissionProofInvalid(account);
if (_gateNonce[address(this)] == admissionNonce) {
_gateNonce[address(this)] = admissionNonce + 1;
}
}
/**
* @notice Commit one parsed certificate set to storage and project the result.
* @dev The single write path behind both registration entry points, so a wallet record and an issuer
* record cannot diverge in how they are stored. Every authorization, parse and pin has already run;
* what is left is the ordering that keeps the record consistent with its indexes.
*
* A rotation RELEASES the previous certificate's binding rather than revoking it: a superseded
* certificate and a compromised one are different facts, and revocation is the louder of the two.
* The sender binding moves with the transaction key for the same reason — a rotation is the account
* disowning that key, and a gate that still resolved the old sender would honour a retired key.
*
* A certificate already bound to another account is refused, and so is a version that does not
* advance, so neither a replayed registration nor a stolen certificate can take a record over.
* @param account The identity being written. Zero is refused.
* @param live The parsed live-stage certificate; for an issuer, its single certificate.
* @param recovery The parsed recovery-stage certificate; for an issuer, the same value, discarded.
* @param roles The complete capability bitmask to store.
* @param version Monotonic per account. Must exceed the stored value.
* @param isCa Whether this is a certificate authority, which stores no recovery, seal or
* encapsulation material.
*/
function _write(
address account,
FinalCertificate.Parsed memory live,
FinalCertificate.Parsed memory recovery,
uint256 roles,
uint64 version,
bool isCa
) private {
if (account == address(0)) revert UnknownAccount(account);
if (certificateRevoked[live.certHash]) revert CertificateIsRevoked(live.certHash);
address boundTo = accountOfCertificate[live.certHash];
if (boundTo != address(0) && boundTo != account) {
revert CertificateAlreadyBound(live.certHash, boundTo);
}
Identity storage id = _identity[account];
if (!id.registered) {
_accounts.push(account);
id.registered = true;
} else {
if (version <= id.version) revert VersionNotNewer(id.version, version);
if (id.revoked) revert CertificateIsRevoked(id.certHash);
// A rotation releases the previous certificate's binding. It is NOT
// revoked — a superseded certificate and a compromised one are
// different facts and revocation is the louder of the two.
if (id.certHash != live.certHash) delete accountOfCertificate[id.certHash];
}
id.certHash = live.certHash;
id.recoveryCertHash = recovery.certHash;
id.serial = live.serial;
id.subjectKeyId = live.subjectKeyId;
id.roles = roles;
id.depth = live.depth;
id.maxDelegationDepth = live.maxDelegationDepth;
id.notBefore = live.notBefore;
id.notAfter = live.notAfter;
id.version = version;
// The sender binding moves with the transaction key. The old sender is
// released rather than kept: a rotation is the account disowning that
// key, and a gate that still resolved it would honour a retired key.
address sender = senderFor(live.transactionKey);
address senderBoundTo = accountOfSender[sender];
if (senderBoundTo != address(0) && senderBoundTo != account) {
revert SenderAlreadyBound(sender, senderBoundTo);
}
if (_activeTransactionKey[account].length != 0) {
address previousSender = senderFor(_activeTransactionKey[account]);
if (previousSender != sender) delete accountOfSender[previousSender];
}
accountOfSender[sender] = account;
_activeTransactionKey[account] = live.transactionKey;
_activeAccessKey[account] = live.accessKey;
// A CA has no recovery pair; the two active slots are all it has.
_recoveryTransactionKey[account] = isCa ? bytes("") : recovery.transactionKey;
_recoveryAccessKey[account] = isCa ? bytes("") : recovery.accessKey;
// Cleared on a rotation to a certificate without one, for the same
// reason the encapsulation pair is: a stale seal surviving a rotation
// would let a retired key keep co-signing execution.
_activeSealKey[account] = isCa ? bytes("") : live.sealKey;
// The encapsulation pair, validated before it is stored.
//
// **The registry is where a sender looks up "encapsulate to this
// party", so a malformed key here is not a bad record — it is an
// account nobody can seal an intent to.** The discovery would happen at
// the first attempt, and on the hybrid path it would happen as a pair
// silently reduced to one family, which is identical on the wire. The
// precompiles make it a refusal at registration instead.
//
// Neither is a re-implementation of the KEM: `0x0203` runs FIPS 203
// §7.2's own encapsulation-key check and `0x0207` runs the structural
// check HQC-5's encoding admits. Encapsulation is a sender operation
// and decapsulation needs the secret key, so nothing more belongs here.
//
// A CA is sealed to by nobody and carries no encapsulation stage, so
// its slots are cleared rather than checked.
_storeKemPair(account, isCa, live.kemMlKem, live.kemHqc, true);
_storeKemPair(account, isCa, recovery.kemMlKem, recovery.kemHqc, false);
accountOfCertificate[live.certHash] = account;
emit IdentityRegistered(account, live.certHash, roles, version);
// Same-tx: a registration or rotation is visible to every execution
// chain's admission set the moment it is visible here.
_projectIdentity(account);
}
/**
* @notice Store one stage's encapsulation pair, or clear it.
* @dev Empty is legitimate and is not the same as absent-and-wrong: a certificate authority has no
* encapsulation stage, and a certificate may be issued without one. The parser has already refused
* the half-populated case, so by here the pair is both or neither.
*
* Cleared rather than left alone on a rotation to an empty pair. A stale key surviving a rotation is
* a sender encapsulating to a credential the account has disowned, and the message then never
* decrypts — the failure mode with no error attached, and the one this pairing exists to avoid.
* @param account The identity being written.
* @param isCa Whether the record is a certificate authority, which carries no encapsulation stage.
* @param mlKem The stage's ML-KEM-1024 key, or empty.
* @param hqc The stage's HQC-5 key, or empty.
* @param isLive Whether this is the live stage; false selects the recovery slots.
*/
function _storeKemPair(address account, bool isCa, bytes memory mlKem, bytes memory hqc, bool isLive)
private
{
if (isCa || mlKem.length == 0) {
delete (isLive ? _activeKemMlKem : _recoveryKemMlKem)[account];
delete (isLive ? _activeKemHqc : _recoveryKemHqc)[account];
return;
}
if (!FinalChainPrecompiles.isWellFormedMlKem1024(mlKem)) {
revert MalformedEncapsulationKey(account, FinalCertificate.ALG_ML_KEM_1024);
}
if (!FinalChainPrecompiles.isWellFormedHqc5(hqc)) {
revert MalformedEncapsulationKey(account, FinalCertificate.ALG_HQC_5);
}
if (isLive) {
_activeKemMlKem[account] = mlKem;
_activeKemHqc[account] = hqc;
} else {
_recoveryKemMlKem[account] = mlKem;
_recoveryKemHqc[account] = hqc;
}
}
/// @notice Grant or withdraw capabilities without rotating keys.
/// @dev Separate from registration because the two have different cadences: a role changes when a
/// service's job changes, a key changes when it is compromised or aged out. Folding them together
/// would force a key rotation to express a role change, which is the more dangerous of the two
/// operations doing the work of the safer one.
/// @param account Must already be registered and not revoked.
/// @param roles The complete new capability bitmask; it replaces the old one rather than merging.
/// @param anchorBlock The block the registrars read the roster at.
/// @param approvals The sealed registrar quorum. Empty while bootstrap is open.
function setRoles(
address account,
uint256 roles,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
_requireMembershipAuthority(
DOMAIN_SET_ROLES, keccak256(abi.encode(account, roles)), anchorBlock, approvals
);
Identity storage id = _identity[account];
if (!id.registered) revert UnknownAccount(account);
if (id.revoked) revert CertificateIsRevoked(id.certHash);
uint256 previous = id.roles;
id.roles = roles;
_requireRegistrarQuorumReachable();
emit IdentityRolesChanged(account, previous, roles);
// Roles are not in the tree-8 leaf, so this rewrites the same value —
// kept anyway so "every identity mutation projects" has no exceptions
// to remember.
_projectIdentity(account);
}
/// @notice Refuse a mutation that would leave the registrar quorum unreachable.
/// @dev Once bootstrap is sealed, that is the one change nothing could ever undo: a registry whose
/// threshold exceeds its sealable membership can never be written to again, including to fix
/// itself. Checked AFTER the write so the count reflects the mutation being attempted.
function _requireRegistrarQuorumReachable() private view {
if (!bootstrapSealed) return;
uint256 sealable = sealableMemberCount(ROLE_REGISTRAR);
if (sealable < registrarThreshold) {
revert RegistrarThresholdUnreachable(sealable, registrarThreshold);
}
}
/// @notice Revoke an identity and its certificate. Irreversible.
/// @dev Clears the roles as well as setting the flag. Both are checked everywhere, but leaving a revoked
/// record carrying roles invites a future reader that checks only one of them. The fingerprints of
/// the named LMS slots are recorded into the revocation log after the flag lands, so the log's own
/// permanence gate sees the transition it requires.
/// @param account The identity to retire.
/// @param chainIds The chains whose LMS-key slots this account holds. The registrars supply the list and
/// the approval digest binds it, because a mapping cannot enumerate its own keys. A chain with no
/// slot is skipped, and a fingerprint an incomplete list missed stays permanently recordable
/// through the revocation log's permissionless door, since a revoked account never regains
/// standing.
/// @param anchorBlock The block the registrars read the roster at.
/// @param approvals The sealed registrar quorum. Empty while bootstrap is open.
function revoke(
address account,
uint64[] calldata chainIds,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
_requireMembershipAuthority(
DOMAIN_REVOKE, keccak256(abi.encode(account, chainIds)), anchorBlock, approvals
);
Identity storage id = _identity[account];
if (!id.registered) revert UnknownAccount(account);
id.revoked = true;
id.roles = 0;
certificateRevoked[id.certHash] = true;
_requireRegistrarQuorumReachable();
emit IdentityRevoked(account, id.certHash);
// AFTER the flag lands, so the log's own gate sees the permanent
// transition it requires.
for (uint256 i = 0; i < chainIds.length; i++) {
LmsKey storage k = _lmsKey[account][chainIds[i]];
if (k.registered) _recordRevokedSigner(lmsSignerId(k.keyId, k.height, k.root));
}
_projectIdentity(account);
}
/**
* @notice Root-plane GLOBAL certificate revocation, by `certHash`.
* @dev The half of the revocation lane that gates registration and covers break-glass: any certificate —
* registered here, issued off chain, or never seen — can be killed by handle under the registrar
* quorum, because the handle is all a break-glass caller may have.
*
* When the handle is a registered identity's CURRENT certificate the identity falls with it: flag,
* roles cleared, same-transaction projection. So revoking by handle is never weaker than {revoke};
* it only skips the LMS-slot enumeration, and those fingerprints stay permanently recordable
* through the revocation log's own permissionless door.
* @param certHash The certificate to revoke. Need not correspond to any record.
* @param anchorBlock The block the registrars read the roster at.
* @param approvals The sealed registrar quorum. Empty while bootstrap is open.
*/
function revokeCertificate(
bytes32 certHash,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
_requireMembershipAuthority(
DOMAIN_REVOKE_CERTIFICATE, keccak256(abi.encode(certHash)), anchorBlock, approvals
);
certificateRevoked[certHash] = true;
address bound = accountOfCertificate[certHash];
if (bound != address(0)) {
Identity storage id = _identity[bound];
if (!id.revoked) {
id.revoked = true;
id.roles = 0;
_requireRegistrarQuorumReachable();
emit IdentityRevoked(bound, certHash);
_projectIdentity(bound);
}
}
emit CertificateRevoked(certHash, address(0));
}
/**
* @notice The issuing identity's half of the revocation lane: a registered issuer revokes a certificate
* it signed off chain, by `certHash`.
* @dev This records WHO revoked, and a verifier honours the entry only when the recorded revoker is the
* certificate's own issuer — which the verifier knows, because it holds the certificate. It
* deliberately does NOT set the global `certificateRevoked` flag: that flag gates registration, and
* letting any registered issuer set it for an arbitrary handle would be a griefing lane over other
* people's certificates.
*
* Anyone may SUBMIT. Authority is the two signatures — the issuer's registered cert-signing keys
* over a digest binding this registry, this chain, the handle and the issuer's own gate nonce, both
* verified in the precompiles inside this transaction. The keys come from storage, so a submitter
* cannot supply the pair its own signatures verify under.
*
* One-way: the first revoker of a handle is recorded and a second write is refused, because
* "revoked twice by two parties" is two facts where this lane models one.
* @param issuer The registered certificate authority making the statement.
* @param certHash The certificate being revoked.
* @param proof The issuer's own ML-DSA-87 and SLH-DSA-SHAKE-256s signatures over the revocation digest.
*/
function revokeIssuedCertificate(
address issuer,
bytes32 certHash,
AdmissionProof calldata proof
) external {
if (!hasRole(issuer, ROLE_CERTIFICATE_AUTHORITY)) {
revert IssuerNotACertificateAuthority(issuer);
}
if (certificateRevokedBy[certHash] != address(0)) revert CertificateIsRevoked(certHash);
uint64 nonce = _gateNonce[issuer];
_gateNonce[issuer] = nonce + 1;
bytes memory message = abi.encodePacked(
keccak256(
abi.encode(
DOMAIN_ISSUER_CERT_REVOCATION,
block.chainid,
address(this),
issuer,
certHash,
nonce
)
)
);
if (
!FinalChainPrecompiles.verifyMlDsa87(
_activeTransactionKey[issuer], message, proof.mlDsaSignature
)
|| !FinalChainPrecompiles.verifySlhDsa(
_activeAccessKey[issuer], message, proof.slhDsaSignature
)
) revert AdmissionProofInvalid(issuer);
certificateRevokedBy[certHash] = issuer;
emit CertificateRevoked(certHash, issuer);
}
// ---------------------------------------------------------------- views
/// @notice The full identity record.
/// @dev Returns the zero struct for an address no record claims, so `registered` is the field to branch
/// on rather than any of the hashes.
/// @param account The identity to read.
/// @return The stored record, copied to memory.
function identityOf(address account) external view returns (Identity memory) {
return _identity[account];
}
/// @notice The live transaction key, ML-DSA-87: what a quorum vote is verified against.
/// @dev Read from STORAGE by every quorum on this chain, never from a caller's argument — a key supplied
/// as calldata proves nothing, because anyone holding a keypair can sign under it.
/// @param account The identity to read.
/// @return The raw public key, or empty when the account holds none.
function activeTransactionKeyOf(address account) external view returns (bytes memory) {
return _activeTransactionKey[account];
}
/// @notice The live access key, SLH-DSA-SHAKE-256s: identity, rotation, and guardianship.
/// @dev A different hardness assumption from the transaction key, so a lattice break leaves the key that
/// governs identity standing intact.
/// @param account The identity to read.
/// @return The raw public key, or empty when the account holds none.
function activeAccessKeyOf(address account) external view returns (bytes memory) {
return _activeAccessKey[account];
}
/// @notice The seal key, SLH-DSA-SHAKE-256s: what `FinalPqQuorum` verifies an approval's seal against.
/// @dev A service's second hash-based key, distinct from its access key, so a quorum decision carries
/// one signature from each hardness assumption. Empty when the identity carries no seal, in which
/// case it cannot take part in a sealed quorum at all — which is why {sealableMemberCount} counts
/// this rather than counting role bits.
/// @param account The identity to read.
/// @return The raw public key, or empty when the account holds no seal.
function activeSealKeyOf(address account) external view returns (bytes memory) {
return _activeSealKey[account];
}
/// @notice The recovery-stage transaction key, ML-DSA-87.
/// @dev Authorizes rotating this account's own credentials and nothing else — acting as a guardian is an
/// ordinary action for an account and uses the live keys. Empty for a certificate authority.
/// @param account The identity to read.
/// @return The raw public key, or empty when the account holds none.
function recoveryTransactionKeyOf(address account) external view returns (bytes memory) {
return _recoveryTransactionKey[account];
}
/// @notice The recovery-stage access key, SLH-DSA-SHAKE-256s.
/// @dev The other half of the pre-committed recovery stage. Empty for a certificate authority, which has
/// no recovery stage at all.
/// @param account The identity to read.
/// @return The raw public key, or empty when the account holds none.
function recoveryAccessKeyOf(address account) external view returns (bytes memory) {
return _recoveryAccessKey[account];
}
/// @notice The four signing-key commitments, in the order tree 1's leaf wants them.
/// @dev keccak, not SHA3: these feed `FinalWalletFactory.accountStateLeafHash`, which every execution
/// chain verifies with, and that one hashes with keccak. An account missing a slot commits to the
/// hash of the empty string rather than reverting, so the leaf stays buildable for a certificate
/// authority, which holds no recovery pair.
/// @param account The identity to commit to.
/// @return liveAccess Commitment to the live access key.
/// @return liveTransaction Commitment to the live transaction key.
/// @return recoveryAccess Commitment to the recovery access key.
/// @return recoveryTransaction Commitment to the recovery transaction key.
function keyCommitments(address account)
external
view
returns (
bytes32 liveAccess,
bytes32 liveTransaction,
bytes32 recoveryAccess,
bytes32 recoveryTransaction
)
{
liveAccess = keccak256(_activeAccessKey[account]);
liveTransaction = keccak256(_activeTransactionKey[account]);
recoveryAccess = keccak256(_recoveryAccessKey[account]);
recoveryTransaction = keccak256(_recoveryTransactionKey[account]);
}
/**
* @notice The tree-8 leaf `account` currently earns: the execution chains' identity leaf while the
* identity stands, zero once it does not.
* @dev The leaf VALUE is `keccak256(DOMAIN_IDENTITY_LEAF ‖ serial ‖ keysHash)` — byte-identical to
* `IdentityRootModule.identityLeafHash`, which is also the `certHash` inside a wallet's address
* derivation — with `keysHash` folded exactly as the certificate issuer folds it:
* `keccak256(activeAccess ‖ activeTransaction ‖ recoveryAccess ‖ recoveryTransaction ‖ activeKem ‖
* recoveryKem)`, six commitment words packed in slot order. The issuing tooling and this function
* are pinned against each other by test over the premined certificate fixtures, because a wallet
* whose address was derived from a different fold is a wallet no chain can admit.
*
* Zero — the empty slot's own value, unprovable as a leaf because no certificate hashes to it — for
* anything that must not admit a wallet creation: a revoked identity, one outside its validity
* window, and any certificate authority. The authority exclusion is STRUCTURAL rather than a role
* read: an end entity has `depth == maxDelegationDepth` because it issues nothing, an authority
* never does, and that pair is immutable per version where `roles` is not.
*
* Lives here rather than on the state-trees contract that consumes it because every input is this
* contract's storage, and the trees contract has no bytecode headroom to spare.
* @param account The identity to project. Reverts for an account with no record at all.
* @return The tree-8 leaf value, or zero while the identity does not stand.
*/
function identityTreeLeafOf(address account) external view returns (bytes32) {
Identity storage id = _identity[account];
if (!id.registered) revert UnknownAccount(account);
if (id.revoked || !_withinValidity(id)) return bytes32(0);
if (id.depth != id.maxDelegationDepth) {
// An ISSUER exists in tree 8 under its own domain, so its record is stapleable for offline
// licence verification while the distinct domain keeps it out of wallet admission. `certHash`
// suffices — it covers the whole TBS and the verifier holds the certificate — `version` makes
// supersession move the leaf, and the third word RESERVES the issuer's own certificate-tree
// anchor, zero until one is wired. Zero-on-revoke above is load-bearing for both record kinds:
// a fresh staple is an unrevoked statement.
return keccak256(
abi.encodePacked(DOMAIN_ISSUER_LEAF, id.certHash, uint64(id.version), bytes32(0))
);
}
bytes32 liveKem = keccak256(
abi.encodePacked(DOMAIN_KEM_BUNDLE, _activeKemMlKem[account], _activeKemHqc[account]));
bytes32 recoveryKem = keccak256(
abi.encodePacked(DOMAIN_KEM_BUNDLE, _recoveryKemMlKem[account], _recoveryKemHqc[account]));
bytes32 keysHash = keccak256(
abi.encodePacked(
keccak256(_activeAccessKey[account]),
keccak256(_activeTransactionKey[account]),
keccak256(_recoveryAccessKey[account]),
keccak256(_recoveryTransactionKey[account]),
liveKem,
recoveryKem
)
);
return keccak256(abi.encodePacked(DOMAIN_IDENTITY_LEAF, id.serial, keysHash));
}
/// @notice Per-stage encapsulation commitments, in the order the account-state leaf wants them.
/// @dev One word per STAGE, folded over both of that stage's encapsulation public keys under
/// `DOMAIN_KEM_BUNDLE`. The pair is the unit — an account holds both keys or neither — so
/// committing to them separately would model a state the protocol does not recognise, and every
/// downstream record would carry two words where one says the same thing.
///
/// An account whose certificate carries no encapsulation stage folds the empty string here rather
/// than reverting: the projection into the state trees must keep succeeding for it, and a leaf that
/// cannot be built is a party that cannot be revoked.
/// @param account The identity to commit to.
/// @return liveKem The live stage's encapsulation commitment.
/// @return recoveryKem The recovery stage's encapsulation commitment.
function kemCommitments(address account)
external
view
returns (bytes32 liveKem, bytes32 recoveryKem)
{
liveKem = keccak256(
abi.encodePacked(DOMAIN_KEM_BUNDLE, _activeKemMlKem[account], _activeKemHqc[account]));
recoveryKem = keccak256(
abi.encodePacked(DOMAIN_KEM_BUNDLE, _recoveryKemMlKem[account], _recoveryKemHqc[account]));
}
/// @notice The live-stage encapsulation keys themselves, for a party composing a sealed message.
/// @dev Returns both halves of the pair together because the pair is the unit: encapsulating to one
/// family alone is indistinguishable on the wire from a hybrid, and silently dropping the hedge is
/// the failure this pairing exists to prevent. Empty for an account with no encapsulation stage.
/// @param account The party to encapsulate to.
/// @return activeMlKem The lattice half, ML-KEM-1024.
/// @return activeHqc The code-based half, HQC-5.
function kemKeysOf(address account)
external
view
returns (bytes memory activeMlKem, bytes memory activeHqc)
{
return (_activeKemMlKem[account], _activeKemHqc[account]);
}
// ------------------------------------------------------------- senders
/**
* @notice The sender address a transaction key produces on this chain.
* @dev `keccak256(uint8(4) ‖ publicKey)[12:]` — byte-identical to what the node derives from a
* post-quantum transaction envelope and to the backend's own derivation. The leading algorithm byte
* is what domain-separates it, so a key of another family can never derive the same address.
*
* Pure, so a client can compute the address from a certificate before the identity is registered —
* which is what lets an admission transaction be funded and submitted from the very sender it is
* about to bind.
* @param transactionKey The raw ML-DSA-87 public key.
* @return The sender address that key signs from.
*/
function senderFor(bytes memory transactionKey) public pure returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(ENVELOPE_ALG_ML_DSA_87, transactionKey)))));
}
/// @notice The sender `account`'s transactions arrive from.
/// @dev The forward direction of {accountOfSender}, derived rather than stored, so it cannot disagree
/// with the transaction key on record.
/// @param account The identity to resolve.
/// @return The derived sender, or zero for an account with no transaction key on record.
function senderOf(address account) external view returns (address) {
bytes storage key = _activeTransactionKey[account];
if (key.length == 0) return address(0);
return senderFor(key);
}
/// @notice {hasRole} for a `msg.sender`: resolves the sender to its identity first.
/// @dev The form every `msg.sender` gate on this chain uses. A sender is derived from a transaction key
/// and holds no authority itself, so asking it directly would be asking the wrong address. False for
/// a sender no identity claims.
/// @param sender The address a transaction arrived from.
/// @param roleMask The capability required.
/// @return Whether the identity behind that sender stands and carries the whole mask.
function senderHasRole(address sender, uint256 roleMask) external view returns (bool) {
address account = accountOfSender[sender];
return account != address(0) && hasRole(account, roleMask);
}
/// @notice How many accounts carrying `roleMask` also hold a seal key — the members that can take part
/// in a sealed quorum.
/// @dev The count every membership threshold is checked against, because membership approvals are the
/// hybrid class and a member with no seal can never contribute one. A certificate authority
/// carrying `ROLE_REGISTRAR` is registered from a certificate with no seal slot, so it is counted
/// out here rather than being discovered at the first quorum that fails to reach its threshold.
/// @param roleMask The capability the quorum is over.
/// @return sealable How many standing accounts carry the mask and hold a seal key.
function sealableMemberCount(uint256 roleMask) public view returns (uint256 sealable) {
uint256 n = _accounts.length;
for (uint256 i = 0; i < n; i++) {
address a = _accounts[i];
if (hasRole(a, roleMask) && _activeSealKey[a].length != 0) sealable++;
}
}
/// @notice Number of registered accounts.
/// @dev Never decreases: revocation clears a record's roles and sets its flag but leaves it in the list,
/// so an index handed out once keeps pointing at the same account for good.
/// @return How many accounts have ever been registered.
function accountCount() external view returns (uint256) {
return _accounts.length;
}
/// @notice Registered account by index, in registration order.
/// @dev Reverts on an out-of-range index rather than answering zero, so a caller paging the list cannot
/// mistake the end of it for a hole in the middle.
/// @param index Position in the registration-ordered list, below {accountCount}.
/// @return The account at that position.
function accountAt(uint256 index) external view returns (address) {
return _accounts[index];
}
/// @notice Every account carrying every bit in `roleMask`.
/// @dev A view, so the linear scan over the account list costs nothing to a caller reading off chain.
/// Callers that need a roster inside a transaction pass the member list explicitly instead — see
/// `FinalPqQuorum`, which takes signers rather than searching for them, so a quorum's cost does not
/// grow with the size of the registry.
/// @param roleMask The capability to filter on.
/// @return found The matching accounts, in registration order.
function accountsWithRole(uint256 roleMask) external view returns (address[] memory found) {
uint256 n = _accounts.length;
address[] memory buf = new address[](n);
uint256 count;
for (uint256 i = 0; i < n; i++) {
if (hasRole(_accounts[i], roleMask)) {
buf[count++] = _accounts[i];
}
}
found = new address[](count);
for (uint256 i = 0; i < count; i++) {
found[i] = buf[i];
}
}
/**
* @notice How many accounts could satisfy a quorum for `roleMask` right now.
* @dev The number a threshold has to be reachable against. A threshold above it is not a strict quorum,
* it is a quorum that cannot be met — and the way that presents is an operation reverting forever
* with nothing naming the roster as the cause. Counts standing alone; use {sealableMemberCount} for
* a quorum that also needs a seal.
* @param roleMask The capability the quorum is over.
* @return live How many standing accounts carry the whole mask.
*/
function liveMemberCount(uint256 roleMask) public view returns (uint256 live) {
uint256 n = _accounts.length;
for (uint256 i = 0; i < n; i++) {
if (hasRole(_accounts[i], roleMask)) live++;
}
}
/**
* @notice Whether `account` currently carries every bit in `roleMask`.
* @dev Every gate in this system asks this one question, so every gate gets the same answer: registered,
* not revoked, inside its validity window, and holding the capability. A caller that checked only
* the role bit would accept an expired certificate.
*
* `roleMask == 0` is false. A zero mask asks nothing and must not read as "yes" — that is the shape
* of an uninitialised configuration variable, and the one reading it must not be a universal pass.
*
* Every bit in the mask must be present, so a mask naming two capabilities asks for both rather than
* either.
* @param account The account to test.
* @param roleMask One or more `ROLE_*` bits, OR-ed together.
* @return Whether the account stands and carries the whole mask.
*/
function hasRole(address account, uint256 roleMask) public view returns (bool) {
if (roleMask == 0) return false;
Identity storage id = _identity[account];
if (!id.registered || id.revoked) return false;
if (id.roles & roleMask != roleMask) return false;
return _withinValidity(id);
}
/// @notice Whether `account` is registered, unrevoked and in date, regardless of capability.
/// @dev The standing half of {hasRole}, for callers that care that a party is honoured at all rather
/// than that it holds a particular capability. {lmsSignerIsLive} asks this rather than spelling the
/// three conditions out a second time, because a second spelling is how two answers drift apart.
/// @param account The account to test. An address no record claims answers false.
/// @return Whether the identity currently stands.
function isActive(address account) public view returns (bool) {
Identity storage id = _identity[account];
return id.registered && !id.revoked && _withinValidity(id);
}
/// @notice Whether a record's certificate is inside its validity window right now.
/// @dev Both bounds are milliseconds on this chain's clock and both are optional: a zero `notBefore`
/// means valid from issuance and a zero `notAfter` means never expires, which the certificate
/// schema allows and personal identity certificates use. The upper bound is exclusive, so a
/// certificate stops being honoured on the millisecond it names rather than after it.
/// @param id The record to test, taken as a storage pointer so no copy of a multi-word struct is made.
/// @return Whether the window admits the current block time.
function _withinValidity(Identity storage id) private view returns (bool) {
if (id.notBefore != 0 && FinalChainTime.nowMs() < id.notBefore) return false;
if (id.notAfter != 0 && FinalChainTime.nowMs() >= id.notAfter) return false;
return true;
}
// ------------------------------------------------------------------ sweep
/// @inheritdoc FinalSweep
/// @dev The registry's own configuration gate, in the `msg.sender` form a no-argument seam can express:
/// the bootstrap admin alone while the window is open, a live registrar afterwards.
///
/// The rest of the state plane inherits this rule from `FinalPlaneSweep`, which reads it off a
/// registry pointer. This contract answers it from its own storage because it IS that registry, and
/// importing the shared mixin here would make this file import a file that imports it back.
///
/// The sealed half of the gate is a K-of-N over `ROLE_REGISTRAR` whose approvals arrive in calldata,
/// which `sweepAsset`'s shared signature has no room for; what survives is membership in that same
/// roster. The narrowing is safe because the other two gates hold regardless: a sweep moves surplus
/// only, this contract owes nothing, so there is nothing behind the line to reach — and the
/// destination is not the caller's to invent.
function _requireSweepAuthority() internal view override {
if (!bootstrapSealed && msg.sender == bootstrapAdmin) return;
if (hasRole(msg.sender, ROLE_REGISTRAR)) return;
revert SweepUnauthorized(msg.sender);
}
/// @inheritdoc FinalSweep
/// @dev The bootstrap admin, and the proven authority that called. The first of those is zero once the
/// window is sealed, which `FinalSweep` refuses as a destination, so a sealed registry can only
/// sweep to the registrar that authorised the sweep.
function _sweepDestinations() internal view override returns (address, address) {
return (bootstrapAdmin, msg.sender);
}
/// @dev Nothing is reserved because nothing is owed: the registry holds
/// certificates and role bits, has no payable entrypoint and no custody
/// line. Anything it carries arrived by accident.
}
contracts/finalchain/FinalPqQuorum.sol
// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2024-2026 Final DeFi
// Licensed under the Business Source License 1.1 (the "License")
//
// Change Date: 2029-01-01
// Change License: GPL-2.0-or-later
//
// Additional Use Grant:
// 1. Any person or entity may deploy and operate this quorum as part of a
// Final DeFi Protocol chain, and may inherit it to gate an action behind a
// post-quantum K-of-N.
// 2. Integrators, auditors, and node operators may read its membership and
// thresholds and independently re-verify any approval it recorded, as part
// of their integration with the Final DeFi Protocol.
// 3. For the avoidance of doubt, this Grant does NOT permit the commercial
// deployment of a Fork of this quorum or a competing identity or
// authorization plane derived from it without permission prior to the
// Change Date.
//
// @author Final DeFi
// @version 1.0.0
pragma solidity ^0.8.20;
import {FinalChainPrecompiles} from "./FinalChainPrecompiles.sol";
import {FinalIdentityRegistry} from "./FinalIdentityRegistry.sol";
/**
* @title FinalPqQuorum
* @notice K-of-N approval where the signatures are post-quantum and the chain
* is what checks them.
*
* @dev This library is the reason Final Chain exists in this design.
*
* `FinalBackend/src/pq/credential.js` carries a rule it had to enforce in code
* because nothing else could: **a surface whose signature is verified on chain
* cannot be PQ.** A co-signer approval reaching `FinalRootAuthority` is checked
* by ECDSA/ERC-1271 in Solidity, so a PQ co-signer would produce approvals the
* contract cannot read, and the quorum would stop reaching threshold with
* nothing in any log naming the cause. `PQ_SURFACE` and `assertBackendVerified`
* exist to keep anyone from crossing that line by accident.
*
* Here the line is gone. The precompiles verify ML-DSA-87 and
* SLH-DSA-SHAKE-256s natively, so a quorum can be PQ *and* on chain, and
* "the backend says these four signatures verified" becomes "these four
* signatures verify, and any node re-derives that independently".
*
* ## Three rules, each closing a specific hole
*
* 1. **Keys come from the registry, never from calldata.** A key passed as an
* argument proves nothing — anyone with a keypair can sign under it. This is
* the difference between a 4-of-5 quorum and a 1-of-1 held by whoever built
* the transaction.
*
* 2. **Signers strictly ascending.** One comparison per entry rejects duplicates
* outright, so a single member cannot supply four approvals and satisfy a
* threshold of four. The alternative — an O(n²) seen-check — is the same
* guarantee with more ways to get it wrong.
*
* 3. **The digest binds chain id and verifying contract.** Without both, an
* approval collected for one contract is replayable against another with the
* same payload shape, and an approval from the test chain is replayable on
* the production one. These co-signers hold one key across environments.
*
* ## Which algorithm
*
* The stack splits its keys by hardness assumption, not by convenience:
* ML-DSA-87 (lattice) signs transactions, SLH-DSA-SHAKE-256s (hash-based) signs
* identity. Two families, so one cryptanalytic result cannot take both.
*
* So an action inherits the class of what it authorizes. Advancing a state root
* is operational and high-cadence: transaction class. Registering or revoking
* an identity is the thing the access class exists for. `ALG_ANY` is available
* and should be used sparingly — accepting either means a break in one family
* takes the quorum.
*
* An action that authorizes EXECUTION takes both: the ML-DSA-87 approval and a
* `seal`, an SLH-DSA-SHAKE-256s signature over the same digest by the member's
* `activeSeal` key. Neither family alone can then move funds, and the seal key
* is its own slot — never the access key — so the process that seals cannot
* also rotate the identity it seals for.
*
* Every digest binds an `anchorBlock`: the block at which the members read
* tree 1 to decide who is in the round. Binding it means every approval in a
* round was made against ONE roster view, and the window in `require_` means a
* view older than `ANCHOR_WINDOW` blocks is refused rather than honoured.
*
* The practical cost is worth stating: an SLH-DSA signature is 29,792 bytes, so
* a 4-of-5 access-class quorum is ~119 KB of calldata. That is affordable here
* only because this is our own chain. Do not carry this pattern to a chain
* where it is not.
*/
library FinalPqQuorum {
/// @notice ML-DSA-87 — FIPS 204. Algorithm ids are the FIPS numbers: the
/// same ids `FinalCertificate` and the backend registry use, and the numbers
/// the precompile addresses end in (`0x0204`).
uint8 internal constant ALG_ML_DSA_87 = 4;
/// @notice SLH-DSA-SHAKE-256s — FIPS 205 (`0x0205`).
uint8 internal constant ALG_SLH_DSA_SHAKE_256S = 5;
/// @notice Either scheme is acceptable for this action.
uint8 internal constant ALG_ANY = 0;
/// @notice How far behind the chain head an approval's anchor may sit.
/// @dev Members evaluate roster membership against tree 1 AT the anchor
/// block. 600 blocks is ten minutes at the chain's one-second cadence —
/// generous against a round that takes seconds, and short enough that a
/// roster rotated away is refused rather than counted.
uint64 internal constant ANCHOR_WINDOW = 600;
/// @dev Domain separator for every quorum digest. Distinct from any
/// EIP-712 domain in the stack: these are not typed-data signatures and
/// must not be confusable with one.
bytes32 internal constant DOMAIN_PQ_QUORUM = keccak256("FINAL_CHAIN_PQ_QUORUM_v01");
/// @notice One member's approval.
struct Approval {
/// The member's account, which is also the key it is looked up by.
address signer;
/// `ALG_ML_DSA_87` or `ALG_SLH_DSA_SHAKE_256S`.
uint8 algorithm;
/// Over the 32-byte digest from `digest()`, verbatim. Both schemes
/// hash internally, so the digest is not re-hashed before signing.
bytes signature;
/// SLH-DSA-SHAKE-256s over the same digest, by the member's `activeSeal`
/// key. Required where the action authorizes execution; empty otherwise.
bytes seal;
}
/// @notice Thrown when fewer valid approvals were supplied than the action requires.
/// @param valid Approvals that verified.
/// @param required Approvals the action demands.
error ThresholdNotMet(uint256 valid, uint256 required);
/// @notice Thrown when approvals are not in strictly ascending signer order.
/// @dev Ascending order is what makes duplicate detection a single comparison instead of a quadratic scan,
/// so it is the rule that stops one signer being counted twice toward a threshold.
/// @param previous The preceding signer.
/// @param next The signer that failed to exceed it.
error SignersNotAscending(address previous, address next);
/// @notice Thrown when an approving signer does not hold the role this action is gated on.
/// @param signer The approving signer.
/// @param roleMask The role the action requires.
error SignerLacksRole(address signer, uint256 roleMask);
/// @notice Thrown when an approval is signed under an algorithm this action does not accept.
/// @param signer The approving signer.
/// @param got The algorithm the approval declared.
/// @param required The algorithm the action demands.
error WrongAlgorithm(address signer, uint8 got, uint8 required);
/// @notice Thrown when an approval's signature fails verification in the precompile.
/// @param signer The approving signer.
/// @param algorithm The algorithm it was verified under.
error BadSignature(address signer, uint8 algorithm);
/// @notice Thrown when an approval's access seal fails verification.
/// @param signer The approving signer.
error BadSeal(address signer);
/// @notice Thrown when an approval anchors to a block this chain has not reached.
/// @param anchorBlock The block the approval anchored to.
/// @param blockNumber The current block.
error AnchorAhead(uint64 anchorBlock, uint256 blockNumber);
/// @notice Thrown when an approval's anchor is older than the accepted window.
/// @dev Bounding the window is what stops an approval collected once being replayed indefinitely later.
/// @param anchorBlock The block the approval anchored to.
/// @param blockNumber The current block.
error AnchorStale(uint64 anchorBlock, uint256 blockNumber);
/// @notice Thrown when an action is gated on a threshold of zero.
/// @dev Refused rather than treated as "no approvals needed": a zero threshold is always a
/// misconfiguration, and reading it as permissive would silently remove the quorum.
error ThresholdIsZero();
/**
* @notice The message every member of this quorum signs.
* @param verifyingContract The contract consuming the approvals. Binding it
* stops an approval collected for one contract being replayed
* against another with the same payload shape.
* @param actionDomain What is being authorized — a per-action constant, so
* an approval for "advance the accounts tree" cannot be replayed as
* one for "revoke an identity".
* @param anchorBlock The Final Chain block the members read tree 1 at to
* decide the roster. Bound here so every approval in a round names
* the same view; checked against `ANCHOR_WINDOW` by `require_`.
* @param payloadDigest The action's own committed content. Callers MUST
* include a nonce or a monotonic counter in it; nothing here can
* tell a replay of round 7 from a fresh round 7.
*/
function digest(
address verifyingContract,
bytes32 actionDomain,
uint64 anchorBlock,
bytes32 payloadDigest
) internal view returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_PQ_QUORUM,
block.chainid,
verifyingContract,
actionDomain,
anchorBlock,
payloadDigest
)
);
}
/**
* @notice Reverts unless at least `threshold` distinct members holding
* `roleMask` have signed `quorumDigest`.
* @param registry Where public keys and roles come from. Not a parameter
* for flexibility — a parameter so the caller's own immutable
* registry address is what is used, rather than one from calldata.
* @param requiredAlgorithm `ALG_ANY` to accept either scheme.
* @param anchorBlock The anchor the digest was built over. Refused if it is
* ahead of this block or more than `ANCHOR_WINDOW` behind it.
* @param requireSeal Whether every approval must also carry a valid `seal`
* by the member's `activeSeal` key — the execution class.
* @return valid The number of approvals that verified, which is at least
* `threshold` if this returns at all.
*
* @dev Every failure reverts with the offending signer named. A quorum that
* silently skipped bad approvals and counted the rest would let a
* misconfigured co-signer sit broken indefinitely: the threshold would keep
* being met by the others and nothing would say one member had stopped
* contributing. That is exactly the failure this program has already had,
* in `fanOut`, where a per-chain advance failure was recorded and execution
* continued.
*/
function require_(
FinalIdentityRegistry registry,
Approval[] calldata approvals,
bytes32 quorumDigest,
uint256 roleMask,
uint256 threshold,
uint8 requiredAlgorithm,
uint64 anchorBlock,
bool requireSeal
) internal view returns (uint256 valid) {
if (threshold == 0) revert ThresholdIsZero();
if (anchorBlock > block.number) revert AnchorAhead(anchorBlock, block.number);
if (block.number - anchorBlock > ANCHOR_WINDOW) revert AnchorStale(anchorBlock, block.number);
bytes memory message = abi.encodePacked(quorumDigest);
address previous = address(0);
uint256 n = approvals.length;
for (uint256 i = 0; i < n; i++) {
Approval calldata a = approvals[i];
// Strictly ascending. `address(0)` as the initial value works
// because it can never be a registered signer.
if (a.signer <= previous) revert SignersNotAscending(previous, a.signer);
previous = a.signer;
if (!registry.hasRole(a.signer, roleMask)) revert SignerLacksRole(a.signer, roleMask);
if (requiredAlgorithm != ALG_ANY && a.algorithm != requiredAlgorithm) {
revert WrongAlgorithm(a.signer, a.algorithm, requiredAlgorithm);
}
if (!_verify(registry, a, message)) revert BadSignature(a.signer, a.algorithm);
if (requireSeal && !_verifySeal(registry, a, message)) revert BadSeal(a.signer);
valid++;
}
if (valid < threshold) revert ThresholdNotMet(valid, threshold);
}
/// @notice Non-reverting form, for views and for callers that want to
/// report rather than refuse.
function count(
FinalIdentityRegistry registry,
Approval[] calldata approvals,
bytes32 quorumDigest,
uint256 roleMask,
uint8 requiredAlgorithm,
uint64 anchorBlock,
bool requireSeal
) internal view returns (uint256 valid) {
if (anchorBlock > block.number || block.number - anchorBlock > ANCHOR_WINDOW) return 0;
bytes memory message = abi.encodePacked(quorumDigest);
address previous = address(0);
uint256 n = approvals.length;
for (uint256 i = 0; i < n; i++) {
Approval calldata a = approvals[i];
if (a.signer <= previous) return valid;
previous = a.signer;
if (!registry.hasRole(a.signer, roleMask)) continue;
if (requiredAlgorithm != ALG_ANY && a.algorithm != requiredAlgorithm) continue;
if (!_verify(registry, a, message)) continue;
if (requireSeal && !_verifySeal(registry, a, message)) continue;
valid++;
}
}
/// @dev The seal: SLH-DSA-SHAKE-256s by the member's `activeSeal` key over
/// the same digest. A member with no seal key on record cannot seal, and an
/// approval with no seal bytes is not one.
function _verifySeal(
FinalIdentityRegistry registry,
Approval calldata a,
bytes memory message
) private view returns (bool) {
bytes memory key = registry.activeSealKeyOf(a.signer);
if (key.length == 0 || a.seal.length == 0) return false;
return FinalChainPrecompiles.verifySlhDsa(key, message, a.seal);
}
/// @dev Verifies one approval against the key the REGISTRY holds for that signer, never against a key
/// supplied in the approval. A key passed as an argument proves nothing, because anyone holding a
/// keypair can sign under it; reading from storage is what makes the verdict re-derivable from public
/// state rather than a claim by whoever assembled the call.
/// @param registry The identity registry that holds each signer's live keys.
/// @param a The approval being verified.
/// @param message The exact bytes the approval must cover.
/// @return valid True when the signature verifies under the signer's live key for the declared algorithm.
function _verify(
FinalIdentityRegistry registry,
Approval calldata a,
bytes memory message
) private view returns (bool) {
// The LIVE pair, always. The recovery pair authorizes rotating this
// account's own credentials and NOTHING else — a quorum that accepted
// it would hand the recovery keys everyday authority, which is exactly
// the separation the two stages exist to draw.
if (a.algorithm == ALG_ML_DSA_87) {
return FinalChainPrecompiles.verifyMlDsa87(
registry.activeTransactionKeyOf(a.signer), message, a.signature
);
}
if (a.algorithm == ALG_SLH_DSA_SHAKE_256S) {
return FinalChainPrecompiles.verifySlhDsa(
registry.activeAccessKeyOf(a.signer), message, a.signature
);
}
// Any other id is a refusal, never a default — including the KEM ids
// (3, 7) and the reserved FN-DSA id (6), none of which is a signature
// scheme this quorum verifies.
return false;
}
}
contracts/utils/FinalSweep.sol
// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2024-2026 Final DeFi
// Licensed under the Business Source License 1.1 (the "License")
//
// Change Date: 2029-01-01
// Change License: GPL-2.0-or-later
//
// Additional Use Grant:
// 1. Any person or entity may inherit this sweep surface into contracts that
// integrate with the Final DeFi Protocol, in order to recover assets sent to
// them by mistake.
// 2. Protocol operators and integrators may call the sweep entrypoints it
// declares, subject to each inheriting contract's own authority and reserved
// balance rules, as part of their integration with the Final DeFi Protocol.
// 3. For the avoidance of doubt, this Grant does NOT permit the commercial
// deployment of a Fork of this sweep surface or a competing asset-recovery
// plane derived from it without permission prior to the Change Date.
//
// @author Final DeFi
// @version 1.0.0
pragma solidity ^0.8.20;
/// @notice The asset kinds a sweep can move. `Native` ignores `asset` and
/// `id`; `Erc20` ignores `id`; `Erc721` reads `id` as the token id and moves
/// exactly one; `Erc1155` reads both.
enum SweepKind { Native, Erc20, Erc721, Erc1155 }
/**
* @title Final Sweep
* @notice One sweep surface, on every contract of ours that can end up holding
* an asset it does not owe to anybody.
*
* @dev Assets arrive at protocol contracts that were never meant to hold them:
* a bridge delivers to the wrong leg, a user sends an ERC-20 to a registry, an
* airdrop lands on the gateway, an NFT is safe-transferred into the vault. Left
* alone that value is destroyed. The sweep is how it comes back — and the
* single rule it must never break is that a sweep moves SURPLUS and nothing
* else.
*
* Three seams make that rule per-contract:
*
* - `_requireSweepAuthority()` — the treasury role, expressed in whatever
* access plane the host contract already has (`FinalAccessController` roles,
* a cross-chain authority, a quorum). No new authority is introduced.
* - `_sweepDestinations()` — where a sweep may pay. Ours is a two-address
* answer because a contract normally has exactly two legitimate ones (the
* gateway and the treasury); a contract with one returns it twice.
* `FinalGateway` overrides `_requireSweepDestination` outright: the gateway
* is the drain of the whole system and sweeps ONWARD to anywhere.
* - `_sweepReserved(kind, asset, id)` — the part of the raw balance that is
* NOT surplus: fee deposits, the pending-settlement bucket, searcher
* collateral, settlement custody, vaulted entries, locked PHI. The default
* is zero, which is correct for a contract that custodies nothing; every
* contract that custodies something overrides it and is the one place the
* liability is stated.
*
* The surplus is measured LIVE against the raw balance at call time, so a
* re-entrant destination re-measures against a balance that already fell —
* there is no cached figure to double-spend. Nothing here writes storage, so
* there is no state for a callback to observe half-updated either.
*
* The three ERC-721/ERC-1155 receiver hooks are part of the same surface and
* for the same reason: `safeTransferFrom` reverts into a contract that does not
* answer them, so without these an NFT sent to one of ours does not land at
* all — which is not safety, it is a different way to lose it.
*/
abstract contract FinalSweep {
/// @notice `msg.sender` does not hold this contract's sweep authority.
error SweepUnauthorized(address caller);
/// @notice `to` is neither of this contract's sweep destinations.
error SweepDestinationNotAllowed(address to);
/// @notice The requested amount is above the surplus: the difference is
/// owed to somebody (a deposit, a custody total, a vaulted entry).
error SweepAboveSurplus(address asset, uint256 requested, uint256 surplus);
/// @notice A sweep of nothing.
error SweepZeroAmount();
/// @notice The transfer leg failed, or the token returned `false`.
error SweepTransferFailed(address asset);
/// @notice `amount` of `asset` (`id` for the non-fungible kinds) left this
/// contract for `to` under the sweep authority.
event AssetSwept(SweepKind indexed kind, address indexed asset, address indexed to, uint256 id, uint256 amount);
// ─────────────────────────────── seams ───────────────────────────────
/// @dev Reverts unless `msg.sender` may sweep. The host contract's own
/// treasury role — never a new one.
function _requireSweepAuthority() internal view virtual;
/// @dev The (at most two) addresses a sweep may pay. A contract with one
/// legitimate destination returns it twice.
function _sweepDestinations() internal view virtual returns (address a, address b);
/// @dev The part of the raw balance that is owed and therefore never
/// sweepable. Zero for a contract that custodies nothing.
function _sweepReserved(SweepKind, address, uint256) internal view virtual returns (uint256) {
return 0;
}
/// @dev Destination policy. Overridden by `FinalGateway`, which may sweep
/// onward to anywhere.
function _requireSweepDestination(address to) internal view virtual {
(address a, address b) = _sweepDestinations();
if (to == address(0) || (to != a && to != b)) revert SweepDestinationNotAllowed(to);
}
// ────────────────────────────── surface ──────────────────────────────
/// @notice The surplus of `asset` (`id` for the non-fungible kinds) — the
/// raw balance above everything this contract owes. What a sweep may move,
/// readable before calling one.
function sweepableSurplus(SweepKind kind, address asset, uint256 id) public view returns (uint256 surplus) {
uint256 raw = _rawBalance(kind, asset, id);
uint256 reserved = _sweepReserved(kind, asset, id);
return raw > reserved ? raw - reserved : 0;
}
/// @notice Move `amount` of an asset this contract does not owe to `to`.
/// @dev Role-gated, destination-gated and bounded by the live surplus. The
/// three gates are independent: a treasury key cannot pay a destination
/// the contract does not recognize, and neither key nor destination can
/// reach a wei that backs a liability.
/// @param kind Which asset kind is being moved.
/// @param asset Token contract; ignored for `Native`.
/// @param id Token id for `Erc721` / `Erc1155`; ignored otherwise.
/// @param amount Amount to move. `type(uint256).max` means the whole
/// surplus, which is what an operator draining a stray balance wants and
/// what avoids a race with an inflow landing between the read and the call.
/// @param to Destination.
/// @return moved Amount actually moved.
function sweepAsset(SweepKind kind, address asset, uint256 id, uint256 amount, address to)
external
returns (uint256 moved)
{
_requireSweepAuthority();
_requireSweepDestination(to);
uint256 surplus = sweepableSurplus(kind, asset, id);
moved = amount == type(uint256).max ? surplus : amount;
if (moved == 0) revert SweepZeroAmount();
if (moved > surplus) revert SweepAboveSurplus(asset, moved, surplus);
if (kind == SweepKind.Native) {
(bool ok,) = payable(to).call{value: moved}("");
if (!ok) revert SweepTransferFailed(address(0));
} else if (kind == SweepKind.Erc20) {
_callToken(asset, abi.encodeWithSelector(0xa9059cbb, to, moved)); // transfer(address,uint256)
} else if (kind == SweepKind.Erc721) {
// `transferFrom`, not `safeTransferFrom`: a rescue must not fail
// because the treasury destination declines a hook. Which
// destination is legitimate is already decided above.
moved = 1;
_callToken(asset, abi.encodeWithSelector(0x23b872dd, address(this), to, id)); // transferFrom
} else {
_callToken(
asset,
abi.encodeWithSelector(0xf242432a, address(this), to, id, moved, "") // safeTransferFrom(...)
);
}
emit AssetSwept(kind, asset, to, id, moved);
}
// ───────────────────────────── receivers ─────────────────────────────
/// @notice Accept safe ERC-721 transfers, so one sent here is recoverable
/// rather than rejected at the door.
function onERC721Received(address, address, uint256, bytes calldata) external pure virtual returns (bytes4) {
return 0x150b7a02;
}
/// @notice Accept safe ERC-1155 single transfers.
function onERC1155Received(address, address, uint256, uint256, bytes calldata)
external
pure
virtual
returns (bytes4)
{
return 0xf23a6e61;
}
/// @notice Accept safe ERC-1155 batch transfers.
function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
external
pure
virtual
returns (bytes4)
{
return 0xbc197c81;
}
// ───────────────────────────── internals ─────────────────────────────
/// @dev The raw held amount, before anything owed is subtracted.
function _rawBalance(SweepKind kind, address asset, uint256 id) internal view returns (uint256) {
if (kind == SweepKind.Native) return address(this).balance;
if (kind == SweepKind.Erc20) {
(bool ok, bytes memory ret) = asset.staticcall(abi.encodeWithSelector(0x70a08231, address(this)));
return (ok && ret.length >= 32) ? abi.decode(ret, (uint256)) : 0;
}
if (kind == SweepKind.Erc721) {
(bool ok, bytes memory ret) = asset.staticcall(abi.encodeWithSelector(0x6352211e, id)); // ownerOf
return (ok && ret.length >= 32 && abi.decode(ret, (address)) == address(this)) ? 1 : 0;
}
(bool ok1155, bytes memory ret1155) =
asset.staticcall(abi.encodeWithSelector(0x00fdd58e, address(this), id)); // balanceOf(address,uint256)
return (ok1155 && ret1155.length >= 32) ? abi.decode(ret1155, (uint256)) : 0;
}
/// @dev One transfer leg, tolerant of the legacy no-return ERC-20 shape the
/// way `FinalDeployer`'s rescue helpers are: success is "the call did not
/// revert AND it did not return `false`".
function _callToken(address token, bytes memory data) private {
if (token.code.length == 0) revert SweepTransferFailed(token);
(bool ok, bytes memory ret) = token.call(data);
if (!ok || (ret.length != 0 && !abi.decode(ret, (bool)))) revert SweepTransferFailed(token);
}
}
abi
[
{
"type": "constructor",
"inputs": [
{
"name": "admin",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "DOMAIN_IDENTITY_ADMISSION",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "DOMAIN_ISSUER_CERT_REVOCATION",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "DOMAIN_REGISTER_ISSUER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "DOMAIN_REGISTER_LMS_KEY",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "DOMAIN_REGISTER_WALLET",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "DOMAIN_REVOKE",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "DOMAIN_REVOKE_CERTIFICATE",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "DOMAIN_SET_REGISTRAR_THRESHOLD",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "DOMAIN_SET_ROLES",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "MAX_ISSUER_VALIDITY_MS",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_ACCOUNT_COSIGNER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_CERTIFICATE_AUTHORITY",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_GUARDIAN",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_MMR_COSIGNER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_ORACLE_PUBLISHER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_PHI_PUBLISHER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_REGISTRAR",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_REGISTRY_PUBLISHER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_RELAYER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_SETTLEMENT_COSIGNER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ROLE_VASSET_PUBLISHER",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "accountAt",
"inputs": [
{
"name": "index",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "accountCount",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "accountOfCertificate",
"inputs": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "accountOfSender",
"inputs": [
{
"name": "sender",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "accountsWithRole",
"inputs": [
{
"name": "roleMask",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "found",
"type": "address[]",
"internalType": "address[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "activeAccessKeyOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "activeSealKeyOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "activeTransactionKeyOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "bootstrapAdmin",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "bootstrapSealed",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "certificateRevoked",
"inputs": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "certificateRevokedBy",
"inputs": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "gateNonceOf",
"inputs": [
{
"name": "caller",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "hasRole",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "roleMask",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "identityOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalIdentityRegistry.Identity",
"components": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "recoveryCertHash",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "serial",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "subjectKeyId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "depth",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "maxDelegationDepth",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "notBefore",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "notAfter",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "version",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "revoked",
"type": "bool",
"internalType": "bool"
},
{
"name": "registered",
"type": "bool",
"internalType": "bool"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "identityTreeLeafOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "isActive",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "kemCommitments",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "liveKem",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "recoveryKem",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "kemKeysOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "activeMlKem",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "activeHqc",
"type": "bytes",
"internalType": "bytes"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "keyCommitments",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "liveAccess",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "liveTransaction",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "recoveryAccess",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "recoveryTransaction",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "liveMemberCount",
"inputs": [
{
"name": "roleMask",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "live",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "lmsBindingOf",
"inputs": [
{
"name": "signerId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "chainId",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "lmsKeyOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "chainId",
"type": "uint64",
"internalType": "uint64"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalIdentityRegistry.LmsKey",
"components": [
{
"name": "keyId",
"type": "bytes16",
"internalType": "bytes16"
},
{
"name": "height",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "root",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "version",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "registered",
"type": "bool",
"internalType": "bool"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "lmsSignerId",
"inputs": [
{
"name": "keyId",
"type": "bytes16",
"internalType": "bytes16"
},
{
"name": "height",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "root",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "lmsSignerIsLive",
"inputs": [
{
"name": "signerId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "live",
"type": "bool",
"internalType": "bool"
},
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "onERC1155BatchReceived",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
},
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
},
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "bytes4",
"internalType": "bytes4"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "onERC1155Received",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "bytes4",
"internalType": "bytes4"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "onERC721Received",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "bytes4",
"internalType": "bytes4"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "recoveryAccessKeyOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "recoveryTransactionKeyOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "registerIssuer",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "tbs",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "parent",
"type": "address",
"internalType": "address"
},
{
"name": "proof",
"type": "tuple",
"internalType": "struct FinalIdentityRegistry.AdmissionProof",
"components": [
{
"name": "mlDsaSignature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "slhDsaSignature",
"type": "bytes",
"internalType": "bytes"
}
]
},
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "version",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "approvals",
"type": "tuple[]",
"internalType": "struct FinalPqQuorum.Approval[]",
"components": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "seal",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "registerLmsKey",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "chainId",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "keyId",
"type": "bytes16",
"internalType": "bytes16"
},
{
"name": "height",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "root",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "version",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "approvals",
"type": "tuple[]",
"internalType": "struct FinalPqQuorum.Approval[]",
"components": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "seal",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "registerWallet",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "liveTbs",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "recoveryTbs",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "proof",
"type": "tuple",
"internalType": "struct FinalIdentityRegistry.AdmissionProof",
"components": [
{
"name": "mlDsaSignature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "slhDsaSignature",
"type": "bytes",
"internalType": "bytes"
}
]
},
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "version",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "approvals",
"type": "tuple[]",
"internalType": "struct FinalPqQuorum.Approval[]",
"components": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "seal",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "registrarThreshold",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "requireRegistrarQuorum",
"inputs": [
{
"name": "actionDomain",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "payloadDigest",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "approvals",
"type": "tuple[]",
"internalType": "struct FinalPqQuorum.Approval[]",
"components": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "seal",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "revocationLog",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "revoke",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "chainIds",
"type": "uint64[]",
"internalType": "uint64[]"
},
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "approvals",
"type": "tuple[]",
"internalType": "struct FinalPqQuorum.Approval[]",
"components": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "seal",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "revokeCertificate",
"inputs": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "approvals",
"type": "tuple[]",
"internalType": "struct FinalPqQuorum.Approval[]",
"components": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "seal",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "revokeIssuedCertificate",
"inputs": [
{
"name": "issuer",
"type": "address",
"internalType": "address"
},
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "proof",
"type": "tuple",
"internalType": "struct FinalIdentityRegistry.AdmissionProof",
"components": [
{
"name": "mlDsaSignature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "slhDsaSignature",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "sealBootstrap",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "sealableMemberCount",
"inputs": [
{
"name": "roleMask",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "sealable",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "senderFor",
"inputs": [
{
"name": "transactionKey",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "senderHasRole",
"inputs": [
{
"name": "sender",
"type": "address",
"internalType": "address"
},
{
"name": "roleMask",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "senderOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "setRegistrarThreshold",
"inputs": [
{
"name": "threshold",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "approvals",
"type": "tuple[]",
"internalType": "struct FinalPqQuorum.Approval[]",
"components": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "seal",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "setRoles",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "approvals",
"type": "tuple[]",
"internalType": "struct FinalPqQuorum.Approval[]",
"components": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "seal",
"type": "bytes",
"internalType": "bytes"
}
]
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "stateTrees",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "sweepAsset",
"inputs": [
{
"name": "kind",
"type": "uint8",
"internalType": "enum SweepKind"
},
{
"name": "asset",
"type": "address",
"internalType": "address"
},
{
"name": "id",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "to",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "moved",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "sweepableSurplus",
"inputs": [
{
"name": "kind",
"type": "uint8",
"internalType": "enum SweepKind"
},
{
"name": "asset",
"type": "address",
"internalType": "address"
},
{
"name": "id",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "surplus",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "wireStatePlane",
"inputs": [
{
"name": "stateTrees_",
"type": "address",
"internalType": "address"
},
{
"name": "revocationLog_",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "event",
"name": "AssetSwept",
"inputs": [
{
"name": "kind",
"type": "uint8",
"indexed": true,
"internalType": "enum SweepKind"
},
{
"name": "asset",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "to",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "id",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "BootstrapSealed",
"inputs": [
{
"name": "sealedBy",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"type": "event",
"name": "CertificateRevoked",
"inputs": [
{
"name": "certHash",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "revoker",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"type": "event",
"name": "IdentityRegistered",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "certHash",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "roles",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "version",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "IdentityRevoked",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "certHash",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
}
],
"anonymous": false
},
{
"type": "event",
"name": "IdentityRolesChanged",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "previousRoles",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "newRoles",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "LmsKeyRegistered",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "signerId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "chainId",
"type": "uint64",
"indexed": true,
"internalType": "uint64"
},
{
"name": "keyId",
"type": "bytes16",
"indexed": false,
"internalType": "bytes16"
},
{
"name": "height",
"type": "uint8",
"indexed": false,
"internalType": "uint8"
},
{
"name": "root",
"type": "bytes32",
"indexed": false,
"internalType": "bytes32"
},
{
"name": "version",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "RegistrarQuorumApproved",
"inputs": [
{
"name": "verifyingContract",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "actionDomain",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "nonce",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
},
{
"name": "valid",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "RegistrarThresholdSet",
"inputs": [
{
"name": "threshold",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "StatePlaneWired",
"inputs": [
{
"name": "stateTrees",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "revocationLog",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"type": "error",
"name": "AdmissionProofInvalid",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "AnchorAhead",
"inputs": [
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "blockNumber",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "AnchorStale",
"inputs": [
{
"name": "anchorBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "blockNumber",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "AuthorityKeyIdMismatch",
"inputs": [
{
"name": "got",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "want",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "BadSeal",
"inputs": [
{
"name": "signer",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "BadSignature",
"inputs": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "BootstrapAlreadySealed",
"inputs": []
},
{
"type": "error",
"name": "CertificateAlreadyBound",
"inputs": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "boundTo",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "CertificateIsRevoked",
"inputs": [
{
"name": "certHash",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "DelegationWidened",
"inputs": [
{
"name": "child",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "issuer",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "IssuerCannotSign",
"inputs": [
{
"name": "depth",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "maxDelegationDepth",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "IssuerMayNotSign",
"inputs": [
{
"name": "issuer",
"type": "address",
"internalType": "address"
},
{
"name": "depth",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "maxDelegationDepth",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "IssuerMustExpire",
"inputs": []
},
{
"type": "error",
"name": "IssuerNotACertificateAuthority",
"inputs": [
{
"name": "issuer",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "IssuerValidityTooLong",
"inputs": [
{
"name": "notBefore",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "notAfter",
"type": "uint64",
"internalType": "uint64"
}
]
},
{
"type": "error",
"name": "JurisdictionMismatch",
"inputs": []
},
{
"type": "error",
"name": "JurisdictionMissing",
"inputs": []
},
{
"type": "error",
"name": "LmsHeightOutOfRange",
"inputs": [
{
"name": "height",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "LmsKeyAlreadyBound",
"inputs": [
{
"name": "signerId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "boundTo",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "LmsRootIsZero",
"inputs": []
},
{
"type": "error",
"name": "MalformedEncapsulationKey",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "algorithmId",
"type": "uint16",
"internalType": "uint16"
}
]
},
{
"type": "error",
"name": "NotAnEndEntity",
"inputs": [
{
"name": "depth",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "maxDelegationDepth",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "NotAuthorized",
"inputs": [
{
"name": "caller",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "NotChainAttested",
"inputs": [
{
"name": "authorityKeyId",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "PrecompileUnavailable",
"inputs": [
{
"name": "precompile",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "RegistrarThresholdIsZero",
"inputs": []
},
{
"type": "error",
"name": "RegistrarThresholdUnreachable",
"inputs": [
{
"name": "sealable",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "threshold",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "SenderAlreadyBound",
"inputs": [
{
"name": "sender",
"type": "address",
"internalType": "address"
},
{
"name": "boundTo",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "SignerLacksRole",
"inputs": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "roleMask",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "SignersNotAscending",
"inputs": [
{
"name": "previous",
"type": "address",
"internalType": "address"
},
{
"name": "next",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "StagesDisagree",
"inputs": [
{
"name": "liveSerial",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "recoverySerial",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "StatePlaneAlreadyWired",
"inputs": []
},
{
"type": "error",
"name": "SweepAboveSurplus",
"inputs": [
{
"name": "asset",
"type": "address",
"internalType": "address"
},
{
"name": "requested",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "surplus",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "SweepDestinationNotAllowed",
"inputs": [
{
"name": "to",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "SweepTransferFailed",
"inputs": [
{
"name": "asset",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "SweepUnauthorized",
"inputs": [
{
"name": "caller",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "SweepZeroAmount",
"inputs": []
},
{
"type": "error",
"name": "ThresholdIsZero",
"inputs": []
},
{
"type": "error",
"name": "ThresholdNotMet",
"inputs": [
{
"name": "valid",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "required",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "UnknownAccount",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "VersionNotNewer",
"inputs": [
{
"name": "current",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "offered",
"type": "uint64",
"internalType": "uint64"
}
]
},
{
"type": "error",
"name": "WrongAlgorithm",
"inputs": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "got",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "required",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "WrongDepth",
"inputs": [
{
"name": "got",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "want",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "WrongIssuerDn",
"inputs": [
{
"name": "issuerDnHash",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "ZeroStatePlane",
"inputs": []
}
]read contract
bytecode · 25,865 bytes
0x6080806040526004361015610012575f80fd5b5f905f3560e01c9081630408a87f14613f1f5750806304525da814613ef75780630d641df014613e2b5780631479bc3a14613d55578063150b7a0214613cff57806315ba3c1d14613ce45780631d4f107714613ba65780631f394fb314613b715780631f9dc0bb14613989578063209f80441461396e578063222e6f0e1461393f578063228bf014146139245780632741cb02146136fd5780632ca02983146136535780633222383e1461361b57806332596d71146135fe578063342f61631461358c57806345e7e88e14613543578063461de6c114613509578063488429d9146134cf5780634c4af1a41461313e5780634ccf0005146130a55780634f8d3216146130315780635a232a3914612ff75780635b88250114612ec45780635c97f4a214612e9d57806360a1800814612e6957806361dbd45214612e2f57806364d2218d14612e1057806370f64f0a14612dc557806377e223b014612daa5780638673175514612d705780638c6820a914611e0d5780638cfc6aa014611ddb57806390ecddbf14611d9a578063926590d614611cd95780639433876514611c9757806396f51f3a14611a2e5780639aae87ad146119f45780639c11e31b146119d95780639d07ae09146118b15780639e5adaeb146118795780639f8a13d71461184c578063a0c176c414611827578063a760c295146117f5578063a874eea5146117da578063ad20445d146117c0578063ad84ad1314611788578063b0538d5a1461176d578063b16abc6c14611735578063b3c26283146116d4578063b7af85d714611684578063baadbb321461164a578063bc197c81146115b2578063c071672914611596578063c177a97e14611573578063c5768fe41461154b578063c634562614611388578063cbe6ebb414610704578063ce092d1c146106e7578063de043e95146106ac578063e0cd096a14610680578063e14c465b14610663578063e4af29fc14610645578063e51442e31461060d578063ec964baa14610542578063ef23f9f814610501578063f23a6e61146104a6578063f5778b031461047d578063fce072f8146104205763ffd326f31461032b575f80fd5b3461041d57604036600319011261041d576001600160401b03604061034e614021565b92610357613f57565b9381608084516103668161409e565b8281528260208201528286820152826060820152015260018060a01b03168152806020522091165f5260205260a060405f206040516103a48161409e565b6001600160401b038254916001600160801b03198360801b169384825260ff602083019460801c16845260ff60026001830154926040850193845201549481608060608601958789168752019660401c161515865260405196875251166020860152516040850152511660608301525115156080820152f35b80fd5b503461041d57602036600319011261041d57600435906001600160401b03821161041d573660238301121561041d57602061046b61046636600486013560248701614111565b614bd8565b6040516001600160a01b039091168152f35b503461041d578060031936011261041d576010546040516001600160a01b039091168152602090f35b503461041d5760a036600319011261041d576104c0614021565b506104c9614037565b506084356001600160401b0381116104fd576104e990369060040161404d565b505060405163f23a6e6160e01b8152602090f35b5080fd5b503461041d57602036600319011261041d576020906001600160a01b03610526614021565b16815260158252604060018060a01b0391205416604051908152f35b503461041d578060031936011261041d576010546001600160a01b03811633036105fa5760ff8160a01c166105eb5760135480156105dc57610582614394565b8181106105c65750506001600160a81b031916600160a01b17601055337fdd5280e26bdef754c085fd1fe40c0ab76df98368bb6014f85b49f8852a81be578280a280f35b6304951c2160e11b845260045260245250604490fd5b631de31ea560e11b8352600483fd5b630f80bc0560e31b8252600482fd5b634a0bfec160e01b825233600452602482fd5b503461041d57602036600319011261041d57602061062c600435614190565b905460405160039290921b1c6001600160a01b03168152f35b503461041d578060031936011261041d576020600f54604051908152f35b503461041d578060031936011261041d5760206040516101008152f35b503461041d57602036600319011261041d5760206106a461069f614021565b614980565b604051908152f35b503461041d578060031936011261041d5760206040517f664b0e72d3e91e88f8d9d2c0d916d51aff408110b8760394f6d677a5e6b24a2e8152f35b503461041d578060031936011261041d5760206040516102008152f35b34611384576101003660031901126113845761071e614021565b6024356001600160401b0381116113845761073d90369060040161404d565b90916044356001600160401b0381116113845761075e90369060040161404d565b606492919235936001600160401b038511611384578460040193604060031987360301126113845760843595610792613f83565b9561079b613f99565b9360e435956001600160401b0387116113845789966107be903690600401613faf565b929096305f52601460205260405f20546001600160401b0316938d8336906107e592614111565b80519060200120988b8d366107fb90888d614111565b805190602001209c604051809e6020820194600160a01b60019003169e8f865260408301526060820152608001526001600160401b03169b8c60a082015260a0815261084860c0826140d5565b5190209261085593614e35565b735a7be3e057b3cf85dbf4e8d79208c6a5343e9c498091604051809e81926325f1701d60e21b8352600483019161088b926147ab565b03815a935f94f4958615611357576108c59c5f97611362575b50905f9291604051809e8194829363b389b84f60e01b8452600484016147ab565b03915af4998a15611357575f9a611333575b506020840191825160208c01519081810361131e5750506108f78561597f565b6109008b61597f565b8a518551906040519060208201927f6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f81688452466040840152306060840152608083015260a08201528360c082015260c0815261095c60e0826140d5565b51902093604051946020860152602085526109786040866140d5565b61016086019461099e865182610998610991878061428a565b3691614111565b91615009565b159283156112f3575b5050506112e057305f526014602052806001600160401b0360405f205416146112ac575b5082519684156112995783515f52600d60205260ff60405f2054166112855783515f908152600c60205260409020546001600160a01b03168015158061127b575b6112655750845f52600260205260405f206005810192835460ff8160d81c16155f146111e3575090600f5490600160401b821015610ee2578b92610a808b610a5c85600160049701600f55614190565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b855460ff60d81b1916600160d81b1786555b875182558d51600183015551600282015560c08701516003820155015560e084015182546101008601516101208701516101408801516001600160d01b031990931660ff949094169390931760089190911b61ff00161760109290921b69ffffffffffffffff0000169190911760509190911b67ffffffffffffffff60501b161760909190911b67ffffffffffffffff60901b1617905580516001600160a01b0390610b3d90614bd8565b165f818152601560205260409020546001600160a01b0316801515806111d9575b6111c35750835f526003602052610b7860405f20546142bc565b611175575b5f52601560205260405f20836001600160601b0360a01b82541617905551825f52600360205260405f20908051906001600160401b038211610ee257610bc383546142bc565b601f811161113b575b50602090601f83116001146110d857610bfc92915f9183610ef6575b50508160011b915f199060031b1c19161790565b90555b610180810151825f52600460205260405f20908051906001600160401b038211610ee257610c2d83546142bc565b601f811161109e575b50602090601f831160011461103b57610c6592915f9183610ef65750508160011b915f199060031b1c19161790565b90555b610160870151825f52600560205260405f20908051906001600160401b038211610ee257610c9683546142bc565b601f8111611001575b50602090601f8311600114610f9e57610cce92915f9183610ef65750508160011b915f199060031b1c19161790565b90555b610180870151825f52600660205260405f20908051906001600160401b038211610ee257610cff83546142bc565b601f8111610f64575b50602090601f8311600114610f0157610d3792915f9183610ef65750508160011b915f199060031b1c19161790565b90555b6101e081015196825f52600760205260405f2088516001600160401b038111610ee257602099610d6a83546142bc565b601f8111610ea1575b508a90601f8311600114610e2457936106a4999a93610dbd845f5160206164c95f395f51905f529895610deb956040995f92610e195750508160011b915f199060031b1c19161790565b90555b610dd76101a08401516101c0850151905f8c615aba565b6101c06101a0820151910151905f8a615d7e565b80515f52600c8a52825f20856001600160601b0360a01b8254161790555194825191825289820152a3614eb2565b015190505f80610be8565b90601f19831691845f52815f20925f5b818110610e8a575084610deb94604098946106a49e9f98945f5160206164c95f395f51905f529b9860019510610e72575b505050811b019055610dc0565b01515f1960f88460031b161c191690558f8080610e65565b92938e600181928786015181550195019301610e34565b82811115610d7357610ed490845f528c5f2090601f850160051c908e8610610eda575b601f82910160051c03910161587a565b8b610d73565b5f9150610ec4565b634e487b7160e01b5f52604160045260245ffd5b015190508b80610be8565b90601f19831691845f52815f20925f5b818110610f4c5750908460019594939210610f34575b505050811b019055610d3a565b01515f1960f88460031b161c191690558a8080610f27565b92936020600181928786015181550195019301610f11565b82811115610d0857610f9890845f5260205f2090601f850160051c9060208610610eda57601f82910160051c03910161587a565b8a610d08565b90601f19831691845f52815f20925f5b818110610fe95750908460019594939210610fd1575b505050811b019055610cd1565b01515f1960f88460031b161c191690558a8080610fc4565b92936020600181928786015181550195019301610fae565b82811115610c9f5761103590845f5260205f2090601f850160051c9060208610610eda57601f82910160051c03910161587a565b8a610c9f565b90601f19831691845f52815f20925f5b818110611086575090846001959493921061106e575b505050811b019055610c68565b01515f1960f88460031b161c191690558a8080611061565b9293602060018192878601518155019501930161104b565b82811115610c36576110d290845f5260205f2090601f850160051c9060208610610eda57601f82910160051c03910161587a565b8a610c36565b90601f19831691845f52815f20925f5b818110611123575090846001959493921061110b575b505050811b019055610bff565b01515f1960f88460031b161c191690558a80806110fe565b929360206001819287860151815501950193016110e8565b82811115610bcc5761116f90845f5260205f2090601f850160051c9060208610610eda57601f82910160051c03910161587a565b8a610bcc565b835f52600360205260018060a01b0361119361046660405f206142f4565b168181036111a2575b50610b7d565b5f52601560205260405f206001600160601b0360a01b81541690558961119c565b906313362de360e01b5f5260045260245260445ffd5b5084811415610b5e565b6001600160401b038160901c16808a111561124e575060d01c60ff1661123a57816004918c935488518103611219575b50610a92565b5f52600c60205260405f206001600160601b0360a01b81541690558e611213565b505463e41b98f760e01b5f5260045260245ffd5b899063df5abcab60e01b5f5260045260245260445ffd5b8451623bdaab60e51b5f5260045260245260445ffd5b5085811415610a0c565b835163e41b98f760e01b5f5260045260245ffd5b84633131bf7960e21b5f5260045260245ffd5b6112b59061426c565b305f5260146020526001600160401b0360405f2091166001600160401b0319825416179055896109cb565b84639ae9f73b60e01b5f5260045260245ffd5b611315935061099161130f9160246101808b015195019061428a565b916150b0565b158b80806109a7565b6388e0872960e01b5f5260045260245260445ffd5b611350919a503d805f833e61134881836140d5565b8101906145db565b988a6108d7565b6040513d5f823e3d90fd5b5f939291975061137b903d8086833e61134881836140d5565b969091926108a4565b5f80fd5b34611384576020366003190112611384576113a1614021565b6040516113ad816140b9565b5f8152602081015f9052604081015f9052606081015f9052608081015f905260a081015f905260c081015f905260e081015f905261010081015f905261012081015f905261014081015f9052610160015f9052600160a01b60019003165f52600260205260405f2060405190611422826140b9565b80549182815260018201546020820190815260028301546040830190815260038401546060840190815260048501549460808501958652600501549460a0850160ff8716815260c08601918760081c60ff16835260e08701938860101c6001600160401b031685526101008801958960501c6001600160401b031687526101208901978a60901c6001600160401b031689526101408a01998b60d01c60ff1615158b52610160019a60d81c60ff1615158b526040519b8c525160208c01525160408b01525160608a01525160808901525160ff1660a08801525160ff1660c0870152516001600160401b031660e0860152516001600160401b0316610100850152516001600160401b031661012084015251151561014083015251151561016082015261018090f35b34611384575f366003190112611384576012546040516001600160a01b039091168152602090f35b3461138457602036600319011261138457602061046b611591614021565b6148b9565b34611384575f3660031901126113845760206040516104008152f35b346113845760a0366003190112611384576115cb614021565b506115d4614037565b506044356001600160401b038111611384576115f4903690600401613faf565b50506064356001600160401b03811161138457611615903690600401613faf565b50506084356001600160401b0381116113845761163690369060040161404d565b505060405163bc197c8160e01b8152602090f35b34611384575f3660031901126113845760206040517f6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f81688152f35b34611384576020366003190112611384576001600160a01b036116a5614021565b165f5260036020526116d06116bc60405f206142f4565b60405191829160208352602083019061407a565b0390f35b34611384576040366003190112611384576001600160a01b036116f5614021565b165f526015602052602060018060a01b0360405f2054168015159081611721575b506040519015158152f35b61172f9150602435906144ca565b82611716565b34611384576020366003190112611384576001600160a01b03611756614021565b165f5260056020526116d06116bc60405f206142f4565b34611384575f36600319011261138457602060405160108152f35b34611384576020366003190112611384576001600160a01b036117a9614021565b165f5260076020526116d06116bc60405f206142f4565b34611384575f366003190112611384576020604051818152f35b34611384575f36600319011261138457602060405160028152f35b34611384576020366003190112611384576004355f52600c602052602060018060a01b0360405f205416604051908152f35b34611384575f36600319011261138457602060ff60105460a01c166040519015158152f35b3461138457602036600319011261138457602061186f61186a614021565b61486b565b6040519015158152f35b34611384576020366003190112611384576001600160a01b0361189a614021565b165f5260046020526116d06116bc60405f206142f4565b34611384576080366003190112611384576118ca614021565b602435906118d6613f6d565b606435906001600160401b038211611384576118f9611936923690600401613faf565b604080516001600160a01b0387166020820190815291810188905291939161192e81606081015b03601f1981018352826140d5565b519020614ddd565b60018060a01b038116805f52600260205260405f2092600584015460ff8160d81c16156119c65760d01c60ff166119b25760407ffea1c9b6eea77fb9c209307935fb32ed2f3299cb9c639571480c67b17929807f9160046119b096019080825492556119a0614e8d565b82519182526020820152a2614eb2565b005b835463e41b98f760e01b5f5260045260245ffd5b82633131bf7960e21b5f5260045260245ffd5b34611384575f36600319011261138457602060405160088152f35b34611384575f3660031901126113845760206040517fc1f01606bfc0d9242f3def839a8589d5b92fe65d09df4befd80f0439f60344cc8152f35b346113845760a036600319011261138457600435600481101561138457611a53614037565b90606435906084356001600160a01b038116916044359183810361138457611a79615895565b6010548415906001600160a01b03168115611c7c575b50611c6957611a9f838784614527565b945f198103611c645750845b80958115611c5557808211611c2e57505f9183611b4c5750505f80808088885af1611ad461483c565b5015611b39575b611b2557604080519283526020838101869052956001600160a01b0316927f7643c83e539cea2f6bf506545392e52cfd5f917e327efbcd0ba28f29c28d042e9190a4604051908152f35b634e487b7160e01b5f52602160045260245ffd5b6365f4a9ef60e11b5f525f60045260245ffd5b5f92509060018403611b9c575060405163a9059cbb60e01b60208201526001600160a01b03909116602482015260448101869052611b9790611b918160648101611920565b876158df565b611adb565b5f969250905060028303611be3575050600193611b976040516323b872dd60e01b602082015230602482015285604482015284606482015260648152611b916084826140d5565b611b979060409692965190637921219560e11b6020830152306024830152866044830152856064830152608482015260a060a48201525f60c482015260c48152611b9160e4826140d5565b632190968160e01b5f9081526001600160a01b038916600452602492909252604452606490fd5b637c2e506f60e11b5f5260045ffd5b611aab565b836315150d4d60e31b5f5260045260245ffd5b905084141580611c8d575b87611a8f565b5033841415611c87565b34611384576020366003190112611384576004355f5260016020526040805f20546001600160401b0382519160018060a01b038116835260a01c166020820152f35b346113845760203660031901126113845760406001600160a01b03611cfc614021565b16805f526008602052815f20815f526009602052611920611d45845f208551928391611d3f60208401965f5160206164e95f395f51905f528852898501906147d2565b906147d2565b51902090805f52600a602052825f20905f52600b602052611920611d8b845f208551928391611d3f60208401965f5160206164e95f395f51905f528852898501906147d2565b51902082519182526020820152f35b34611384576020366003190112611384576001600160a01b03611dbb614021565b165f52601460205260206001600160401b0360405f205416604051908152f35b34611384576020366003190112611384576004355f52600e602052602060018060a01b0360405f205416604051908152f35b346113845761010036600319011261138457611e27614021565b6024356001600160401b03811161138457611e4690369060040161404d565b90916044356001600160a01b038116929183820361138457606435936001600160401b038511611384578460040192604060031987360301126113845760843593611e8f613f83565b611e97613f99565b9260e4356001600160401b03811161138457611eb7903690600401613faf565b989094305f52601460205260405f20546001600160401b0316998c833690611ede92614111565b80519060200120966040519860208a0190600160a01b600190038c16998a835260408c015260608b01528b60808b01526001600160401b038716998a60a082015260a08152611f2e60c0826140d5565b51902092611f3b93614d85565b604051633ba294e360e01b81529a8b91611f5891600484016147ab565b038a735a7be3e057b3cf85dbf4e8d79208c6a5343e9c4991815a935f94f4998a15611357575f9a612d54575b5060e08a019160ff8351168015818d8215612d41575b5050612d2257506101408b01986001600160401b038a511615612d13576001600160401b038a5116926101208d01936001600160401b0385511690036001600160401b038111612bd7576001600160401b03640eb9af1000911611612ced5760408d01517fa5f1406585e87ef6c3645d6270cf45d62ae1c5d1abf447614cd94657a3f429a08103612cdb57508c61203091615693565b60608c01519a5f9b5f5f5b60048101808211612bd75783518111612cc55781158015612c97575b80612c76575b80612c48575b6120765750612071906141a8565b61203b565b91509192939495969798999a9b9c9d50825181109081612c23575b50612c005760028101808211612bd7576001600160f81b0319906120b59084615801565b511660038201809211612bd7576120e5926120d86120e09360ff60f81b92615801565b511690615812565b61583d565b60015b15612c005760808d01516006815110612c0057805115612c0f5761ff0080602083015160f01c1616815160011015612c0f57602182015160f81c17806002019081600211612bd75782516004820190818411612bd75710612c005761ff00806121518486615801565b5160f01c161660038201808411612bd75761216c9085615801565b5160f81c179081600201600211612bd75701600401908110612bd7578151906002810191828211612bd7578211612c005761ff00806121ab8386615801565b5160f01c16166001820190818311612bd7576121c960029286615801565b5160f81c1714801590612beb575b612bc85760036001600160f81b03196121f08486615801565b51169101809211612bd757612211926120d86120e09360ff60f81b92615801565b6001600160f01b0319918216911603612bc8578b90815160405160208101917f6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f8168835246604083015230606083015260808201525f60a08201528460c082015260c0815261227f60e0826140d5565b5190206040519060208201526020815261229a6040826140d5565b6101608301976122b3895183610998610991858061428a565b15938415612ba2575b50505050612b8f57305f526014602052806001600160401b0360405f20541614612b5b575b506102008a519817988515612b48578a515f52600d60205260ff60405f205416612b34578a515f908152600c60205260409020546001600160a01b031680151580612b2a575b612b145750855f52600260205260405f20926005840194855460ff8160d81c16155f14612a885750600f5490600160401b821015610ee2578c60048f9761237a8e610a5c87600160ff9901600f55614190565b895460d886901b1916600160d81b178a555b88518155885160018201556020890151600282015560c08901516003820155015551169069ffffffffffffffff000061ff00610100885497015160081b16915160101b16926001600160401b0360501b905160501b16936001600160401b0360901b9060901b16946001600160401b0360901b19926001600160401b0360501b199169ffffffffffffffffffff191617161716171717905560018060a01b036124358251614bd8565b165f818152601560205260409020546001600160a01b031680151580612a7e575b6111c35750825f52600360205261247060405f20546142bc565b612a30575b5f52601560205260405f20826001600160601b0360a01b82541617905551815f52600360205260405f20908051906001600160401b038211610ee2576124bb83546142bc565b601f81116129f6575b50602090601f8311600114612993576124f392915f91836128eb5750508160011b915f199060031b1c19161790565b90555b610180860151815f52600460205260405f20908051906001600160401b038211610ee25761252483546142bc565b601f8111612959575b50602090601f83116001146128f65761255c92915f91836128eb5750508160011b915f199060031b1c19161790565b90555b60209560405161256f88826140d5565b5f8152825f526005885260405f20908051906001600160401b038211610ee25761259983546142bc565b601f81116128b3575b508990601f8311600114612850576125d092915f9183610ef65750508160011b915f199060031b1c19161790565b90555b6040516125e088826140d5565b5f8152825f526006885260405f20908051906001600160401b038211610ee25761260a83546142bc565b601f8111612818575b508990601f83116001146127b55761264192915f9183610ef65750508160011b915f199060031b1c19161790565b90555b60405161265188826140d5565b5f8152825f526007885260405f20908051906001600160401b038211610ee25761267b83546142bc565b601f811161277d575b508990601f831160011461270357926126ca836106a49a9b945f5160206164c95f395f51905f5297946040975f926126f85750508160011b915f199060031b1c19161790565b90555b610deb6101a082018051906126ec6101c085019283519060018d615aba565b5190519060018a615d7e565b015190508e80610be8565b90601f19831691845f528b5f20925f5b8d828210612767575050935f5160206164c95f395f51905f52969360409693600193836106a49e9f981061274f575b505050811b0190556126cd565b01515f1960f88460031b161c191690558d8080612742565b6001859682939686015181550195019301612713565b82811115612684576127af90845f528b5f2090601f850160051c908d8610610eda57601f82910160051c03910161587a565b8a612684565b90601f19831691845f528b5f20925f5b8d8282106128025750509084600195949392106127ea575b505050811b019055612644565b01515f1960f88460031b161c191690558a80806127dd565b60018596829396860151815501950193016127c5565b828111156126135761284a90845f528b5f2090601f850160051c908d8610610eda57601f82910160051c03910161587a565b8a612613565b90601f19831691845f528b5f20925f5b8d82821061289d575050908460019594939210612885575b505050811b0190556125d3565b01515f1960f88460031b161c191690558a8080612878565b6001859682939686015181550195019301612860565b828111156125a2576128e590845f528b5f2090601f850160051c908d8610610eda57601f82910160051c03910161587a565b8a6125a2565b015190508a80610be8565b90601f19831691845f52815f20925f5b8181106129415750908460019594939210612929575b505050811b01905561255f565b01515f1960f88460031b161c1916905589808061291c565b92936020600181928786015181550195019301612906565b8281111561252d5761298d90845f5260205f2090601f850160051c9060208610610eda57601f82910160051c03910161587a565b8961252d565b90601f19831691845f52815f20925f5b8181106129de57509084600195949392106129c6575b505050811b0190556124f6565b01515f1960f88460031b161c191690558980806129b9565b929360206001819287860151815501950193016129a3565b828111156124c457612a2a90845f5260205f2090601f850160051c9060208610610eda57601f82910160051c03910161587a565b896124c4565b825f52600360205260018060a01b03612a4e61046660405f206142f4565b16818103612a5d575b50612475565b5f52601560205260405f206001600160601b0360a01b815416905588612a57565b5083811415612456565b6001600160401b038160909793949596971c16808b1115612afd575060d01c60ff16612aea578c949392918c60048360ff945489518103612aca575b5061238c565b5f908152600c6020526040812080546001600160a01b0319169055612ac4565b5463e41b98f760e01b5f5260045260245ffd5b8a9063df5abcab60e01b5f5260045260245260445ffd5b8b51623bdaab60e51b5f5260045260245260445ffd5b5086811415612327565b8a5163e41b98f760e01b5f5260045260245ffd5b85633131bf7960e21b5f5260045260245ffd5b612b649061426c565b305f5260146020526001600160401b0360405f2091166001600160401b03198254161790558a6122e1565b85639ae9f73b60e01b5f5260045260245ffd5b612bbe945061130f91602461018061099193015195019061428a565b158c80808e6122bc565b63261ab3b160e21b5f5260045ffd5b634e487b7160e01b5f52601160045260245ffd5b5082516004820190818411612bd757106121d7565b6301c8a09760e41b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b600b60fa1b91506001600160f81b031990612c3e9085615801565b511614158f612091565b5060018201808311612bd757603d60f81b906001600160f81b031990612c6e9087615801565b511614612063565b50604360f81b6001600160f81b0319612c8f8487615801565b51161461205d565b505f198201828111612bd757600b60fa1b906001600160f81b031990612cbd9087615801565b511614612057565b505090509b909192939495969798999a9b6120e8565b63914a64c360e01b5f5260045260245ffd5b8a6001600160401b03808651169151169063950cff1d60e01b5f5260045260245260445ffd5b63ea0930b960e01b5f5260045ffd5b60ff6101008d01511690634f67a11560e01b5f5260045260245260445ffd5b610100015160ff1611159050818d611f9a565b612d69919a503d805f833e61134881836140d5565b988a611f84565b34611384575f3660031901126113845760206040517f617214620b6c0e190ece7e2c8e3b97354e56dc3501b8a6811dd7e742dc6637c08152f35b34611384575f36600319011261138457602060405160408152f35b34611384576060366003190112611384576004356fffffffffffffffffffffffffffffffff19811681036113845760243560ff81168103611384576020916106a4916044359161453b565b34611384575f366003190112611384576020604051640eb9af10008152f35b34611384575f3660031901126113845760206040517f9e01a06a8e87fa34b0b3b7f97e4f2ba5e105d644fac817f11f688ac916421fa28152f35b34611384576060366003190112611384576004356004811015611384576106a4602091612e94614037565b60443591614527565b3461138457604036600319011261138457602061186f612ebb614021565b602435906144ca565b3461138457604036600319011261138457612edd614021565b612ee5614037565b9060105460ff8160a01c16908115612fe3575b50612fd057601154906001600160a01b03821615801590612fbc575b612fad576001600160a01b031680158015612f9c575b612f8d577f518f647756fa8c0e3a5d52ac2a56a5c3d0d4296b6f72486107a91a8cc8d9a8cd92816040936001600160601b0360a01b161760115560018060a01b0316806001600160601b0360a01b601254161760125582519182526020820152a1005b632bd300bd60e21b5f5260045ffd5b506001600160a01b03831615612f2a565b63586ccc6160e11b5f5260045ffd5b506012546001600160a01b03161515612f14565b634a0bfec160e01b5f523360045260245ffd5b6001600160a01b0316331415905083612ef8565b34611384575f3660031901126113845760206040517f3dbb5a17e2dda6a97acc2c59c4b3d3368b94b07f1b32afec7987d57e37e4fd078152f35b34611384576020366003190112611384576130976001600160a01b03613055614021565b16805f52600860205260405f20905f5260096020526116d061308361307d60405f20936142f4565b926142f4565b60405193849360408552604085019061407a565b90838203602085015261407a565b3461138457602036600319011261138457600f545f90600435825b8281106130d257602084604051908152f35b6130db81614190565b905460039190911b1c6001600160a01b03166130f783826144ca565b9081613120575b5061310c575b6001016130c0565b926131186001916141a8565b939050613104565b90505f52600760205261313660405f20546142bc565b1515856130fe565b346113845761010036600319011261138457613158614021565b613160613f57565b906044356001600160801b0319811690818103611384576064359160ff83168084036113845760843595613192613f83565b9261319b613f99565b9560e4356001600160401b038111611384576131bb903690600401613faf565b6040989198519860208a01600160a01b600190038c16968782526001600160401b0316988960408d01528660608d01528860808d01528d60a08d01526001600160401b03169a8b60c082015260c0815261321660e0826140d5565b5190209261322393614d2d565b825f52600260205260405f2060058101548060d81c60ff16156112995760d01c60ff16612aea5750831580156134c5575b6134b25788156134a357825f525f60205260405f20855f5260205260405f20906002820154966001600160401b038816808a111561124e5750613298918b9161453b565b5f818152600160205260409020549096906001600160a01b031680151580613499575b613483575060401c60ff161561347b578060016132e792549101549060ff8160801c169060801b61453b565b955b886040516132f68161409e565b8381526001600160401b0380600260208401938985526040810195865260608101958787526080820195600187528a5f525f60205260405f208d5f5260205260405f20925160801c83549160ff60801b905160801b169170ffffffffffffffffffffffffffffffffff19161717825551600182015501935116166001600160401b031983541617825551151560ff60401b82549160401b169060ff60401b19161790556040519160408301918383106001600160401b03841117610ee257604092835284845260208085018881525f8a8152600183528590209551865491516001600160e01b03199092166001600160a01b03919091161760a09190911b67ffffffffffffffff60a01b161790945582519182529281019490945283019790975260608201969096526119b09583917f6ad0a6fc4d05a6986296bd82532c1c289660c306537630a1da2b80727d33a44f90608090a48115159081613470575b50613461575b50614eb2565b61346a90614f64565b8261345b565b905081141584613455565b505f956132e9565b87633e9d9ea960e11b5f5260045260245260445ffd5b50848114156132bb565b630211d2d560e21b5f5260045ffd5b836385f6e97760e01b5f5260045260245ffd5b5060188411613254565b34611384575f3660031901126113845760206040517ff6cc552646468a7983c31df0f953b1703ccadc1760ee2c78464c51e7c443171c8152f35b34611384575f3660031901126113845760206040517fcf993e293750106c3db70c784002b78c8fba024f618e4e3bb2748ef6a46321c28152f35b346113845760803660031901126113845761355c613f6d565b606435906001600160401b0382116113845761357f6119b0923690600401613faf565b916024356004353361512c565b3461138457602036600319011261138457600f545f90600435825b8281106135b957602084604051908152f35b6135dd826135c683614190565b905460039190911b1c6001600160a01b03166144ca565b6135ea575b6001016135a7565b926135f66001916141a8565b9390506135e2565b34611384575f366003190112611384576020601354604051908152f35b34611384576020366003190112611384576001600160a01b0361363c614021565b165f5260066020526116d06116bc60405f206142f4565b346113845760203660031901126113845760806001600160a01b03613676614021565b16805f52600460205261368b60405f206142f4565b6020815191012090805f5260036020526136a760405f206142f4565b6020815191012090805f5260066020526136c360405f206142f4565b60208151910120905f5260056020526136de60405f206142f4565b6020815191012091604051938452602084015260408301526060820152f35b3461138457606036600319011261138457613716614021565b602435604435916001600160401b038311611384578260040190604060031985360301126113845761374781614414565b15613904575f838152600e60205260409020546001600160a01b03166138f1576001600160a01b03165f818152601460205260409020549093906001600160401b03166137938161426c565b855f5260146020526001600160401b0360405f2091166001600160401b031982541617905560405160208101917f3dbb5a17e2dda6a97acc2c59c4b3d3368b94b07f1b32afec7987d57e37e4fd0783524660408301523060608301528660808301528560a083015260c082015260c0815261380f60e0826140d5565b519020906040519160208301526020825261382b6040836140d5565b845f52600360205261385b6138538361099860405f2061384b888061428a565b9490916142f4565b933691614111565b159283156138c1575b5050506138ae575f818152600e6020526040812080546001600160a01b031916841790557f1dbd7f0554a6447214abb6405781d5ee119037a7024fcb7fffd542c3c12cd7769080a3005b50639ae9f73b60e01b5f5260045260245ffd5b6138e89350613853929161384b61130f92885f526004602052602460405f2093019061428a565b15838080613864565b8263e41b98f760e01b5f5260045260245ffd5b633210722560e21b5f9081526001600160a01b0391909116600452602490fd5b34611384575f36600319011261138457602060405160048152f35b34611384576020366003190112611384576004355f52600d602052602060ff60405f2054166040519015158152f35b34611384575f36600319011261138457602060405160808152f35b34611384576080366003190112611384576139a2614021565b6024356001600160401b038111611384576139c1903690600401613faf565b6139c9613f6d565b6064356001600160401b038111611384576139e8903690600401613faf565b60409591955195602087019360608801948660018060a01b038616968783526040808c0152526080890198885f5b898110613b48575050613a3981613a419798999a9b03601f1981018352826140d5565b519020614cd5565b815f52600260205260405f2060058101805460ff8160d81c16156112995760ff60d01b1916600160d01b1790555f6004820181905581548152600d60205260409020805460ff19166001179055613a96614e8d565b54827f898185a9e0933bcd25b9f43bc589ab95570a5c4b75d013955435eaf290380d4c5f80a35f5b83811015613b3f57825f525f60205260405f20908060051b860135916001600160401b0383168303611384576001600160401b03600193165f5260205260405f2060ff600282015460401c16613b16575b5001613abe565b613b348184613b3993549101549060ff8160801c169060801b61453b565b614f64565b86613b0f565b6119b082614eb2565b909a8b35906001600160401b03821680920361138457602081600193829352019c019101613a16565b34611384576020366003190112611384576040613b8f6004356141ca565b825191151582526001600160a01b03166020820152f35b3461138457606036600319011261138457600435613bc2613f57565b906044356001600160401b038111611384575f92613be7613c0a923690600401613faf565b91604051602081019086825260208152613c026040826140d5565b519020614c7d565b808252600d60209081526040808420805460ff19166001179055828452600c9091528220546001600160a01b031680613c65575b507f1dbd7f0554a6447214abb6405781d5ee119037a7024fcb7fffd542c3c12cd7768280a3005b8083526002602052604083209060058201805460ff8160d01c1615613c8d575b505050613c3e565b60ff60d01b1916600160d01b1790556004909101839055613cdb90613cb0614e8d565b82817f898185a9e0933bcd25b9f43bc589ab95570a5c4b75d013955435eaf290380d4c8680a3614eb2565b82808080613c85565b34611384575f36600319011261138457602060405160018152f35b3461138457608036600319011261138457613d18614021565b50613d21614037565b506064356001600160401b03811161138457613d4190369060040161404d565b5050604051630a85bd0160e11b8152602090f35b3461138457602036600319011261138457600f54600435613d758261415e565b915f915f5b828110613dcd57505050613d8d8161415e565b915f5b828110613da557604051806116d08682613fdf565b6001906001600160a01b03613dba82856141b6565b5116613dc682876141b6565b5201613d90565b80613df083613ddd600194614190565b858060a01b0391549060031b1c166144ca565b613dfb575b01613d7a565b613e0481614190565b838060a01b0391549060031b1c16613e25613e1e876141a8565b96886141b6565b52613df5565b3461138457606036600319011261138457600435613e47613f57565b604435906001600160401b03821161138457613e6a613e8d923690600401613faf565b91604051602081019086825260208152613e856040826140d5565b519020614c1f565b8015613ee857613e9b614394565b818110613ed3577f08808b872c36595369a5004f16ab8777648833a654e6b43a1a7e36b081f2b32460208380601355604051908152a1005b6304951c2160e11b5f5260045260245260445ffd5b631de31ea560e11b5f5260045ffd5b34611384575f366003190112611384576011546040516001600160a01b039091168152602090f35b34611384575f36600319011261138457807f6c9c22b535529e02295bb8861d935a916462e2e1bb844c50ef1dd822975a61f060209252f35b602435906001600160401b038216820361138457565b604435906001600160401b038216820361138457565b60a435906001600160401b038216820361138457565b60c435906001600160401b038216820361138457565b9181601f84011215611384578235916001600160401b038311611384576020808501948460051b01011161138457565b60206040818301928281528451809452019201905f5b8181106140025750505090565b82516001600160a01b0316845260209384019390920191600101613ff5565b600435906001600160a01b038216820361138457565b602435906001600160a01b038216820361138457565b9181601f84011215611384578235916001600160401b038311611384576020838186019501011161138457565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60a081019081106001600160401b03821117610ee257604052565b61018081019081106001600160401b03821117610ee257604052565b90601f801991011681019081106001600160401b03821117610ee257604052565b6001600160401b038111610ee257601f01601f191660200190565b92919261411d826140f6565b9161412b60405193846140d5565b829481845281830111611384578281602093845f960137010152565b6001600160401b038111610ee25760051b60200190565b9061416882614147565b61417560405191826140d5565b8281528092614186601f1991614147565b0190602036910137565b600f54811015612c0f57600f5f5260205f2001905f90565b5f198114612bd75760010190565b8051821015612c0f5760209160051b010190565b5f818152600160205260409020546001600160a01b03811691908215614263576141f38361486b565b1561425c5760018060a01b0383165f525f6020526001600160401b038060405f209260a01c16165f5260205260405f209060ff600283015460401c16918261423a57505091565b614258919250600181549101549060ff8160801c169060801b61453b565b1491565b505f929050565b5050505f905f90565b6001600160401b036001911601906001600160401b038211612bd757565b903590601e198136030182121561138457018035906001600160401b0382116113845760200191813603831361138457565b90600182811c921680156142ea575b60208310146142d657565b634e487b7160e01b5f52602260045260245ffd5b91607f16916142cb565b9060405191825f825492614307846142bc565b8084529360018116908115614372575060011461432e575b5061432c925003836140d5565b565b90505f9291925260205f20905f915b81831061435657505090602061432c928201015f61431f565b602091935080600191548385890101520191019091849261433d565b90506020925061432c94915060ff191682840152151560051b8201015f61431f565b5f90600f545f5b8181106143a6575050565b6143af81614190565b905460039190911b1c6001600160a01b03166143cd610100826144ca565b90816143f6575b506143e2575b60010161439b565b926143ee6001916141a8565b9390506143da565b90505f52600760205261440c60405f20546142bc565b15155f6143d4565b6001600160a01b03165f908152600260205260409020600581015460d881901c60ff1615908115614465575b506144605761020080600483015416036144605761445d906154d7565b90565b505f90565b60ff915060d01c165f614440565b6001600160a01b03165f908152600260205260409020600581015460d881901c60ff16159081156144bc575b506144605761010080600483015416036144605761445d906154d7565b60ff915060d01c165f61449f565b81156145135760018060a01b03165f52600260205260405f2090600582015460ff8160d81c1615908115614519575b506145135780600483015416036144605761445d906154d7565b50505f90565b60ff915060d01c165f6144f9565b906145329291615542565b80156144605790565b9160ff6040519260208401946001600160801b031916855216604083015260608201526060815261456d6080826140d5565b51902090565b81601f820112156113845780519061458a826140f6565b9261459860405194856140d5565b8284526020838301011161138457815f9260208093018386015e8301015290565b519060ff8216820361138457565b51906001600160401b038216820361138457565b602081830312611384578051906001600160401b03821161138457019061022082820312611384576040519161022083018381106001600160401b03821117610ee25760405280518352602081015160208401526040810151604084015260608101516001600160401b0381116113845782614658918301614573565b606084015260808101516001600160401b038111611384578261467c918301614573565b608084015260a081015160a084015260c081015160c08401526146a160e082016145b9565b60e08401526146b361010082016145b9565b6101008401526146c661012082016145c7565b6101208401526146d961014082016145c7565b6101408401526101608101516001600160401b03811161138457826146ff918301614573565b6101608401526101808101516001600160401b0381116113845782614725918301614573565b6101808401526101a08101516001600160401b038111611384578261474b918301614573565b6101a08401526101c08101516001600160401b0381116113845782614771918301614573565b6101c08401526101e08101516001600160401b038111611384576102009261479a918301614573565b6101e0840152015161020082015290565b90918060409360208452816020850152848401375f828201840152601f01601f1916010190565b5f92918154916147e1836142bc565b926001811690811561482957506001146147fa57505050565b90919293505f5260205f205f905b8382106148155750500190565b600181602092548486015201910190614808565b60ff191683525050811515909102019150565b3d15614866573d9061484d826140f6565b9161485b60405193846140d5565b82523d5f602084013e565b606090565b6001600160a01b03165f908152600260205260409020600581015460d881901c60ff169190826148aa575b50816148a0575090565b61445d91506154d7565b60d01c60ff161591505f614896565b6001600160a01b03165f9081526003602052604090208054906148db826142bc565b1561451357604051915f916148ef826142bc565b80855291600181169081156149595750600114614919575b5050906104668161445d9303826140d5565b5f90815260208120939250905b80821061493f5750909150810160200161046682614907565b919260018160209254838588010152019101909291614926565b60ff191660208087019190915292151560051b850190920192506104669150839050614907565b6001600160a01b03165f81815260026020526040902060058101549160d883901c60ff1615614bc65760ff8360d01c168015614bb6575b614baf5760ff8360081c1660ff841603614b51576002919250805f52600860205260405f20815f526009602052611920614a1660405f20604051928391611d3f60208401965f5160206164e95f395f51905f52885260408501906147d2565b51902090805f52600a60205260405f20815f52600b602052611920614a6060405f20604051928391611d3f60208401965f5160206164e95f395f51905f52885260408501906147d2565b519020815f526004602052614a7760405f206142f4565b6020815191012092825f526003602052614a9360405f206142f4565b6020815191012092805f526006602052614aaf60405f206142f4565b60208151910120905f526005602052614aca60405f206142f4565b6020815191012090604051946020860196875260408601526060850152608084015260a083015260c082015260c08152614b0560e0826140d5565b519020910154906040519060208201927fcc25d3fea88291f95ddfb5590a6b760f02245a0e4ca7c0b69285c6cd26543afd8452604083015260608201526060815261456d6080826140d5565b5054906040519060208201927fa654ea8b191780d915fe370e2900652d600c179f2abeb8967e1b06ba97fe7094845260408301526001600160401b0360c01b9060301b1660608201525f60688201526068815261456d6088826140d5565b5050505f90565b50614bc0826154d7565b156149b7565b633131bf7960e21b5f5260045260245ffd5b60405190614c0f602183602080820194600160fa1b86528051918291018484015e81015f838201520301601f1981018452836140d5565b905190206001600160a01b031690565b92919060105460ff8160a01c16159081614c6a575b50614c645761432c937f6c9c22b535529e02295bb8861d935a916462e2e1bb844c50ef1dd822975a61f03061512c565b50505050565b6001600160a01b0316331490505f614c34565b92919060105460ff8160a01c16159081614cc2575b50614c645761432c937f617214620b6c0e190ece7e2c8e3b97354e56dc3501b8a6811dd7e742dc6637c03061512c565b6001600160a01b0316331490505f614c92565b92919060105460ff8160a01c16159081614d1a575b50614c645761432c937fc1f01606bfc0d9242f3def839a8589d5b92fe65d09df4befd80f0439f60344cc3061512c565b6001600160a01b0316331490505f614cea565b92919060105460ff8160a01c16159081614d72575b50614c645761432c937ff6cc552646468a7983c31df0f953b1703ccadc1760ee2c78464c51e7c443171c3061512c565b6001600160a01b0316331490505f614d42565b92919060105460ff8160a01c16159081614dca575b50614c645761432c937fcf993e293750106c3db70c784002b78c8fba024f618e4e3bb2748ef6a46321c23061512c565b6001600160a01b0316331490505f614d9a565b92919060105460ff8160a01c16159081614e22575b50614c645761432c937f9e01a06a8e87fa34b0b3b7f97e4f2ba5e105d644fac817f11f688ac916421fa23061512c565b6001600160a01b0316331490505f614df2565b92919060105460ff8160a01c16159081614e7a575b50614c645761432c937f664b0e72d3e91e88f8d9d2c0d916d51aff408110b8760394f6d677a5e6b24a2e3061512c565b6001600160a01b0316331490505f614e4a565b60ff60105460a01c161561432c57614ea3614394565b60135490818110613ed3575050565b6011546001600160a01b03168015614f4857604090815192614ed483856140d5565b600184526020840190601f198401368337845115612c0f576001600160a01b03169052803b156113845781516301e8a3a760e01b8152925f918491829084908290614f229060048301613fdf565b03925af1908115614f3f5750614f355750565b5f61432c916140d5565b513d5f823e3d90fd5b5050565b90816020910312611384575180151581036113845790565b6012546001600160a01b0316908115614f4857604051630d63997960e41b815260048101829052602081602481865afa908115611357575f91614fda575b50614f4857813b15611384575f9160248392604051948593849263b5c645bd60e01b845260048401525af1801561135757614f355750565b614ffc915060203d602011615002575b614ff481836140d5565b810190614f4c565b5f614fa2565b503d614fea565b610a208151148015906150a3575b614baf57602061506a5f948286958160405195869481808701998051918291018b5e8601908282018b8152815193849201905e010190878252805192839101825e0185815203601f1981018352826140d5565b51906102045afa61507961483c565b81615097575b81615088575090565b6150929150615a27565b151590565b8051602014915061507f565b5061121383511415615017565b604081511480159061511f575b614baf5760206151105f948286958160405195869481808701998051918291018b5e8601908282018b8152815193849201905e010190878252805192839101825e0185815203601f1981018352826140d5565b51906102055afa61507961483c565b50617460835114156150bd565b9594919390929560135415613ee85760018060a01b031694855f5260146020526001600160401b0360405f205416946151648661426c565b875f5260146020526001600160401b0360405f2091166001600160401b031982541617905560405160208101918783526040820152604081526151a86060826140d5565b519020966040516001600160401b0360208201927fd850f5df47b124511e8e6ec99cf1a0beaf7c6237eff0a31305ce53d85f312675845246604084015289606084015287608084015216988960a083015260c082015260c0815261520d60e0826140d5565b51902093601354965f9888156154c8574381116154b257804303438111612bd7576102581061549c575060409894979851956020870152602086526152536040876140d5565b5f965f99607e1986360301965b8a8c1015615440578b60051b87013588811215611384578701996152838b615a49565b6001600160a01b03918216911681101561541457506152a18a615a49565b996152ab81615a49565b604051632e4bfa5160e11b81526001600160a01b0390911660048201526101006024820152602081604481305afa908115611357575f916153f6575b50156153c75760208101600460ff6152fe83615a5d565b16036153955761530f8b8330616268565b1561536257506153208a8230616397565b1561533957506153316001916141a8565b9b019a615260565b61534290615a49565b63c082266360e01b5f9081526001600160a01b0391909116600452602490fd5b61537661537060ff93615a49565b91615a5d565b9063bbf82ba360e01b5f5260018060a01b03166004521660245260445ffd5b6153a361537060ff93615a49565b9063587548c360e11b5f5260018060a01b031660045216602452600460445260645ffd5b6153d090615a49565b63ae8bb03960e01b5f9081526001600160a01b0391909116600452610100602452604490fd5b61540e915060203d811161500257614ff481836140d5565b5f6152e7565b61541d8b615a49565b6311641feb60e21b5f9081526004929092526001600160a01b0316602452604490fd5b96509450955095509550955080821061548657507fd0ab8cbd6c510239be8b7cbd29e233d090728605764b8a4149ead15ae98677b49160409182519182526020820152a3565b906305bc216760e51b5f5260045260245260445ffd5b630ed38fd160e41b5f526004524360245260445ffd5b637b51505560e01b5f526004524360245260445ffd5b631fc460bf60e11b5f5260045ffd5b600501546001600160401b038160101c16801515908161552f575b506144605760501c6001600160401b0316801515908161551b575b5061551757600190565b5f90565b90506001600160401b03421610155f61550d565b90506001600160401b034216105f6154f2565b906004821015611b2557811561567a575f92839260018114615651576002146155cd57604051627eeac760e11b6020820190815230602483015260448201929092526155918160648101611920565b51915afa61559d61483c565b90806155c1575b156144605760208151918180820193849201010312611384575190565b506020815110156155a4565b60405160208101916331a9108f60e11b83526024820152602481526155f36044826140d5565b51915afa6155ff61483c565b81615643575b81615616575b501561551757600190565b905060208180518101031261138457602001516001600160a01b038116908190036113845730145f61560b565b905060208151101590615605565b505060405160208101906370a0823160e01b8252306024820152602481526155916044826140d5565b5050504790565b60ff60019116019060ff8211612bd757565b906001600160a01b038216801561579657805f5260026020526156b960405f2093614414565b1561578457600583015460ff8082169160081c16918282101561576c575060e0830160ff81511660ff6156eb84615681565b160361574557505060ff6101008301511681811161573057505060a060039101519101549081810361571b575050565b635a6d501b60e11b5f5260045260245260445ffd5b63a96ac3e360e01b5f5260045260245260445ffd5b9061575560ff8093511691615681565b9063239acc5160e21b5f526004521660245260445ffd5b632026243960e01b5f5260045260245260445260645ffd5b633210722560e21b5f5260045260245ffd5b50905060a08101517f9a6a5d8139ad2d28957698330aaa691017dba7dc80eb7cbec585239fb680bbab81036157ef575060e0015160ff165f1981016157d85750565b63239acc5160e21b5f52600452600160245260445ffd5b637557fe2160e11b5f5260045260245ffd5b908151811015612c0f570160200190565b6040516001600160f81b0319918216602082015291166021820152600281529061432c6022836140d5565b80516020909101516001600160f01b031981169291906002821061585f575050565b6001600160f01b031960029290920360031b82901b16169150565b5f5b82811061588857505050565b5f8282015560010161587c565b60105460ff8160a01c161590816158cc575b5061432c576158b533614473565b61432c5763321cbc0960e21b5f523360045260245ffd5b6001600160a01b0316331490505f6158a7565b90813b1561595e575f816020829351910182855af16158fc61483c565b901590811561592e575b5061590e5750565b6365f4a9ef60e11b5f9081526001600160a01b0391909116600452602490fd5b8051801515925082615943575b50505f615906565b6159569250602080918301019101614f4c565b155f8061593b565b506365f4a9ef60e11b5f9081526001600160a01b0391909116600452602490fd5b60a08101517f9a6a5d8139ad2d28957698330aaa691017dba7dc80eb7cbec585239fb680bbab81036157ef575060408101517fa5f1406585e87ef6c3645d6270cf45d62ae1c5d1abf447614cd94657a3f429a08103612cdb575060ff60e0820151169060018214801590615a15575b6159f6575050565b61010060ff9101511690631a25415160e21b5f5260045260245260445ffd5b508160ff6101008301511614156159ee565b602081519101519060208110615a3b575090565b5f199060200360031b1b1690565b356001600160a01b03811681036113845790565b3560ff811681036113845790565b615a7581546142bc565b9081615a7f575050565b81601f5f9311600114615a90575055565b81835260208320615aad91601f0160051c8419019060010161587a565b8082528160208120915555565b909392938015615d75575b615d3657615ad28261642e565b15615d1157615ae08461647b565b15615cec576001600160a01b03165f818152600860205260409020825191929091906001600160401b038211610ee257615b1a83546142bc565b601f8111615cb2575b50602090601f8311600114615c4f57615b5292915f9183610e195750508160011b915f199060031b1c19161790565b90555b5f52600960205260405f2082516001600160401b038111610ee257615b7a82546142bc565b601f8111615c15575b506020601f8211600114615bb7578190615bb39394955f92610e195750508160011b915f199060031b1c19161790565b9055565b601f19821690835f52805f20915f5b818110615bfd57509583600195969710615be5575b505050811b019055565b01515f1960f88460031b161c191690555f8080615bdb565b9192602060018192868b015181550194019201615bc6565b81811115615b8357615c4990835f5260205f2090601f840160051c9060208510610eda57601f82910160051c03910161587a565b5f615b83565b90601f19831691845f52815f20925f5b818110615c9a5750908460019594939210615c82575b505050811b019055615b55565b01515f1960f88460031b161c191690555f8080615c75565b92936020600181928786015181550195019301615c5f565b82811115615b2357615ce690845f5260205f2090601f850160051c9060208610610eda57601f82910160051c03910161587a565b5f615b23565b63c4facfbf60e01b5f9081526001600160a01b03919091166004526007602452604490fd5b63c4facfbf60e01b5f9081526001600160a01b03919091166004526003602452604490fd5b9192505060018060a01b0381165f526008602052615d5660405f20615a6b565b6001600160a01b03165f90815260096020526040902061432c90615a6b565b50815115615ac5565b9293925f9291801561623a575b6161fa57615d988261642e565b156161da57615da68561647b565b156161ba578215615fd1576001600160a01b0316808352600860205260408320825191929091906001600160401b038211615fbd57615de583546142bc565b601f8111615f7b575b50602090601f8311600114615f1957615e1d9291869183610e195750508160011b915f199060031b1c19161790565b90555b8152600960205260408120908351906001600160401b038211615f0557615e4783546142bc565b601f8111615ec3575b50602090601f8311600114615e7f5790615bb393949583610e195750508160011b915f199060031b1c19161790565b90601f198316848352818320925b818110615eab57509583600195969710615be557505050811b019055565b9192602060018192868b015181550194019201615e8d565b82811115615e5057615ef7908483526020832090601f850160051c9060208610615efd57601f82910160051c03910161587a565b5f615e50565b849150610ec4565b634e487b7160e01b81526041600452602490fd5b8386528186209190601f198416875b818110615f635750908460019594939210615f4b575b505050811b019055615e20565b01515f1960f88460031b161c191690555f8080615f3e565b92936020600181928786015181550195019301615f28565b82811115615dee57615faf908487526020872090601f850160051c9060208610615fb557601f82910160051c03910161587a565b5f615dee565b889150610ec4565b634e487b7160e01b85526041600452602485fd5b6001600160a01b03165f818152600a602052604090208251919350916001600160401b038211610ee25761600583546142bc565b601f8111616180575b50602090601f831160011461611d5761603d92915f9183610e195750508160011b915f199060031b1c19161790565b90555b5f52600b60205260405f2082516001600160401b038111610ee25761606582546142bc565b601f81116160e3575b506020601f821160011461609e578190615bb39394955f92610e195750508160011b915f199060031b1c19161790565b601f19821690835f52805f20915f5b8181106160cb57509583600195969710615be557505050811b019055565b9192602060018192868b0151815501940192016160ad565b8181111561606e5761611790835f5260205f2090601f840160051c9060208510610eda57601f82910160051c03910161587a565b5f61606e565b90601f19831691845f52815f20925f5b8181106161685750908460019594939210616150575b505050811b019055616040565b01515f1960f88460031b161c191690555f8080616143565b9293602060018192878601518155019501930161612d565b8281111561600e576161b490845f5260205f2090601f850160051c9060208610610eda57601f82910160051c03910161587a565b5f61600e565b63c4facfbf60e01b83526001600160a01b03166004526007602452604482fd5b63c4facfbf60e01b83526001600160a01b03166004526003602452604482fd5b929350505060018060a01b0381165f52600a60205261621b60405f20615a6b565b6001600160a01b03165f908152600b6020526040902061432c90615a6b565b50815115615d8b565b906020828203126113845781516001600160401b0381116113845761445d9201614573565b9160208201600460ff61627a83615a5d565b161461631c5760ff61628d600592615a5d565b161461629a575050505f90565b5f6162a483615a49565b604051639e5adaeb60e01b81526001600160a01b0391821660048201529485916024918391165afa9182156113575761445d935f936162f0575b5061099181604061130f93019061428a565b61130f919350616314610991913d805f833e61630c81836140d5565b810190616243565b9391506162de565b505f61632783615a49565b60405163b7af85d760e01b81526001600160a01b0391821660048201529485916024918391165afa9182156113575761445d935f93616373575b5061099181604061099893019061428a565b61099891935061638f610991913d805f833e61630c81836140d5565b939150616361565b90915f6163a384615a49565b60405163ad84ad1360e01b81526001600160a01b0391821660048201529384916024918391165afa918215611357575f92616412575b5081511580156163fc575b614baf5761130f61099184606061445d96019061428a565b5061640a606084018461428a565b9050156163e4565b6164279192503d805f833e61630c81836140d5565b905f6163d9565b5f80916020815191016102035afa61644461483c565b9015801561646f575b61645a5761509290615a27565b634e6f9bdf60e11b5f5261020360045260245ffd5b5060208151141561644d565b5f80916020815191016102075afa61649161483c565b901580156164bc575b6164a75761509290615a27565b634e6f9bdf60e11b5f5261020760045260245ffd5b5060208151141561649a56fecd32e76e8e1fee2c6f45e0ccbed91002a21c754eaaa76564a10aa391f628a2b5374d5630a5adaa53fb387074872486662e4eff70f8ef438306c8697c24e7016c
No CBOR metadata tail — this bytecode was built with cbor_metadata off, the setting our own contracts pin for CREATE2 address invariance.
disassembly (first 4,000 ops)
| pc | op | operand |
|---|---|---|
| 0000 | PUSH1 | 0x80 |
| 0002 | DUP1 | |
| 0003 | PUSH1 | 0x40 |
| 0005 | MSTORE | |
| 0006 | PUSH1 | 0x04 |
| 0008 | CALLDATASIZE | |
| 0009 | LT | |
| 000a | ISZERO | |
| 000b | PUSH2 | 0x0012 |
| 000e | JUMPI | |
| 000f | PUSH0 | |
| 0010 | DUP1 | |
| 0011 | REVERT | |
| 0012 | JUMPDEST | |
| 0013 | PUSH0 | |
| 0014 | SWAP1 | |
| 0015 | PUSH0 | |
| 0016 | CALLDATALOAD | |
| 0017 | PUSH1 | 0xe0 |
| 0019 | SHR | |
| 001a | SWAP1 | |
| 001b | DUP2 | |
| 001c | PUSH4 | 0x0408a87f |
| 0021 | EQ | |
| 0022 | PUSH2 | 0x3f1f |
| 0025 | JUMPI | |
| 0026 | POP | |
| 0027 | DUP1 | |
| 0028 | PUSH4 | 0x04525da8 |
| 002d | EQ | |
| 002e | PUSH2 | 0x3ef7 |
| 0031 | JUMPI | |
| 0032 | DUP1 | |
| 0033 | PUSH4 | 0x0d641df0 |
| 0038 | EQ | |
| 0039 | PUSH2 | 0x3e2b |
| 003c | JUMPI | |
| 003d | DUP1 | |
| 003e | PUSH4 | 0x1479bc3a |
| 0043 | EQ | |
| 0044 | PUSH2 | 0x3d55 |
| 0047 | JUMPI | |
| 0048 | DUP1 | |
| 0049 | PUSH4 | 0x150b7a02 |
| 004e | EQ | |
| 004f | PUSH2 | 0x3cff |
| 0052 | JUMPI | |
| 0053 | DUP1 | |
| 0054 | PUSH4 | 0x15ba3c1d |
| 0059 | EQ | |
| 005a | PUSH2 | 0x3ce4 |
| 005d | JUMPI | |
| 005e | DUP1 | |
| 005f | PUSH4 | 0x1d4f1077 |
| 0064 | EQ | |
| 0065 | PUSH2 | 0x3ba6 |
| 0068 | JUMPI | |
| 0069 | DUP1 | |
| 006a | PUSH4 | 0x1f394fb3 |
| 006f | EQ | |
| 0070 | PUSH2 | 0x3b71 |
| 0073 | JUMPI | |
| 0074 | DUP1 | |
| 0075 | PUSH4 | 0x1f9dc0bb |
| 007a | EQ | |
| 007b | PUSH2 | 0x3989 |
| 007e | JUMPI | |
| 007f | DUP1 | |
| 0080 | PUSH4 | 0x209f8044 |
| 0085 | EQ | |
| 0086 | PUSH2 | 0x396e |
| 0089 | JUMPI | |
| 008a | DUP1 | |
| 008b | PUSH4 | 0x222e6f0e |
| 0090 | EQ | |
| 0091 | PUSH2 | 0x393f |
| 0094 | JUMPI | |
| 0095 | DUP1 | |
| 0096 | PUSH4 | 0x228bf014 |
| 009b | EQ | |
| 009c | PUSH2 | 0x3924 |
| 009f | JUMPI | |
| 00a0 | DUP1 | |
| 00a1 | PUSH4 | 0x2741cb02 |
| 00a6 | EQ | |
| 00a7 | PUSH2 | 0x36fd |
| 00aa | JUMPI | |
| 00ab | DUP1 | |
| 00ac | PUSH4 | 0x2ca02983 |
| 00b1 | EQ | |
| 00b2 | PUSH2 | 0x3653 |
| 00b5 | JUMPI | |
| 00b6 | DUP1 | |
| 00b7 | PUSH4 | 0x3222383e |
| 00bc | EQ | |
| 00bd | PUSH2 | 0x361b |
| 00c0 | JUMPI | |
| 00c1 | DUP1 | |
| 00c2 | PUSH4 | 0x32596d71 |
| 00c7 | EQ | |
| 00c8 | PUSH2 | 0x35fe |
| 00cb | JUMPI | |
| 00cc | DUP1 | |
| 00cd | PUSH4 | 0x342f6163 |
| 00d2 | EQ | |
| 00d3 | PUSH2 | 0x358c |
| 00d6 | JUMPI | |
| 00d7 | DUP1 | |
| 00d8 | PUSH4 | 0x45e7e88e |
| 00dd | EQ | |
| 00de | PUSH2 | 0x3543 |
| 00e1 | JUMPI | |
| 00e2 | DUP1 | |
| 00e3 | PUSH4 | 0x461de6c1 |
| 00e8 | EQ | |
| 00e9 | PUSH2 | 0x3509 |
| 00ec | JUMPI | |
| 00ed | DUP1 | |
| 00ee | PUSH4 | 0x488429d9 |
| 00f3 | EQ | |
| 00f4 | PUSH2 | 0x34cf |
| 00f7 | JUMPI | |
| 00f8 | DUP1 | |
| 00f9 | PUSH4 | 0x4c4af1a4 |
| 00fe | EQ | |
| 00ff | PUSH2 | 0x313e |
| 0102 | JUMPI | |
| 0103 | DUP1 | |
| 0104 | PUSH4 | 0x4ccf0005 |
| 0109 | EQ | |
| 010a | PUSH2 | 0x30a5 |
| 010d | JUMPI | |
| 010e | DUP1 | |
| 010f | PUSH4 | 0x4f8d3216 |
| 0114 | EQ | |
| 0115 | PUSH2 | 0x3031 |
| 0118 | JUMPI | |
| 0119 | DUP1 | |
| 011a | PUSH4 | 0x5a232a39 |
| 011f | EQ | |
| 0120 | PUSH2 | 0x2ff7 |
| 0123 | JUMPI | |
| 0124 | DUP1 | |
| 0125 | PUSH4 | 0x5b882501 |
| 012a | EQ | |
| 012b | PUSH2 | 0x2ec4 |
| 012e | JUMPI | |
| 012f | DUP1 | |
| 0130 | PUSH4 | 0x5c97f4a2 |
| 0135 | EQ | |
| 0136 | PUSH2 | 0x2e9d |
| 0139 | JUMPI | |
| 013a | DUP1 | |
| 013b | PUSH4 | 0x60a18008 |
| 0140 | EQ | |
| 0141 | PUSH2 | 0x2e69 |
| 0144 | JUMPI | |
| 0145 | DUP1 | |
| 0146 | PUSH4 | 0x61dbd452 |
| 014b | EQ | |
| 014c | PUSH2 | 0x2e2f |
| 014f | JUMPI | |
| 0150 | DUP1 | |
| 0151 | PUSH4 | 0x64d2218d |
| 0156 | EQ | |
| 0157 | PUSH2 | 0x2e10 |
| 015a | JUMPI | |
| 015b | DUP1 | |
| 015c | PUSH4 | 0x70f64f0a |
| 0161 | EQ | |
| 0162 | PUSH2 | 0x2dc5 |
| 0165 | JUMPI | |
| 0166 | DUP1 | |
| 0167 | PUSH4 | 0x77e223b0 |
| 016c | EQ | |
| 016d | PUSH2 | 0x2daa |
| 0170 | JUMPI | |
| 0171 | DUP1 | |
| 0172 | PUSH4 | 0x86731755 |
| 0177 | EQ | |
| 0178 | PUSH2 | 0x2d70 |
| 017b | JUMPI | |
| 017c | DUP1 | |
| 017d | PUSH4 | 0x8c6820a9 |
| 0182 | EQ | |
| 0183 | PUSH2 | 0x1e0d |
| 0186 | JUMPI | |
| 0187 | DUP1 | |
| 0188 | PUSH4 | 0x8cfc6aa0 |
| 018d | EQ | |
| 018e | PUSH2 | 0x1ddb |
| 0191 | JUMPI | |
| 0192 | DUP1 | |
| 0193 | PUSH4 | 0x90ecddbf |
| 0198 | EQ | |
| 0199 | PUSH2 | 0x1d9a |
| 019c | JUMPI | |
| 019d | DUP1 | |
| 019e | PUSH4 | 0x926590d6 |
| 01a3 | EQ | |
| 01a4 | PUSH2 | 0x1cd9 |
| 01a7 | JUMPI | |
| 01a8 | DUP1 | |
| 01a9 | PUSH4 | 0x94338765 |
| 01ae | EQ | |
| 01af | PUSH2 | 0x1c97 |
| 01b2 | JUMPI | |
| 01b3 | DUP1 | |
| 01b4 | PUSH4 | 0x96f51f3a |
| 01b9 | EQ | |
| 01ba | PUSH2 | 0x1a2e |
| 01bd | JUMPI | |
| 01be | DUP1 | |
| 01bf | PUSH4 | 0x9aae87ad |
| 01c4 | EQ | |
| 01c5 | PUSH2 | 0x19f4 |
| 01c8 | JUMPI | |
| 01c9 | DUP1 | |
| 01ca | PUSH4 | 0x9c11e31b |
| 01cf | EQ | |
| 01d0 | PUSH2 | 0x19d9 |
| 01d3 | JUMPI | |
| 01d4 | DUP1 | |
| 01d5 | PUSH4 | 0x9d07ae09 |
| 01da | EQ | |
| 01db | PUSH2 | 0x18b1 |
| 01de | JUMPI | |
| 01df | DUP1 | |
| 01e0 | PUSH4 | 0x9e5adaeb |
| 01e5 | EQ | |
| 01e6 | PUSH2 | 0x1879 |
| 01e9 | JUMPI | |
| 01ea | DUP1 | |
| 01eb | PUSH4 | 0x9f8a13d7 |
| 01f0 | EQ | |
| 01f1 | PUSH2 | 0x184c |
| 01f4 | JUMPI | |
| 01f5 | DUP1 | |
| 01f6 | PUSH4 | 0xa0c176c4 |
| 01fb | EQ | |
| 01fc | PUSH2 | 0x1827 |
| 01ff | JUMPI | |
| 0200 | DUP1 | |
| 0201 | PUSH4 | 0xa760c295 |
| 0206 | EQ | |
| 0207 | PUSH2 | 0x17f5 |
| 020a | JUMPI | |
| 020b | DUP1 | |
| 020c | PUSH4 | 0xa874eea5 |
| 0211 | EQ | |
| 0212 | PUSH2 | 0x17da |
| 0215 | JUMPI | |
| 0216 | DUP1 | |
| 0217 | PUSH4 | 0xad20445d |
| 021c | EQ | |
| 021d | PUSH2 | 0x17c0 |
| 0220 | JUMPI | |
| 0221 | DUP1 | |
| 0222 | PUSH4 | 0xad84ad13 |
| 0227 | EQ | |
| 0228 | PUSH2 | 0x1788 |
| 022b | JUMPI | |
| 022c | DUP1 | |
| 022d | PUSH4 | 0xb0538d5a |
| 0232 | EQ | |
| 0233 | PUSH2 | 0x176d |
| 0236 | JUMPI | |
| 0237 | DUP1 | |
| 0238 | PUSH4 | 0xb16abc6c |
| 023d | EQ | |
| 023e | PUSH2 | 0x1735 |
| 0241 | JUMPI | |
| 0242 | DUP1 | |
| 0243 | PUSH4 | 0xb3c26283 |
| 0248 | EQ | |
| 0249 | PUSH2 | 0x16d4 |
| 024c | JUMPI | |
| 024d | DUP1 | |
| 024e | PUSH4 | 0xb7af85d7 |
| 0253 | EQ | |
| 0254 | PUSH2 | 0x1684 |
| 0257 | JUMPI | |
| 0258 | DUP1 | |
| 0259 | PUSH4 | 0xbaadbb32 |
| 025e | EQ | |
| 025f | PUSH2 | 0x164a |
| 0262 | JUMPI | |
| 0263 | DUP1 | |
| 0264 | PUSH4 | 0xbc197c81 |
| 0269 | EQ | |
| 026a | PUSH2 | 0x15b2 |
| 026d | JUMPI | |
| 026e | DUP1 | |
| 026f | PUSH4 | 0xc0716729 |
| 0274 | EQ | |
| 0275 | PUSH2 | 0x1596 |
| 0278 | JUMPI | |
| 0279 | DUP1 | |
| 027a | PUSH4 | 0xc177a97e |
| 027f | EQ | |
| 0280 | PUSH2 | 0x1573 |
| 0283 | JUMPI | |
| 0284 | DUP1 | |
| 0285 | PUSH4 | 0xc5768fe4 |
| 028a | EQ | |
| 028b | PUSH2 | 0x154b |
| 028e | JUMPI | |
| 028f | DUP1 | |
| 0290 | PUSH4 | 0xc6345626 |
| 0295 | EQ | |
| 0296 | PUSH2 | 0x1388 |
| 0299 | JUMPI | |
| 029a | DUP1 | |
| 029b | PUSH4 | 0xcbe6ebb4 |
| 02a0 | EQ | |
| 02a1 | PUSH2 | 0x0704 |
| 02a4 | JUMPI | |
| 02a5 | DUP1 | |
| 02a6 | PUSH4 | 0xce092d1c |
| 02ab | EQ | |
| 02ac | PUSH2 | 0x06e7 |
| 02af | JUMPI | |
| 02b0 | DUP1 | |
| 02b1 | PUSH4 | 0xde043e95 |
| 02b6 | EQ | |
| 02b7 | PUSH2 | 0x06ac |
| 02ba | JUMPI | |
| 02bb | DUP1 | |
| 02bc | PUSH4 | 0xe0cd096a |
| 02c1 | EQ | |
| 02c2 | PUSH2 | 0x0680 |
| 02c5 | JUMPI | |
| 02c6 | DUP1 | |
| 02c7 | PUSH4 | 0xe14c465b |
| 02cc | EQ | |
| 02cd | PUSH2 | 0x0663 |
| 02d0 | JUMPI | |
| 02d1 | DUP1 | |
| 02d2 | PUSH4 | 0xe4af29fc |
| 02d7 | EQ | |
| 02d8 | PUSH2 | 0x0645 |
| 02db | JUMPI | |
| 02dc | DUP1 | |
| 02dd | PUSH4 | 0xe51442e3 |
| 02e2 | EQ | |
| 02e3 | PUSH2 | 0x060d |
| 02e6 | JUMPI | |
| 02e7 | DUP1 | |
| 02e8 | PUSH4 | 0xec964baa |
| 02ed | EQ | |
| 02ee | PUSH2 | 0x0542 |
| 02f1 | JUMPI | |
| 02f2 | DUP1 | |
| 02f3 | PUSH4 | 0xef23f9f8 |
| 02f8 | EQ | |
| 02f9 | PUSH2 | 0x0501 |
| 02fc | JUMPI | |
| 02fd | DUP1 | |
| 02fe | PUSH4 | 0xf23a6e61 |
| 0303 | EQ | |
| 0304 | PUSH2 | 0x04a6 |
| 0307 | JUMPI | |
| 0308 | DUP1 | |
| 0309 | PUSH4 | 0xf5778b03 |
| 030e | EQ | |
| 030f | PUSH2 | 0x047d |
| 0312 | JUMPI | |
| 0313 | DUP1 | |
| 0314 | PUSH4 | 0xfce072f8 |
| 0319 | EQ | |
| 031a | PUSH2 | 0x0420 |
| 031d | JUMPI | |
| 031e | PUSH4 | 0xffd326f3 |
| 0323 | EQ | |
| 0324 | PUSH2 | 0x032b |
| 0327 | JUMPI | |
| 0328 | PUSH0 | |
| 0329 | DUP1 | |
| 032a | REVERT | |
| 032b | JUMPDEST | |
| 032c | CALLVALUE | |
| 032d | PUSH2 | 0x041d |
| 0330 | JUMPI | |
| 0331 | PUSH1 | 0x40 |
| 0333 | CALLDATASIZE | |
| 0334 | PUSH1 | 0x03 |
| 0336 | NOT | |
| 0337 | ADD | |
| 0338 | SLT | |
| 0339 | PUSH2 | 0x041d |
| 033c | JUMPI | |
| 033d | PUSH1 | 0x01 |
| 033f | PUSH1 | 0x01 |
| 0341 | PUSH1 | 0x40 |
| 0343 | SHL | |
| 0344 | SUB | |
| 0345 | PUSH1 | 0x40 |
| 0347 | PUSH2 | 0x034e |
| 034a | PUSH2 | 0x4021 |
| 034d | JUMP | |
| 034e | JUMPDEST | |
| 034f | SWAP3 | |
| 0350 | PUSH2 | 0x0357 |
| 0353 | PUSH2 | 0x3f57 |
| 0356 | JUMP | |
| 0357 | JUMPDEST | |
| 0358 | SWAP4 | |
| 0359 | DUP2 | |
| 035a | PUSH1 | 0x80 |
| 035c | DUP5 | |
| 035d | MLOAD | |
| 035e | PUSH2 | 0x0366 |
| 0361 | DUP2 | |
| 0362 | PUSH2 | 0x409e |
| 0365 | JUMP | |
| 0366 | JUMPDEST | |
| 0367 | DUP3 | |
| 0368 | DUP2 | |
| 0369 | MSTORE | |
| 036a | DUP3 | |
| 036b | PUSH1 | 0x20 |
| 036d | DUP3 | |
| 036e | ADD | |
| 036f | MSTORE | |
| 0370 | DUP3 | |
| 0371 | DUP7 | |
| 0372 | DUP3 | |
| 0373 | ADD | |
| 0374 | MSTORE | |
| 0375 | DUP3 | |
| 0376 | PUSH1 | 0x60 |
| 0378 | DUP3 | |
| 0379 | ADD | |
| 037a | MSTORE | |
| 037b | ADD | |
| 037c | MSTORE | |
| 037d | PUSH1 | 0x01 |
| 037f | DUP1 | |
| 0380 | PUSH1 | 0xa0 |
| 0382 | SHL | |
| 0383 | SUB | |
| 0384 | AND | |
| 0385 | DUP2 | |
| 0386 | MSTORE | |
| 0387 | DUP1 | |
| 0388 | PUSH1 | 0x20 |
| 038a | MSTORE | |
| 038b | KECCAK256 | |
| 038c | SWAP2 | |
| 038d | AND | |
| 038e | PUSH0 | |
| 038f | MSTORE | |
| 0390 | PUSH1 | 0x20 |
| 0392 | MSTORE | |
| 0393 | PUSH1 | 0xa0 |
| 0395 | PUSH1 | 0x40 |
| 0397 | PUSH0 | |
| 0398 | KECCAK256 | |
| 0399 | PUSH1 | 0x40 |
| 039b | MLOAD | |
| 039c | PUSH2 | 0x03a4 |
| 039f | DUP2 | |
| 03a0 | PUSH2 | 0x409e |
| 03a3 | JUMP | |
| 03a4 | JUMPDEST | |
| 03a5 | PUSH1 | 0x01 |
| 03a7 | PUSH1 | 0x01 |
| 03a9 | PUSH1 | 0x40 |
| 03ab | SHL | |
| 03ac | SUB | |
| 03ad | DUP3 | |
| 03ae | SLOAD | |
| 03af | SWAP2 | |
| 03b0 | PUSH1 | 0x01 |
| 03b2 | PUSH1 | 0x01 |
| 03b4 | PUSH1 | 0x80 |
| 03b6 | SHL | |
| 03b7 | SUB | |
| 03b8 | NOT | |
| 03b9 | DUP4 | |
| 03ba | PUSH1 | 0x80 |
| 03bc | SHL | |
| 03bd | AND | |
| 03be | SWAP4 | |
| 03bf | DUP5 | |
| 03c0 | DUP3 | |
| 03c1 | MSTORE | |
| 03c2 | PUSH1 | 0xff |
| 03c4 | PUSH1 | 0x20 |
| 03c6 | DUP4 | |
| 03c7 | ADD | |
| 03c8 | SWAP5 | |
| 03c9 | PUSH1 | 0x80 |
| 03cb | SHR | |
| 03cc | AND | |
| 03cd | DUP5 | |
| 03ce | MSTORE | |
| 03cf | PUSH1 | 0xff |
| 03d1 | PUSH1 | 0x02 |
| 03d3 | PUSH1 | 0x01 |
| 03d5 | DUP4 | |
| 03d6 | ADD | |
| 03d7 | SLOAD | |
| 03d8 | SWAP3 | |
| 03d9 | PUSH1 | 0x40 |
| 03db | DUP6 | |
| 03dc | ADD | |
| 03dd | SWAP4 | |
| 03de | DUP5 | |
| 03df | MSTORE | |
| 03e0 | ADD | |
| 03e1 | SLOAD | |
| 03e2 | SWAP5 | |
| 03e3 | DUP2 | |
| 03e4 | PUSH1 | 0x80 |
| 03e6 | PUSH1 | 0x60 |
| 03e8 | DUP7 | |
| 03e9 | ADD | |
| 03ea | SWAP6 | |
| 03eb | DUP8 | |
| 03ec | DUP10 | |
| 03ed | AND | |
| 03ee | DUP8 | |
| 03ef | MSTORE | |
| 03f0 | ADD | |
| 03f1 | SWAP7 | |
| 03f2 | PUSH1 | 0x40 |
| 03f4 | SHR | |
| 03f5 | AND | |
| 03f6 | ISZERO | |
| 03f7 | ISZERO | |
| 03f8 | DUP7 | |
| 03f9 | MSTORE | |
| 03fa | PUSH1 | 0x40 |
| 03fc | MLOAD | |
| 03fd | SWAP7 | |
| 03fe | DUP8 | |
| 03ff | MSTORE | |
| 0400 | MLOAD | |
| 0401 | AND | |
| 0402 | PUSH1 | 0x20 |
| 0404 | DUP7 | |
| 0405 | ADD | |
| 0406 | MSTORE | |
| 0407 | MLOAD | |
| 0408 | PUSH1 | 0x40 |
| 040a | DUP6 | |
| 040b | ADD | |
| 040c | MSTORE | |
| 040d | MLOAD | |
| 040e | AND | |
| 040f | PUSH1 | 0x60 |
| 0411 | DUP4 | |
| 0412 | ADD | |
| 0413 | MSTORE | |
| 0414 | MLOAD | |
| 0415 | ISZERO | |
| 0416 | ISZERO | |
| 0417 | PUSH1 | 0x80 |
| 0419 | DUP3 | |
| 041a | ADD | |
| 041b | MSTORE | |
| 041c | RETURN | |
| 041d | JUMPDEST | |
| 041e | DUP1 | |
| 041f | REVERT | |
| 0420 | JUMPDEST | |
| 0421 | POP | |
| 0422 | CALLVALUE | |
| 0423 | PUSH2 | 0x041d |
| 0426 | JUMPI | |
| 0427 | PUSH1 | 0x20 |
| 0429 | CALLDATASIZE | |
| 042a | PUSH1 | 0x03 |
| 042c | NOT | |
| 042d | ADD | |
| 042e | SLT | |
| 042f | PUSH2 | 0x041d |
| 0432 | JUMPI | |
| 0433 | PUSH1 | 0x04 |
| 0435 | CALLDATALOAD | |
| 0436 | SWAP1 | |
| 0437 | PUSH1 | 0x01 |
| 0439 | PUSH1 | 0x01 |
| 043b | PUSH1 | 0x40 |
| 043d | SHL | |
| 043e | SUB | |
| 043f | DUP3 | |
| 0440 | GT | |
| 0441 | PUSH2 | 0x041d |
| 0444 | JUMPI | |
| 0445 | CALLDATASIZE | |
| 0446 | PUSH1 | 0x23 |
| 0448 | DUP4 | |
| 0449 | ADD | |
| 044a | SLT | |
| 044b | ISZERO | |
| 044c | PUSH2 | 0x041d |
| 044f | JUMPI | |
| 0450 | PUSH1 | 0x20 |
| 0452 | PUSH2 | 0x046b |
| 0455 | PUSH2 | 0x0466 |
| 0458 | CALLDATASIZE | |
| 0459 | PUSH1 | 0x04 |
| 045b | DUP7 | |
| 045c | ADD | |
| 045d | CALLDATALOAD | |
| 045e | PUSH1 | 0x24 |
| 0460 | DUP8 | |
| 0461 | ADD | |
| 0462 | PUSH2 | 0x4111 |
| 0465 | JUMP | |
| 0466 | JUMPDEST | |
| 0467 | PUSH2 | 0x4bd8 |
| 046a | JUMP | |
| 046b | JUMPDEST | |
| 046c | PUSH1 | 0x40 |
| 046e | MLOAD | |
| 046f | PUSH1 | 0x01 |
| 0471 | PUSH1 | 0x01 |
| 0473 | PUSH1 | 0xa0 |
| 0475 | SHL | |
| 0476 | SUB | |
| 0477 | SWAP1 | |
| 0478 | SWAP2 | |
| 0479 | AND | |
| 047a | DUP2 | |
| 047b | MSTORE | |
| 047c | RETURN | |
| 047d | JUMPDEST | |
| 047e | POP | |
| 047f | CALLVALUE | |
| 0480 | PUSH2 | 0x041d |
| 0483 | JUMPI | |
| 0484 | DUP1 | |
| 0485 | PUSH1 | 0x03 |
| 0487 | NOT | |
| 0488 | CALLDATASIZE | |
| 0489 | ADD | |
| 048a | SLT | |
| 048b | PUSH2 | 0x041d |
| 048e | JUMPI | |
| 048f | PUSH1 | 0x10 |
| 0491 | SLOAD | |
| 0492 | PUSH1 | 0x40 |
| 0494 | MLOAD | |
| 0495 | PUSH1 | 0x01 |
| 0497 | PUSH1 | 0x01 |
| 0499 | PUSH1 | 0xa0 |
| 049b | SHL | |
| 049c | SUB | |
| 049d | SWAP1 | |
| 049e | SWAP2 | |
| 049f | AND | |
| 04a0 | DUP2 | |
| 04a1 | MSTORE | |
| 04a2 | PUSH1 | 0x20 |
| 04a4 | SWAP1 | |
| 04a5 | RETURN | |
| 04a6 | JUMPDEST | |
| 04a7 | POP | |
| 04a8 | CALLVALUE | |
| 04a9 | PUSH2 | 0x041d |
| 04ac | JUMPI | |
| 04ad | PUSH1 | 0xa0 |
| 04af | CALLDATASIZE | |
| 04b0 | PUSH1 | 0x03 |
| 04b2 | NOT | |
| 04b3 | ADD | |
| 04b4 | SLT | |
| 04b5 | PUSH2 | 0x041d |
| 04b8 | JUMPI | |
| 04b9 | PUSH2 | 0x04c0 |
| 04bc | PUSH2 | 0x4021 |
| 04bf | JUMP | |
| 04c0 | JUMPDEST | |
| 04c1 | POP | |
| 04c2 | PUSH2 | 0x04c9 |
| 04c5 | PUSH2 | 0x4037 |
| 04c8 | JUMP | |
| 04c9 | JUMPDEST | |
| 04ca | POP | |
| 04cb | PUSH1 | 0x84 |
| 04cd | CALLDATALOAD | |
| 04ce | PUSH1 | 0x01 |
| 04d0 | PUSH1 | 0x01 |
| 04d2 | PUSH1 | 0x40 |
| 04d4 | SHL | |
| 04d5 | SUB | |
| 04d6 | DUP2 | |
| 04d7 | GT | |
| 04d8 | PUSH2 | 0x04fd |
| 04db | JUMPI | |
| 04dc | PUSH2 | 0x04e9 |
| 04df | SWAP1 | |
| 04e0 | CALLDATASIZE | |
| 04e1 | SWAP1 | |
| 04e2 | PUSH1 | 0x04 |
| 04e4 | ADD | |
| 04e5 | PUSH2 | 0x404d |
| 04e8 | JUMP | |
| 04e9 | JUMPDEST | |
| 04ea | POP | |
| 04eb | POP | |
| 04ec | PUSH1 | 0x40 |
| 04ee | MLOAD | |
| 04ef | PUSH4 | 0xf23a6e61 |
| 04f4 | PUSH1 | 0xe0 |
| 04f6 | SHL | |
| 04f7 | DUP2 | |
| 04f8 | MSTORE | |
| 04f9 | PUSH1 | 0x20 |
| 04fb | SWAP1 | |
| 04fc | RETURN | |
| 04fd | JUMPDEST | |
| 04fe | POP | |
| 04ff | DUP1 | |
| 0500 | REVERT | |
| 0501 | JUMPDEST | |
| 0502 | POP | |
| 0503 | CALLVALUE | |
| 0504 | PUSH2 | 0x041d |
| 0507 | JUMPI | |
| 0508 | PUSH1 | 0x20 |
| 050a | CALLDATASIZE | |
| 050b | PUSH1 | 0x03 |
| 050d | NOT | |
| 050e | ADD | |
| 050f | SLT | |
| 0510 | PUSH2 | 0x041d |
| 0513 | JUMPI | |
| 0514 | PUSH1 | 0x20 |
| 0516 | SWAP1 | |
| 0517 | PUSH1 | 0x01 |
| 0519 | PUSH1 | 0x01 |
| 051b | PUSH1 | 0xa0 |
| 051d | SHL | |
| 051e | SUB | |
| 051f | PUSH2 | 0x0526 |
| 0522 | PUSH2 | 0x4021 |
| 0525 | JUMP | |
| 0526 | JUMPDEST | |
| 0527 | AND | |
| 0528 | DUP2 | |
| 0529 | MSTORE | |
| 052a | PUSH1 | 0x15 |
| 052c | DUP3 | |
| 052d | MSTORE | |
| 052e | PUSH1 | 0x40 |
| 0530 | PUSH1 | 0x01 |
| 0532 | DUP1 | |
| 0533 | PUSH1 | 0xa0 |
| 0535 | SHL | |
| 0536 | SUB | |
| 0537 | SWAP2 | |
| 0538 | KECCAK256 | |
| 0539 | SLOAD | |
| 053a | AND | |
| 053b | PUSH1 | 0x40 |
| 053d | MLOAD | |
| 053e | SWAP1 | |
| 053f | DUP2 | |
| 0540 | MSTORE | |
| 0541 | RETURN | |
| 0542 | JUMPDEST | |
| 0543 | POP | |
| 0544 | CALLVALUE | |
| 0545 | PUSH2 | 0x041d |
| 0548 | JUMPI | |
| 0549 | DUP1 | |
| 054a | PUSH1 | 0x03 |
| 054c | NOT | |
| 054d | CALLDATASIZE | |
| 054e | ADD | |
| 054f | SLT | |
| 0550 | PUSH2 | 0x041d |
| 0553 | JUMPI | |
| 0554 | PUSH1 | 0x10 |
| 0556 | SLOAD | |
| 0557 | PUSH1 | 0x01 |
| 0559 | PUSH1 | 0x01 |
| 055b | PUSH1 | 0xa0 |
| 055d | SHL | |
| 055e | SUB | |
| 055f | DUP2 | |
| 0560 | AND | |
| 0561 | CALLER | |
| 0562 | SUB | |
| 0563 | PUSH2 | 0x05fa |
| 0566 | JUMPI | |
| 0567 | PUSH1 | 0xff |
| 0569 | DUP2 | |
| 056a | PUSH1 | 0xa0 |
| 056c | SHR | |
| 056d | AND | |
| 056e | PUSH2 | 0x05eb |
| 0571 | JUMPI | |
| 0572 | PUSH1 | 0x13 |
| 0574 | SLOAD | |
| 0575 | DUP1 | |
| 0576 | ISZERO | |
| 0577 | PUSH2 | 0x05dc |
| 057a | JUMPI | |
| 057b | PUSH2 | 0x0582 |
| 057e | PUSH2 | 0x4394 |
| 0581 | JUMP | |
| 0582 | JUMPDEST | |
| 0583 | DUP2 | |
| 0584 | DUP2 | |
| 0585 | LT | |
| 0586 | PUSH2 | 0x05c6 |
| 0589 | JUMPI | |
| 058a | POP | |
| 058b | POP | |
| 058c | PUSH1 | 0x01 |
| 058e | PUSH1 | 0x01 |
| 0590 | PUSH1 | 0xa8 |
| 0592 | SHL | |
| 0593 | SUB | |
| 0594 | NOT | |
| 0595 | AND | |
| 0596 | PUSH1 | 0x01 |
| 0598 | PUSH1 | 0xa0 |
| 059a | SHL | |
| 059b | OR | |
| 059c | PUSH1 | 0x10 |
| 059e | SSTORE | |
| 059f | CALLER | |
| 05a0 | PUSH32 | 0xdd5280e26bdef754c085fd1fe40c0ab76df98368bb6014f85b49f8852a81be57 |
| 05c1 | DUP3 | |
| 05c2 | DUP1 | |
| 05c3 | LOG2 | |
| 05c4 | DUP1 | |
| 05c5 | RETURN | |
| 05c6 | JUMPDEST | |
| 05c7 | PUSH4 | 0x04951c21 |
| 05cc | PUSH1 | 0xe1 |
| 05ce | SHL | |
| 05cf | DUP5 | |
| 05d0 | MSTORE | |
| 05d1 | PUSH1 | 0x04 |
| 05d3 | MSTORE | |
| 05d4 | PUSH1 | 0x24 |
| 05d6 | MSTORE | |
| 05d7 | POP | |
| 05d8 | PUSH1 | 0x44 |
| 05da | SWAP1 | |
| 05db | REVERT | |
| 05dc | JUMPDEST | |
| 05dd | PUSH4 | 0x1de31ea5 |
| 05e2 | PUSH1 | 0xe1 |
| 05e4 | SHL | |
| 05e5 | DUP4 | |
| 05e6 | MSTORE | |
| 05e7 | PUSH1 | 0x04 |
| 05e9 | DUP4 | |
| 05ea | REVERT | |
| 05eb | JUMPDEST | |
| 05ec | PUSH4 | 0x0f80bc05 |
| 05f1 | PUSH1 | 0xe3 |
| 05f3 | SHL | |
| 05f4 | DUP3 | |
| 05f5 | MSTORE | |
| 05f6 | PUSH1 | 0x04 |
| 05f8 | DUP3 | |
| 05f9 | REVERT | |
| 05fa | JUMPDEST | |
| 05fb | PUSH4 | 0x4a0bfec1 |
| 0600 | PUSH1 | 0xe0 |
| 0602 | SHL | |
| 0603 | DUP3 | |
| 0604 | MSTORE | |
| 0605 | CALLER | |
| 0606 | PUSH1 | 0x04 |
| 0608 | MSTORE | |
| 0609 | PUSH1 | 0x24 |
| 060b | DUP3 | |
| 060c | REVERT | |
| 060d | JUMPDEST | |
| 060e | POP | |
| 060f | CALLVALUE | |
| 0610 | PUSH2 | 0x041d |
| 0613 | JUMPI | |
| 0614 | PUSH1 | 0x20 |
| 0616 | CALLDATASIZE | |
| 0617 | PUSH1 | 0x03 |
| 0619 | NOT | |
| 061a | ADD | |
| 061b | SLT | |
| 061c | PUSH2 | 0x041d |
| 061f | JUMPI | |
| 0620 | PUSH1 | 0x20 |
| 0622 | PUSH2 | 0x062c |
| 0625 | PUSH1 | 0x04 |
| 0627 | CALLDATALOAD | |
| 0628 | PUSH2 | 0x4190 |
| 062b | JUMP | |
| 062c | JUMPDEST | |
| 062d | SWAP1 | |
| 062e | SLOAD | |
| 062f | PUSH1 | 0x40 |
| 0631 | MLOAD | |
| 0632 | PUSH1 | 0x03 |
| 0634 | SWAP3 | |
| 0635 | SWAP1 | |
| 0636 | SWAP3 | |
| 0637 | SHL | |
| 0638 | SHR | |
| 0639 | PUSH1 | 0x01 |
| 063b | PUSH1 | 0x01 |
| 063d | PUSH1 | 0xa0 |
| 063f | SHL | |
| 0640 | SUB | |
| 0641 | AND | |
| 0642 | DUP2 | |
| 0643 | MSTORE | |
| 0644 | RETURN | |
| 0645 | JUMPDEST | |
| 0646 | POP | |
| 0647 | CALLVALUE | |
| 0648 | PUSH2 | 0x041d |
| 064b | JUMPI | |
| 064c | DUP1 | |
| 064d | PUSH1 | 0x03 |
| 064f | NOT | |
| 0650 | CALLDATASIZE | |
| 0651 | ADD | |
| 0652 | SLT | |
| 0653 | PUSH2 | 0x041d |
| 0656 | JUMPI | |
| 0657 | PUSH1 | 0x20 |
| 0659 | PUSH1 | 0x0f |
| 065b | SLOAD | |
| 065c | PUSH1 | 0x40 |
| 065e | MLOAD | |
| 065f | SWAP1 | |
| 0660 | DUP2 | |
| 0661 | MSTORE | |
| 0662 | RETURN | |
| 0663 | JUMPDEST | |
| 0664 | POP | |
| 0665 | CALLVALUE | |
| 0666 | PUSH2 | 0x041d |
| 0669 | JUMPI | |
| 066a | DUP1 | |
| 066b | PUSH1 | 0x03 |
| 066d | NOT | |
| 066e | CALLDATASIZE | |
| 066f | ADD | |
| 0670 | SLT | |
| 0671 | PUSH2 | 0x041d |
| 0674 | JUMPI | |
| 0675 | PUSH1 | 0x20 |
| 0677 | PUSH1 | 0x40 |
| 0679 | MLOAD | |
| 067a | PUSH2 | 0x0100 |
| 067d | DUP2 | |
| 067e | MSTORE | |
| 067f | RETURN | |
| 0680 | JUMPDEST | |
| 0681 | POP | |
| 0682 | CALLVALUE | |
| 0683 | PUSH2 | 0x041d |
| 0686 | JUMPI | |
| 0687 | PUSH1 | 0x20 |
| 0689 | CALLDATASIZE | |
| 068a | PUSH1 | 0x03 |
| 068c | NOT | |
| 068d | ADD | |
| 068e | SLT | |
| 068f | PUSH2 | 0x041d |
| 0692 | JUMPI | |
| 0693 | PUSH1 | 0x20 |
| 0695 | PUSH2 | 0x06a4 |
| 0698 | PUSH2 | 0x069f |
| 069b | PUSH2 | 0x4021 |
| 069e | JUMP | |
| 069f | JUMPDEST | |
| 06a0 | PUSH2 | 0x4980 |
| 06a3 | JUMP | |
| 06a4 | JUMPDEST | |
| 06a5 | PUSH1 | 0x40 |
| 06a7 | MLOAD | |
| 06a8 | SWAP1 | |
| 06a9 | DUP2 | |
| 06aa | MSTORE | |
| 06ab | RETURN | |
| 06ac | JUMPDEST | |
| 06ad | POP | |
| 06ae | CALLVALUE | |
| 06af | PUSH2 | 0x041d |
| 06b2 | JUMPI | |
| 06b3 | DUP1 | |
| 06b4 | PUSH1 | 0x03 |
| 06b6 | NOT | |
| 06b7 | CALLDATASIZE | |
| 06b8 | ADD | |
| 06b9 | SLT | |
| 06ba | PUSH2 | 0x041d |
| 06bd | JUMPI | |
| 06be | PUSH1 | 0x20 |
| 06c0 | PUSH1 | 0x40 |
| 06c2 | MLOAD | |
| 06c3 | PUSH32 | 0x664b0e72d3e91e88f8d9d2c0d916d51aff408110b8760394f6d677a5e6b24a2e |
| 06e4 | DUP2 | |
| 06e5 | MSTORE | |
| 06e6 | RETURN | |
| 06e7 | JUMPDEST | |
| 06e8 | POP | |
| 06e9 | CALLVALUE | |
| 06ea | PUSH2 | 0x041d |
| 06ed | JUMPI | |
| 06ee | DUP1 | |
| 06ef | PUSH1 | 0x03 |
| 06f1 | NOT | |
| 06f2 | CALLDATASIZE | |
| 06f3 | ADD | |
| 06f4 | SLT | |
| 06f5 | PUSH2 | 0x041d |
| 06f8 | JUMPI | |
| 06f9 | PUSH1 | 0x20 |
| 06fb | PUSH1 | 0x40 |
| 06fd | MLOAD | |
| 06fe | PUSH2 | 0x0200 |
| 0701 | DUP2 | |
| 0702 | MSTORE | |
| 0703 | RETURN | |
| 0704 | JUMPDEST | |
| 0705 | CALLVALUE | |
| 0706 | PUSH2 | 0x1384 |
| 0709 | JUMPI | |
| 070a | PUSH2 | 0x0100 |
| 070d | CALLDATASIZE | |
| 070e | PUSH1 | 0x03 |
| 0710 | NOT | |
| 0711 | ADD | |
| 0712 | SLT | |
| 0713 | PUSH2 | 0x1384 |
| 0716 | JUMPI | |
| 0717 | PUSH2 | 0x071e |
| 071a | PUSH2 | 0x4021 |
| 071d | JUMP | |
| 071e | JUMPDEST | |
| 071f | PUSH1 | 0x24 |
| 0721 | CALLDATALOAD | |
| 0722 | PUSH1 | 0x01 |
| 0724 | PUSH1 | 0x01 |
| 0726 | PUSH1 | 0x40 |
| 0728 | SHL | |
| 0729 | SUB | |
| 072a | DUP2 | |
| 072b | GT | |
| 072c | PUSH2 | 0x1384 |
| 072f | JUMPI | |
| 0730 | PUSH2 | 0x073d |
| 0733 | SWAP1 | |
| 0734 | CALLDATASIZE | |
| 0735 | SWAP1 | |
| 0736 | PUSH1 | 0x04 |
| 0738 | ADD | |
| 0739 | PUSH2 | 0x404d |
| 073c | JUMP | |
| 073d | JUMPDEST | |
| 073e | SWAP1 | |
| 073f | SWAP2 | |
| 0740 | PUSH1 | 0x44 |
| 0742 | CALLDATALOAD | |
| 0743 | PUSH1 | 0x01 |
| 0745 | PUSH1 | 0x01 |
| 0747 | PUSH1 | 0x40 |
| 0749 | SHL | |
| 074a | SUB | |
| 074b | DUP2 | |
| 074c | GT | |
| 074d | PUSH2 | 0x1384 |
| 0750 | JUMPI | |
| 0751 | PUSH2 | 0x075e |
| 0754 | SWAP1 | |
| 0755 | CALLDATASIZE | |
| 0756 | SWAP1 | |
| 0757 | PUSH1 | 0x04 |
| 0759 | ADD | |
| 075a | PUSH2 | 0x404d |
| 075d | JUMP | |
| 075e | JUMPDEST | |
| 075f | PUSH1 | 0x64 |
| 0761 | SWAP3 | |
| 0762 | SWAP2 | |
| 0763 | SWAP3 | |
| 0764 | CALLDATALOAD | |
| 0765 | SWAP4 | |
| 0766 | PUSH1 | 0x01 |
| 0768 | PUSH1 | 0x01 |
| 076a | PUSH1 | 0x40 |
| 076c | SHL | |
| 076d | SUB | |
| 076e | DUP6 | |
| 076f | GT | |
| 0770 | PUSH2 | 0x1384 |
| 0773 | JUMPI | |
| 0774 | DUP5 | |
| 0775 | PUSH1 | 0x04 |
| 0777 | ADD | |
| 0778 | SWAP4 | |
| 0779 | PUSH1 | 0x40 |
| 077b | PUSH1 | 0x03 |
| 077d | NOT | |
| 077e | DUP8 | |
| 077f | CALLDATASIZE | |
| 0780 | SUB | |
| 0781 | ADD | |
| 0782 | SLT | |
| 0783 | PUSH2 | 0x1384 |
| 0786 | JUMPI | |
| 0787 | PUSH1 | 0x84 |
| 0789 | CALLDATALOAD | |
| 078a | SWAP6 | |
| 078b | PUSH2 | 0x0792 |
| 078e | PUSH2 | 0x3f83 |
| 0791 | JUMP | |
| 0792 | JUMPDEST | |
| 0793 | SWAP6 | |
| 0794 | PUSH2 | 0x079b |
| 0797 | PUSH2 | 0x3f99 |
| 079a | JUMP | |
| 079b | JUMPDEST | |
| 079c | SWAP4 | |
| 079d | PUSH1 | 0xe4 |
| 079f | CALLDATALOAD | |
| 07a0 | SWAP6 | |
| 07a1 | PUSH1 | 0x01 |
| 07a3 | PUSH1 | 0x01 |
| 07a5 | PUSH1 | 0x40 |
| 07a7 | SHL | |
| 07a8 | SUB | |
| 07a9 | DUP8 | |
| 07aa | GT | |
| 07ab | PUSH2 | 0x1384 |
| 07ae | JUMPI | |
| 07af | DUP10 | |
| 07b0 | SWAP7 | |
| 07b1 | PUSH2 | 0x07be |
| 07b4 | SWAP1 | |
| 07b5 | CALLDATASIZE | |
| 07b6 | SWAP1 | |
| 07b7 | PUSH1 | 0x04 |
| 07b9 | ADD | |
| 07ba | PUSH2 | 0x3faf |
| 07bd | JUMP | |
| 07be | JUMPDEST | |
| 07bf | SWAP3 | |
| 07c0 | SWAP1 | |
| 07c1 | SWAP7 | |
| 07c2 | ADDRESS | |
| 07c3 | PUSH0 | |
| 07c4 | MSTORE | |
| 07c5 | PUSH1 | 0x14 |
| 07c7 | PUSH1 | 0x20 |
| 07c9 | MSTORE | |
| 07ca | PUSH1 | 0x40 |
| 07cc | PUSH0 | |
| 07cd | KECCAK256 | |
| 07ce | SLOAD | |
| 07cf | PUSH1 | 0x01 |
| 07d1 | PUSH1 | 0x01 |
| 07d3 | PUSH1 | 0x40 |
| 07d5 | SHL | |
| 07d6 | SUB | |
| 07d7 | AND | |
| 07d8 | SWAP4 | |
| 07d9 | DUP14 | |
| 07da | DUP4 | |
| 07db | CALLDATASIZE | |
| 07dc | SWAP1 | |
| 07dd | PUSH2 | 0x07e5 |
| 07e0 | SWAP3 | |
| 07e1 | PUSH2 | 0x4111 |
| 07e4 | JUMP | |
| 07e5 | JUMPDEST | |
| 07e6 | DUP1 | |
| 07e7 | MLOAD | |
| 07e8 | SWAP1 | |
| 07e9 | PUSH1 | 0x20 |
| 07eb | ADD | |
| 07ec | KECCAK256 | |
| 07ed | SWAP9 | |
| 07ee | DUP12 | |
| 07ef | DUP14 | |
| 07f0 | CALLDATASIZE | |
| 07f1 | PUSH2 | 0x07fb |
| 07f4 | SWAP1 | |
| 07f5 | DUP9 | |
| 07f6 | DUP14 | |
| 07f7 | PUSH2 | 0x4111 |
| 07fa | JUMP | |
| 07fb | JUMPDEST | |
| 07fc | DUP1 | |
| 07fd | MLOAD | |
| 07fe | SWAP1 | |
| 07ff | PUSH1 | 0x20 |
| 0801 | ADD | |
| 0802 | KECCAK256 | |
| 0803 | SWAP13 | |
| 0804 | PUSH1 | 0x40 |
| 0806 | MLOAD | |
| 0807 | DUP1 | |
| 0808 | SWAP15 | |
| 0809 | PUSH1 | 0x20 |
| 080b | DUP3 | |
| 080c | ADD | |
| 080d | SWAP5 | |
| 080e | PUSH1 | 0x01 |
| 0810 | PUSH1 | 0xa0 |
| 0812 | SHL | |
| 0813 | PUSH1 | 0x01 |
| 0815 | SWAP1 | |
| 0816 | SUB | |
| 0817 | AND | |
| 0818 | SWAP15 | |
| 0819 | DUP16 | |
| 081a | DUP7 | |
| 081b | MSTORE | |
| 081c | PUSH1 | 0x40 |
| 081e | DUP4 | |
| 081f | ADD | |
| 0820 | MSTORE | |
| 0821 | PUSH1 | 0x60 |
| 0823 | DUP3 | |
| 0824 | ADD | |
| 0825 | MSTORE | |
| 0826 | PUSH1 | 0x80 |
| 0828 | ADD | |
| 0829 | MSTORE | |
| 082a | PUSH1 | 0x01 |
| 082c | PUSH1 | 0x01 |
| 082e | PUSH1 | 0x40 |
| 0830 | SHL | |
| 0831 | SUB | |
| 0832 | AND | |
| 0833 | SWAP12 | |
| 0834 | DUP13 | |
| 0835 | PUSH1 | 0xa0 |
| 0837 | DUP3 | |
| 0838 | ADD | |
| 0839 | MSTORE | |
| 083a | PUSH1 | 0xa0 |
| 083c | DUP2 | |
| 083d | MSTORE | |
| 083e | PUSH2 | 0x0848 |
| 0841 | PUSH1 | 0xc0 |
| 0843 | DUP3 | |
| 0844 | PUSH2 | 0x40d5 |
| 0847 | JUMP | |
| 0848 | JUMPDEST | |
| 0849 | MLOAD | |
| 084a | SWAP1 | |
| 084b | KECCAK256 | |
| 084c | SWAP3 | |
| 084d | PUSH2 | 0x0855 |
| 0850 | SWAP4 | |
| 0851 | PUSH2 | 0x4e35 |
| 0854 | JUMP | |
| 0855 | JUMPDEST | |
| 0856 | PUSH20 | 0x5a7be3e057b3cf85dbf4e8d79208c6a5343e9c49 |
| 086b | DUP1 | |
| 086c | SWAP2 | |
| 086d | PUSH1 | 0x40 |
| 086f | MLOAD | |
| 0870 | DUP1 | |
| 0871 | SWAP15 | |
| 0872 | DUP2 | |
| 0873 | SWAP3 | |
| 0874 | PUSH4 | 0x25f1701d |
| 0879 | PUSH1 | 0xe2 |
| 087b | SHL | |
| 087c | DUP4 | |
| 087d | MSTORE | |
| 087e | PUSH1 | 0x04 |
| 0880 | DUP4 | |
| 0881 | ADD | |
| 0882 | SWAP2 | |
| 0883 | PUSH2 | 0x088b |
| 0886 | SWAP3 | |
| 0887 | PUSH2 | 0x47ab |
| 088a | JUMP | |
| 088b | JUMPDEST | |
| 088c | SUB | |
| 088d | DUP2 | |
| 088e | GAS | |
| 088f | SWAP4 | |
| 0890 | PUSH0 | |
| 0891 | SWAP5 | |
| 0892 | DELEGATECALL | |
| 0893 | SWAP6 | |
| 0894 | DUP7 | |
| 0895 | ISZERO | |
| 0896 | PUSH2 | 0x1357 |
| 0899 | JUMPI | |
| 089a | PUSH2 | 0x08c5 |
| 089d | SWAP13 | |
| 089e | PUSH0 | |
| 089f | SWAP8 | |
| 08a0 | PUSH2 | 0x1362 |
| 08a3 | JUMPI | |
| 08a4 | JUMPDEST | |
| 08a5 | POP | |
| 08a6 | SWAP1 | |
| 08a7 | PUSH0 | |
| 08a8 | SWAP3 | |
| 08a9 | SWAP2 | |
| 08aa | PUSH1 | 0x40 |
| 08ac | MLOAD | |
| 08ad | DUP1 | |
| 08ae | SWAP15 | |
| 08af | DUP2 | |
| 08b0 | SWAP5 | |
| 08b1 | DUP3 | |
| 08b2 | SWAP4 | |
| 08b3 | PUSH4 | 0xb389b84f |
| 08b8 | PUSH1 | 0xe0 |
| 08ba | SHL | |
| 08bb | DUP5 | |
| 08bc | MSTORE | |
| 08bd | PUSH1 | 0x04 |
| 08bf | DUP5 | |
| 08c0 | ADD | |
| 08c1 | PUSH2 | 0x47ab |
| 08c4 | JUMP | |
| 08c5 | JUMPDEST | |
| 08c6 | SUB | |
| 08c7 | SWAP2 | |
| 08c8 | GAS | |
| 08c9 | DELEGATECALL | |
| 08ca | SWAP10 | |
| 08cb | DUP11 | |
| 08cc | ISZERO | |
| 08cd | PUSH2 | 0x1357 |
| 08d0 | JUMPI | |
| 08d1 | PUSH0 | |
| 08d2 | SWAP11 | |
| 08d3 | PUSH2 | 0x1333 |
| 08d6 | JUMPI | |
| 08d7 | JUMPDEST | |
| 08d8 | POP | |
| 08d9 | PUSH1 | 0x20 |
| 08db | DUP5 | |
| 08dc | ADD | |
| 08dd | SWAP2 | |
| 08de | DUP3 | |
| 08df | MLOAD | |
| 08e0 | PUSH1 | 0x20 |
| 08e2 | DUP13 | |
| 08e3 | ADD | |
| 08e4 | MLOAD | |
| 08e5 | SWAP1 | |
| 08e6 | DUP2 | |
| 08e7 | DUP2 | |
| 08e8 | SUB | |
| 08e9 | PUSH2 | 0x131e |
| 08ec | JUMPI | |
| 08ed | POP | |
| 08ee | POP | |
| 08ef | PUSH2 | 0x08f7 |
| 08f2 | DUP6 | |
| 08f3 | PUSH2 | 0x597f |
| 08f6 | JUMP | |
| 08f7 | JUMPDEST | |
| 08f8 | PUSH2 | 0x0900 |
| 08fb | DUP12 | |
| 08fc | PUSH2 | 0x597f |
| 08ff | JUMP | |
| 0900 | JUMPDEST | |
| 0901 | DUP11 | |
| 0902 | MLOAD | |
| 0903 | DUP6 | |
| 0904 | MLOAD | |
| 0905 | SWAP1 | |
| 0906 | PUSH1 | 0x40 |
| 0908 | MLOAD | |
| 0909 | SWAP1 | |
| 090a | PUSH1 | 0x20 |
| 090c | DUP3 | |
| 090d | ADD | |
| 090e | SWAP3 | |
| 090f | PUSH32 | 0x6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f8168 |
| 0930 | DUP5 | |
| 0931 | MSTORE | |
| 0932 | CHAINID | |
| 0933 | PUSH1 | 0x40 |
| 0935 | DUP5 | |
| 0936 | ADD | |
| 0937 | MSTORE | |
| 0938 | ADDRESS | |
| 0939 | PUSH1 | 0x60 |
| 093b | DUP5 | |
| 093c | ADD | |
| 093d | MSTORE | |
| 093e | PUSH1 | 0x80 |
| 0940 | DUP4 | |
| 0941 | ADD | |
| 0942 | MSTORE | |
| 0943 | PUSH1 | 0xa0 |
| 0945 | DUP3 | |
| 0946 | ADD | |
| 0947 | MSTORE | |
| 0948 | DUP4 | |
| 0949 | PUSH1 | 0xc0 |
| 094b | DUP3 | |
| 094c | ADD | |
| 094d | MSTORE | |
| 094e | PUSH1 | 0xc0 |
| 0950 | DUP2 | |
| 0951 | MSTORE | |
| 0952 | PUSH2 | 0x095c |
| 0955 | PUSH1 | 0xe0 |
| 0957 | DUP3 | |
| 0958 | PUSH2 | 0x40d5 |
| 095b | JUMP | |
| 095c | JUMPDEST | |
| 095d | MLOAD | |
| 095e | SWAP1 | |
| 095f | KECCAK256 | |
| 0960 | SWAP4 | |
| 0961 | PUSH1 | 0x40 |
| 0963 | MLOAD | |
| 0964 | SWAP5 | |
| 0965 | PUSH1 | 0x20 |
| 0967 | DUP7 | |
| 0968 | ADD | |
| 0969 | MSTORE | |
| 096a | PUSH1 | 0x20 |
| 096c | DUP6 | |
| 096d | MSTORE | |
| 096e | PUSH2 | 0x0978 |
| 0971 | PUSH1 | 0x40 |
| 0973 | DUP7 | |
| 0974 | PUSH2 | 0x40d5 |
| 0977 | JUMP | |
| 0978 | JUMPDEST | |
| 0979 | PUSH2 | 0x0160 |
| 097c | DUP7 | |
| 097d | ADD | |
| 097e | SWAP5 | |
| 097f | PUSH2 | 0x099e |
| 0982 | DUP7 | |
| 0983 | MLOAD | |
| 0984 | DUP3 | |
| 0985 | PUSH2 | 0x0998 |
| 0988 | PUSH2 | 0x0991 |
| 098b | DUP8 | |
| 098c | DUP1 | |
| 098d | PUSH2 | 0x428a |
| 0990 | JUMP | |
| 0991 | JUMPDEST | |
| 0992 | CALLDATASIZE | |
| 0993 | SWAP2 | |
| 0994 | PUSH2 | 0x4111 |
| 0997 | JUMP | |
| 0998 | JUMPDEST | |
| 0999 | SWAP2 | |
| 099a | PUSH2 | 0x5009 |
| 099d | JUMP | |
| 099e | JUMPDEST | |
| 099f | ISZERO | |
| 09a0 | SWAP3 | |
| 09a1 | DUP4 | |
| 09a2 | ISZERO | |
| 09a3 | PUSH2 | 0x12f3 |
| 09a6 | JUMPI | |
| 09a7 | JUMPDEST | |
| 09a8 | POP | |
| 09a9 | POP | |
| 09aa | POP | |
| 09ab | PUSH2 | 0x12e0 |
| 09ae | JUMPI | |
| 09af | ADDRESS | |
| 09b0 | PUSH0 | |
| 09b1 | MSTORE | |
| 09b2 | PUSH1 | 0x14 |
| 09b4 | PUSH1 | 0x20 |
| 09b6 | MSTORE | |
| 09b7 | DUP1 | |
| 09b8 | PUSH1 | 0x01 |
| 09ba | PUSH1 | 0x01 |
| 09bc | PUSH1 | 0x40 |
| 09be | SHL | |
| 09bf | SUB | |
| 09c0 | PUSH1 | 0x40 |
| 09c2 | PUSH0 | |
| 09c3 | KECCAK256 | |
| 09c4 | SLOAD | |
| 09c5 | AND | |
| 09c6 | EQ | |
| 09c7 | PUSH2 | 0x12ac |
| 09ca | JUMPI | |
| 09cb | JUMPDEST | |
| 09cc | POP | |
| 09cd | DUP3 | |
| 09ce | MLOAD | |
| 09cf | SWAP7 | |
| 09d0 | DUP5 | |
| 09d1 | ISZERO | |
| 09d2 | PUSH2 | 0x1299 |
| 09d5 | JUMPI | |
| 09d6 | DUP4 | |
| 09d7 | MLOAD | |
| 09d8 | PUSH0 | |
| 09d9 | MSTORE | |
| 09da | PUSH1 | 0x0d |
| 09dc | PUSH1 | 0x20 |
| 09de | MSTORE | |
| 09df | PUSH1 | 0xff |
| 09e1 | PUSH1 | 0x40 |
| 09e3 | PUSH0 | |
| 09e4 | KECCAK256 | |
| 09e5 | SLOAD | |
| 09e6 | AND | |
| 09e7 | PUSH2 | 0x1285 |
| 09ea | JUMPI | |
| 09eb | DUP4 | |
| 09ec | MLOAD | |
| 09ed | PUSH0 | |
| 09ee | SWAP1 | |
| 09ef | DUP2 | |
| 09f0 | MSTORE | |
| 09f1 | PUSH1 | 0x0c |
| 09f3 | PUSH1 | 0x20 |
| 09f5 | MSTORE | |
| 09f6 | PUSH1 | 0x40 |
| 09f8 | SWAP1 | |
| 09f9 | KECCAK256 | |
| 09fa | SLOAD | |
| 09fb | PUSH1 | 0x01 |
| 09fd | PUSH1 | 0x01 |
| 09ff | PUSH1 | 0xa0 |
| 0a01 | SHL | |
| 0a02 | SUB | |
| 0a03 | AND | |
| 0a04 | DUP1 | |
| 0a05 | ISZERO | |
| 0a06 | ISZERO | |
| 0a07 | DUP1 | |
| 0a08 | PUSH2 | 0x127b |
| 0a0b | JUMPI | |
| 0a0c | JUMPDEST | |
| 0a0d | PUSH2 | 0x1265 |
| 0a10 | JUMPI | |
| 0a11 | POP | |
| 0a12 | DUP5 | |
| 0a13 | PUSH0 | |
| 0a14 | MSTORE | |
| 0a15 | PUSH1 | 0x02 |
| 0a17 | PUSH1 | 0x20 |
| 0a19 | MSTORE | |
| 0a1a | PUSH1 | 0x40 |
| 0a1c | PUSH0 | |
| 0a1d | KECCAK256 | |
| 0a1e | PUSH1 | 0x05 |
| 0a20 | DUP2 | |
| 0a21 | ADD | |
| 0a22 | SWAP3 | |
| 0a23 | DUP4 | |
| 0a24 | SLOAD | |
| 0a25 | PUSH1 | 0xff |
| 0a27 | DUP2 | |
| 0a28 | PUSH1 | 0xd8 |
| 0a2a | SHR | |
| 0a2b | AND | |
| 0a2c | ISZERO | |
| 0a2d | PUSH0 | |
| 0a2e | EQ | |
| 0a2f | PUSH2 | 0x11e3 |
| 0a32 | JUMPI | |
| 0a33 | POP | |
| 0a34 | SWAP1 | |
| 0a35 | PUSH1 | 0x0f |
| 0a37 | SLOAD | |
| 0a38 | SWAP1 | |
| 0a39 | PUSH1 | 0x01 |
| 0a3b | PUSH1 | 0x40 |
| 0a3d | SHL | |
| 0a3e | DUP3 | |
| 0a3f | LT | |
| 0a40 | ISZERO | |
| 0a41 | PUSH2 | 0x0ee2 |
| 0a44 | JUMPI | |
| 0a45 | DUP12 | |
| 0a46 | SWAP3 | |
| 0a47 | PUSH2 | 0x0a80 |
| 0a4a | DUP12 | |
| 0a4b | PUSH2 | 0x0a5c |
| 0a4e | DUP6 | |
| 0a4f | PUSH1 | 0x01 |
| 0a51 | PUSH1 | 0x04 |
| 0a53 | SWAP8 | |
| 0a54 | ADD | |
| 0a55 | PUSH1 | 0x0f |
| 0a57 | SSTORE | |
| 0a58 | PUSH2 | 0x4190 |
| 0a5b | JUMP | |
| 0a5c | JUMPDEST | |
| 0a5d | DUP2 | |
| 0a5e | SLOAD | |
| 0a5f | PUSH1 | 0x01 |
| 0a61 | PUSH1 | 0x01 |
| 0a63 | PUSH1 | 0xa0 |
| 0a65 | SHL | |
| 0a66 | SUB | |
| 0a67 | SWAP4 | |
| 0a68 | DUP5 | |
| 0a69 | AND | |
| 0a6a | PUSH1 | 0x03 |
| 0a6c | SWAP3 | |
| 0a6d | SWAP1 | |
| 0a6e | SWAP3 | |
| 0a6f | SHL | |
| 0a70 | SWAP2 | |
| 0a71 | DUP3 | |
| 0a72 | SHL | |
| 0a73 | SWAP4 | |
| 0a74 | SWAP1 | |
| 0a75 | SWAP2 | |
| 0a76 | SHL | |
| 0a77 | NOT | |
| 0a78 | AND | |
| 0a79 | SWAP2 | |
| 0a7a | SWAP1 | |
| 0a7b | SWAP2 | |
| 0a7c | OR | |
| 0a7d | SWAP1 | |
| 0a7e | SSTORE | |
| 0a7f | JUMP | |
| 0a80 | JUMPDEST | |
| 0a81 | DUP6 | |
| 0a82 | SLOAD | |
| 0a83 | PUSH1 | 0xff |
| 0a85 | PUSH1 | 0xd8 |
| 0a87 | SHL | |
| 0a88 | NOT | |
| 0a89 | AND | |
| 0a8a | PUSH1 | 0x01 |
| 0a8c | PUSH1 | 0xd8 |
| 0a8e | SHL | |
| 0a8f | OR | |
| 0a90 | DUP7 | |
| 0a91 | SSTORE | |
| 0a92 | JUMPDEST | |
| 0a93 | DUP8 | |
| 0a94 | MLOAD | |
| 0a95 | DUP3 | |
| 0a96 | SSTORE | |
| 0a97 | DUP14 | |
| 0a98 | MLOAD | |
| 0a99 | PUSH1 | 0x01 |
| 0a9b | DUP4 | |
| 0a9c | ADD | |
| 0a9d | SSTORE | |
| 0a9e | MLOAD | |
| 0a9f | PUSH1 | 0x02 |
| 0aa1 | DUP3 | |
| 0aa2 | ADD | |
| 0aa3 | SSTORE | |
| 0aa4 | PUSH1 | 0xc0 |
| 0aa6 | DUP8 | |
| 0aa7 | ADD | |
| 0aa8 | MLOAD | |
| 0aa9 | PUSH1 | 0x03 |
| 0aab | DUP3 | |
| 0aac | ADD | |
| 0aad | SSTORE | |
| 0aae | ADD | |
| 0aaf | SSTORE | |
| 0ab0 | PUSH1 | 0xe0 |
| 0ab2 | DUP5 | |
| 0ab3 | ADD | |
| 0ab4 | MLOAD | |
| 0ab5 | DUP3 | |
| 0ab6 | SLOAD | |
| 0ab7 | PUSH2 | 0x0100 |
| 0aba | DUP7 | |
| 0abb | ADD | |
| 0abc | MLOAD | |
| 0abd | PUSH2 | 0x0120 |
| 0ac0 | DUP8 | |
| 0ac1 | ADD | |
| 0ac2 | MLOAD | |
| 0ac3 | PUSH2 | 0x0140 |
| 0ac6 | DUP9 | |
| 0ac7 | ADD | |
| 0ac8 | MLOAD | |
| 0ac9 | PUSH1 | 0x01 |
| 0acb | PUSH1 | 0x01 |
| 0acd | PUSH1 | 0xd0 |
| 0acf | SHL | |
| 0ad0 | SUB | |
| 0ad1 | NOT | |
| 0ad2 | SWAP1 | |
| 0ad3 | SWAP4 | |
| 0ad4 | AND | |
| 0ad5 | PUSH1 | 0xff |
| 0ad7 | SWAP5 | |
| 0ad8 | SWAP1 | |
| 0ad9 | SWAP5 | |
| 0ada | AND | |
| 0adb | SWAP4 | |
| 0adc | SWAP1 | |
| 0add | SWAP4 | |
| 0ade | OR | |
| 0adf | PUSH1 | 0x08 |
| 0ae1 | SWAP2 | |
| 0ae2 | SWAP1 | |
| 0ae3 | SWAP2 | |
| 0ae4 | SHL | |
| 0ae5 | PUSH2 | 0xff00 |
| 0ae8 | AND | |
| 0ae9 | OR | |
| 0aea | PUSH1 | 0x10 |
| 0aec | SWAP3 | |
| 0aed | SWAP1 | |
| 0aee | SWAP3 | |
| 0aef | SHL | |
| 0af0 | PUSH10 | 0xffffffffffffffff0000 |
| 0afb | AND | |
| 0afc | SWAP2 | |
| 0afd | SWAP1 | |
| 0afe | SWAP2 | |
| 0aff | OR | |
| 0b00 | PUSH1 | 0x50 |
| 0b02 | SWAP2 | |
| 0b03 | SWAP1 | |
| 0b04 | SWAP2 | |
| 0b05 | SHL | |
| 0b06 | PUSH8 | 0xffffffffffffffff |
| 0b0f | PUSH1 | 0x50 |
| 0b11 | SHL | |
| 0b12 | AND | |
| 0b13 | OR | |
| 0b14 | PUSH1 | 0x90 |
| 0b16 | SWAP2 | |
| 0b17 | SWAP1 | |
| 0b18 | SWAP2 | |
| 0b19 | SHL | |
| 0b1a | PUSH8 | 0xffffffffffffffff |
| 0b23 | PUSH1 | 0x90 |
| 0b25 | SHL | |
| 0b26 | AND | |
| 0b27 | OR | |
| 0b28 | SWAP1 | |
| 0b29 | SSTORE | |
| 0b2a | DUP1 | |
| 0b2b | MLOAD | |
| 0b2c | PUSH1 | 0x01 |
| 0b2e | PUSH1 | 0x01 |
| 0b30 | PUSH1 | 0xa0 |
| 0b32 | SHL | |
| 0b33 | SUB | |
| 0b34 | SWAP1 | |
| 0b35 | PUSH2 | 0x0b3d |
| 0b38 | SWAP1 | |
| 0b39 | PUSH2 | 0x4bd8 |
| 0b3c | JUMP | |
| 0b3d | JUMPDEST | |
| 0b3e | AND | |
| 0b3f | PUSH0 | |
| 0b40 | DUP2 | |
| 0b41 | DUP2 | |
| 0b42 | MSTORE | |
| 0b43 | PUSH1 | 0x15 |
| 0b45 | PUSH1 | 0x20 |
| 0b47 | MSTORE | |
| 0b48 | PUSH1 | 0x40 |
| 0b4a | SWAP1 | |
| 0b4b | KECCAK256 | |
| 0b4c | SLOAD | |
| 0b4d | PUSH1 | 0x01 |
| 0b4f | PUSH1 | 0x01 |
| 0b51 | PUSH1 | 0xa0 |
| 0b53 | SHL | |
| 0b54 | SUB | |
| 0b55 | AND | |
| 0b56 | DUP1 | |
| 0b57 | ISZERO | |
| 0b58 | ISZERO | |
| 0b59 | DUP1 | |
| 0b5a | PUSH2 | 0x11d9 |
| 0b5d | JUMPI | |
| 0b5e | JUMPDEST | |
| 0b5f | PUSH2 | 0x11c3 |
| 0b62 | JUMPI | |
| 0b63 | POP | |
| 0b64 | DUP4 | |
| 0b65 | PUSH0 | |
| 0b66 | MSTORE | |
| 0b67 | PUSH1 | 0x03 |
| 0b69 | PUSH1 | 0x20 |
| 0b6b | MSTORE | |
| 0b6c | PUSH2 | 0x0b78 |
| 0b6f | PUSH1 | 0x40 |
| 0b71 | PUSH0 | |
| 0b72 | KECCAK256 | |
| 0b73 | SLOAD | |
| 0b74 | PUSH2 | 0x42bc |
| 0b77 | JUMP | |
| 0b78 | JUMPDEST | |
| 0b79 | PUSH2 | 0x1175 |
| 0b7c | JUMPI | |
| 0b7d | JUMPDEST | |
| 0b7e | PUSH0 | |
| 0b7f | MSTORE | |
| 0b80 | PUSH1 | 0x15 |
| 0b82 | PUSH1 | 0x20 |
| 0b84 | MSTORE | |
| 0b85 | PUSH1 | 0x40 |
| 0b87 | PUSH0 | |
| 0b88 | KECCAK256 | |
| 0b89 | DUP4 | |
| 0b8a | PUSH1 | 0x01 |
| 0b8c | PUSH1 | 0x01 |
| 0b8e | PUSH1 | 0x60 |
| 0b90 | SHL | |
| 0b91 | SUB | |
| 0b92 | PUSH1 | 0xa0 |
| 0b94 | SHL | |
| 0b95 | DUP3 | |
| 0b96 | SLOAD | |
| 0b97 | AND | |
| 0b98 | OR | |
| 0b99 | SWAP1 | |
| 0b9a | SSTORE | |
| 0b9b | MLOAD | |
| 0b9c | DUP3 | |
| 0b9d | PUSH0 | |
| 0b9e | MSTORE | |
| 0b9f | PUSH1 | 0x03 |
| 0ba1 | PUSH1 | 0x20 |
| 0ba3 | MSTORE | |
| 0ba4 | PUSH1 | 0x40 |
| 0ba6 | PUSH0 | |
| 0ba7 | KECCAK256 | |
| 0ba8 | SWAP1 | |
| 0ba9 | DUP1 | |
| 0baa | MLOAD | |
| 0bab | SWAP1 | |
| 0bac | PUSH1 | 0x01 |
| 0bae | PUSH1 | 0x01 |
| 0bb0 | PUSH1 | 0x40 |
| 0bb2 | SHL | |
| 0bb3 | SUB | |
| 0bb4 | DUP3 | |
| 0bb5 | GT | |
| 0bb6 | PUSH2 | 0x0ee2 |
| 0bb9 | JUMPI | |
| 0bba | PUSH2 | 0x0bc3 |
| 0bbd | DUP4 | |
| 0bbe | SLOAD | |
| 0bbf | PUSH2 | 0x42bc |
| 0bc2 | JUMP | |
| 0bc3 | JUMPDEST | |
| 0bc4 | PUSH1 | 0x1f |
| 0bc6 | DUP2 | |
| 0bc7 | GT | |
| 0bc8 | PUSH2 | 0x113b |
| 0bcb | JUMPI | |
| 0bcc | JUMPDEST | |
| 0bcd | POP | |
| 0bce | PUSH1 | 0x20 |
| 0bd0 | SWAP1 | |
| 0bd1 | PUSH1 | 0x1f |
| 0bd3 | DUP4 | |
| 0bd4 | GT | |
| 0bd5 | PUSH1 | 0x01 |
| 0bd7 | EQ | |
| 0bd8 | PUSH2 | 0x10d8 |
| 0bdb | JUMPI | |
| 0bdc | PUSH2 | 0x0bfc |
| 0bdf | SWAP3 | |
| 0be0 | SWAP2 | |
| 0be1 | PUSH0 | |
| 0be2 | SWAP2 | |
| 0be3 | DUP4 | |
| 0be4 | PUSH2 | 0x0ef6 |
| 0be7 | JUMPI | |
| 0be8 | JUMPDEST | |
| 0be9 | POP | |
| 0bea | POP | |
| 0beb | DUP2 | |
| 0bec | PUSH1 | 0x01 |
| 0bee | SHL | |
| 0bef | SWAP2 | |
| 0bf0 | PUSH0 | |
| 0bf1 | NOT | |
| 0bf2 | SWAP1 | |
| 0bf3 | PUSH1 | 0x03 |
| 0bf5 | SHL | |
| 0bf6 | SHR | |
| 0bf7 | NOT | |
| 0bf8 | AND | |
| 0bf9 | OR | |
| 0bfa | SWAP1 | |
| 0bfb | JUMP | |
| 0bfc | JUMPDEST | |
| 0bfd | SWAP1 | |
| 0bfe | SSTORE | |
| 0bff | JUMPDEST | |
| 0c00 | PUSH2 | 0x0180 |
| 0c03 | DUP2 | |
| 0c04 | ADD | |
| 0c05 | MLOAD | |
| 0c06 | DUP3 | |
| 0c07 | PUSH0 | |
| 0c08 | MSTORE | |
| 0c09 | PUSH1 | 0x04 |
| 0c0b | PUSH1 | 0x20 |
| 0c0d | MSTORE | |
| 0c0e | PUSH1 | 0x40 |
| 0c10 | PUSH0 | |
| 0c11 | KECCAK256 | |
| 0c12 | SWAP1 | |
| 0c13 | DUP1 | |
| 0c14 | MLOAD | |
| 0c15 | SWAP1 | |
| 0c16 | PUSH1 | 0x01 |
| 0c18 | PUSH1 | 0x01 |
| 0c1a | PUSH1 | 0x40 |
| 0c1c | SHL | |
| 0c1d | SUB | |
| 0c1e | DUP3 | |
| 0c1f | GT | |
| 0c20 | PUSH2 | 0x0ee2 |
| 0c23 | JUMPI | |
| 0c24 | PUSH2 | 0x0c2d |
| 0c27 | DUP4 | |
| 0c28 | SLOAD | |
| 0c29 | PUSH2 | 0x42bc |
| 0c2c | JUMP | |
| 0c2d | JUMPDEST | |
| 0c2e | PUSH1 | 0x1f |
| 0c30 | DUP2 | |
| 0c31 | GT | |
| 0c32 | PUSH2 | 0x109e |
| 0c35 | JUMPI | |
| 0c36 | JUMPDEST | |
| 0c37 | POP | |
| 0c38 | PUSH1 | 0x20 |
| 0c3a | SWAP1 | |
| 0c3b | PUSH1 | 0x1f |
| 0c3d | DUP4 | |
| 0c3e | GT | |
| 0c3f | PUSH1 | 0x01 |
| 0c41 | EQ | |
| 0c42 | PUSH2 | 0x103b |
| 0c45 | JUMPI | |
| 0c46 | PUSH2 | 0x0c65 |
| 0c49 | SWAP3 | |
| 0c4a | SWAP2 | |
| 0c4b | PUSH0 | |
| 0c4c | SWAP2 | |
| 0c4d | DUP4 | |
| 0c4e | PUSH2 | 0x0ef6 |
| 0c51 | JUMPI | |
| 0c52 | POP | |
| 0c53 | POP | |
| 0c54 | DUP2 | |
| 0c55 | PUSH1 | 0x01 |
| 0c57 | SHL | |
| 0c58 | SWAP2 | |
| 0c59 | PUSH0 | |
| 0c5a | NOT | |
| 0c5b | SWAP1 | |
| 0c5c | PUSH1 | 0x03 |
| 0c5e | SHL | |
| 0c5f | SHR | |
| 0c60 | NOT | |
| 0c61 | AND | |
| 0c62 | OR | |
| 0c63 | SWAP1 | |
| 0c64 | JUMP | |
| 0c65 | JUMPDEST | |
| 0c66 | SWAP1 | |
| 0c67 | SSTORE | |
| 0c68 | JUMPDEST | |
| 0c69 | PUSH2 | 0x0160 |
| 0c6c | DUP8 | |
| 0c6d | ADD | |
| 0c6e | MLOAD | |
| 0c6f | DUP3 | |
| 0c70 | PUSH0 | |
| 0c71 | MSTORE | |
| 0c72 | PUSH1 | 0x05 |
| 0c74 | PUSH1 | 0x20 |
| 0c76 | MSTORE | |
| 0c77 | PUSH1 | 0x40 |
| 0c79 | PUSH0 | |
| 0c7a | KECCAK256 | |
| 0c7b | SWAP1 | |
| 0c7c | DUP1 | |
| 0c7d | MLOAD | |
| 0c7e | SWAP1 | |
| 0c7f | PUSH1 | 0x01 |
| 0c81 | PUSH1 | 0x01 |
| 0c83 | PUSH1 | 0x40 |
| 0c85 | SHL | |
| 0c86 | SUB | |
| 0c87 | DUP3 | |
| 0c88 | GT | |
| 0c89 | PUSH2 | 0x0ee2 |
| 0c8c | JUMPI | |
| 0c8d | PUSH2 | 0x0c96 |
| 0c90 | DUP4 | |
| 0c91 | SLOAD | |
| 0c92 | PUSH2 | 0x42bc |
| 0c95 | JUMP | |
| 0c96 | JUMPDEST | |
| 0c97 | PUSH1 | 0x1f |
| 0c99 | DUP2 | |
| 0c9a | GT | |
| 0c9b | PUSH2 | 0x1001 |
| 0c9e | JUMPI | |
| 0c9f | JUMPDEST | |
| 0ca0 | POP | |
| 0ca1 | PUSH1 | 0x20 |
| 0ca3 | SWAP1 | |
| 0ca4 | PUSH1 | 0x1f |
| 0ca6 | DUP4 | |
| 0ca7 | GT | |
| 0ca8 | PUSH1 | 0x01 |
| 0caa | EQ | |
| 0cab | PUSH2 | 0x0f9e |
| 0cae | JUMPI | |
| 0caf | PUSH2 | 0x0cce |
| 0cb2 | SWAP3 | |
| 0cb3 | SWAP2 | |
| 0cb4 | PUSH0 | |
| 0cb5 | SWAP2 | |
| 0cb6 | DUP4 | |
| 0cb7 | PUSH2 | 0x0ef6 |
| 0cba | JUMPI | |
| 0cbb | POP | |
| 0cbc | POP | |
| 0cbd | DUP2 | |
| 0cbe | PUSH1 | 0x01 |
| 0cc0 | SHL | |
| 0cc1 | SWAP2 | |
| 0cc2 | PUSH0 | |
| 0cc3 | NOT | |
| 0cc4 | SWAP1 | |
| 0cc5 | PUSH1 | 0x03 |
| 0cc7 | SHL | |
| 0cc8 | SHR | |
| 0cc9 | NOT | |
| 0cca | AND | |
| 0ccb | OR | |
| 0ccc | SWAP1 | |
| 0ccd | JUMP | |
| 0cce | JUMPDEST | |
| 0ccf | SWAP1 | |
| 0cd0 | SSTORE | |
| 0cd1 | JUMPDEST | |
| 0cd2 | PUSH2 | 0x0180 |
| 0cd5 | DUP8 | |
| 0cd6 | ADD | |
| 0cd7 | MLOAD | |
| 0cd8 | DUP3 | |
| 0cd9 | PUSH0 | |
| 0cda | MSTORE | |
| 0cdb | PUSH1 | 0x06 |
| 0cdd | PUSH1 | 0x20 |
| 0cdf | MSTORE | |
| 0ce0 | PUSH1 | 0x40 |
| 0ce2 | PUSH0 | |
| 0ce3 | KECCAK256 | |
| 0ce4 | SWAP1 | |
| 0ce5 | DUP1 | |
| 0ce6 | MLOAD | |
| 0ce7 | SWAP1 | |
| 0ce8 | PUSH1 | 0x01 |
| 0cea | PUSH1 | 0x01 |
| 0cec | PUSH1 | 0x40 |
| 0cee | SHL | |
| 0cef | SUB | |
| 0cf0 | DUP3 | |
| 0cf1 | GT | |
| 0cf2 | PUSH2 | 0x0ee2 |
| 0cf5 | JUMPI | |
| 0cf6 | PUSH2 | 0x0cff |
| 0cf9 | DUP4 | |
| 0cfa | SLOAD | |
| 0cfb | PUSH2 | 0x42bc |
| 0cfe | JUMP | |
| 0cff | JUMPDEST | |
| 0d00 | PUSH1 | 0x1f |
| 0d02 | DUP2 | |
| 0d03 | GT | |
| 0d04 | PUSH2 | 0x0f64 |
| 0d07 | JUMPI | |
| 0d08 | JUMPDEST | |
| 0d09 | POP | |
| 0d0a | PUSH1 | 0x20 |
| 0d0c | SWAP1 | |
| 0d0d | PUSH1 | 0x1f |
| 0d0f | DUP4 | |
| 0d10 | GT | |
| 0d11 | PUSH1 | 0x01 |
| 0d13 | EQ | |
| 0d14 | PUSH2 | 0x0f01 |
| 0d17 | JUMPI | |
| 0d18 | PUSH2 | 0x0d37 |
| 0d1b | SWAP3 | |
| 0d1c | SWAP2 | |
| 0d1d | PUSH0 | |
| 0d1e | SWAP2 | |
| 0d1f | DUP4 | |
| 0d20 | PUSH2 | 0x0ef6 |
| 0d23 | JUMPI | |
| 0d24 | POP | |
| 0d25 | POP | |
| 0d26 | DUP2 | |
| 0d27 | PUSH1 | 0x01 |
| 0d29 | SHL | |
| 0d2a | SWAP2 | |
| 0d2b | PUSH0 | |
| 0d2c | NOT | |
| 0d2d | SWAP1 | |
| 0d2e | PUSH1 | 0x03 |
| 0d30 | SHL | |
| 0d31 | SHR | |
| 0d32 | NOT | |
| 0d33 | AND | |
| 0d34 | OR | |
| 0d35 | SWAP1 | |
| 0d36 | JUMP | |
| 0d37 | JUMPDEST | |
| 0d38 | SWAP1 | |
| 0d39 | SSTORE | |
| 0d3a | JUMPDEST | |
| 0d3b | PUSH2 | 0x01e0 |
| 0d3e | DUP2 | |
| 0d3f | ADD | |
| 0d40 | MLOAD | |
| 0d41 | SWAP7 | |
| 0d42 | DUP3 | |
| 0d43 | PUSH0 | |
| 0d44 | MSTORE | |
| 0d45 | PUSH1 | 0x07 |
| 0d47 | PUSH1 | 0x20 |
| 0d49 | MSTORE | |
| 0d4a | PUSH1 | 0x40 |
| 0d4c | PUSH0 | |
| 0d4d | KECCAK256 | |
| 0d4e | DUP9 | |
| 0d4f | MLOAD | |
| 0d50 | PUSH1 | 0x01 |
| 0d52 | PUSH1 | 0x01 |
| 0d54 | PUSH1 | 0x40 |
| 0d56 | SHL | |
| 0d57 | SUB | |
| 0d58 | DUP2 | |
| 0d59 | GT | |
| 0d5a | PUSH2 | 0x0ee2 |
| 0d5d | JUMPI | |
| 0d5e | PUSH1 | 0x20 |
| 0d60 | SWAP10 | |
| 0d61 | PUSH2 | 0x0d6a |
| 0d64 | DUP4 | |
| 0d65 | SLOAD | |
| 0d66 | PUSH2 | 0x42bc |
| 0d69 | JUMP | |
| 0d6a | JUMPDEST | |
| 0d6b | PUSH1 | 0x1f |
| 0d6d | DUP2 | |
| 0d6e | GT | |
| 0d6f | PUSH2 | 0x0ea1 |
| 0d72 | JUMPI | |
| 0d73 | JUMPDEST | |
| 0d74 | POP | |
| 0d75 | DUP11 | |
| 0d76 | SWAP1 | |
| 0d77 | PUSH1 | 0x1f |
| 0d79 | DUP4 | |
| 0d7a | GT | |
| 0d7b | PUSH1 | 0x01 |
| 0d7d | EQ | |
| 0d7e | PUSH2 | 0x0e24 |
| 0d81 | JUMPI | |
| 0d82 | SWAP4 | |
| 0d83 | PUSH2 | 0x06a4 |
| 0d86 | SWAP10 | |
| 0d87 | SWAP11 | |
| 0d88 | SWAP4 | |
| 0d89 | PUSH2 | 0x0dbd |
| 0d8c | DUP5 | |
| 0d8d | PUSH0 | |
| 0d8e | MLOAD | |
| 0d8f | PUSH1 | 0x20 |
| 0d91 | PUSH2 | 0x64c9 |
| 0d94 | PUSH0 | |
| 0d95 | CODECOPY | |
| 0d96 | PUSH0 | |
| 0d97 | MLOAD | |
| 0d98 | SWAP1 | |
| 0d99 | PUSH0 | |
| 0d9a | MSTORE | |
| 0d9b | SWAP9 | |
| 0d9c | SWAP6 | |
| 0d9d | PUSH2 | 0x0deb |
| 0da0 | SWAP6 | |
| 0da1 | PUSH1 | 0x40 |
| 0da3 | SWAP10 | |
| 0da4 | PUSH0 | |
| 0da5 | SWAP3 | |
| 0da6 | PUSH2 | 0x0e19 |
| 0da9 | JUMPI | |
| 0daa | POP | |
| 0dab | POP | |
| 0dac | DUP2 | |
| 0dad | PUSH1 | 0x01 |
| 0daf | SHL | |
| 0db0 | SWAP2 | |
| 0db1 | PUSH0 | |
| 0db2 | NOT | |
| 0db3 | SWAP1 | |
| 0db4 | PUSH1 | 0x03 |
| 0db6 | SHL | |
| 0db7 | SHR | |
| 0db8 | NOT | |
| 0db9 | AND | |
| 0dba | OR | |
| 0dbb | SWAP1 | |
| 0dbc | JUMP | |
| 0dbd | JUMPDEST | |
| 0dbe | SWAP1 | |
| 0dbf | SSTORE | |
| 0dc0 | JUMPDEST | |
| 0dc1 | PUSH2 | 0x0dd7 |
| 0dc4 | PUSH2 | 0x01a0 |
| 0dc7 | DUP5 | |
| 0dc8 | ADD | |
| 0dc9 | MLOAD | |
| 0dca | PUSH2 | 0x01c0 |
| 0dcd | DUP6 | |
| 0dce | ADD | |
| 0dcf | MLOAD | |
| 0dd0 | SWAP1 | |
| 0dd1 | PUSH0 | |
| 0dd2 | DUP13 | |
| 0dd3 | PUSH2 | 0x5aba |
| 0dd6 | JUMP | |
| 0dd7 | JUMPDEST | |
| 0dd8 | PUSH2 | 0x01c0 |
| 0ddb | PUSH2 | 0x01a0 |
| 0dde | DUP3 | |
| 0ddf | ADD | |
| 0de0 | MLOAD | |
| 0de1 | SWAP2 | |
| 0de2 | ADD | |
| 0de3 | MLOAD | |
| 0de4 | SWAP1 | |
| 0de5 | PUSH0 | |
| 0de6 | DUP11 | |
| 0de7 | PUSH2 | 0x5d7e |
| 0dea | JUMP | |
| 0deb | JUMPDEST | |
| 0dec | DUP1 | |
| 0ded | MLOAD | |
| 0dee | PUSH0 | |
| 0def | MSTORE | |
| 0df0 | PUSH1 | 0x0c |
| 0df2 | DUP11 | |
| 0df3 | MSTORE | |
| 0df4 | DUP3 | |
| 0df5 | PUSH0 | |
| 0df6 | KECCAK256 | |
| 0df7 | DUP6 | |
| 0df8 | PUSH1 | 0x01 |
| 0dfa | PUSH1 | 0x01 |
| 0dfc | PUSH1 | 0x60 |
| 0dfe | SHL | |
| 0dff | SUB | |
| 0e00 | PUSH1 | 0xa0 |
| 0e02 | SHL | |
| 0e03 | DUP3 | |
| 0e04 | SLOAD | |
| 0e05 | AND | |
| 0e06 | OR | |
| 0e07 | SWAP1 | |
| 0e08 | SSTORE | |
| 0e09 | MLOAD | |
| 0e0a | SWAP5 | |
| 0e0b | DUP3 | |
| 0e0c | MLOAD | |
| 0e0d | SWAP2 | |
| 0e0e | DUP3 | |
| 0e0f | MSTORE | |
| 0e10 | DUP10 | |
| 0e11 | DUP3 | |
| 0e12 | ADD | |
| 0e13 | MSTORE | |
| 0e14 | LOG3 | |
| 0e15 | PUSH2 | 0x4eb2 |
| 0e18 | JUMP | |
| 0e19 | JUMPDEST | |
| 0e1a | ADD | |
| 0e1b | MLOAD | |
| 0e1c | SWAP1 | |
| 0e1d | POP | |
| 0e1e | PUSH0 | |
| 0e1f | DUP1 | |
| 0e20 | PUSH2 | 0x0be8 |
| 0e23 | JUMP | |
| 0e24 | JUMPDEST | |
| 0e25 | SWAP1 | |
| 0e26 | PUSH1 | 0x1f |
| 0e28 | NOT | |
| 0e29 | DUP4 | |
| 0e2a | AND | |
| 0e2b | SWAP2 | |
| 0e2c | DUP5 | |
| 0e2d | PUSH0 | |
| 0e2e | MSTORE | |
| 0e2f | DUP2 | |
| 0e30 | PUSH0 | |
| 0e31 | KECCAK256 | |
| 0e32 | SWAP3 | |
| 0e33 | PUSH0 | |
| 0e34 | JUMPDEST | |
| 0e35 | DUP2 | |
| 0e36 | DUP2 | |
| 0e37 | LT | |
| 0e38 | PUSH2 | 0x0e8a |
| 0e3b | JUMPI | |
| 0e3c | POP | |
| 0e3d | DUP5 | |
| 0e3e | PUSH2 | 0x0deb |
| 0e41 | SWAP5 | |
| 0e42 | PUSH1 | 0x40 |
| 0e44 | SWAP9 | |
| 0e45 | SWAP5 | |
| 0e46 | PUSH2 | 0x06a4 |
| 0e49 | SWAP15 | |
| 0e4a | SWAP16 | |
| 0e4b | SWAP9 | |
| 0e4c | SWAP5 | |
| 0e4d | PUSH0 | |
| 0e4e | MLOAD | |
| 0e4f | PUSH1 | 0x20 |
| 0e51 | PUSH2 | 0x64c9 |
| 0e54 | PUSH0 | |
| 0e55 | CODECOPY | |
| 0e56 | PUSH0 | |
| 0e57 | MLOAD | |
| 0e58 | SWAP1 | |
| 0e59 | PUSH0 | |
| 0e5a | MSTORE | |
| 0e5b | SWAP12 | |
| 0e5c | SWAP9 | |
| 0e5d | PUSH1 | 0x01 |
| 0e5f | SWAP6 | |
| 0e60 | LT | |
| 0e61 | PUSH2 | 0x0e72 |
| 0e64 | JUMPI | |
| 0e65 | JUMPDEST | |
| 0e66 | POP | |
| 0e67 | POP | |
| 0e68 | POP | |
| 0e69 | DUP2 | |
| 0e6a | SHL | |
| 0e6b | ADD | |
| 0e6c | SWAP1 | |
| 0e6d | SSTORE | |
| 0e6e | PUSH2 | 0x0dc0 |
| 0e71 | JUMP | |
| 0e72 | JUMPDEST | |
| 0e73 | ADD | |
| 0e74 | MLOAD | |
| 0e75 | PUSH0 | |
| 0e76 | NOT | |
| 0e77 | PUSH1 | 0xf8 |
| 0e79 | DUP5 | |
| 0e7a | PUSH1 | 0x03 |
| 0e7c | SHL | |
| 0e7d | AND | |
| 0e7e | SHR | |
| 0e7f | NOT | |
| 0e80 | AND | |
| 0e81 | SWAP1 | |
| 0e82 | SSTORE | |
| 0e83 | DUP16 | |
| 0e84 | DUP1 | |
| 0e85 | DUP1 | |
| 0e86 | PUSH2 | 0x0e65 |
| 0e89 | JUMP | |
| 0e8a | JUMPDEST | |
| 0e8b | SWAP3 | |
| 0e8c | SWAP4 | |
| 0e8d | DUP15 | |
| 0e8e | PUSH1 | 0x01 |
| 0e90 | DUP2 | |
| 0e91 | SWAP3 | |
| 0e92 | DUP8 | |
| 0e93 | DUP7 | |
| 0e94 | ADD | |
| 0e95 | MLOAD | |
| 0e96 | DUP2 | |
| 0e97 | SSTORE | |
| 0e98 | ADD | |
| 0e99 | SWAP6 | |
| 0e9a | ADD | |
| 0e9b | SWAP4 | |
| 0e9c | ADD | |
| 0e9d | PUSH2 | 0x0e34 |
| 0ea0 | JUMP | |
| 0ea1 | JUMPDEST | |
| 0ea2 | DUP3 | |
| 0ea3 | DUP2 | |
| 0ea4 | GT | |
| 0ea5 | ISZERO | |
| 0ea6 | PUSH2 | 0x0d73 |
| 0ea9 | JUMPI | |
| 0eaa | PUSH2 | 0x0ed4 |
| 0ead | SWAP1 | |
| 0eae | DUP5 | |
| 0eaf | PUSH0 | |
| 0eb0 | MSTORE | |
| 0eb1 | DUP13 | |
| 0eb2 | PUSH0 | |
| 0eb3 | KECCAK256 | |
| 0eb4 | SWAP1 | |
| 0eb5 | PUSH1 | 0x1f |
| 0eb7 | DUP6 | |
| 0eb8 | ADD | |
| 0eb9 | PUSH1 | 0x05 |
| 0ebb | SHR | |
| 0ebc | SWAP1 | |
| 0ebd | DUP15 | |
| 0ebe | DUP7 | |
| 0ebf | LT | |
| 0ec0 | PUSH2 | 0x0eda |
| 0ec3 | JUMPI | |
| 0ec4 | JUMPDEST | |
| 0ec5 | PUSH1 | 0x1f |
| 0ec7 | DUP3 | |
| 0ec8 | SWAP2 | |
| 0ec9 | ADD | |
| 0eca | PUSH1 | 0x05 |
| 0ecc | SHR | |
| 0ecd | SUB | |
| 0ece | SWAP2 | |
| 0ecf | ADD | |
| 0ed0 | PUSH2 | 0x587a |
| 0ed3 | JUMP | |
| 0ed4 | JUMPDEST | |
| 0ed5 | DUP12 | |
| 0ed6 | PUSH2 | 0x0d73 |
| 0ed9 | JUMP | |
| 0eda | JUMPDEST | |
| 0edb | PUSH0 | |
| 0edc | SWAP2 | |
| 0edd | POP | |
| 0ede | PUSH2 | 0x0ec4 |
| 0ee1 | JUMP | |
| 0ee2 | JUMPDEST | |
| 0ee3 | PUSH4 | 0x4e487b71 |
| 0ee8 | PUSH1 | 0xe0 |
| 0eea | SHL | |
| 0eeb | PUSH0 | |
| 0eec | MSTORE | |
| 0eed | PUSH1 | 0x41 |
| 0eef | PUSH1 | 0x04 |
| 0ef1 | MSTORE | |
| 0ef2 | PUSH1 | 0x24 |
| 0ef4 | PUSH0 | |
| 0ef5 | REVERT | |
| 0ef6 | JUMPDEST | |
| 0ef7 | ADD | |
| 0ef8 | MLOAD | |
| 0ef9 | SWAP1 | |
| 0efa | POP | |
| 0efb | DUP12 | |
| 0efc | DUP1 | |
| 0efd | PUSH2 | 0x0be8 |
| 0f00 | JUMP | |
| 0f01 | JUMPDEST | |
| 0f02 | SWAP1 | |
| 0f03 | PUSH1 | 0x1f |
| 0f05 | NOT | |
| 0f06 | DUP4 | |
| 0f07 | AND | |
| 0f08 | SWAP2 | |
| 0f09 | DUP5 | |
| 0f0a | PUSH0 | |
| 0f0b | MSTORE | |
| 0f0c | DUP2 | |
| 0f0d | PUSH0 | |
| 0f0e | KECCAK256 | |
| 0f0f | SWAP3 | |
| 0f10 | PUSH0 | |
| 0f11 | JUMPDEST | |
| 0f12 | DUP2 | |
| 0f13 | DUP2 | |
| 0f14 | LT | |
| 0f15 | PUSH2 | 0x0f4c |
| 0f18 | JUMPI | |
| 0f19 | POP | |
| 0f1a | SWAP1 | |
| 0f1b | DUP5 | |
| 0f1c | PUSH1 | 0x01 |
| 0f1e | SWAP6 | |
| 0f1f | SWAP5 | |
| 0f20 | SWAP4 | |
| 0f21 | SWAP3 | |
| 0f22 | LT | |
| 0f23 | PUSH2 | 0x0f34 |
| 0f26 | JUMPI | |
| 0f27 | JUMPDEST | |
| 0f28 | POP | |
| 0f29 | POP | |
| 0f2a | POP | |
| 0f2b | DUP2 | |
| 0f2c | SHL | |
| 0f2d | ADD | |
| 0f2e | SWAP1 | |
| 0f2f | SSTORE | |
| 0f30 | PUSH2 | 0x0d3a |
| 0f33 | JUMP | |
| 0f34 | JUMPDEST | |
| 0f35 | ADD | |
| 0f36 | MLOAD | |
| 0f37 | PUSH0 | |
| 0f38 | NOT | |
| 0f39 | PUSH1 | 0xf8 |
| 0f3b | DUP5 | |
| 0f3c | PUSH1 | 0x03 |
| 0f3e | SHL | |
| 0f3f | AND | |
| 0f40 | SHR | |
| 0f41 | NOT | |
| 0f42 | AND | |
| 0f43 | SWAP1 | |
| 0f44 | SSTORE | |
| 0f45 | DUP11 | |
| 0f46 | DUP1 | |
| 0f47 | DUP1 | |
| 0f48 | PUSH2 | 0x0f27 |
| 0f4b | JUMP | |
| 0f4c | JUMPDEST | |
| 0f4d | SWAP3 | |
| 0f4e | SWAP4 | |
| 0f4f | PUSH1 | 0x20 |
| 0f51 | PUSH1 | 0x01 |
| 0f53 | DUP2 | |
| 0f54 | SWAP3 | |
| 0f55 | DUP8 | |
| 0f56 | DUP7 | |
| 0f57 | ADD | |
| 0f58 | MLOAD | |
| 0f59 | DUP2 | |
| 0f5a | SSTORE | |
| 0f5b | ADD | |
| 0f5c | SWAP6 | |
| 0f5d | ADD | |
| 0f5e | SWAP4 | |
| 0f5f | ADD | |
| 0f60 | PUSH2 | 0x0f11 |
| 0f63 | JUMP | |
| 0f64 | JUMPDEST | |
| 0f65 | DUP3 | |
| 0f66 | DUP2 | |
| 0f67 | GT | |
| 0f68 | ISZERO | |
| 0f69 | PUSH2 | 0x0d08 |
| 0f6c | JUMPI | |
| 0f6d | PUSH2 | 0x0f98 |
| 0f70 | SWAP1 | |
| 0f71 | DUP5 | |
| 0f72 | PUSH0 | |
| 0f73 | MSTORE | |
| 0f74 | PUSH1 | 0x20 |
| 0f76 | PUSH0 | |
| 0f77 | KECCAK256 | |
| 0f78 | SWAP1 | |
| 0f79 | PUSH1 | 0x1f |
| 0f7b | DUP6 | |
| 0f7c | ADD | |
| 0f7d | PUSH1 | 0x05 |
| 0f7f | SHR | |
| 0f80 | SWAP1 | |
| 0f81 | PUSH1 | 0x20 |
| 0f83 | DUP7 | |
| 0f84 | LT | |
| 0f85 | PUSH2 | 0x0eda |
| 0f88 | JUMPI | |
| 0f89 | PUSH1 | 0x1f |
| 0f8b | DUP3 | |
| 0f8c | SWAP2 | |
| 0f8d | ADD | |
| 0f8e | PUSH1 | 0x05 |
| 0f90 | SHR | |
| 0f91 | SUB | |
| 0f92 | SWAP2 | |
| 0f93 | ADD | |
| 0f94 | PUSH2 | 0x587a |
| 0f97 | JUMP | |
| 0f98 | JUMPDEST | |
| 0f99 | DUP11 | |
| 0f9a | PUSH2 | 0x0d08 |
| 0f9d | JUMP | |
| 0f9e | JUMPDEST | |
| 0f9f | SWAP1 | |
| 0fa0 | PUSH1 | 0x1f |
| 0fa2 | NOT | |
| 0fa3 | DUP4 | |
| 0fa4 | AND | |
| 0fa5 | SWAP2 | |
| 0fa6 | DUP5 | |
| 0fa7 | PUSH0 | |
| 0fa8 | MSTORE | |
| 0fa9 | DUP2 | |
| 0faa | PUSH0 | |
| 0fab | KECCAK256 | |
| 0fac | SWAP3 | |
| 0fad | PUSH0 | |
| 0fae | JUMPDEST | |
| 0faf | DUP2 | |
| 0fb0 | DUP2 | |
| 0fb1 | LT | |
| 0fb2 | PUSH2 | 0x0fe9 |
| 0fb5 | JUMPI | |
| 0fb6 | POP | |
| 0fb7 | SWAP1 | |
| 0fb8 | DUP5 | |
| 0fb9 | PUSH1 | 0x01 |
| 0fbb | SWAP6 | |
| 0fbc | SWAP5 | |
| 0fbd | SWAP4 | |
| 0fbe | SWAP3 | |
| 0fbf | LT | |
| 0fc0 | PUSH2 | 0x0fd1 |
| 0fc3 | JUMPI | |
| 0fc4 | JUMPDEST | |
| 0fc5 | POP | |
| 0fc6 | POP | |
| 0fc7 | POP | |
| 0fc8 | DUP2 | |
| 0fc9 | SHL | |
| 0fca | ADD | |
| 0fcb | SWAP1 | |
| 0fcc | SSTORE | |
| 0fcd | PUSH2 | 0x0cd1 |
| 0fd0 | JUMP | |
| 0fd1 | JUMPDEST | |
| 0fd2 | ADD | |
| 0fd3 | MLOAD | |
| 0fd4 | PUSH0 | |
| 0fd5 | NOT | |
| 0fd6 | PUSH1 | 0xf8 |
| 0fd8 | DUP5 | |
| 0fd9 | PUSH1 | 0x03 |
| 0fdb | SHL | |
| 0fdc | AND | |
| 0fdd | SHR | |
| 0fde | NOT | |
| 0fdf | AND | |
| 0fe0 | SWAP1 | |
| 0fe1 | SSTORE | |
| 0fe2 | DUP11 | |
| 0fe3 | DUP1 | |
| 0fe4 | DUP1 | |
| 0fe5 | PUSH2 | 0x0fc4 |
| 0fe8 | JUMP | |
| 0fe9 | JUMPDEST | |
| 0fea | SWAP3 | |
| 0feb | SWAP4 | |
| 0fec | PUSH1 | 0x20 |
| 0fee | PUSH1 | 0x01 |
| 0ff0 | DUP2 | |
| 0ff1 | SWAP3 | |
| 0ff2 | DUP8 | |
| 0ff3 | DUP7 | |
| 0ff4 | ADD | |
| 0ff5 | MLOAD | |
| 0ff6 | DUP2 | |
| 0ff7 | SSTORE | |
| 0ff8 | ADD | |
| 0ff9 | SWAP6 | |
| 0ffa | ADD | |
| 0ffb | SWAP4 | |
| 0ffc | ADD | |
| 0ffd | PUSH2 | 0x0fae |
| 1000 | JUMP | |
| 1001 | JUMPDEST | |
| 1002 | DUP3 | |
| 1003 | DUP2 | |
| 1004 | GT | |
| 1005 | ISZERO | |
| 1006 | PUSH2 | 0x0c9f |
| 1009 | JUMPI | |
| 100a | PUSH2 | 0x1035 |
| 100d | SWAP1 | |
| 100e | DUP5 | |
| 100f | PUSH0 | |
| 1010 | MSTORE | |
| 1011 | PUSH1 | 0x20 |
| 1013 | PUSH0 | |
| 1014 | KECCAK256 | |
| 1015 | SWAP1 | |
| 1016 | PUSH1 | 0x1f |
| 1018 | DUP6 | |
| 1019 | ADD | |
| 101a | PUSH1 | 0x05 |
| 101c | SHR | |
| 101d | SWAP1 | |
| 101e | PUSH1 | 0x20 |
| 1020 | DUP7 | |
| 1021 | LT | |
| 1022 | PUSH2 | 0x0eda |
| 1025 | JUMPI | |
| 1026 | PUSH1 | 0x1f |
| 1028 | DUP3 | |
| 1029 | SWAP2 | |
| 102a | ADD | |
| 102b | PUSH1 | 0x05 |
| 102d | SHR | |
| 102e | SUB | |
| 102f | SWAP2 | |
| 1030 | ADD | |
| 1031 | PUSH2 | 0x587a |
| 1034 | JUMP | |
| 1035 | JUMPDEST | |
| 1036 | DUP11 | |
| 1037 | PUSH2 | 0x0c9f |
| 103a | JUMP | |
| 103b | JUMPDEST | |
| 103c | SWAP1 | |
| 103d | PUSH1 | 0x1f |
| 103f | NOT | |
| 1040 | DUP4 | |
| 1041 | AND | |
| 1042 | SWAP2 | |
| 1043 | DUP5 | |
| 1044 | PUSH0 | |
| 1045 | MSTORE | |
| 1046 | DUP2 | |
| 1047 | PUSH0 | |
| 1048 | KECCAK256 | |
| 1049 | SWAP3 | |
| 104a | PUSH0 | |
| 104b | JUMPDEST | |
| 104c | DUP2 | |
| 104d | DUP2 | |
| 104e | LT | |
| 104f | PUSH2 | 0x1086 |
| 1052 | JUMPI | |
| 1053 | POP | |
| 1054 | SWAP1 | |
| 1055 | DUP5 | |
| 1056 | PUSH1 | 0x01 |
| 1058 | SWAP6 | |
| 1059 | SWAP5 | |
| 105a | SWAP4 | |
| 105b | SWAP3 | |
| 105c | LT | |
| 105d | PUSH2 | 0x106e |
| 1060 | JUMPI | |
| 1061 | JUMPDEST | |
| 1062 | POP | |
| 1063 | POP | |
| 1064 | POP | |
| 1065 | DUP2 | |
| 1066 | SHL | |
| 1067 | ADD | |
| 1068 | SWAP1 | |
| 1069 | SSTORE | |
| 106a | PUSH2 | 0x0c68 |
| 106d | JUMP | |
| 106e | JUMPDEST | |
| 106f | ADD | |
| 1070 | MLOAD | |
| 1071 | PUSH0 | |
| 1072 | NOT | |
| 1073 | PUSH1 | 0xf8 |
| 1075 | DUP5 | |
| 1076 | PUSH1 | 0x03 |
| 1078 | SHL | |
| 1079 | AND | |
| 107a | SHR | |
| 107b | NOT | |
| 107c | AND | |
| 107d | SWAP1 | |
| 107e | SSTORE | |
| 107f | DUP11 | |
| 1080 | DUP1 | |
| 1081 | DUP1 | |
| 1082 | PUSH2 | 0x1061 |
| 1085 | JUMP | |
| 1086 | JUMPDEST | |
| 1087 | SWAP3 | |
| 1088 | SWAP4 | |
| 1089 | PUSH1 | 0x20 |
| 108b | PUSH1 | 0x01 |
| 108d | DUP2 | |
| 108e | SWAP3 | |
| 108f | DUP8 | |
| 1090 | DUP7 | |
| 1091 | ADD | |
| 1092 | MLOAD | |
| 1093 | DUP2 | |
| 1094 | SSTORE | |
| 1095 | ADD | |
| 1096 | SWAP6 | |
| 1097 | ADD | |
| 1098 | SWAP4 | |
| 1099 | ADD | |
| 109a | PUSH2 | 0x104b |
| 109d | JUMP | |
| 109e | JUMPDEST | |
| 109f | DUP3 | |
| 10a0 | DUP2 | |
| 10a1 | GT | |
| 10a2 | ISZERO | |
| 10a3 | PUSH2 | 0x0c36 |
| 10a6 | JUMPI | |
| 10a7 | PUSH2 | 0x10d2 |
| 10aa | SWAP1 | |
| 10ab | DUP5 | |
| 10ac | PUSH0 | |
| 10ad | MSTORE | |
| 10ae | PUSH1 | 0x20 |
| 10b0 | PUSH0 | |
| 10b1 | KECCAK256 | |
| 10b2 | SWAP1 | |
| 10b3 | PUSH1 | 0x1f |
| 10b5 | DUP6 | |
| 10b6 | ADD | |
| 10b7 | PUSH1 | 0x05 |
| 10b9 | SHR | |
| 10ba | SWAP1 | |
| 10bb | PUSH1 | 0x20 |
| 10bd | DUP7 | |
| 10be | LT | |
| 10bf | PUSH2 | 0x0eda |
| 10c2 | JUMPI | |
| 10c3 | PUSH1 | 0x1f |
| 10c5 | DUP3 | |
| 10c6 | SWAP2 | |
| 10c7 | ADD | |
| 10c8 | PUSH1 | 0x05 |
| 10ca | SHR | |
| 10cb | SUB | |
| 10cc | SWAP2 | |
| 10cd | ADD | |
| 10ce | PUSH2 | 0x587a |
| 10d1 | JUMP | |
| 10d2 | JUMPDEST | |
| 10d3 | DUP11 | |
| 10d4 | PUSH2 | 0x0c36 |
| 10d7 | JUMP | |
| 10d8 | JUMPDEST | |
| 10d9 | SWAP1 | |
| 10da | PUSH1 | 0x1f |
| 10dc | NOT | |
| 10dd | DUP4 | |
| 10de | AND | |
| 10df | SWAP2 | |
| 10e0 | DUP5 | |
| 10e1 | PUSH0 | |
| 10e2 | MSTORE | |
| 10e3 | DUP2 | |
| 10e4 | PUSH0 | |
| 10e5 | KECCAK256 | |
| 10e6 | SWAP3 | |
| 10e7 | PUSH0 | |
| 10e8 | JUMPDEST | |
| 10e9 | DUP2 | |
| 10ea | DUP2 | |
| 10eb | LT | |
| 10ec | PUSH2 | 0x1123 |
| 10ef | JUMPI | |
| 10f0 | POP | |
| 10f1 | SWAP1 | |
| 10f2 | DUP5 | |
| 10f3 | PUSH1 | 0x01 |
| 10f5 | SWAP6 | |
| 10f6 | SWAP5 | |
| 10f7 | SWAP4 | |
| 10f8 | SWAP3 | |
| 10f9 | LT | |
| 10fa | PUSH2 | 0x110b |
| 10fd | JUMPI | |
| 10fe | JUMPDEST | |
| 10ff | POP | |
| 1100 | POP | |
| 1101 | POP | |
| 1102 | DUP2 | |
| 1103 | SHL | |
| 1104 | ADD | |
| 1105 | SWAP1 | |
| 1106 | SSTORE | |
| 1107 | PUSH2 | 0x0bff |
| 110a | JUMP | |
| 110b | JUMPDEST | |
| 110c | ADD | |
| 110d | MLOAD | |
| 110e | PUSH0 | |
| 110f | NOT | |
| 1110 | PUSH1 | 0xf8 |
| 1112 | DUP5 | |
| 1113 | PUSH1 | 0x03 |
| 1115 | SHL | |
| 1116 | AND | |
| 1117 | SHR | |
| 1118 | NOT | |
| 1119 | AND | |
| 111a | SWAP1 | |
| 111b | SSTORE | |
| 111c | DUP11 | |
| 111d | DUP1 | |
| 111e | DUP1 | |
| 111f | PUSH2 | 0x10fe |
| 1122 | JUMP | |
| 1123 | JUMPDEST | |
| 1124 | SWAP3 | |
| 1125 | SWAP4 | |
| 1126 | PUSH1 | 0x20 |
| 1128 | PUSH1 | 0x01 |
| 112a | DUP2 | |
| 112b | SWAP3 | |
| 112c | DUP8 | |
| 112d | DUP7 | |
| 112e | ADD | |
| 112f | MLOAD | |
| 1130 | DUP2 | |
| 1131 | SSTORE | |
| 1132 | ADD | |
| 1133 | SWAP6 | |
| 1134 | ADD | |
| 1135 | SWAP4 | |
| 1136 | ADD | |
| 1137 | PUSH2 | 0x10e8 |
| 113a | JUMP | |
| 113b | JUMPDEST | |
| 113c | DUP3 | |
| 113d | DUP2 | |
| 113e | GT | |
| 113f | ISZERO | |
| 1140 | PUSH2 | 0x0bcc |
| 1143 | JUMPI | |
| 1144 | PUSH2 | 0x116f |
| 1147 | SWAP1 | |
| 1148 | DUP5 | |
| 1149 | PUSH0 | |
| 114a | MSTORE | |
| 114b | PUSH1 | 0x20 |
| 114d | PUSH0 | |
| 114e | KECCAK256 | |
| 114f | SWAP1 | |
| 1150 | PUSH1 | 0x1f |
| 1152 | DUP6 | |
| 1153 | ADD | |
| 1154 | PUSH1 | 0x05 |
| 1156 | SHR | |
| 1157 | SWAP1 | |
| 1158 | PUSH1 | 0x20 |
| 115a | DUP7 | |
| 115b | LT | |
| 115c | PUSH2 | 0x0eda |
| 115f | JUMPI | |
| 1160 | PUSH1 | 0x1f |
| 1162 | DUP3 | |
| 1163 | SWAP2 | |
| 1164 | ADD | |
| 1165 | PUSH1 | 0x05 |
| 1167 | SHR | |
| 1168 | SUB | |
| 1169 | SWAP2 | |
| 116a | ADD | |
| 116b | PUSH2 | 0x587a |
| 116e | JUMP | |
| 116f | JUMPDEST | |
| 1170 | DUP11 | |
| 1171 | PUSH2 | 0x0bcc |
| 1174 | JUMP | |
| 1175 | JUMPDEST | |
| 1176 | DUP4 | |
| 1177 | PUSH0 | |
| 1178 | MSTORE | |
| 1179 | PUSH1 | 0x03 |
| 117b | PUSH1 | 0x20 |
| 117d | MSTORE | |
| 117e | PUSH1 | 0x01 |
| 1180 | DUP1 | |
| 1181 | PUSH1 | 0xa0 |
| 1183 | SHL | |
| 1184 | SUB | |
| 1185 | PUSH2 | 0x1193 |
| 1188 | PUSH2 | 0x0466 |
| 118b | PUSH1 | 0x40 |
| 118d | PUSH0 | |
| 118e | KECCAK256 | |
| 118f | PUSH2 | 0x42f4 |
| 1192 | JUMP | |
| 1193 | JUMPDEST | |
| 1194 | AND | |
| 1195 | DUP2 | |
| 1196 | DUP2 | |
| 1197 | SUB | |
| 1198 | PUSH2 | 0x11a2 |
| 119b | JUMPI | |
| 119c | JUMPDEST | |
| 119d | POP | |
| 119e | PUSH2 | 0x0b7d |
| 11a1 | JUMP | |
| 11a2 | JUMPDEST | |
| 11a3 | PUSH0 | |
| 11a4 | MSTORE | |
| 11a5 | PUSH1 | 0x15 |
| 11a7 | PUSH1 | 0x20 |
| 11a9 | MSTORE | |
| 11aa | PUSH1 | 0x40 |
| 11ac | PUSH0 | |
| 11ad | KECCAK256 | |
| 11ae | PUSH1 | 0x01 |
| 11b0 | PUSH1 | 0x01 |
| 11b2 | PUSH1 | 0x60 |
| 11b4 | SHL | |
| 11b5 | SUB | |
| 11b6 | PUSH1 | 0xa0 |
| 11b8 | SHL | |
| 11b9 | DUP2 | |
| 11ba | SLOAD | |
| 11bb | AND | |
| 11bc | SWAP1 | |
| 11bd | SSTORE | |
| 11be | DUP10 | |
| 11bf | PUSH2 | 0x119c |
| 11c2 | JUMP | |
| 11c3 | JUMPDEST | |
| 11c4 | SWAP1 | |
| 11c5 | PUSH4 | 0x13362de3 |
| 11ca | PUSH1 | 0xe0 |
| 11cc | SHL | |
| 11cd | PUSH0 | |
| 11ce | MSTORE | |
| 11cf | PUSH1 | 0x04 |
| 11d1 | MSTORE | |
| 11d2 | PUSH1 | 0x24 |
| 11d4 | MSTORE | |
| 11d5 | PUSH1 | 0x44 |
| 11d7 | PUSH0 | |
| 11d8 | REVERT | |
| 11d9 | JUMPDEST | |
| 11da | POP | |
| 11db | DUP5 | |
| 11dc | DUP2 | |
| 11dd | EQ | |
| 11de | ISZERO | |
| 11df | PUSH2 | 0x0b5e |
| 11e2 | JUMP | |
| 11e3 | JUMPDEST | |
| 11e4 | PUSH1 | 0x01 |
| 11e6 | PUSH1 | 0x01 |
| 11e8 | PUSH1 | 0x40 |
| 11ea | SHL | |
| 11eb | SUB | |
| 11ec | DUP2 | |
| 11ed | PUSH1 | 0x90 |
| 11ef | SHR | |
| 11f0 | AND | |
| 11f1 | DUP1 | |
| 11f2 | DUP11 | |
| 11f3 | GT | |
| 11f4 | ISZERO | |
| 11f5 | PUSH2 | 0x124e |
| 11f8 | JUMPI | |
| 11f9 | POP | |
| 11fa | PUSH1 | 0xd0 |
| 11fc | SHR | |
| 11fd | PUSH1 | 0xff |
| 11ff | AND | |
| 1200 | PUSH2 | 0x123a |
| 1203 | JUMPI | |
| 1204 | DUP2 | |
| 1205 | PUSH1 | 0x04 |
| 1207 | SWAP2 | |
| 1208 | DUP13 | |
| 1209 | SWAP4 | |
| 120a | SLOAD | |
| 120b | DUP9 | |
| 120c | MLOAD | |
| 120d | DUP2 | |
| 120e | SUB | |
| 120f | PUSH2 | 0x1219 |
| 1212 | JUMPI | |
| 1213 | JUMPDEST | |
| 1214 | POP | |
| 1215 | PUSH2 | 0x0a92 |
| 1218 | JUMP | |
| 1219 | JUMPDEST | |
| 121a | PUSH0 | |
| 121b | MSTORE | |
| 121c | PUSH1 | 0x0c |
| 121e | PUSH1 | 0x20 |
| 1220 | MSTORE | |
| 1221 | PUSH1 | 0x40 |
| 1223 | PUSH0 | |
| 1224 | KECCAK256 | |
| 1225 | PUSH1 | 0x01 |
| 1227 | PUSH1 | 0x01 |
| 1229 | PUSH1 | 0x60 |
| 122b | SHL | |
| 122c | SUB | |
| 122d | PUSH1 | 0xa0 |
| 122f | SHL | |
| 1230 | DUP2 | |
| 1231 | SLOAD | |
| 1232 | AND | |
| 1233 | SWAP1 | |
| 1234 | SSTORE | |
| 1235 | DUP15 | |
| 1236 | PUSH2 | 0x1213 |
| 1239 | JUMP | |
| 123a | JUMPDEST | |
| 123b | POP | |
| 123c | SLOAD | |
| 123d | PUSH4 | 0xe41b98f7 |
| 1242 | PUSH1 | 0xe0 |
| 1244 | SHL | |
| 1245 | PUSH0 | |
| 1246 | MSTORE | |
| 1247 | PUSH1 | 0x04 |
| 1249 | MSTORE | |
| 124a | PUSH1 | 0x24 |
| 124c | PUSH0 | |
| 124d | REVERT | |
| 124e | JUMPDEST | |
| 124f | DUP10 | |
| 1250 | SWAP1 | |
| 1251 | PUSH4 | 0xdf5abcab |
| 1256 | PUSH1 | 0xe0 |
| 1258 | SHL | |
| 1259 | PUSH0 | |
| 125a | MSTORE | |
| 125b | PUSH1 | 0x04 |
| 125d | MSTORE | |
| 125e | PUSH1 | 0x24 |
| 1260 | MSTORE | |
| 1261 | PUSH1 | 0x44 |
| 1263 | PUSH0 | |
| 1264 | REVERT | |
| 1265 | JUMPDEST | |
| 1266 | DUP5 | |
| 1267 | MLOAD | |
| 1268 | PUSH3 | 0x3bdaab |
| 126c | PUSH1 | 0xe5 |
| 126e | SHL | |
| 126f | PUSH0 | |
| 1270 | MSTORE | |
| 1271 | PUSH1 | 0x04 |
| 1273 | MSTORE | |
| 1274 | PUSH1 | 0x24 |
| 1276 | MSTORE | |
| 1277 | PUSH1 | 0x44 |
| 1279 | PUSH0 | |
| 127a | REVERT | |
| 127b | JUMPDEST | |
| 127c | POP | |
| 127d | DUP6 | |
| 127e | DUP2 | |
| 127f | EQ | |
| 1280 | ISZERO | |
| 1281 | PUSH2 | 0x0a0c |
| 1284 | JUMP | |
| 1285 | JUMPDEST | |
| 1286 | DUP4 | |
| 1287 | MLOAD | |
| 1288 | PUSH4 | 0xe41b98f7 |
| 128d | PUSH1 | 0xe0 |
| 128f | SHL | |
| 1290 | PUSH0 | |
| 1291 | MSTORE | |
| 1292 | PUSH1 | 0x04 |
| 1294 | MSTORE | |
| 1295 | PUSH1 | 0x24 |
| 1297 | PUSH0 | |
| 1298 | REVERT | |
| 1299 | JUMPDEST | |
| 129a | DUP5 | |
| 129b | PUSH4 | 0x3131bf79 |
| 12a0 | PUSH1 | 0xe2 |
| 12a2 | SHL | |
| 12a3 | PUSH0 | |
| 12a4 | MSTORE | |
| 12a5 | PUSH1 | 0x04 |
| 12a7 | MSTORE | |
| 12a8 | PUSH1 | 0x24 |
| 12aa | PUSH0 | |
| 12ab | REVERT | |
| 12ac | JUMPDEST | |
| 12ad | PUSH2 | 0x12b5 |
| 12b0 | SWAP1 | |
| 12b1 | PUSH2 | 0x426c |
| 12b4 | JUMP | |
| 12b5 | JUMPDEST | |
| 12b6 | ADDRESS | |
| 12b7 | PUSH0 | |
| 12b8 | MSTORE | |
| 12b9 | PUSH1 | 0x14 |
| 12bb | PUSH1 | 0x20 |
| 12bd | MSTORE | |
| 12be | PUSH1 | 0x01 |
| 12c0 | PUSH1 | 0x01 |
| 12c2 | PUSH1 | 0x40 |
| 12c4 | SHL | |
| 12c5 | SUB | |
| 12c6 | PUSH1 | 0x40 |
| 12c8 | PUSH0 | |
| 12c9 | KECCAK256 | |
| 12ca | SWAP2 | |
| 12cb | AND | |
| 12cc | PUSH1 | 0x01 |
| 12ce | PUSH1 | 0x01 |
| 12d0 | PUSH1 | 0x40 |
| 12d2 | SHL | |
| 12d3 | SUB | |
| 12d4 | NOT | |
| 12d5 | DUP3 | |
| 12d6 | SLOAD | |
| 12d7 | AND | |
| 12d8 | OR | |
| 12d9 | SWAP1 | |
| 12da | SSTORE | |
| 12db | DUP10 | |
| 12dc | PUSH2 | 0x09cb |
| 12df | JUMP | |
| 12e0 | JUMPDEST | |
| 12e1 | DUP5 | |
| 12e2 | PUSH4 | 0x9ae9f73b |
| 12e7 | PUSH1 | 0xe0 |
| 12e9 | SHL | |
| 12ea | PUSH0 | |
| 12eb | MSTORE | |
| 12ec | PUSH1 | 0x04 |
| 12ee | MSTORE | |
| 12ef | PUSH1 | 0x24 |
| 12f1 | PUSH0 | |
| 12f2 | REVERT | |
| 12f3 | JUMPDEST | |
| 12f4 | PUSH2 | 0x1315 |
| 12f7 | SWAP4 | |
| 12f8 | POP | |
| 12f9 | PUSH2 | 0x0991 |
| 12fc | PUSH2 | 0x130f |
| 12ff | SWAP2 | |
| 1300 | PUSH1 | 0x24 |
| 1302 | PUSH2 | 0x0180 |
| 1305 | DUP12 | |
| 1306 | ADD | |
| 1307 | MLOAD | |
| 1308 | SWAP6 | |
| 1309 | ADD | |
| 130a | SWAP1 | |
| 130b | PUSH2 | 0x428a |
| 130e | JUMP | |
| 130f | JUMPDEST | |
| 1310 | SWAP2 | |
| 1311 | PUSH2 | 0x50b0 |
| 1314 | JUMP | |
| 1315 | JUMPDEST | |
| 1316 | ISZERO | |
| 1317 | DUP12 | |
| 1318 | DUP1 | |
| 1319 | DUP1 | |
| 131a | PUSH2 | 0x09a7 |
| 131d | JUMP | |
| 131e | JUMPDEST | |
| 131f | PUSH4 | 0x88e08729 |
| 1324 | PUSH1 | 0xe0 |
| 1326 | SHL | |
| 1327 | PUSH0 | |
| 1328 | MSTORE | |
| 1329 | PUSH1 | 0x04 |
| 132b | MSTORE | |
| 132c | PUSH1 | 0x24 |
| 132e | MSTORE | |
| 132f | PUSH1 | 0x44 |
| 1331 | PUSH0 | |
| 1332 | REVERT | |
| 1333 | JUMPDEST | |
| 1334 | PUSH2 | 0x1350 |
| 1337 | SWAP2 | |
| 1338 | SWAP11 | |
| 1339 | POP | |
| 133a | RETURNDATASIZE | |
| 133b | DUP1 | |
| 133c | PUSH0 | |
| 133d | DUP4 | |
| 133e | RETURNDATACOPY | |
| 133f | PUSH2 | 0x1348 |
| 1342 | DUP2 | |
| 1343 | DUP4 | |
| 1344 | PUSH2 | 0x40d5 |
| 1347 | JUMP | |
| 1348 | JUMPDEST | |
| 1349 | DUP2 | |
| 134a | ADD | |
| 134b | SWAP1 | |
| 134c | PUSH2 | 0x45db |
| 134f | JUMP | |
| 1350 | JUMPDEST | |
| 1351 | SWAP9 | |
| 1352 | DUP11 | |
| 1353 | PUSH2 | 0x08d7 |
| 1356 | JUMP | |
| 1357 | JUMPDEST | |
| 1358 | PUSH1 | 0x40 |
| 135a | MLOAD | |
| 135b | RETURNDATASIZE | |
| 135c | PUSH0 | |
| 135d | DUP3 | |
| 135e | RETURNDATACOPY | |
| 135f | RETURNDATASIZE | |
| 1360 | SWAP1 | |
| 1361 | REVERT | |
| 1362 | JUMPDEST | |
| 1363 | PUSH0 | |
| 1364 | SWAP4 | |
| 1365 | SWAP3 | |
| 1366 | SWAP2 | |
| 1367 | SWAP8 | |
| 1368 | POP | |
| 1369 | PUSH2 | 0x137b |
| 136c | SWAP1 | |
| 136d | RETURNDATASIZE | |
| 136e | DUP1 | |
| 136f | DUP7 | |
| 1370 | DUP4 | |
| 1371 | RETURNDATACOPY | |
| 1372 | PUSH2 | 0x1348 |
| 1375 | DUP2 | |
| 1376 | DUP4 | |
| 1377 | PUSH2 | 0x40d5 |
| 137a | JUMP | |
| 137b | JUMPDEST | |
| 137c | SWAP7 | |
| 137d | SWAP1 | |
| 137e | SWAP2 | |
| 137f | SWAP3 | |
| 1380 | PUSH2 | 0x08a4 |
| 1383 | JUMP | |
| 1384 | JUMPDEST | |
| 1385 | PUSH0 | |
| 1386 | DUP1 | |
| 1387 | REVERT | |
| 1388 | JUMPDEST | |
| 1389 | CALLVALUE | |
| 138a | PUSH2 | 0x1384 |
| 138d | JUMPI | |
| 138e | PUSH1 | 0x20 |
| 1390 | CALLDATASIZE | |
| 1391 | PUSH1 | 0x03 |
| 1393 | NOT | |
| 1394 | ADD | |
| 1395 | SLT | |
| 1396 | PUSH2 | 0x1384 |
| 1399 | JUMPI | |
| 139a | PUSH2 | 0x13a1 |
| 139d | PUSH2 | 0x4021 |
| 13a0 | JUMP | |
| 13a1 | JUMPDEST | |
| 13a2 | PUSH1 | 0x40 |
| 13a4 | MLOAD | |
| 13a5 | PUSH2 | 0x13ad |
| 13a8 | DUP2 | |
| 13a9 | PUSH2 | 0x40b9 |
| 13ac | JUMP | |
| 13ad | JUMPDEST | |
| 13ae | PUSH0 | |
| 13af | DUP2 | |
| 13b0 | MSTORE | |
| 13b1 | PUSH1 | 0x20 |
| 13b3 | DUP2 | |
| 13b4 | ADD | |
| 13b5 | PUSH0 | |
| 13b6 | SWAP1 | |
| 13b7 | MSTORE | |
| 13b8 | PUSH1 | 0x40 |
| 13ba | DUP2 | |
| 13bb | ADD | |
| 13bc | PUSH0 | |
| 13bd | SWAP1 | |
| 13be | MSTORE | |
| 13bf | PUSH1 | 0x60 |
| 13c1 | DUP2 | |
| 13c2 | ADD | |
| 13c3 | PUSH0 | |
| 13c4 | SWAP1 | |
| 13c5 | MSTORE | |
| 13c6 | PUSH1 | 0x80 |
| 13c8 | DUP2 | |
| 13c9 | ADD | |
| 13ca | PUSH0 | |
| 13cb | SWAP1 | |
| 13cc | MSTORE | |
| 13cd | PUSH1 | 0xa0 |
| 13cf | DUP2 | |
| 13d0 | ADD | |
| 13d1 | PUSH0 | |
| 13d2 | SWAP1 | |
| 13d3 | MSTORE | |
| 13d4 | PUSH1 | 0xc0 |
| 13d6 | DUP2 | |
| 13d7 | ADD | |
| 13d8 | PUSH0 | |
| 13d9 | SWAP1 | |
| 13da | MSTORE | |
| 13db | PUSH1 | 0xe0 |
| 13dd | DUP2 | |
| 13de | ADD | |
| 13df | PUSH0 | |
| 13e0 | SWAP1 | |
| 13e1 | MSTORE | |
| 13e2 | PUSH2 | 0x0100 |
| 13e5 | DUP2 | |
| 13e6 | ADD | |
| 13e7 | PUSH0 | |
| 13e8 | SWAP1 | |
| 13e9 | MSTORE | |
| 13ea | PUSH2 | 0x0120 |
| 13ed | DUP2 | |
| 13ee | ADD | |
| 13ef | PUSH0 | |
| 13f0 | SWAP1 | |
| 13f1 | MSTORE | |
| 13f2 | PUSH2 | 0x0140 |
| 13f5 | DUP2 | |
| 13f6 | ADD | |
| 13f7 | PUSH0 | |
| 13f8 | SWAP1 | |
| 13f9 | MSTORE | |
| 13fa | PUSH2 | 0x0160 |
| 13fd | ADD | |
| 13fe | PUSH0 | |
| 13ff | SWAP1 | |
| 1400 | MSTORE | |
| 1401 | PUSH1 | 0x01 |
| 1403 | PUSH1 | 0xa0 |
| 1405 | SHL | |
| 1406 | PUSH1 | 0x01 |
| 1408 | SWAP1 | |
| 1409 | SUB | |
| 140a | AND | |
| 140b | PUSH0 | |
| 140c | MSTORE | |
| 140d | PUSH1 | 0x02 |
| 140f | PUSH1 | 0x20 |
| 1411 | MSTORE | |
| 1412 | PUSH1 | 0x40 |
| 1414 | PUSH0 | |
| 1415 | KECCAK256 | |
| 1416 | PUSH1 | 0x40 |
| 1418 | MLOAD | |
| 1419 | SWAP1 | |
| 141a | PUSH2 | 0x1422 |
| 141d | DUP3 | |
| 141e | PUSH2 | 0x40b9 |
| 1421 | JUMP | |
| 1422 | JUMPDEST | |
| 1423 | DUP1 | |
| 1424 | SLOAD | |
| 1425 | SWAP2 | |
| 1426 | DUP3 | |
| 1427 | DUP2 | |
| 1428 | MSTORE | |
| 1429 | PUSH1 | 0x01 |
| 142b | DUP3 | |
| 142c | ADD | |
| 142d | SLOAD | |
| 142e | PUSH1 | 0x20 |
| 1430 | DUP3 | |
| 1431 | ADD | |
| 1432 | SWAP1 | |
| 1433 | DUP2 | |
| 1434 | MSTORE | |
| 1435 | PUSH1 | 0x02 |
| 1437 | DUP4 | |
| 1438 | ADD | |
| 1439 | SLOAD | |
| 143a | PUSH1 | 0x40 |
| 143c | DUP4 | |
| 143d | ADD | |
| 143e | SWAP1 | |
| 143f | DUP2 | |
| 1440 | MSTORE | |
| 1441 | PUSH1 | 0x03 |
| 1443 | DUP5 | |
| 1444 | ADD | |
| 1445 | SLOAD | |
| 1446 | PUSH1 | 0x60 |
| 1448 | DUP5 | |
| 1449 | ADD | |
| 144a | SWAP1 | |
| 144b | DUP2 | |
| 144c | MSTORE | |
| 144d | PUSH1 | 0x04 |
| 144f | DUP6 | |
| 1450 | ADD | |
| 1451 | SLOAD | |
| 1452 | SWAP5 | |
| 1453 | PUSH1 | 0x80 |
| 1455 | DUP6 | |
| 1456 | ADD | |
| 1457 | SWAP6 | |
| 1458 | DUP7 | |
| 1459 | MSTORE | |
| 145a | PUSH1 | 0x05 |
| 145c | ADD | |
| 145d | SLOAD | |
| 145e | SWAP5 | |
| 145f | PUSH1 | 0xa0 |
| 1461 | DUP6 | |
| 1462 | ADD | |
| 1463 | PUSH1 | 0xff |
| 1465 | DUP8 | |
| 1466 | AND | |
| 1467 | DUP2 | |
| 1468 | MSTORE | |
| 1469 | PUSH1 | 0xc0 |
| 146b | DUP7 | |
| 146c | ADD | |
| 146d | SWAP2 | |
| 146e | DUP8 | |
| 146f | PUSH1 | 0x08 |
| 1471 | SHR | |
| 1472 | PUSH1 | 0xff |
| 1474 | AND | |
| 1475 | DUP4 | |
| 1476 | MSTORE | |
| 1477 | PUSH1 | 0xe0 |
| 1479 | DUP8 | |
| 147a | ADD | |
| 147b | SWAP4 | |
| 147c | DUP9 | |
| 147d | PUSH1 | 0x10 |
| 147f | SHR | |
| 1480 | PUSH1 | 0x01 |
| 1482 | PUSH1 | 0x01 |
| 1484 | PUSH1 | 0x40 |
| 1486 | SHL | |
| 1487 | SUB | |
| 1488 | AND | |
| 1489 | DUP6 | |
| 148a | MSTORE | |
| 148b | PUSH2 | 0x0100 |
| 148e | DUP9 | |
| 148f | ADD | |
| 1490 | SWAP6 | |
| 1491 | DUP10 | |
| 1492 | PUSH1 | 0x50 |
| 1494 | SHR | |
| 1495 | PUSH1 | 0x01 |
| 1497 | PUSH1 | 0x01 |
| 1499 | PUSH1 | 0x40 |
| 149b | SHL | |
| 149c | SUB | |
| 149d | AND | |
| 149e | DUP8 | |
| 149f | MSTORE | |
| 14a0 | PUSH2 | 0x0120 |
| 14a3 | DUP10 | |
| 14a4 | ADD | |
| 14a5 | SWAP8 | |
| 14a6 | DUP11 | |
| 14a7 | PUSH1 | 0x90 |
| 14a9 | SHR | |
| 14aa | PUSH1 | 0x01 |
| 14ac | PUSH1 | 0x01 |
| 14ae | PUSH1 | 0x40 |
| 14b0 | SHL | |
| 14b1 | SUB | |
| 14b2 | AND | |
| 14b3 | DUP10 | |
| 14b4 | MSTORE | |
| 14b5 | PUSH2 | 0x0140 |
| 14b8 | DUP11 | |
| 14b9 | ADD | |
| 14ba | SWAP10 | |
| 14bb | DUP12 | |
| 14bc | PUSH1 | 0xd0 |
| 14be | SHR | |
| 14bf | PUSH1 | 0xff |
| 14c1 | AND | |
| 14c2 | ISZERO | |
| 14c3 | ISZERO | |
| 14c4 | DUP12 | |
| 14c5 | MSTORE | |
| 14c6 | PUSH2 | 0x0160 |
| 14c9 | ADD | |
| 14ca | SWAP11 | |
| 14cb | PUSH1 | 0xd8 |
| 14cd | SHR | |
| 14ce | PUSH1 | 0xff |
| 14d0 | AND | |
| 14d1 | ISZERO | |
| 14d2 | ISZERO | |
| 14d3 | DUP12 | |
| 14d4 | MSTORE | |
| 14d5 | PUSH1 | 0x40 |
| 14d7 | MLOAD | |
| 14d8 | SWAP12 | |
| 14d9 | DUP13 | |
| 14da | MSTORE | |
| 14db | MLOAD | |
| 14dc | PUSH1 | 0x20 |
| 14de | DUP13 | |
| 14df | ADD | |
| 14e0 | MSTORE | |
| 14e1 | MLOAD | |
| 14e2 | PUSH1 | 0x40 |
| 14e4 | DUP12 | |
| 14e5 | ADD | |
| 14e6 | MSTORE | |
| 14e7 | MLOAD | |
| 14e8 | PUSH1 | 0x60 |
| 14ea | DUP11 | |
| 14eb | ADD | |
| 14ec | MSTORE | |
| 14ed | MLOAD | |
| 14ee | PUSH1 | 0x80 |
| 14f0 | DUP10 | |
| 14f1 | ADD | |
| 14f2 | MSTORE | |
| 14f3 | MLOAD | |
| 14f4 | PUSH1 | 0xff |
| 14f6 | AND | |
| 14f7 | PUSH1 | 0xa0 |
| 14f9 | DUP9 | |
| 14fa | ADD | |
| 14fb | MSTORE | |
| 14fc | MLOAD | |
| 14fd | PUSH1 | 0xff |
| 14ff | AND | |
| 1500 | PUSH1 | 0xc0 |
| 1502 | DUP8 | |
| 1503 | ADD | |
| 1504 | MSTORE | |
| 1505 | MLOAD | |
| 1506 | PUSH1 | 0x01 |
| 1508 | PUSH1 | 0x01 |
| 150a | PUSH1 | 0x40 |
| 150c | SHL | |
| 150d | SUB | |
| 150e | AND | |
| 150f | PUSH1 | 0xe0 |
| 1511 | DUP7 | |
| 1512 | ADD | |
| 1513 | MSTORE | |
| 1514 | MLOAD | |
| 1515 | PUSH1 | 0x01 |
| 1517 | PUSH1 | 0x01 |
| 1519 | PUSH1 | 0x40 |
| 151b | SHL | |
| 151c | SUB | |
| 151d | AND | |
| 151e | PUSH2 | 0x0100 |
| 1521 | DUP6 | |
| 1522 | ADD | |
| 1523 | MSTORE | |
| 1524 | MLOAD | |
| 1525 | PUSH1 | 0x01 |
| 1527 | PUSH1 | 0x01 |
| 1529 | PUSH1 | 0x40 |
| 152b | SHL | |
| 152c | SUB | |
| 152d | AND | |
| 152e | PUSH2 | 0x0120 |
| 1531 | DUP5 | |
| 1532 | ADD | |
| 1533 | MSTORE | |
| 1534 | MLOAD | |
| 1535 | ISZERO | |
| 1536 | ISZERO | |
| 1537 | PUSH2 | 0x0140 |
| 153a | DUP4 | |
| 153b | ADD | |
| 153c | MSTORE | |
| 153d | MLOAD | |
| 153e | ISZERO | |
| 153f | ISZERO | |
| 1540 | PUSH2 | 0x0160 |
| 1543 | DUP3 | |
| 1544 | ADD | |
| 1545 | MSTORE | |
| 1546 | PUSH2 | 0x0180 |
| 1549 | SWAP1 | |
| 154a | RETURN | |
| 154b | JUMPDEST | |
| 154c | CALLVALUE | |
| 154d | PUSH2 | 0x1384 |
| 1550 | JUMPI | |
| 1551 | PUSH0 | |
| 1552 | CALLDATASIZE | |
| 1553 | PUSH1 | 0x03 |
| 1555 | NOT | |
| 1556 | ADD | |
| 1557 | SLT | |
| 1558 | PUSH2 | 0x1384 |
| 155b | JUMPI | |
| 155c | PUSH1 | 0x12 |
| 155e | SLOAD | |
| 155f | PUSH1 | 0x40 |
| 1561 | MLOAD | |
| 1562 | PUSH1 | 0x01 |
| 1564 | PUSH1 | 0x01 |
| 1566 | PUSH1 | 0xa0 |
| 1568 | SHL | |
| 1569 | SUB | |
| 156a | SWAP1 | |
| 156b | SWAP2 | |
| 156c | AND | |
| 156d | DUP2 | |
| 156e | MSTORE | |
| 156f | PUSH1 | 0x20 |
| 1571 | SWAP1 | |
| 1572 | RETURN | |
| 1573 | JUMPDEST | |
| 1574 | CALLVALUE | |
| 1575 | PUSH2 | 0x1384 |
| 1578 | JUMPI | |
| 1579 | PUSH1 | 0x20 |
| 157b | CALLDATASIZE | |
| 157c | PUSH1 | 0x03 |
| 157e | NOT | |
| 157f | ADD | |
| 1580 | SLT | |
| 1581 | PUSH2 | 0x1384 |
| 1584 | JUMPI | |
| 1585 | PUSH1 | 0x20 |
| 1587 | PUSH2 | 0x046b |
| 158a | PUSH2 | 0x1591 |
| 158d | PUSH2 | 0x4021 |
| 1590 | JUMP | |
| 1591 | JUMPDEST | |
| 1592 | PUSH2 | 0x48b9 |
| 1595 | JUMP | |
| 1596 | JUMPDEST | |
| 1597 | CALLVALUE | |
| 1598 | PUSH2 | 0x1384 |
| 159b | JUMPI | |
| 159c | PUSH0 | |
| 159d | CALLDATASIZE | |
| 159e | PUSH1 | 0x03 |
| 15a0 | NOT | |
| 15a1 | ADD | |
| 15a2 | SLT | |
| 15a3 | PUSH2 | 0x1384 |
| 15a6 | JUMPI | |
| 15a7 | PUSH1 | 0x20 |
| 15a9 | PUSH1 | 0x40 |
| 15ab | MLOAD | |
| 15ac | PUSH2 | 0x0400 |
| 15af | DUP2 | |
| 15b0 | MSTORE | |
| 15b1 | RETURN | |
| 15b2 | JUMPDEST | |
| 15b3 | CALLVALUE | |
| 15b4 | PUSH2 | 0x1384 |
| 15b7 | JUMPI | |
| 15b8 | PUSH1 | 0xa0 |
| 15ba | CALLDATASIZE | |
| 15bb | PUSH1 | 0x03 |
| 15bd | NOT | |
| 15be | ADD | |
| 15bf | SLT | |
| 15c0 | PUSH2 | 0x1384 |
| 15c3 | JUMPI | |
| 15c4 | PUSH2 | 0x15cb |
| 15c7 | PUSH2 | 0x4021 |
| 15ca | JUMP | |
| 15cb | JUMPDEST | |
| 15cc | POP | |
| 15cd | PUSH2 | 0x15d4 |
| 15d0 | PUSH2 | 0x4037 |
| 15d3 | JUMP | |
| 15d4 | JUMPDEST | |
| 15d5 | POP | |
| 15d6 | PUSH1 | 0x44 |
| 15d8 | CALLDATALOAD | |
| 15d9 | PUSH1 | 0x01 |
| 15db | PUSH1 | 0x01 |
| 15dd | PUSH1 | 0x40 |
| 15df | SHL | |
| 15e0 | SUB | |
| 15e1 | DUP2 | |
| 15e2 | GT | |
| 15e3 | PUSH2 | 0x1384 |
| 15e6 | JUMPI | |
| 15e7 | PUSH2 | 0x15f4 |
| 15ea | SWAP1 | |
| 15eb | CALLDATASIZE | |
| 15ec | SWAP1 | |
| 15ed | PUSH1 | 0x04 |
| 15ef | ADD | |
| 15f0 | PUSH2 | 0x3faf |
| 15f3 | JUMP | |
| 15f4 | JUMPDEST | |
| 15f5 | POP | |
| 15f6 | POP | |
| 15f7 | PUSH1 | 0x64 |
| 15f9 | CALLDATALOAD | |
| 15fa | PUSH1 | 0x01 |
| 15fc | PUSH1 | 0x01 |
| 15fe | PUSH1 | 0x40 |
| 1600 | SHL | |
| 1601 | SUB | |
| 1602 | DUP2 | |
| 1603 | GT | |
| 1604 | PUSH2 | 0x1384 |
| 1607 | JUMPI | |
| 1608 | PUSH2 | 0x1615 |
| 160b | SWAP1 | |
| 160c | CALLDATASIZE | |
| 160d | SWAP1 | |
| 160e | PUSH1 | 0x04 |
| 1610 | ADD | |
| 1611 | PUSH2 | 0x3faf |
| 1614 | JUMP | |
| 1615 | JUMPDEST | |
| 1616 | POP | |
| 1617 | POP | |
| 1618 | PUSH1 | 0x84 |
| 161a | CALLDATALOAD | |
| 161b | PUSH1 | 0x01 |
| 161d | PUSH1 | 0x01 |
| 161f | PUSH1 | 0x40 |
| 1621 | SHL | |
| 1622 | SUB | |
| 1623 | DUP2 | |
| 1624 | GT | |
| 1625 | PUSH2 | 0x1384 |
| 1628 | JUMPI | |
| 1629 | PUSH2 | 0x1636 |
| 162c | SWAP1 | |
| 162d | CALLDATASIZE | |
| 162e | SWAP1 | |
| 162f | PUSH1 | 0x04 |
| 1631 | ADD | |
| 1632 | PUSH2 | 0x404d |
| 1635 | JUMP | |
| 1636 | JUMPDEST | |
| 1637 | POP | |
| 1638 | POP | |
| 1639 | PUSH1 | 0x40 |
| 163b | MLOAD | |
| 163c | PUSH4 | 0xbc197c81 |
| 1641 | PUSH1 | 0xe0 |
| 1643 | SHL | |
| 1644 | DUP2 | |
| 1645 | MSTORE | |
| 1646 | PUSH1 | 0x20 |
| 1648 | SWAP1 | |
| 1649 | RETURN | |
| 164a | JUMPDEST | |
| 164b | CALLVALUE | |
| 164c | PUSH2 | 0x1384 |
| 164f | JUMPI | |
| 1650 | PUSH0 | |
| 1651 | CALLDATASIZE | |
| 1652 | PUSH1 | 0x03 |
| 1654 | NOT | |
| 1655 | ADD | |
| 1656 | SLT | |
| 1657 | PUSH2 | 0x1384 |
| 165a | JUMPI | |
| 165b | PUSH1 | 0x20 |
| 165d | PUSH1 | 0x40 |
| 165f | MLOAD | |
| 1660 | PUSH32 | 0x6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f8168 |
| 1681 | DUP2 | |
| 1682 | MSTORE | |
| 1683 | RETURN | |
| 1684 | JUMPDEST | |
| 1685 | CALLVALUE | |
| 1686 | PUSH2 | 0x1384 |
| 1689 | JUMPI | |
| 168a | PUSH1 | 0x20 |
| 168c | CALLDATASIZE | |
| 168d | PUSH1 | 0x03 |
| 168f | NOT | |
| 1690 | ADD | |
| 1691 | SLT | |
| 1692 | PUSH2 | 0x1384 |
| 1695 | JUMPI | |
| 1696 | PUSH1 | 0x01 |
| 1698 | PUSH1 | 0x01 |
| 169a | PUSH1 | 0xa0 |
| 169c | SHL | |
| 169d | SUB | |
| 169e | PUSH2 | 0x16a5 |
| 16a1 | PUSH2 | 0x4021 |
| 16a4 | JUMP | |
| 16a5 | JUMPDEST | |
| 16a6 | AND | |
| 16a7 | PUSH0 | |
| 16a8 | MSTORE | |
| 16a9 | PUSH1 | 0x03 |
| 16ab | PUSH1 | 0x20 |
| 16ad | MSTORE | |
| 16ae | PUSH2 | 0x16d0 |
| 16b1 | PUSH2 | 0x16bc |
| 16b4 | PUSH1 | 0x40 |
| 16b6 | PUSH0 | |
| 16b7 | KECCAK256 | |
| 16b8 | PUSH2 | 0x42f4 |
| 16bb | JUMP | |
| 16bc | JUMPDEST | |
| 16bd | PUSH1 | 0x40 |
| 16bf | MLOAD | |
| 16c0 | SWAP2 | |
| 16c1 | DUP3 | |
| 16c2 | SWAP2 | |
| 16c3 | PUSH1 | 0x20 |
| 16c5 | DUP4 | |
| 16c6 | MSTORE | |
| 16c7 | PUSH1 | 0x20 |
| 16c9 | DUP4 | |
| 16ca | ADD | |
| 16cb | SWAP1 | |
| 16cc | PUSH2 | 0x407a |
| 16cf | JUMP | |
| 16d0 | JUMPDEST | |
| 16d1 | SUB | |
| 16d2 | SWAP1 | |
| 16d3 | RETURN | |
| 16d4 | JUMPDEST | |
| 16d5 | CALLVALUE | |
| 16d6 | PUSH2 | 0x1384 |
| 16d9 | JUMPI | |
| 16da | PUSH1 | 0x40 |
| 16dc | CALLDATASIZE | |
| 16dd | PUSH1 | 0x03 |
| 16df | NOT | |
| 16e0 | ADD | |
| 16e1 | SLT | |
| 16e2 | PUSH2 | 0x1384 |
| 16e5 | JUMPI | |
| 16e6 | PUSH1 | 0x01 |
| 16e8 | PUSH1 | 0x01 |
| 16ea | PUSH1 | 0xa0 |
| 16ec | SHL | |
| 16ed | SUB | |
| 16ee | PUSH2 | 0x16f5 |
| 16f1 | PUSH2 | 0x4021 |
| 16f4 | JUMP | |
| 16f5 | JUMPDEST | |
| 16f6 | AND | |
| 16f7 | PUSH0 | |
| 16f8 | MSTORE | |
| 16f9 | PUSH1 | 0x15 |
| 16fb | PUSH1 | 0x20 |
| 16fd | MSTORE | |
| 16fe | PUSH1 | 0x20 |
| 1700 | PUSH1 | 0x01 |
| 1702 | DUP1 | |
| 1703 | PUSH1 | 0xa0 |
| 1705 | SHL | |
| 1706 | SUB | |
| 1707 | PUSH1 | 0x40 |
| 1709 | PUSH0 | |
| 170a | KECCAK256 | |
| 170b | SLOAD | |
| 170c | AND | |
| 170d | DUP1 | |
| 170e | ISZERO | |
| 170f | ISZERO | |
| 1710 | SWAP1 | |
| 1711 | DUP2 | |
| 1712 | PUSH2 | 0x1721 |
| 1715 | JUMPI | |
| 1716 | JUMPDEST | |
| 1717 | POP | |
| 1718 | PUSH1 | 0x40 |
| 171a | MLOAD | |
| 171b | SWAP1 | |
| 171c | ISZERO | |
| 171d | ISZERO | |
| 171e | DUP2 | |
| 171f | MSTORE | |
| 1720 | RETURN | |
| 1721 | JUMPDEST | |
| 1722 | PUSH2 | 0x172f |
| 1725 | SWAP2 | |
| 1726 | POP | |
| 1727 | PUSH1 | 0x24 |
| 1729 | CALLDATALOAD | |
| 172a | SWAP1 | |
| 172b | PUSH2 | 0x44ca |
| 172e | JUMP | |
| 172f | JUMPDEST | |
| 1730 | DUP3 | |
| 1731 | PUSH2 | 0x1716 |
| 1734 | JUMP |