Final Chainexplorer Φ₀ · Final Chain · 20678
it

Contratto

0xa2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6

Indirizzo
0xa2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6
Tipo
contratto verificato FinalIdentityRegistry
Saldo
0 vETH
Nonce
1
Codice
24,120 byte codehash 0xc7fc0a1d2c10a7246f5f4455f89c75fc5dedf707edb37a1182c8e5a5a572946d

albero dei conti

Albero
1 · conti
Presente
nessuna foglia
Chiave
0x8ed7379e1912b74da297b1921c3fd6e9bd3568453b2bb6ffaacf4e79d0fffa88
Radice live
0xe7eb646dfe4cfe9c975fd970c7c565c8d96e5c1edae4440e5f6a2560325fb328
Questo indirizzo non ha una foglia nell'albero dei conti. Ogni Final Wallet — identità di servizio incluse — ne ha una, quindi una foglia assente significa un account ordinario, non una wallet.
transazionieventitrasferimenti di tokencontratto

sorgente verificato

Contratto
FinalIdentityRegistry corrispondenza esatta
Compilatore
v0.8.33+commit.64118f21
Ottimizzatore
attivo · 200 passaggi
Versione EVM
prague
Verificato
2026-09-07T05:08:56.460Z
Provenienza
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
//
// @author Final DeFi
// @version 1.0.0
pragma solidity ^0.8.20;

import {FinalChainPrecompiles} from "./FinalChainPrecompiles.sol";
import {FinalChainTime} from "./FinalChainTime.sol";

/**
 * @title FinalCertificate
 * @notice Reads a Final Certificate (`.fcert`, schema v3) on chain.
 *
 * @dev Final Chain only — it needs the SHA3-256 precompile, because the schema
 * hashes with FIPS-202 SHA3 and the EVM has `keccak256`, which is a different
 * function.
 *
 * ## Why the chain parses this at all
 *
 * `FinalIdentityRegistry.registerWithCertificate` used to take the TBS bytes
 * AND the public keys as separate arguments. It derived `certHash` from the
 * bytes, which sounds like verification and is not: nothing compared the keys
 * to the certificate, so a registrar could bind any certificate to any keypair.
 * The registry would then 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 way for
 * two arguments to disagree.
 *
 * ## The SubjectKeyId check
 *
 * The schema defines `SubjectKeyId` as SHA3-256 of the `PublicKeyBlock`. Having
 * parsed the block, this recomputes that digest and compares. The field is
 * inside the TBS, so it is covered by the CA's signatures — which makes the
 * check a statement about what the CA attested, not merely about internal
 * consistency of bytes the caller supplied.
 *
 * ## What this does NOT do
 *
 * It does not verify the CA's signatures over the TBS, and it does not walk the
 * chain to the root. Both are possible here — the precompiles verify ML-DSA-87
 * and SLH-DSA-SHAKE-256s — and both are deliberately out of scope for the
 * registry's bootstrap path, where the registrar is the party that issued the
 * certificate in the first place. `verifyIssuerSignatures` below is provided for
 * callers that need it, and the identity registry uses it once a CA is itself
 * registered.
 */
library FinalCertificate {
    /// `"PQCF"`.
    uint32 internal constant MAGIC = 0x50514346;
    /// The current wire generation — v5's `Version = 2` (chain-attested
    /// issuance; ruled 2026-09-01). The v4 wire (`Version = 1`) stays
    /// PARSEABLE so pre-cutover artifacts still read; encoders write 2.
    /// fails to parse rather than being reinterpreted: `pqKeysHash` and every
    /// wallet address derive from this exact layout.
    uint32 internal constant VERSION = 2;
    /// The v4 generation, accepted on parse for pre-cutover artifacts.
    uint32 internal constant VERSION_V4 = 1;

    /// @notice The 0x0102 Institution identity extension (issuer profile).
    uint16 internal constant EXT_INSTITUTION = 0x0102;

    /// Algorithm ids ARE the FIPS numbers, in one space for signatures and KEMs
    /// — the same ids the quorum wire format and the backend registry use, and
    /// the numbers the precompile addresses end in.
    /// ML-KEM-1024 (FIPS 203), the lattice half of the encapsulation pair.
    uint16 internal constant ALG_ML_KEM_1024 = 0x0003;
    /// ML-DSA-87 (FIPS 204). Transaction class.
    uint16 internal constant ALG_ML_DSA_87 = 0x0004;
    /// SLH-DSA-SHAKE-256s (FIPS 205). Access class, and the seal.
    uint16 internal constant ALG_SLH_DSA_SHAKE_256S = 0x0005;
    /// FN-DSA (FIPS 206). Reserved: no implementation, never accepted.
    uint16 internal constant ALG_FN_DSA = 0x0006;
    /// HQC-5 (FIPS 207), the code-based half of the encapsulation pair.
    uint16 internal constant ALG_HQC_5 = 0x0007;

    /// Certificate signing. Says which key to verify WITH; it grants nothing —
    /// that comes from `Depth` and `MaxDelegationDepth`.
    uint16 internal constant PURPOSE_CERT_SIGNING = 0x0004;

    /// The wallet's four slots, in two stages of two.
    ///
    /// A certificate carries ONE stage, never all four. The stage is what gets
    /// 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.
    ///
    /// 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;
    uint16 internal constant PURPOSE_ACTIVE_ACCESS = 0x0011;
    uint16 internal constant PURPOSE_RECOVERY_TX = 0x0012;
    uint16 internal constant PURPOSE_RECOVERY_ACCESS = 0x0013;
    /// @dev v4's encapsulation purposes. Parsed, and each stage's pair is
    ///      resolved alongside its signing pair — `FinalIdentityRegistry` then
    ///      stores them so a sender can encapsulate to a registered party
    ///      without a second lookup somewhere less authoritative.
    ///
    ///      They were declared and skipped for one release, which is how the
    ///      registry's four encapsulation-key mappings ended up read in three
    ///      places and written in none: `kemCommitments` hashed the empty
    ///      string for every account and `kemKeysOf` returned nothing.
    uint16 internal constant PURPOSE_ACTIVE_KEM = 0x0014;
    uint16 internal constant PURPOSE_RECOVERY_KEM = 0x0015;
    /// @dev The seal: a second SLH-DSA-SHAKE-256s key, distinct from the access
    ///      key, that co-signs execution-class quorum decisions. Carried by
    ///      SERVICE certificates only — a user's wallet never seals — and
    ///      optional in the schema, so a certificate without it parses
    ///      unchanged. Outside `keysHash`: a seal is operational, rotated by
    ///      issuing a new live certificate, and it must not move a wallet
    ///      address it plays no part in.
    uint16 internal constant PURPOSE_ACTIVE_SEAL = 0x0016;

    /// @dev A purpose no certificate can carry, so `parse` can be told "this
    ///      stage has no encapsulation slot" without a second boolean. `0xffff`
    ///      is outside the registry and reserved by being used here.
    uint16 internal constant NO_KEM_PURPOSE = 0xffff;

    /// Nanoseconds per second. The schema's validity fields are nanoseconds and
    /// `block.timestamp` is seconds; a comparison across the two units is a bug
    /// waiting for the first certificate anybody actually checks.
    /// @dev The schema stamps validity in NANOseconds and this chain's clock is
    ///      MILLIseconds, so a certificate converts down by 1e6 rather than by
    ///      1e9. It was 1e9 — seconds — which made every `notBefore` look 1000x
    ///      too small against `block.timestamp` and every certificate
    ///      permanently "already valid", including one issued for the future.
    uint64 internal constant NS_PER_MILLISECOND = FinalChainTime.NS_PER_MILLISECOND;

    /// @notice What the chain keeps out of one certificate.
    struct Parsed {
        bytes32 certHash;
        bytes32 serial;
        /// keccak256 of the IssuerDN bytes, for the chain-issuer pin: a
        /// chain-attested certificate carries the ruled constant DN and the
        /// registry compares hashes rather than strings.
        bytes32 issuerDnHash;
        /// The SubjectDN bytes verbatim — the jurisdiction rule reads its
        /// `C=` component at issuer registration.
        bytes subjectDn;
        /// The 0x0102 Institution extension VALUE, when present; empty
        /// otherwise. Issuer registration parses jurisdiction out of it.
        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;
        uint8 depth;
        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;
    }

    error BadMagic(uint32 got);
    error BadVersion(uint32 got);
    error Truncated(uint256 needed, uint256 got);
    error SubjectKeyIdMismatch(bytes32 derived, bytes32 declared);
    error MissingSlot(uint16 purpose);
    error WrongAlgorithmForSlot(uint16 purpose, uint16 algorithm);
    error DuplicateKey(uint16 purpose, uint16 algorithm);
    error KeysNotSorted();
    error BadKeyLength(uint16 algorithm, uint256 length);
    error InvalidDepth(uint8 depth, uint8 maxDelegationDepth);
    error ValidityInverted(uint64 notBefore, uint64 notAfter);

    /**
     * @notice Parse and self-check a `TBSCertificate`.
     * @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.
     *
     * @dev Checking for a CAPABILITY rather than a type is the 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.
     */
    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 generations. v4 artifacts predate chain-attested issuance
        // and still parse — supersession is handled at admission (PoP and the
        // chain-issuer pins), not by refusing to read history.
        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: `activeTransaction` + `activeAccess`.
    /// @dev `external`, like the other three entry points below: the registry
    /// sits against the EIP-170 ceiling and the TBS parser is its single
    /// largest inlined dependency, so the four doors it actually calls are
    /// DEPLOY-LINKED — the library is one more contract in the plane's fixed
    /// nonce-0 deploy order (doctrine §2 of `arch/final-chain-regenesis.md`),
    /// its address baked immutably into the registry's bytecode. A linked
    /// library is code, not a key: nothing can repoint it after deployment.
    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, an ordinary action for that
    /// account, uses the live access key. Keeping the two stages in separate
    /// certificates is what makes that boundary something a verifier can see.
    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 CA certificate, whose two keys are both cert-signing.
    /// @dev No encapsulation purpose: a CA signs and is never sealed to, so
    /// `PURPOSE_ACTIVE_KEM` is passed as a value the loop can never match. A
    /// CA certificate carrying encapsulation keys would parse them into slots
    /// `_write` then discards, which is a shape worth refusing to have.
    function parseCa(bytes calldata tbs) external view returns (Parsed memory) {
        return parse(tbs, PURPOSE_CERT_SIGNING, PURPOSE_CERT_SIGNING, NO_KEM_PURPOSE);
    }

    /**
     * @notice Verify a CA's dual signature over `tbs`.
     * @dev Both must verify, not either. Two signatures under two different
     * hardness assumptions is the entire reason the schema carries two, and
     * accepting one would collapse that to whichever family breaks first.
     */
    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);
    }

    function _need(bytes calldata tbs, uint256 upto) private pure {
        if (tbs.length < upto) revert Truncated(upto, tbs.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);
    }

    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
//
// @author Final DeFi
// @version 1.0.0
pragma solidity ^0.8.20;

/**
 * @title FinalChainPrecompiles
 * @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 `final-reth`, the node binary in `FinalBackend/vendor/reth/final/`, 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
pragma solidity ^0.8.20;

/**
 * @title FinalChainTime
 * @notice **On Final Chain, `block.timestamp` is MILLISECONDS.**
 *
 * @dev Every other EVM chain stamps seconds. This one cannot: it mints a block
 * every 100 ms and Ethereum requires block timestamps to strictly increase, so
 * a second-denominated clock would run out of distinct values ten times over
 * per second. Milliseconds is the deliberate choice, and it is a property of
 * the CHAIN — `final-reth` — not of any contract here.
 *
 * Every duration on this chain is therefore in milliseconds, and this library
 * exists so that is stated in one place instead of assumed in fifteen.
 *
 * ## How this was found, which is the reason for the naming rules below
 *
 * It was not found by the test suite. Foundry's `block.timestamp` is seconds,
 * so all 1249 tests agreed with the contracts and every one of them was wrong
 * about the chain they deploy to. It was found the first time anything
 * exercised a deadline against the real chain — a posted intent, which reverted
 * `DeadlinePassed` against a header whose deadline had been computed from wall
 * time.
 *
 * What was actually broken was worse than a posting. `rotationInitiatedAt` is
 * written from `block.timestamp` and compared against `rotationInitiatedAt +
 * delaySeconds`: a millisecond clock plus a second-denominated delay. The
 * 24-hour default recovery delay elapsed in **86 seconds**, and the two-year
 * dormancy threshold in about seventeen hours. That delay is the thing standing
 * between a stolen recovery key and an account.
 *
 * Nothing had noticed because nothing time-dependent had ever run: `walletCount`
 * is 0, `FinalBundleLog.size` is 0, and no intent had been posted.
 *
 * ## The naming rule
 *
 * A field or constant carrying a duration or an instant on this chain ends in
 * `Ms`. Not decoration — the bug was a field named `delaySeconds` that held
 * milliseconds, and a name that lies is how the next reader reintroduces it.
 * `SECONDS` names are gone from `contracts/finalchain/` and must not come back.
 *
 * Solidity's `hours` / `days` suffixes are still the clearest way to write a
 * duration, so they are written as `24 hours * MS_PER_SECOND` rather than as a
 * literal: the intent stays readable and the unit stays explicit.
 */
library FinalChainTime {
    /// @notice Milliseconds per second. The whole conversion, named once.
    uint64 internal constant MS_PER_SECOND = 1_000;

    /// @notice Milliseconds per nanosecond divisor — the certificate schema
    /// stamps validity in NANOseconds, so a certificate converts down to this
    /// chain's clock rather than up.
    uint64 internal constant NS_PER_MILLISECOND = 1_000_000;

    /// @notice This chain's clock, stated as a function so a caller reads the
    /// unit rather than remembering it.
    /// @dev No arithmetic. It exists to make `FinalChainTime.nowMs()` the thing
    /// people write, which is self-describing where `block.timestamp` is not.
    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
//
// @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";

/**
 * @title FinalIdentityRegistry
 * @notice Who every party in the system IS, on chain, with its certificate.
 *
 * @dev Final Chain only. Every service, every co-signer, every certificate
 * authority and every operator has one record here, and that record carries the
 * party's actual public keys — not commitments to them.
 *
 * ## Why the full key and not a hash
 *
 * 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: 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` reads them from storage, and
 * "which key is co-signer 3" has exactly one answer. That question previously
 * had three: an environment variable, an on-chain roster, and a Secret Manager
 * entry, with nothing comparing them. Every configuration failure in this
 * program has been those three disagreeing.
 *
 * ## The certificate is the record, not a pointer to one
 *
 * `certHash` is `SHA3-256(TBSCertificate)` — the certificate's own identity per
 * the v3 schema, and the handle revocation is keyed on. The schema says
 * revocation exists "on Final Chain only"; this is that place.
 *
 `registerWithCertificate` takes the TBS bytes and **reads everything out of
 * them**: the digest, the serial, the key identifiers, the depth pair, the
 * validity window and both public keys. It takes no key arguments at all.
 *
 * That is a correction, and the version it replaces is worth naming because it
 * looked right. It took the TBS *and* the keys, derived `certHash` from the
 * TBS, and never compared the two — so a registrar could bind any certificate
 * to any keypair, and the registry would hold a key the certificate does not
 * contain. Every signature that key produced would then verify against a
 * certificate that never authorised it.
 *
 * ## The root is the first record on this chain, not a file somewhere
 *
 * The schema says Final Chain is the only root CA and that "the root is pinned,
 * not distributed" — chain validation terminates at Final Chain **by identity**,
 * never by finding a self-signed certificate in a local store.
 *
 * `registerRoot` is that pin, and it is the only entry point that accepts a
 * certificate without checking an issuer's signature. It takes a depth-0,
 * self-issued certificate from the bootstrap admin, once. Everything after it
 * is `registerWithCertificate`, which **verifies the issuer's ML-DSA and
 * SLH-DSA signatures on chain, through the precompiles**, against the issuer's
 * own registered keys, and checks that the child's `AuthorityKeyId` is the
 * issuer's `SubjectKeyId` and that the issuer's depth admits it.
 *
 * So there is no path by which a key enters this registry unattested. Not
 * "a registrar should only register certified keys" — a registrar *cannot*
 * register anything else.
 *
 * ## Roles are a bitmask
 *
 * One party is legitimately several things — a co-signer that is also a
 * publisher, an operator that is also a guardian. A single enum would force
 * either duplicate records for one key (two sources of truth about one party)
 * or a role hierarchy nobody agrees on. A mask has neither problem, and a
 * quorum asks "does this account carry ROLE_X" rather than "is this account an
 * X", which is the same distinction the certificate schema draws when it says
 * verifiers check for capabilities and never for types.
 *
 * ## 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, an LMS key,
 * the registrar threshold itself) and every state-plane configuration change
 * that routes 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 exception, and it is the only one: while it is
 * open the bootstrap admin writes alone, because every roster has to be
 * installed by someone before it can install itself.
 *
 * ## The sender is not the account
 *
 * Final Chain transactions are type 0x46, signed by ML-DSA-87, and the node
 * derives `msg.sender` from the key: `keccak256(0x04 ‖ publicKey)[12:]`. That
 * address pays gas and holds no authority. {accountOfSender} binds it to the
 * identity whose `activeTransaction` 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 rather than the roster.
 */
/// @dev Domain for a stage's encapsulation commitment. Byte-equal to
/// `FinalWalletFactory.DOMAIN_KEM_BUNDLE` and to `DOMAIN_KEM_BUNDLE_PREIMAGE` in
/// the issuer; three derivations of one word, and a mismatch in any of them is a
/// certificate that verifies nowhere.
bytes32 constant DOMAIN_KEM_BUNDLE = keccak256("FINAL_KEM_BUNDLE_v01");

/// @dev Tree 8's leaf domain — byte-equal to
/// `IdentityRootModule.DOMAIN_IDENTITY_LEAF` on every execution chain.
/// Restated rather than imported because the module lives on other chains and
/// there is no import that would make them one value; the cross-contract
/// parity test pins the pair. The `_PQ_` spelling is historical and FROZEN:
/// the premined vanity certificates were mined against this exact constant,
/// and the leaf it derives is the `certHash` inside every wallet's CREATE2
/// derivation.
bytes32 constant DOMAIN_IDENTITY_LEAF = keccak256("FINAL_IDENTITY_LEAF_PQ_v01");

/// @dev D7 (ruled 2026-09-01): ISSUER records project into tree 8 under their
/// own domain — `DOMAIN_ISSUER_LEAF ‖ certHash ‖ version ‖ issuerTreeRoot` —
/// so an issuer is stapleable for offline licence verification while the
/// distinct domain keeps its leaf out of wallet admission (the gateway folds
/// with the wallet domain, so an issuer leaf can never satisfy
/// `verifyIdentityCert`). `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 the fixed-depth insertion-ordered state
/// trees cannot prove non-inclusion.
bytes32 constant DOMAIN_ISSUER_LEAF = keccak256("FINAL_ISSUER_LEAF_v01");

/// @dev Chain-issuer constants (ruled 2026-09-01, amended same day: C-less).
/// The chain is the issuer but holds no keypair, so every chain-attested
/// certificate carries these two NAMED values in its issuer fields — required
/// by the wire format, verifying nothing, covered by `certHash`. The DN is
/// deliberately env-agnostic AND jurisdiction-silent: the issuer is the
/// worldwide network, not a legal entity, and an env-specific DN would fork
/// `certHash` per environment. Reference implementation:
/// `dashboard/public/fcert.js` (`CHAIN_ISSUER_DN`, `CHAIN_AUTHORITY_KEY_ID`);
/// `docs/developers/certificate-schema.md` § Chain-issuer constants.
bytes32 constant CHAIN_ISSUER_DN_HASH = keccak256("CN=Final Chain,O=Final DeFi");

/// @dev `SHA3-256(utf8("FINAL_CHAIN_AUTHORITY_v01"))` — a DOMAIN constant, not
/// a key digest (the chain has no PublicKeyBlock). Precomputed because the
/// mock SHA3 precompile under Foundry is deliberately not the real function;
/// pinned against `hashlib.sha3_256` and the dashboard's value by test.
/// Zero-length AuthorityKeyId stays reserved for the retired genesis root
/// alone and is admitted nowhere.
bytes32 constant CHAIN_AUTHORITY_KEY_ID =
    0x9a6a5d8139ad2d28957698330aaa691017dba7dc80eb7cbec585239fb680bbab;

/// @notice The identity tree's projection door on `FinalStateTrees`. 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. Same pattern as `IChainSource` on the trees side.
interface IIdentityLeafSink {
    function syncIdentityLeaves(address[] calldata accounts) external;
}

/// @notice `FinalRevocationLog`'s recording door, same narrow-interface
/// reasoning. `recorded` is read first so a fingerprint someone already
/// recorded permissionlessly cannot revert the registry mutation feeding it.
interface IRevocationRecorder {
    function record(bytes32 signerId) external;
    function recorded(bytes32 signerId) external view returns (bool);
}

contract FinalIdentityRegistry {
    // ---------------------------------------------------------------- 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

    /// @dev One per membership mutation, so an approval to grant a role can
    /// never be replayed as one to revoke. The registry is its own verifying
    /// contract for these.
    bytes32 public constant DOMAIN_REGISTER_WALLET = keccak256("FINAL_REGISTRY_REGISTER_WALLET_v01");
    bytes32 public constant DOMAIN_REGISTER_ISSUER = keccak256("FINAL_REGISTRY_REGISTER_ISSUER_v01");
    /// @notice The admission proof-of-possession digest domain (schema §v5).
    /// 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.
    bytes32 public constant DOMAIN_IDENTITY_ADMISSION = keccak256("FINAL_IDENTITY_ADMISSION_v01");
    /// @notice Root-plane global certificate revocation (D5).
    bytes32 public constant DOMAIN_REVOKE_CERTIFICATE =
        keccak256("FINAL_REGISTRY_REVOKE_CERTIFICATE_v01");
    /// @notice The ISSUING identity's certificate-revocation digest domain.
    bytes32 public constant DOMAIN_ISSUER_CERT_REVOCATION =
        keccak256("FINAL_ISSUER_CERT_REVOCATION_v01");
    bytes32 public constant DOMAIN_REGISTER_LMS_KEY = keccak256("FINAL_REGISTRY_REGISTER_LMS_KEY_v01");
    bytes32 public constant DOMAIN_SET_ROLES = keccak256("FINAL_REGISTRY_SET_ROLES_v01");
    bytes32 public constant DOMAIN_REVOKE = keccak256("FINAL_REGISTRY_REVOKE_v01");
    bytes32 public constant DOMAIN_SET_REGISTRAR_THRESHOLD =
        keccak256("FINAL_REGISTRY_SET_REGISTRAR_THRESHOLD_v01");

    /// @dev The algorithm id the sender derivation is domain-separated by:
    /// ML-DSA-87, FIPS 204, the only algorithm the transaction envelope admits.
    uint8 private constant ENVELOPE_ALG_ML_DSA_87 = 4;

    // ------------------------------------------------------------- storage

    /**
     * @notice One party's on-chain identity.
     * @dev `version` increments on every mutation and is what a rotation is:
     * the record is replaced, not appended to, and the version is how a reader
     * on another chain knows which of two copies it saw 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;
        /// Seconds since epoch. The schema's TBS is nanoseconds; the conversion
        /// happens off chain because block timestamps are seconds and a
        /// comparison across units is a bug waiting for a leap.
        /// @dev MILLISECONDS — this chain's clock. See `FinalChainTime`.
        uint64 notBefore;
        /// Seconds since epoch, or 0 for "never expires" — which the schema
        /// allows and personal identity certificates use.
        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;
    }

    /**
     * @notice A hash-based (LMS) signing key held by a registered account.
     *
     * The protocol plane's quorums verify LMS, not ML-DSA: an execution chain
     * has no PQ precompiles, so `FinalRootAuthority` checks a keccak hash loop
     * instead (`arch/hash-based-authority.md`). Those keys are the authority
     * over `masterRoot`, and therefore over PQ execution — which makes "who
     * holds signer 0x39bb…?" 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 LMS signing key for an account, if it holds one.
    /// @dev One slot per (account, chain) — LMS-01. `nextLeaf` on an
    /// authority is a complete single-use counter 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.
    mapping(address account => mapping(uint64 chainId => LmsKey)) private _lmsKey;
    /// @notice Which account a signer fingerprint belongs to. This is the
    /// lookup the whole record exists for: a gateway roster names fingerprints
    /// and nothing else, so without it the keys are unattributable.
    /// @dev What a fingerprint is bound to: the account that holds it and the
    /// chain it signs for — one slot, written once at registration and left in
    /// place when superseded (attribution is history). The chain names the
    /// (account, chain) slot `lmsSignerIsLive` resolves against.
    // NOTE: this contract sits ~13 bytes under EIP-170 (24,563 of 24,576 at
    // the pinned optimizer settings). The next feature here pays for itself
    // in bytecode first — see the LMS-binding merge and the off-chain
    // zero-chain check for what that looks like.
    struct LmsBinding {
        address account;
        uint64 chainId;
    }

    mapping(bytes32 signerId => LmsBinding) private _lmsBinding;

    /// @notice The identity record for an account.
    mapping(address account => Identity) private _identity;
    /// The four slots, verbatim. All four are stored in full because the
    /// precompiles verify against a KEY, not a commitment — and a key that
    /// arrived in calldata proves nothing about who signed.
    ///
    /// A CA has two keys, not 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;
    mapping(address account => bytes) private _activeAccessKey;
    mapping(address account => bytes) private _recoveryTransactionKey;
    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. Empty for every identity
    /// whose certificate carries no `PURPOSE_ACTIVE_SEAL` entry: users, CAs.
    mapping(address account => bytes) private _activeSealKey;
    /// @notice Encapsulation keys, per stage. Two algorithms each — ML-KEM-1024
    /// (lattice) and HQC-5 (code-based) — so a break in either family leaves the
    /// other standing, the same reasoning that pairs ML-DSA with SLH-DSA above.
    /// @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;
    mapping(address account => bytes) private _activeKemHqc;
    mapping(address account => bytes) private _recoveryKemMlKem;
    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

    event IdentityRegistered(
        address indexed account, bytes32 indexed certHash, uint256 roles, uint64 version
    );
    event IdentityRolesChanged(address indexed account, uint256 previousRoles, uint256 newRoles);
    event LmsKeyRegistered(
        address indexed account,
        bytes32 indexed signerId,
        uint64 indexed chainId,
        bytes16 keyId,
        uint8 height,
        bytes32 root,
        uint64 version
    );
    event IdentityRevoked(address indexed account, bytes32 indexed certHash);
    /// @notice One revocation-lane entry: `revoker` is `address(0)` for the
    /// root plane, the issuing identity otherwise.
    event CertificateRevoked(bytes32 indexed certHash, address indexed revoker);
    event BootstrapSealed(address indexed sealedBy);
    /// @notice The one-shot state-plane wiring landed.
    event StatePlaneWired(address stateTrees, address revocationLog);
    event RegistrarThresholdSet(uint256 threshold);
    /// @notice A registrar quorum authorized an action. `nonce` is the value
    /// the approvals were made over; the next action needs the next one.
    event RegistrarQuorumApproved(
        address indexed verifyingContract, bytes32 indexed actionDomain, uint64 nonce, uint256 valid
    );

    // -------------------------------------------------------------- errors

    error NotAuthorized(address caller);
    error BootstrapAlreadySealed();
    error UnknownAccount(address account);
    /// @notice A certificate's encapsulation key failed the chain's own
    /// well-formedness check. Names the algorithm, because the pair is stored
    /// together and "one of these two" is not an actionable answer.
    error MalformedEncapsulationKey(address account, uint16 algorithmId);
    error CertificateAlreadyBound(bytes32 certHash, address boundTo);
    error CertificateIsRevoked(bytes32 certHash);
    error VersionNotNewer(uint64 current, uint64 offered);
    error IssuerNotACertificateAuthority(address issuer);
    error IssuerMayNotSign(address issuer, uint8 depth, uint8 maxDelegationDepth);
    error WrongDepth(uint8 got, uint8 want);
    error DelegationWidened(uint8 child, uint8 issuer);
    error AuthorityKeyIdMismatch(bytes32 got, bytes32 want);
    error StagesDisagree(bytes32 liveSerial, bytes32 recoverySerial);
    /// @notice `height` outside 1..24. See `FinalLms.MAX_HEIGHT`.
    error LmsHeightOutOfRange(uint8 height);
    /// @notice A zero root commits to no tree.
    error LmsRootIsZero();
    /// @notice This fingerprint already belongs to a different account.
    error LmsKeyAlreadyBound(bytes32 signerId, address boundTo);
    /// @notice Two identities cannot share a transaction key: the sender it
    /// derives would be attributable to both.
    error SenderAlreadyBound(address sender, address boundTo);
    /// @notice Fewer registrars able to seal than the threshold asks for.
    error RegistrarThresholdUnreachable(uint256 sealable, uint256 threshold);
    error RegistrarThresholdIsZero();
    /// @notice {wireStatePlane} ran already, or was handed a zero address.
    error StatePlaneAlreadyWired();
    error ZeroStatePlane();
    /// @notice The holder's admission proof of possession did not verify —
    /// one family failed, or the digest was built over the wrong nonce.
    error AdmissionProofInvalid(address account);
    /// @notice The certificate does not carry the ruled chain-issuer
    /// AuthorityKeyId — it is not a chain-attested certificate.
    error NotChainAttested(bytes32 authorityKeyId);
    /// @notice The certificate's IssuerDN is not the ruled constant.
    error WrongIssuerDn(bytes32 issuerDnHash);
    /// @notice A chain-attested end entity sits at depth 1 with
    /// `maxDelegationDepth == depth`; anything else is not an end entity.
    error NotAnEndEntity(uint8 depth, uint8 maxDelegationDepth);
    /// @notice An issuer that cannot sign is an end entity wearing a profile.
    error IssuerCannotSign(uint8 depth, uint8 maxDelegationDepth);
    /// @notice Third-party issuers carry a real `NotAfter` (ruling 3) —
    /// expiry is the passive half of their lifecycle.
    error IssuerMustExpire();
    /// @notice An issuer validity window past the ~2-year ceiling (ruling 3).
    error IssuerValidityTooLong(uint64 notBefore, uint64 notAfter);
    /// @notice An institution registration without a real ISO 3166 `C=` in
    /// its subject DN, or with a jurisdiction that does not match its
    /// Institution extension. Only the trust root is jurisdiction-silent.
    error JurisdictionMissing();
    error JurisdictionMismatch();

    // --------------------------------------------------------- constructor

    /**
     * @param admin The bootstrap registrar. Genesis names the chain deployer.
     * @dev The precompile probe is the point of the constructor. This contract
     * is meaningless on a chain that cannot verify PQ signatures, and deploying
     * it there would produce a registry full of keys nothing can check.
     */
    constructor(address admin) {
        FinalChainPrecompiles.assertAvailable();
        bootstrapAdmin = admin;
    }

    // ----------------------------------------------------------- authority

    /**
     * @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
     * pretending otherwise produced the one roster that could not be
     * bootstrapped in `FinalRootAuthority`. It is closed by
     * `sealBootstrap`, which is irreversible.
     *
     * While it 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.
     */
    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 state-plane contracts.
     * @dev `msg.sender` — the calling contract — is the verifying contract the
     * digest binds and the counter it burns, so an approval collected for the
     * trees' configuration cannot be spent on the bundle log'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 the quorum.
     */
    function requireRegistrarQuorum(
        bytes32 actionDomain,
        bytes32 payloadDigest,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _requireRegistrarQuorum(msg.sender, actionDomain, payloadDigest, anchorBlock, approvals);
    }

    /// @dev The digest is `FinalPqQuorum.digest(verifyingContract, actionDomain,
    /// anchorBlock, keccak256(abi.encode(nonce, payloadDigest)))`; the seal is
    /// required — membership is the hybrid class.
    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 Bootstrap admin while the window is open; the current registrar
     * quorum afterwards, so a registrar set that grows or shrinks can move it.
     * Refuses a threshold the sealable registrars cannot meet, and refuses zero:
     * both are a registry that can never be written to again.
     */
    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.
    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`. Restated rather
     * than imported because the two live on different chains and there is no
     * import that 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.
     */
    function lmsSignerId(bytes16 keyId, uint8 height, bytes32 root) public pure returns (bytes32) {
        return keccak256(abi.encode(keyId, height, root));
    }

    /**
     * @notice Record the LMS signing key an already-registered account holds.
     * @dev Membership-gated, same as 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-per-key-they-hold.
     *
     * @param account Must already be registered and not revoked.
     * @param version Strictly increasing. 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; see
     *   `FinalPqQuorum`. 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, chain) — LMS-01 made the same
        // operator a different signer on every chain, so chain B starting at
        // version 1 says nothing about chain A 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.
    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. Zeroes for a fingerprint never registered.
    /// @dev The revocation log's permanence gate reads this to find the
    /// (account, chain) SLOT a fingerprint belongs to — the slot's current key
    /// is what separates a superseded fingerprint (permanent, recordable) from
    /// a merely lapsed one (expiry, temporary, refused). Attribution is
    /// history: the binding survives supersession, exactly as the mapping
    /// behind {lmsSignerIsLive} does, because it IS that mapping.
    function lmsBindingOf(bytes32 signerId) external view returns (address account, uint64 chainId) {
        LmsBinding storage binding = _lmsBinding[signerId];
        return (binding.account, binding.chainId);
    }

    /**
     * @notice Is this signer fingerprint held by a live, unrevoked account?
     * @dev The question a verifier actually has. A gateway roster names
     * fingerprints and nothing else, so "is 0x39bb… still good?" is otherwise
     * unanswerable from the state plane.
     */
    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. The
    /// count is of registrars that can SEAL — a certificate authority carrying
    /// the role has no seal key and can never contribute an approval.
    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 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 — both contracts take THIS registry as one — so the deploy
     * tooling calls it in the same nonce-fixed block that deploys them, before
     * any identity is registered.
     */
    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_);
    }

    /// @dev Project `account`'s tree-8 leaf, same-tx. Skipped while the plane
    /// is unwired — the 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 besides forgetting to call it.
    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);
    }

    /// @dev Record a PERMANENTLY retired fingerprint, same-tx, unless the log
    /// is unwired or someone already recorded it permissionlessly.
    function _recordRevokedSigner(bytes32 signerId) private {
        address log = revocationLog;
        if (log == address(0)) return;
        if (IRevocationRecorder(log).recorded(signerId)) return;
        IRevocationRecorder(log).record(signerId);
    }

    // -------------------------------------------------------- registration

    /// @notice The holder's admission proof of possession: both live-stage
    /// families over the admission digest (schema §v5). There is no root
    /// keypair and no CA signature any more — the chain admits, and the
    /// "2 signatures at creation" are the HOLDER's, verified by the
    /// precompiles inside this very transaction.
    struct AdmissionProof {
        bytes mlDsaSignature;
        bytes slhDsaSignature;
    }

    /**
     * @notice Register or rotate a Final Wallet identity from its two public
     *         certificates — CHAIN-ATTESTED (schema §v5, ruled 2026-09-01).
     *
     * @param account The wallet address the certificate set derives.
     * @param liveTbs `live.pub.fcert` TBS — `activeTransaction` + `activeAccess`.
     * @param recoveryTbs `recovery.pub.fcert` TBS — 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), verified in the precompiles inside this
     *        transaction. This replaced the CA signature: issuance authority
     *        is the registrar quorum, possession is this proof, and there is
     *        no root keypair anywhere.
     * @param roles Capability bitmask. The one thing the certificates do not
     *        say, because capability is this system's decision.
     * @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.
     *
     * @dev **Both stages, together.** A wallet has four keys in two stages and
     * the recovery pair is PRE-COMMITTED — written at `initialize` from the same
     * certificate set that determined the address, which is why PQ migration
     * takes no key arguments. The two must share a `SerialNumber`: a serial is
     * per certificate SET, so two stages disagreeing are two different wallets.
     *
     * **Chain-attested means pinned, per stage:** the ruled IssuerDN and
     * AuthorityKeyId constants, depth exactly 1 (directly under the chain),
     * and `maxDelegationDepth == depth` (an end entity signs nothing — the
     * same immutable pair `identityTreeLeafOf` discriminates records 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 our own
     *         intermediate) that signs certificates OFF-chain with the keys
     *         registered here (D2: the superCA).
     *
     * @param account The issuer's account on this chain.
     * @param tbs The single issuer certificate's TBS: two CERT_SIGNING keys
     *        (ML-DSA-87 + SLH-DSA-SHAKE-256s), 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;
     *        `address(0)` for an issuer hanging directly under the chain.
     * @param proof The issuer's OWN two cert-signing keys over the admission
     *        digest (`recoveryCertHash` slot is zero — there is no recovery
     *        stage to bind).
     *
     * @dev Admission is chain-native like any identity: registrar quorum plus
     * the holder's PoP. What the v4 delegation rules said survives verbatim as
     * LINEAGE — a nested issuer's depth, delegation bound and AuthorityKeyId
     * must chain to its registered parent — but no parent SIGNS anything; the
     * chain's admission is the issuance.
     *
     * Ruling 3: a registered issuer always expires (`NotAfter` real, window
     * bounded ~2 years) — the passive liveness touchpoint; renewal re-issues
     * under the same registered keys with a version bump.
     *
     * The jurisdiction rule (ruled 2026-09-01, amended): only the trust root
     * is jurisdiction-silent. An institution MUST carry its real ISO 3166
     * `C=` in its subject DN, matching the `jurisdiction` field of its
     * `0x0102` Institution extension — CA/Browser-Forum practice, enforced at
     * the door because a verifier's legal recourse starts with knowing where
     * an issuer answers for itself.
     */
    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 Ruling 3's validity ceiling for registered issuers, in this
    /// chain's milliseconds: two 366-day years.
    uint64 public constant MAX_ISSUER_VALIDITY_MS = 2 * 366 days * 1000;

    /// @dev The chain-attested end-entity pins, run once per 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);
        }
    }

    /// @dev The v4 delegation rules, surviving as lineage: a nested issuer
    /// chains to a registered, signing-capable parent one level up; a direct
    /// issuer hangs under the chain at depth 1.
    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);
        }
    }

    /// @dev The jurisdiction rule: a real ISO 3166 alpha-2 `C=` in the subject
    /// DN, equal to the Institution extension's `jurisdiction` field. The DN
    /// is canonical comma-separated form, so `C=` matches at the start or
    /// right after a comma; the component value is exactly two bytes.
    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();
    }

    /// @dev Verify the holder's PoP: both live-stage families over the
    /// admission digest, in the precompiles, inside this transaction. Burns
    /// the gate nonce on the bootstrap path (the quorum path burned it in
    /// `_requireRegistrarQuorum` already), so an admission is one-shot in
    /// both regimes.
    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;
        }
    }

    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);
    }

    /**
     * @dev Store one stage's encapsulation pair, or clear it.
     *
     * Empty is legitimate and is not the same as absent-and-wrong: a CA has no
     * encapsulation stage, and a certificate issued before v4 carries none.
     * `FinalCertificate.parse` 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 intent then never decrypts — the failure
     * mode with no error attached, and the one this whole pairing exists to
     * avoid.
     */
    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.
    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);
    }

    /// @dev Once sealed, no mutation may leave the registrar quorum unreachable
    /// — that is the one change nothing could ever undo. Checked after the
    /// write so the count reflects it.
    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.
    /// @param chainIds The chains whose LMS-key slots this account holds — the
    /// registrars supply the list (the digest binds it) because a mapping
    /// cannot enumerate its own keys. Each named slot's current fingerprint is
    /// recorded into the revocation log same-tx; a chain with no slot is
    /// skipped, and a fingerprint missed by an incomplete list stays
    /// permanently recordable through the log's permissionless door, since a
    /// revoked account never regains standing.
    /// @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.
    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` (D5).
     *
     * @dev The half of the one revocation lane that gates registration and
     * covers break-glass: any certificate — registered, off-chain-issued, or
     * never seen — can be killed by handle under the registrar quorum. When
     * the handle is a registered identity's CURRENT certificate the identity
     * falls with it (flag, roles, same-tx projection), so a break-glass by
     * handle is never weaker than {revoke} — it only skips the LMS-slot
     * enumeration, which stays permanently recordable through the revocation
     * log's permissionless door.
     */
    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 "Sub-issuer and us alike" (D5) — but SCOPED: this records WHO
     * revoked, and a verifier honours the entry only when the revoker is the
     * certificate's own issuer (which the verifier knows — it holds the
     * cert). 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, the
     * chain, the handle and the issuer's own gate nonce. 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 the lane models one.
     */
    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. `registered` is the field to branch on.
    function identityOf(address account) external view returns (Identity memory) {
        return _identity[account];
    }

    /// @notice `activeTransaction` — ML-DSA-87. What a quorum verifies against.
    function activeTransactionKeyOf(address account) external view returns (bytes memory) {
        return _activeTransactionKey[account];
    }

    /// @notice `activeAccess` — SLH-DSA-SHAKE-256s. Identity, and guardianship.
    function activeAccessKeyOf(address account) external view returns (bytes memory) {
        return _activeAccessKey[account];
    }

    /// @notice `activeSeal` — SLH-DSA-SHAKE-256s. What `FinalPqQuorum` verifies
    /// an execution-class approval's `seal` against. Empty when the identity
    /// carries no seal, in which case it cannot take part in a sealed quorum.
    function activeSealKeyOf(address account) external view returns (bytes memory) {
        return _activeSealKey[account];
    }

    /// @notice `recoveryTransaction`. Authorizes rotating this account's own
    /// credentials and nothing else. Empty for a CA.
    function recoveryTransactionKeyOf(address account) external view returns (bytes memory) {
        return _recoveryTransactionKey[account];
    }

    /// @notice `recoveryAccess`. Empty for a CA.
    function recoveryAccessKeyOf(address account) external view returns (bytes memory) {
        return _recoveryAccessKey[account];
    }

    /// @notice The four commitments, in the order tree 1's leaf wants them.
    /// @dev keccak, not SHA3 — these feed `FinalWalletFactory.accountStateLeafHash`,
    /// which every other chain verifies with, and that one hashes with keccak.
    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 the wallet's CREATE2 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 (`minePqVanityCerts.cjs` is the reference encoder;
     * the parity test pins this function against the premined fixtures).
     *
     * 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
     * CA. The CA exclusion is structural, not a role read: an end entity has
     * `depth == maxDelegationDepth` (it issues nothing), a CA never does, and
     * the depth pair is immutable per version where roles are not.
     *
     * Lives HERE rather than on `FinalStateTrees` (whose tree 8 consumes it)
     * because every input is this contract's storage and the trees contract
     * sits against EIP-170.
     */
    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) {
            // D7 (ruled 2026-09-01): an ISSUER exists in tree 8 under its own
            // domain, so its record is stapleable for offline licence
            // verification. `certHash` suffices (it covers the whole TBS and
            // the verifier holds the cert), `version` makes supersession move
            // the leaf, and the third word RESERVES the issuer's own
            // certificate-tree anchor — zero until wired. The distinct domain
            // does the wallet-admission exclusion the zero projection used to
            // do; zero-on-revoke above is now 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 `AccountStateLeaf` order.
    /// @dev One word per STAGE, over both of that stage's KEM public keys. The
    /// pair is the unit — an account holds both or neither — so committing 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 registered before the encapsulation slots existed hashes the
    /// empty string here rather than reverting: `syncIdentities` must keep
    /// projecting it, and a leaf that cannot be built is a party that cannot be
    /// revoked.
    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 encapsulation keys themselves, for a party composing a message.
    function kemKeysOf(address account)
        external
        view
        returns (bytes memory activeMlKem, bytes memory activeHqc)
    {
        return (_activeKemMlKem[account], _activeKemHqc[account]);
    }

    // ------------------------------------------------------------- senders

    /**
     * @notice The Final Chain sender a transaction key produces.
     * @dev `keccak256(uint8(4) ‖ publicKey)[12:]` — byte-identical to what the
     * node derives from a type-0x46 envelope and to the backend's
     * `pqTransaction.senderOf`. Pure, so a client can compute it from a
     * certificate before the identity is registered.
     */
    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, 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. False for a sender no identity claims.
    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.
    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.
    function accountCount() external view returns (uint256) {
        return _accounts.length;
    }

    /// @notice Registered account by index, in registration order.
    function accountAt(uint256 index) external view returns (address) {
        return _accounts[index];
    }

    /// @notice Every account carrying every bit in `roleMask`.
    /// @dev A view, so the O(n) scan costs nothing. Callers that need this in a
    /// transaction should pass the member list explicitly instead — see
    /// `FinalPqQuorum`, which takes signers rather than searching for them.
    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 that reverts forever with nothing naming the
     * roster as the cause.
     */
    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 should not be a universal pass.
     */
    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.
    function isActive(address account) public view returns (bool) {
        Identity storage id = _identity[account];
        return id.registered && !id.revoked && _withinValidity(id);
    }

    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;
    }

}

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
//
// @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;
    }

    error ThresholdNotMet(uint256 valid, uint256 required);
    error SignersNotAscending(address previous, address next);
    error SignerLacksRole(address signer, uint256 roleMask);
    error WrongAlgorithm(address signer, uint8 got, uint8 required);
    error BadSignature(address signer, uint8 algorithm);
    error BadSeal(address signer);
    error AnchorAhead(uint64 anchorBlock, uint256 blockNumber);
    error AnchorStale(uint64 anchorBlock, uint256 blockNumber);
    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);
    }

    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;
    }
}

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": "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": "wireStatePlane",
    "inputs": [
      {
        "name": "stateTrees_",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "revocationLog_",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "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": "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": []
  }
]

leggi contratto

bytecode · 24,120 byte

0x6080806040526004361015610012575f80fd5b5f905f3560e01c9081630408a87f14613af85750806304525da814613ad05780630d641df014613a045780631479bc3a1461392e57806315ba3c1d146139135780631d4f1077146137d55780631f394fb3146137a05780631f9dc0bb146135b8578063209f80441461359d578063222e6f0e1461356e578063228bf014146135535780632741cb021461332c5780632ca02983146132825780633222383e1461324a57806332596d711461322d578063342f6163146131bb57806345e7e88e14613172578063461de6c114613138578063488429d9146130fe5780634c4af1a414612d6d5780634ccf000514612cd45780634f8d321614612c605780635a232a3914612c265780635b88250114612af75780635c97f4a214612ad057806361dbd45214612a9657806364d2218d14612a7757806370f64f0a14612a2c57806377e223b014612a1157806386731755146129d75780638c6820a914611a745780638cfc6aa014611a4257806390ecddbf14611a01578063926590d61461193257806394338765146118f05780639aae87ad146118b65780639c11e31b1461189b5780639d07ae09146117875780639e5adaeb1461174f5780639f8a13d714611722578063a0c176c4146116fd578063a760c295146116cb578063a874eea5146116b0578063ad20445d14611696578063ad84ad131461165e578063b0538d5a14611643578063b16abc6c1461160b578063b3c26283146115aa578063b7af85d71461155a578063baadbb3214611520578063c071672914611504578063c177a97e146114e1578063c5768fe4146114b9578063c6345626146112f6578063cbe6ebb414610672578063ce092d1c14610655578063de043e951461061a578063e0cd096a146105ee578063e14c465b146105d1578063e4af29fc146105b3578063e51442e31461057b578063ec964baa146104b0578063ef23f9f81461046f578063f5778b0314610446578063fce072f8146103e95763ffd326f3146102f4575f80fd5b346103e65760403660031901126103e6576001600160401b036040610317613bfa565b92610320613b30565b93816080845161032f81613c61565b8281528260208201528286820152826060820152015260018060a01b03168152806020522091165f5260205260a060405f2060405161036d81613c61565b6001600160401b038254916001600160801b03198360801b169384825260ff602083019460801c16845260ff60026001830154926040850193845201549481608060608601958789168752019660401c161515865260405196875251166020860152516040850152511660608301525115156080820152f35b80fd5b50346103e65760203660031901126103e657600435906001600160401b0382116103e657366023830112156103e657602061043461042f36600486013560248701613cd4565b614701565b6040516001600160a01b039091168152f35b50346103e657806003193601126103e6576010546040516001600160a01b039091168152602090f35b50346103e65760203660031901126103e6576020906001600160a01b03610494613bfa565b16815260158252604060018060a01b0391205416604051908152f35b50346103e657806003193601126103e6576010546001600160a01b03811633036105685760ff8160a01c1661055957601354801561054a576104f0613f57565b8181106105345750506001600160a81b031916600160a01b17601055337fdd5280e26bdef754c085fd1fe40c0ab76df98368bb6014f85b49f8852a81be578280a280f35b6304951c2160e11b845260045260245250604490fd5b631de31ea560e11b8352600483fd5b630f80bc0560e31b8252600482fd5b634a0bfec160e01b825233600452602482fd5b50346103e65760203660031901126103e657602061059a600435613d53565b905460405160039290921b1c6001600160a01b03168152f35b50346103e657806003193601126103e6576020600f54604051908152f35b50346103e657806003193601126103e65760206040516101008152f35b50346103e65760203660031901126103e657602061061261060d613bfa565b6144a9565b604051908152f35b50346103e657806003193601126103e65760206040517f664b0e72d3e91e88f8d9d2c0d916d51aff408110b8760394f6d677a5e6b24a2e8152f35b50346103e657806003193601126103e65760206040516102008152f35b346112f2576101003660031901126112f25761068c613bfa565b6024356001600160401b0381116112f2576106ab903690600401613c34565b90916044356001600160401b0381116112f2576106cc903690600401613c34565b606492919235936001600160401b0385116112f2578460040193604060031987360301126112f25760843595610700613b5c565b95610709613b72565b9360e435956001600160401b0387116112f257899661072c903690600401613b88565b929096305f52601460205260405f20546001600160401b0316938d83369061075392613cd4565b80519060200120988b8d3661076990888d613cd4565b805190602001209c604051809e6020820194600160a01b60019003169e8f865260408301526060820152608001526001600160401b03169b8c60a082015260a081526107b660c082613c98565b519020926107c39361495e565b7383d50624b34718978a0f118a95f7c6b6c9008c478091604051809e81926325f1701d60e21b835260048301916107f992614303565b03815a935f94f49586156112c5576108339c5f976112d0575b50905f9291604051809e8194829363b389b84f60e01b845260048401614303565b03915af4998a156112c5575f9a6112a1575b506020840191825160208c01519081810361128c5750506108658561527f565b61086e8b61527f565b8a518551906040519060208201927f6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f81688452466040840152306060840152608083015260a08201528360c082015260c081526108ca60e082613c98565b51902093604051946020860152602085526108e6604086613c98565b61016086019461090c8651826109066108ff8780613e4d565b3691613cd4565b91614b32565b15928315611261575b50505061124e57305f526014602052806001600160401b0360405f2054161461121a575b5082519684156112075783515f52600d60205260ff60405f2054166111f35783515f908152600c60205260409020546001600160a01b0316801515806111e9575b6111d35750845f52600260205260405f206005810192835460ff8160d81c16155f14611151575090600f5490600160401b821015610e50578b926109ee8b6109ca85600160049701600f55613d53565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b855460ff60d81b1916600160d81b1786555b875182558d51600183015551600282015560c08701516003820155015560e084015182546101008601516101208701516101408801516001600160d01b031990931660ff949094169390931760089190911b61ff00161760109290921b69ffffffffffffffff0000169190911760509190911b67ffffffffffffffff60501b161760909190911b67ffffffffffffffff60901b1617905580516001600160a01b0390610aab90614701565b165f818152601560205260409020546001600160a01b031680151580611147575b6111315750835f526003602052610ae660405f2054613e7f565b6110e3575b5f52601560205260405f20836001600160601b0360a01b82541617905551825f52600360205260405f20908051906001600160401b038211610e5057610b318354613e7f565b601f81116110a9575b50602090601f831160011461104657610b6a92915f9183610e64575b50508160011b915f199060031b1c19161790565b90555b610180810151825f52600460205260405f20908051906001600160401b038211610e5057610b9b8354613e7f565b601f811161100c575b50602090601f8311600114610fa957610bd392915f9183610e645750508160011b915f199060031b1c19161790565b90555b610160870151825f52600560205260405f20908051906001600160401b038211610e5057610c048354613e7f565b601f8111610f6f575b50602090601f8311600114610f0c57610c3c92915f9183610e645750508160011b915f199060031b1c19161790565b90555b610180870151825f52600660205260405f20908051906001600160401b038211610e5057610c6d8354613e7f565b601f8111610ed2575b50602090601f8311600114610e6f57610ca592915f9183610e645750508160011b915f199060031b1c19161790565b90555b6101e081015196825f52600760205260405f2088516001600160401b038111610e5057602099610cd88354613e7f565b601f8111610e0f575b508a90601f8311600114610d925793610612999a93610d2b845f516020615df85f395f51905f529895610d59956040995f92610d875750508160011b915f199060031b1c19161790565b90555b610d456101a08401516101c0850151905f8c6153e9565b6101c06101a0820151910151905f8a6156ad565b80515f52600c8a52825f20856001600160601b0360a01b8254161790555194825191825289820152a36149db565b015190505f80610b56565b90601f19831691845f52815f20925f5b818110610df8575084610d5994604098946106129e9f98945f516020615df85f395f51905f529b9860019510610de0575b505050811b019055610d2e565b01515f1960f88460031b161c191690558f8080610dd3565b92938e600181928786015181550195019301610da2565b82811115610ce157610e4290845f528c5f2090601f850160051c908e8610610e48575b601f82910160051c039101615264565b8b610ce1565b5f9150610e32565b634e487b7160e01b5f52604160045260245ffd5b015190508b80610b56565b90601f19831691845f52815f20925f5b818110610eba5750908460019594939210610ea2575b505050811b019055610ca8565b01515f1960f88460031b161c191690558a8080610e95565b92936020600181928786015181550195019301610e7f565b82811115610c7657610f0690845f5260205f2090601f850160051c9060208610610e4857601f82910160051c039101615264565b8a610c76565b90601f19831691845f52815f20925f5b818110610f575750908460019594939210610f3f575b505050811b019055610c3f565b01515f1960f88460031b161c191690558a8080610f32565b92936020600181928786015181550195019301610f1c565b82811115610c0d57610fa390845f5260205f2090601f850160051c9060208610610e4857601f82910160051c039101615264565b8a610c0d565b90601f19831691845f52815f20925f5b818110610ff45750908460019594939210610fdc575b505050811b019055610bd6565b01515f1960f88460031b161c191690558a8080610fcf565b92936020600181928786015181550195019301610fb9565b82811115610ba45761104090845f5260205f2090601f850160051c9060208610610e4857601f82910160051c039101615264565b8a610ba4565b90601f19831691845f52815f20925f5b8181106110915750908460019594939210611079575b505050811b019055610b6d565b01515f1960f88460031b161c191690558a808061106c565b92936020600181928786015181550195019301611056565b82811115610b3a576110dd90845f5260205f2090601f850160051c9060208610610e4857601f82910160051c039101615264565b8a610b3a565b835f52600360205260018060a01b0361110161042f60405f20613eb7565b16818103611110575b50610aeb565b5f52601560205260405f206001600160601b0360a01b81541690558961110a565b906313362de360e01b5f5260045260245260445ffd5b5084811415610acc565b6001600160401b038160901c16808a11156111bc575060d01c60ff166111a857816004918c935488518103611187575b50610a00565b5f52600c60205260405f206001600160601b0360a01b81541690558e611181565b505463e41b98f760e01b5f5260045260245ffd5b899063df5abcab60e01b5f5260045260245260445ffd5b8451623bdaab60e51b5f5260045260245260445ffd5b508581141561097a565b835163e41b98f760e01b5f5260045260245ffd5b84633131bf7960e21b5f5260045260245ffd5b61122390613e2f565b305f5260146020526001600160401b0360405f2091166001600160401b031982541617905589610939565b84639ae9f73b60e01b5f5260045260245ffd5b61128393506108ff61127d9160246101808b0151950190613e4d565b91614bd9565b158b8080610915565b6388e0872960e01b5f5260045260245260445ffd5b6112be919a503d805f833e6112b68183613c98565b810190614133565b988a610845565b6040513d5f823e3d90fd5b5f93929197506112e9903d8086833e6112b68183613c98565b96909192610812565b5f80fd5b346112f25760203660031901126112f25761130f613bfa565b60405161131b81613c7c565b5f8152602081015f9052604081015f9052606081015f9052608081015f905260a081015f905260c081015f905260e081015f905261010081015f905261012081015f905261014081015f9052610160015f9052600160a01b60019003165f52600260205260405f206040519061139082613c7c565b80549182815260018201546020820190815260028301546040830190815260038401546060840190815260048501549460808501958652600501549460a0850160ff8716815260c08601918760081c60ff16835260e08701938860101c6001600160401b031685526101008801958960501c6001600160401b031687526101208901978a60901c6001600160401b031689526101408a01998b60d01c60ff1615158b52610160019a60d81c60ff1615158b526040519b8c525160208c01525160408b01525160608a01525160808901525160ff1660a08801525160ff1660c0870152516001600160401b031660e0860152516001600160401b0316610100850152516001600160401b031661012084015251151561014083015251151561016082015261018090f35b346112f2575f3660031901126112f2576012546040516001600160a01b039091168152602090f35b346112f25760203660031901126112f25760206104346114ff613bfa565b6143e2565b346112f2575f3660031901126112f25760206040516104008152f35b346112f2575f3660031901126112f25760206040517f6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f81688152f35b346112f25760203660031901126112f2576001600160a01b0361157b613bfa565b165f5260036020526115a661159260405f20613eb7565b604051918291602083526020830190613c10565b0390f35b346112f25760403660031901126112f2576001600160a01b036115cb613bfa565b165f526015602052602060018060a01b0360405f20541680151590816115f7575b506040519015158152f35b611605915060243590614036565b826115ec565b346112f25760203660031901126112f2576001600160a01b0361162c613bfa565b165f5260056020526115a661159260405f20613eb7565b346112f2575f3660031901126112f257602060405160108152f35b346112f25760203660031901126112f2576001600160a01b0361167f613bfa565b165f5260076020526115a661159260405f20613eb7565b346112f2575f3660031901126112f2576020604051818152f35b346112f2575f3660031901126112f257602060405160028152f35b346112f25760203660031901126112f2576004355f52600c602052602060018060a01b0360405f205416604051908152f35b346112f2575f3660031901126112f257602060ff60105460a01c166040519015158152f35b346112f25760203660031901126112f2576020611745611740613bfa565b614394565b6040519015158152f35b346112f25760203660031901126112f2576001600160a01b03611770613bfa565b165f5260046020526115a661159260405f20613eb7565b346112f25760803660031901126112f2576117a0613bfa565b602435906117ac613b46565b6064356001600160401b0381116112f2576117ce611801913690600401613b88565b90604051936020850160018060a01b03871695868252886040820152604081526117f9606082613c98565b519020614906565b805f52600260205260405f2092600584015460ff8160d81c16156118885760d01c60ff166118745760407ffea1c9b6eea77fb9c209307935fb32ed2f3299cb9c639571480c67b17929807f91600461187296019080825492556118626149b6565b82519182526020820152a26149db565b005b835463e41b98f760e01b5f5260045260245ffd5b82633131bf7960e21b5f5260045260245ffd5b346112f2575f3660031901126112f257602060405160088152f35b346112f2575f3660031901126112f25760206040517fc1f01606bfc0d9242f3def839a8589d5b92fe65d09df4befd80f0439f60344cc8152f35b346112f25760203660031901126112f2576004355f5260016020526040805f20546001600160401b0382519160018060a01b038116835260a01c166020820152f35b346112f25760203660031901126112f25760406001600160a01b03611955613bfa565b16805f526008602052815f20815f52600960205261199e6119ac845f20855192839161199860208401965f516020615e185f395f51905f5288528985019061432a565b9061432a565b03601f198101835282613c98565b51902090805f52600a602052825f20905f52600b60205261199e6119f2845f20855192839161199860208401965f516020615e185f395f51905f5288528985019061432a565b51902082519182526020820152f35b346112f25760203660031901126112f2576001600160a01b03611a22613bfa565b165f52601460205260206001600160401b0360405f205416604051908152f35b346112f25760203660031901126112f2576004355f52600e602052602060018060a01b0360405f205416604051908152f35b346112f2576101003660031901126112f257611a8e613bfa565b6024356001600160401b0381116112f257611aad903690600401613c34565b90916044356001600160a01b03811692918382036112f257606435936001600160401b0385116112f2578460040192604060031987360301126112f25760843593611af6613b5c565b611afe613b72565b9260e4356001600160401b0381116112f257611b1e903690600401613b88565b989094305f52601460205260405f20546001600160401b0316998c833690611b4592613cd4565b80519060200120966040519860208a0190600160a01b600190038c16998a835260408c015260608b01528b60808b01526001600160401b038716998a60a082015260a08152611b9560c082613c98565b51902092611ba2936148ae565b604051633ba294e360e01b81529a8b91611bbf9160048401614303565b038a7383d50624b34718978a0f118a95f7c6b6c9008c4791815a935f94f4998a156112c5575f9a6129bb575b5060e08a019160ff8351168015818d82156129a8575b505061298957506101408b01986001600160401b038a51161561297a576001600160401b038a5116926101208d01936001600160401b0385511690036001600160401b03811161283e576001600160401b03640eb9af10009116116129545760408d01517fa5f1406585e87ef6c3645d6270cf45d62ae1c5d1abf447614cd94657a3f429a0810361294257508c611c979161507d565b60608c01519a5f9b5f5f5b6004810180821161283e578351811161292c57811580156128fe575b806128dd575b806128af575b611cdd5750611cd890613d6b565b611ca2565b91509192939495969798999a9b9c9d5082518110908161288a575b50612867576002810180821161283e576001600160f81b031990611d1c90846151eb565b51166003820180921161283e57611d4c92611d3f611d479360ff60f81b926151eb565b5116906151fc565b615227565b60015b156128675760808d01516006815110612867578051156128765761ff0080602083015160f01c161681516001101561287657602182015160f81c1780600201908160021161283e578251600482019081841161283e57106128675761ff0080611db884866151eb565b5160f01c16166003820180841161283e57611dd390856151eb565b5160f81c17908160020160021161283e570160040190811061283e57815190600281019182821161283e5782116128675761ff0080611e1283866151eb565b5160f01c1616600182019081831161283e57611e30600292866151eb565b5160f81c1714801590612852575b61282f5760036001600160f81b0319611e5784866151eb565b5116910180921161283e57611e7892611d3f611d479360ff60f81b926151eb565b6001600160f01b031991821691160361282f578b90815160405160208101917f6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f8168835246604083015230606083015260808201525f60a08201528460c082015260c08152611ee660e082613c98565b51902060405190602082015260208152611f01604082613c98565b610160830197611f1a8951836109066108ff8580613e4d565b15938415612809575b505050506127f657305f526014602052806001600160401b0360405f205416146127c2575b506102008a5198179885156127af578a515f52600d60205260ff60405f20541661279b578a515f908152600c60205260409020546001600160a01b031680151580612791575b61277b5750855f52600260205260405f20926005840194855460ff8160d81c16155f146126ef5750600f5490600160401b821015610e50578c60048f97611fe18e6109ca87600160ff9901600f55613d53565b895460d886901b1916600160d81b178a555b88518155885160018201556020890151600282015560c08901516003820155015551169069ffffffffffffffff000061ff00610100885497015160081b16915160101b16926001600160401b0360501b905160501b16936001600160401b0360901b9060901b16946001600160401b0360901b19926001600160401b0360501b199169ffffffffffffffffffff191617161716171717905560018060a01b0361209c8251614701565b165f818152601560205260409020546001600160a01b0316801515806126e5575b6111315750825f5260036020526120d760405f2054613e7f565b612697575b5f52601560205260405f20826001600160601b0360a01b82541617905551815f52600360205260405f20908051906001600160401b038211610e50576121228354613e7f565b601f811161265d575b50602090601f83116001146125fa5761215a92915f91836125525750508160011b915f199060031b1c19161790565b90555b610180860151815f52600460205260405f20908051906001600160401b038211610e505761218b8354613e7f565b601f81116125c0575b50602090601f831160011461255d576121c392915f91836125525750508160011b915f199060031b1c19161790565b90555b6020956040516121d68882613c98565b5f8152825f526005885260405f20908051906001600160401b038211610e50576122008354613e7f565b601f811161251a575b508990601f83116001146124b75761223792915f9183610e645750508160011b915f199060031b1c19161790565b90555b6040516122478882613c98565b5f8152825f526006885260405f20908051906001600160401b038211610e50576122718354613e7f565b601f811161247f575b508990601f831160011461241c576122a892915f9183610e645750508160011b915f199060031b1c19161790565b90555b6040516122b88882613c98565b5f8152825f526007885260405f20908051906001600160401b038211610e50576122e28354613e7f565b601f81116123e4575b508990601f831160011461236a5792612331836106129a9b945f516020615df85f395f51905f5297946040975f9261235f5750508160011b915f199060031b1c19161790565b90555b610d596101a082018051906123536101c085019283519060018d6153e9565b5190519060018a6156ad565b015190508e80610b56565b90601f19831691845f528b5f20925f5b8d8282106123ce575050935f516020615df85f395f51905f52969360409693600193836106129e9f98106123b6575b505050811b019055612334565b01515f1960f88460031b161c191690558d80806123a9565b600185968293968601518155019501930161237a565b828111156122eb5761241690845f528b5f2090601f850160051c908d8610610e4857601f82910160051c039101615264565b8a6122eb565b90601f19831691845f528b5f20925f5b8d828210612469575050908460019594939210612451575b505050811b0190556122ab565b01515f1960f88460031b161c191690558a8080612444565b600185968293968601518155019501930161242c565b8281111561227a576124b190845f528b5f2090601f850160051c908d8610610e4857601f82910160051c039101615264565b8a61227a565b90601f19831691845f528b5f20925f5b8d8282106125045750509084600195949392106124ec575b505050811b01905561223a565b01515f1960f88460031b161c191690558a80806124df565b60018596829396860151815501950193016124c7565b828111156122095761254c90845f528b5f2090601f850160051c908d8610610e4857601f82910160051c039101615264565b8a612209565b015190508a80610b56565b90601f19831691845f52815f20925f5b8181106125a85750908460019594939210612590575b505050811b0190556121c6565b01515f1960f88460031b161c19169055898080612583565b9293602060018192878601518155019501930161256d565b82811115612194576125f490845f5260205f2090601f850160051c9060208610610e4857601f82910160051c039101615264565b89612194565b90601f19831691845f52815f20925f5b818110612645575090846001959493921061262d575b505050811b01905561215d565b01515f1960f88460031b161c19169055898080612620565b9293602060018192878601518155019501930161260a565b8281111561212b5761269190845f5260205f2090601f850160051c9060208610610e4857601f82910160051c039101615264565b8961212b565b825f52600360205260018060a01b036126b561042f60405f20613eb7565b168181036126c4575b506120dc565b5f52601560205260405f206001600160601b0360a01b8154169055886126be565b50838114156120bd565b6001600160401b038160909793949596971c16808b1115612764575060d01c60ff16612751578c949392918c60048360ff945489518103612731575b50611ff3565b5f908152600c6020526040812080546001600160a01b031916905561272b565b5463e41b98f760e01b5f5260045260245ffd5b8a9063df5abcab60e01b5f5260045260245260445ffd5b8b51623bdaab60e51b5f5260045260245260445ffd5b5086811415611f8e565b8a5163e41b98f760e01b5f5260045260245ffd5b85633131bf7960e21b5f5260045260245ffd5b6127cb90613e2f565b305f5260146020526001600160401b0360405f2091166001600160401b03198254161790558a611f48565b85639ae9f73b60e01b5f5260045260245ffd5b612825945061127d9160246101806108ff930151950190613e4d565b158c80808e611f23565b63261ab3b160e21b5f5260045ffd5b634e487b7160e01b5f52601160045260245ffd5b508251600482019081841161283e5710611e3e565b6301c8a09760e41b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b600b60fa1b91506001600160f81b0319906128a590856151eb565b511614158f611cf8565b506001820180831161283e57603d60f81b906001600160f81b0319906128d590876151eb565b511614611cca565b50604360f81b6001600160f81b03196128f684876151eb565b511614611cc4565b505f19820182811161283e57600b60fa1b906001600160f81b03199061292490876151eb565b511614611cbe565b505090509b909192939495969798999a9b611d4f565b63914a64c360e01b5f5260045260245ffd5b8a6001600160401b03808651169151169063950cff1d60e01b5f5260045260245260445ffd5b63ea0930b960e01b5f5260045ffd5b60ff6101008d01511690634f67a11560e01b5f5260045260245260445ffd5b610100015160ff1611159050818d611c01565b6129d0919a503d805f833e6112b68183613c98565b988a611beb565b346112f2575f3660031901126112f25760206040517f617214620b6c0e190ece7e2c8e3b97354e56dc3501b8a6811dd7e742dc6637c08152f35b346112f2575f3660031901126112f257602060405160408152f35b346112f25760603660031901126112f2576004356fffffffffffffffffffffffffffffffff19811681036112f25760243560ff811681036112f2576020916106129160443591614093565b346112f2575f3660031901126112f2576020604051640eb9af10008152f35b346112f2575f3660031901126112f25760206040517f9e01a06a8e87fa34b0b3b7f97e4f2ba5e105d644fac817f11f688ac916421fa28152f35b346112f25760403660031901126112f2576020611745612aee613bfa565b60243590614036565b346112f25760403660031901126112f257612b10613bfa565b6024356001600160a01b03811691908290036112f25760105460ff8160a01c16908115612c12575b50612bff57601154906001600160a01b03821615801590612beb575b612bdc576001600160a01b031680158015612bd4575b612bc5577f518f647756fa8c0e3a5d52ac2a56a5c3d0d4296b6f72486107a91a8cc8d9a8cd92816040936001600160601b0360a01b1617601155806001600160601b0360a01b601254161760125582519182526020820152a1005b632bd300bd60e21b5f5260045ffd5b508215612b6a565b63586ccc6160e11b5f5260045ffd5b506012546001600160a01b03161515612b54565b634a0bfec160e01b5f523360045260245ffd5b6001600160a01b0316331415905083612b38565b346112f2575f3660031901126112f25760206040517f3dbb5a17e2dda6a97acc2c59c4b3d3368b94b07f1b32afec7987d57e37e4fd078152f35b346112f25760203660031901126112f257612cc66001600160a01b03612c84613bfa565b16805f52600860205260405f20905f5260096020526115a6612cb2612cac60405f2093613eb7565b92613eb7565b604051938493604085526040850190613c10565b908382036020850152613c10565b346112f25760203660031901126112f257600f545f90600435825b828110612d0157602084604051908152f35b612d0a81613d53565b905460039190911b1c6001600160a01b0316612d268382614036565b9081612d4f575b50612d3b575b600101612cef565b92612d47600191613d6b565b939050612d33565b90505f526007602052612d6560405f2054613e7f565b151585612d2d565b346112f2576101003660031901126112f257612d87613bfa565b612d8f613b30565b906044356001600160801b03198116908181036112f2576064359160ff83168084036112f25760843595612dc1613b5c565b92612dca613b72565b9560e4356001600160401b0381116112f257612dea903690600401613b88565b6040989198519860208a01600160a01b600190038c16968782526001600160401b0316988960408d01528660608d01528860808d01528d60a08d01526001600160401b03169a8b60c082015260c08152612e4560e082613c98565b51902092612e5293614856565b825f52600260205260405f2060058101548060d81c60ff16156112075760d01c60ff166127515750831580156130f4575b6130e15788156130d257825f525f60205260405f20855f5260205260405f20906002820154966001600160401b038816808a11156111bc5750612ec7918b91614093565b5f818152600160205260409020549096906001600160a01b0316801515806130c8575b6130b2575060401c60ff16156130aa57806001612f1692549101549060ff8160801c169060801b614093565b955b88604051612f2581613c61565b8381526001600160401b0380600260208401938985526040810195865260608101958787526080820195600187528a5f525f60205260405f208d5f5260205260405f20925160801c83549160ff60801b905160801b169170ffffffffffffffffffffffffffffffffff19161717825551600182015501935116166001600160401b031983541617825551151560ff60401b82549160401b169060ff60401b19161790556040519160408301918383106001600160401b03841117610e5057604092835284845260208085018881525f8a8152600183528590209551865491516001600160e01b03199092166001600160a01b03919091161760a09190911b67ffffffffffffffff60a01b161790945582519182529281019490945283019790975260608201969096526118729583917f6ad0a6fc4d05a6986296bd82532c1c289660c306537630a1da2b80727d33a44f90608090a4811515908161309f575b50613090575b506149db565b61309990614a8d565b8261308a565b905081141584613084565b505f95612f18565b87633e9d9ea960e11b5f5260045260245260445ffd5b5084811415612eea565b630211d2d560e21b5f5260045ffd5b836385f6e97760e01b5f5260045260245ffd5b5060188411612e83565b346112f2575f3660031901126112f25760206040517ff6cc552646468a7983c31df0f953b1703ccadc1760ee2c78464c51e7c443171c8152f35b346112f2575f3660031901126112f25760206040517fcf993e293750106c3db70c784002b78c8fba024f618e4e3bb2748ef6a46321c28152f35b346112f25760803660031901126112f25761318b613b46565b606435906001600160401b0382116112f2576131ae611872923690600401613b88565b9160243560043533614c55565b346112f25760203660031901126112f257600f545f90600435825b8281106131e857602084604051908152f35b61320c826131f583613d53565b905460039190911b1c6001600160a01b0316614036565b613219575b6001016131d6565b92613225600191613d6b565b939050613211565b346112f2575f3660031901126112f2576020601354604051908152f35b346112f25760203660031901126112f2576001600160a01b0361326b613bfa565b165f5260066020526115a661159260405f20613eb7565b346112f25760203660031901126112f25760806001600160a01b036132a5613bfa565b16805f5260046020526132ba60405f20613eb7565b6020815191012090805f5260036020526132d660405f20613eb7565b6020815191012090805f5260066020526132f260405f20613eb7565b60208151910120905f52600560205261330d60405f20613eb7565b6020815191012091604051938452602084015260408301526060820152f35b346112f25760603660031901126112f257613345613bfa565b602435604435916001600160401b0383116112f2578260040190604060031985360301126112f25761337681613fd7565b15613533575f838152600e60205260409020546001600160a01b0316613520576001600160a01b03165f818152601460205260409020549093906001600160401b03166133c281613e2f565b855f5260146020526001600160401b0360405f2091166001600160401b031982541617905560405160208101917f3dbb5a17e2dda6a97acc2c59c4b3d3368b94b07f1b32afec7987d57e37e4fd0783524660408301523060608301528660808301528560a083015260c082015260c0815261343e60e082613c98565b519020906040519160208301526020825261345a604083613c98565b845f52600360205261348a6134828361090660405f2061347a8880613e4d565b949091613eb7565b933691613cd4565b159283156134f0575b5050506134dd575f818152600e6020526040812080546001600160a01b031916841790557f1dbd7f0554a6447214abb6405781d5ee119037a7024fcb7fffd542c3c12cd7769080a3005b50639ae9f73b60e01b5f5260045260245ffd5b6135179350613482929161347a61127d92885f526004602052602460405f20930190613e4d565b15838080613493565b8263e41b98f760e01b5f5260045260245ffd5b633210722560e21b5f9081526001600160a01b0391909116600452602490fd5b346112f2575f3660031901126112f257602060405160048152f35b346112f25760203660031901126112f2576004355f52600d602052602060ff60405f2054166040519015158152f35b346112f2575f3660031901126112f257602060405160808152f35b346112f25760803660031901126112f2576135d1613bfa565b6024356001600160401b0381116112f2576135f0903690600401613b88565b6135f8613b46565b6064356001600160401b0381116112f257613617903690600401613b88565b60409591955195602087019360608801948660018060a01b038616968783526040808c0152526080890198885f5b898110613777575050613668816136709798999a9b03601f198101835282613c98565b5190206147fe565b815f52600260205260405f2060058101805460ff8160d81c16156112075760ff60d01b1916600160d01b1790555f6004820181905581548152600d60205260409020805460ff191660011790556136c56149b6565b54827f898185a9e0933bcd25b9f43bc589ab95570a5c4b75d013955435eaf290380d4c5f80a35f5b8381101561376e57825f525f60205260405f20908060051b860135916001600160401b03831683036112f2576001600160401b03600193165f5260205260405f2060ff600282015460401c16613745575b50016136ed565b613763818461376893549101549060ff8160801c169060801b614093565b614a8d565b8661373e565b611872826149db565b909a8b35906001600160401b0382168092036112f257602081600193829352019c019101613645565b346112f25760203660031901126112f25760406137be600435613d8d565b825191151582526001600160a01b03166020820152f35b346112f25760603660031901126112f2576004356137f1613b30565b906044356001600160401b0381116112f2575f92613816613839923690600401613b88565b91604051602081019086825260208152613831604082613c98565b5190206147a6565b808252600d60209081526040808420805460ff19166001179055828452600c9091528220546001600160a01b031680613894575b507f1dbd7f0554a6447214abb6405781d5ee119037a7024fcb7fffd542c3c12cd7768280a3005b8083526002602052604083209060058201805460ff8160d01c16156138bc575b50505061386d565b60ff60d01b1916600160d01b179055600490910183905561390a906138df6149b6565b82817f898185a9e0933bcd25b9f43bc589ab95570a5c4b75d013955435eaf290380d4c8680a36149db565b828080806138b4565b346112f2575f3660031901126112f257602060405160018152f35b346112f25760203660031901126112f257600f5460043561394e82613d21565b915f915f5b8281106139a65750505061396681613d21565b915f5b82811061397e57604051806115a68682613bb8565b6001906001600160a01b036139938285613d79565b511661399f8287613d79565b5201613969565b806139c9836139b6600194613d53565b858060a01b0391549060031b1c16614036565b6139d4575b01613953565b6139dd81613d53565b838060a01b0391549060031b1c166139fe6139f787613d6b565b9688613d79565b526139ce565b346112f25760603660031901126112f257600435613a20613b30565b604435906001600160401b0382116112f257613a43613a66923690600401613b88565b91604051602081019086825260208152613a5e604082613c98565b519020614748565b8015613ac157613a74613f57565b818110613aac577f08808b872c36595369a5004f16ab8777648833a654e6b43a1a7e36b081f2b32460208380601355604051908152a1005b6304951c2160e11b5f5260045260245260445ffd5b631de31ea560e11b5f5260045ffd5b346112f2575f3660031901126112f2576011546040516001600160a01b039091168152602090f35b346112f2575f3660031901126112f257807f6c9c22b535529e02295bb8861d935a916462e2e1bb844c50ef1dd822975a61f060209252f35b602435906001600160401b03821682036112f257565b604435906001600160401b03821682036112f257565b60a435906001600160401b03821682036112f257565b60c435906001600160401b03821682036112f257565b9181601f840112156112f2578235916001600160401b0383116112f2576020808501948460051b0101116112f257565b60206040818301928281528451809452019201905f5b818110613bdb5750505090565b82516001600160a01b0316845260209384019390920191600101613bce565b600435906001600160a01b03821682036112f257565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f840112156112f2578235916001600160401b0383116112f257602083818601950101116112f257565b60a081019081106001600160401b03821117610e5057604052565b61018081019081106001600160401b03821117610e5057604052565b90601f801991011681019081106001600160401b03821117610e5057604052565b6001600160401b038111610e5057601f01601f191660200190565b929192613ce082613cb9565b91613cee6040519384613c98565b8294818452818301116112f2578281602093845f960137010152565b6001600160401b038111610e505760051b60200190565b90613d2b82613d0a565b613d386040519182613c98565b8281528092613d49601f1991613d0a565b0190602036910137565b600f5481101561287657600f5f5260205f2001905f90565b5f19811461283e5760010190565b80518210156128765760209160051b010190565b5f818152600160205260409020546001600160a01b03811691908215613e2657613db683614394565b15613e1f5760018060a01b0383165f525f6020526001600160401b038060405f209260a01c16165f5260205260405f209060ff600283015460401c169182613dfd57505091565b613e1b919250600181549101549060ff8160801c169060801b614093565b1491565b505f929050565b5050505f905f90565b6001600160401b036001911601906001600160401b03821161283e57565b903590601e19813603018212156112f257018035906001600160401b0382116112f2576020019181360383136112f257565b90600182811c92168015613ead575b6020831014613e9957565b634e487b7160e01b5f52602260045260245ffd5b91607f1691613e8e565b9060405191825f825492613eca84613e7f565b8084529360018116908115613f355750600114613ef1575b50613eef92500383613c98565b565b90505f9291925260205f20905f915b818310613f19575050906020613eef928201015f613ee2565b6020919350806001915483858901015201910190918492613f00565b905060209250613eef94915060ff191682840152151560051b8201015f613ee2565b5f90600f545f5b818110613f69575050565b613f7281613d53565b905460039190911b1c6001600160a01b0316613f9061010082614036565b9081613fb9575b50613fa5575b600101613f5e565b92613fb1600191613d6b565b939050613f9d565b90505f526007602052613fcf60405f2054613e7f565b15155f613f97565b6001600160a01b03165f908152600260205260409020600581015460d881901c60ff1615908115614028575b506140235761020080600483015416036140235761402090615000565b90565b505f90565b60ff915060d01c165f614003565b811561407f5760018060a01b03165f52600260205260405f2090600582015460ff8160d81c1615908115614085575b5061407f5780600483015416036140235761402090615000565b50505f90565b60ff915060d01c165f614065565b9160ff6040519260208401946001600160801b03191685521660408301526060820152606081526140c5608082613c98565b51902090565b81601f820112156112f2578051906140e282613cb9565b926140f06040519485613c98565b828452602083830101116112f257815f9260208093018386015e8301015290565b519060ff821682036112f257565b51906001600160401b03821682036112f257565b6020818303126112f2578051906001600160401b0382116112f2570190610220828203126112f2576040519161022083018381106001600160401b03821117610e505760405280518352602081015160208401526040810151604084015260608101516001600160401b0381116112f257826141b09183016140cb565b606084015260808101516001600160401b0381116112f257826141d49183016140cb565b608084015260a081015160a084015260c081015160c08401526141f960e08201614111565b60e084015261420b6101008201614111565b61010084015261421e610120820161411f565b610120840152614231610140820161411f565b6101408401526101608101516001600160401b0381116112f257826142579183016140cb565b6101608401526101808101516001600160401b0381116112f2578261427d9183016140cb565b6101808401526101a08101516001600160401b0381116112f257826142a39183016140cb565b6101a08401526101c08101516001600160401b0381116112f257826142c99183016140cb565b6101c08401526101e08101516001600160401b0381116112f257610200926142f29183016140cb565b6101e0840152015161020082015290565b90918060409360208452816020850152848401375f828201840152601f01601f1916010190565b5f929181549161433983613e7f565b9260018116908115614381575060011461435257505050565b90919293505f5260205f205f905b83821061436d5750500190565b600181602092548486015201910190614360565b60ff191683525050811515909102019150565b6001600160a01b03165f908152600260205260409020600581015460d881901c60ff169190826143d3575b50816143c9575090565b6140209150615000565b60d01c60ff161591505f6143bf565b6001600160a01b03165f90815260036020526040902080549061440482613e7f565b1561407f57604051915f9161441882613e7f565b80855291600181169081156144825750600114614442575b50509061042f81614020930382613c98565b5f90815260208120939250905b8082106144685750909150810160200161042f82614430565b91926001816020925483858801015201910190929161444f565b60ff191660208087019190915292151560051b8501909201925061042f9150839050614430565b6001600160a01b03165f81815260026020526040902060058101549160d883901c60ff16156146ef5760ff8360d01c1680156146df575b6146d85760ff8360081c1660ff84160361467a576002919250805f52600860205260405f20815f52600960205261199e61453f60405f2060405192839161199860208401965f516020615e185f395f51905f528852604085019061432a565b51902090805f52600a60205260405f20815f52600b60205261199e61458960405f2060405192839161199860208401965f516020615e185f395f51905f528852604085019061432a565b519020815f5260046020526145a060405f20613eb7565b6020815191012092825f5260036020526145bc60405f20613eb7565b6020815191012092805f5260066020526145d860405f20613eb7565b60208151910120905f5260056020526145f360405f20613eb7565b6020815191012090604051946020860196875260408601526060850152608084015260a083015260c082015260c0815261462e60e082613c98565b519020910154906040519060208201927fcc25d3fea88291f95ddfb5590a6b760f02245a0e4ca7c0b69285c6cd26543afd845260408301526060820152606081526140c5608082613c98565b5054906040519060208201927fa654ea8b191780d915fe370e2900652d600c179f2abeb8967e1b06ba97fe7094845260408301526001600160401b0360c01b9060301b1660608201525f6068820152606881526140c5608882613c98565b5050505f90565b506146e982615000565b156144e0565b633131bf7960e21b5f5260045260245ffd5b60405190614738602183602080820194600160fa1b86528051918291018484015e81015f838201520301601f198101845283613c98565b905190206001600160a01b031690565b92919060105460ff8160a01c16159081614793575b5061478d57613eef937f6c9c22b535529e02295bb8861d935a916462e2e1bb844c50ef1dd822975a61f030614c55565b50505050565b6001600160a01b0316331490505f61475d565b92919060105460ff8160a01c161590816147eb575b5061478d57613eef937f617214620b6c0e190ece7e2c8e3b97354e56dc3501b8a6811dd7e742dc6637c030614c55565b6001600160a01b0316331490505f6147bb565b92919060105460ff8160a01c16159081614843575b5061478d57613eef937fc1f01606bfc0d9242f3def839a8589d5b92fe65d09df4befd80f0439f60344cc30614c55565b6001600160a01b0316331490505f614813565b92919060105460ff8160a01c1615908161489b575b5061478d57613eef937ff6cc552646468a7983c31df0f953b1703ccadc1760ee2c78464c51e7c443171c30614c55565b6001600160a01b0316331490505f61486b565b92919060105460ff8160a01c161590816148f3575b5061478d57613eef937fcf993e293750106c3db70c784002b78c8fba024f618e4e3bb2748ef6a46321c230614c55565b6001600160a01b0316331490505f6148c3565b92919060105460ff8160a01c1615908161494b575b5061478d57613eef937f9e01a06a8e87fa34b0b3b7f97e4f2ba5e105d644fac817f11f688ac916421fa230614c55565b6001600160a01b0316331490505f61491b565b92919060105460ff8160a01c161590816149a3575b5061478d57613eef937f664b0e72d3e91e88f8d9d2c0d916d51aff408110b8760394f6d677a5e6b24a2e30614c55565b6001600160a01b0316331490505f614973565b60ff60105460a01c1615613eef576149cc613f57565b60135490818110613aac575050565b6011546001600160a01b03168015614a71576040908151926149fd8385613c98565b600184526020840190601f198401368337845115612876576001600160a01b03169052803b156112f25781516301e8a3a760e01b8152925f918491829084908290614a4b9060048301613bb8565b03925af1908115614a685750614a5e5750565b5f613eef91613c98565b513d5f823e3d90fd5b5050565b908160209103126112f2575180151581036112f25790565b6012546001600160a01b0316908115614a7157604051630d63997960e41b815260048101829052602081602481865afa9081156112c5575f91614b03575b50614a7157813b156112f2575f9160248392604051948593849263b5c645bd60e01b845260048401525af180156112c557614a5e5750565b614b25915060203d602011614b2b575b614b1d8183613c98565b810190614a75565b5f614acb565b503d614b13565b610a20815114801590614bcc575b6146d8576020614b935f948286958160405195869481808701998051918291018b5e8601908282018b8152815193849201905e010190878252805192839101825e0185815203601f198101835282613c98565b51906102045afa614ba2615327565b81614bc0575b81614bb1575090565b614bbb9150615356565b151590565b80516020149150614ba8565b5061121383511415614b40565b6040815114801590614c48575b6146d8576020614c395f948286958160405195869481808701998051918291018b5e8601908282018b8152815193849201905e010190878252805192839101825e0185815203601f198101835282613c98565b51906102055afa614ba2615327565b5061746083511415614be6565b9594919390929560135415613ac15760018060a01b031694855f5260146020526001600160401b0360405f20541694614c8d86613e2f565b875f5260146020526001600160401b0360405f2091166001600160401b03198254161790556040516020810191878352604082015260408152614cd1606082613c98565b519020966040516001600160401b0360208201927fd850f5df47b124511e8e6ec99cf1a0beaf7c6237eff0a31305ce53d85f312675845246604084015289606084015287608084015216988960a083015260c082015260c08152614d3660e082613c98565b51902093601354965f988815614ff157438111614fdb5780430343811161283e5761025810614fc557506040989497985195602087015260208652614d7c604087613c98565b5f965f99607e1986360301965b8a8c1015614f69578b60051b870135888112156112f257870199614dac8b615378565b6001600160a01b039182169116811015614f3d5750614dca8a615378565b99614dd481615378565b604051632e4bfa5160e11b81526001600160a01b0390911660048201526101006024820152602081604481305afa9081156112c5575f91614f1f575b5015614ef05760208101600460ff614e278361538c565b1603614ebe57614e388b8330615b97565b15614e8b5750614e498a8230615cc6565b15614e625750614e5a600191613d6b565b9b019a614d89565b614e6b90615378565b63c082266360e01b5f9081526001600160a01b0391909116600452602490fd5b614e9f614e9960ff93615378565b9161538c565b9063bbf82ba360e01b5f5260018060a01b03166004521660245260445ffd5b614ecc614e9960ff93615378565b9063587548c360e11b5f5260018060a01b031660045216602452600460445260645ffd5b614ef990615378565b63ae8bb03960e01b5f9081526001600160a01b0391909116600452610100602452604490fd5b614f37915060203d8111614b2b57614b1d8183613c98565b5f614e10565b614f468b615378565b6311641feb60e21b5f9081526004929092526001600160a01b0316602452604490fd5b965094509550955095509550808210614faf57507fd0ab8cbd6c510239be8b7cbd29e233d090728605764b8a4149ead15ae98677b49160409182519182526020820152a3565b906305bc216760e51b5f5260045260245260445ffd5b630ed38fd160e41b5f526004524360245260445ffd5b637b51505560e01b5f526004524360245260445ffd5b631fc460bf60e11b5f5260045ffd5b600501546001600160401b038160101c168015159081615058575b506140235760501c6001600160401b03168015159081615044575b5061504057600190565b5f90565b90506001600160401b03421610155f615036565b90506001600160401b034216105f61501b565b60ff60019116019060ff821161283e57565b906001600160a01b038216801561518057805f5260026020526150a360405f2093613fd7565b1561516e57600583015460ff8082169160081c169182821015615156575060e0830160ff81511660ff6150d58461506b565b160361512f57505060ff6101008301511681811161511a57505060a0600391015191015490818103615105575050565b635a6d501b60e11b5f5260045260245260445ffd5b63a96ac3e360e01b5f5260045260245260445ffd5b9061513f60ff809351169161506b565b9063239acc5160e21b5f526004521660245260445ffd5b632026243960e01b5f5260045260245260445260645ffd5b633210722560e21b5f5260045260245ffd5b50905060a08101517f9a6a5d8139ad2d28957698330aaa691017dba7dc80eb7cbec585239fb680bbab81036151d9575060e0015160ff165f1981016151c25750565b63239acc5160e21b5f52600452600160245260445ffd5b637557fe2160e11b5f5260045260245ffd5b908151811015612876570160200190565b6040516001600160f81b03199182166020820152911660218201526002815290613eef602283613c98565b80516020909101516001600160f01b0319811692919060028210615249575050565b6001600160f01b031960029290920360031b82901b16169150565b5f5b82811061527257505050565b5f82820155600101615266565b60a08101517f9a6a5d8139ad2d28957698330aaa691017dba7dc80eb7cbec585239fb680bbab81036151d9575060408101517fa5f1406585e87ef6c3645d6270cf45d62ae1c5d1abf447614cd94657a3f429a08103612942575060ff60e0820151169060018214801590615315575b6152f6575050565b61010060ff9101511690631a25415160e21b5f5260045260245260445ffd5b508160ff6101008301511614156152ee565b3d15615351573d9061533882613cb9565b916153466040519384613c98565b82523d5f602084013e565b606090565b60208151910151906020811061536a575090565b5f199060200360031b1b1690565b356001600160a01b03811681036112f25790565b3560ff811681036112f25790565b6153a48154613e7f565b90816153ae575050565b81601f5f93116001146153bf575055565b818352602083206153dc91601f0160051c84190190600101615264565b8082528160208120915555565b9093929380156156a4575b6156655761540182615d5d565b156156405761540f84615daa565b1561561b576001600160a01b03165f818152600860205260409020825191929091906001600160401b038211610e50576154498354613e7f565b601f81116155e1575b50602090601f831160011461557e5761548192915f9183610d875750508160011b915f199060031b1c19161790565b90555b5f52600960205260405f2082516001600160401b038111610e50576154a98254613e7f565b601f8111615544575b506020601f82116001146154e65781906154e29394955f92610d875750508160011b915f199060031b1c19161790565b9055565b601f19821690835f52805f20915f5b81811061552c57509583600195969710615514575b505050811b019055565b01515f1960f88460031b161c191690555f808061550a565b9192602060018192868b0151815501940192016154f5565b818111156154b25761557890835f5260205f2090601f840160051c9060208510610e4857601f82910160051c039101615264565b5f6154b2565b90601f19831691845f52815f20925f5b8181106155c957509084600195949392106155b1575b505050811b019055615484565b01515f1960f88460031b161c191690555f80806155a4565b9293602060018192878601518155019501930161558e565b828111156154525761561590845f5260205f2090601f850160051c9060208610610e4857601f82910160051c039101615264565b5f615452565b63c4facfbf60e01b5f9081526001600160a01b03919091166004526007602452604490fd5b63c4facfbf60e01b5f9081526001600160a01b03919091166004526003602452604490fd5b9192505060018060a01b0381165f52600860205261568560405f2061539a565b6001600160a01b03165f908152600960205260409020613eef9061539a565b508151156153f4565b9293925f92918015615b69575b615b29576156c782615d5d565b15615b09576156d585615daa565b15615ae9578215615900576001600160a01b0316808352600860205260408320825191929091906001600160401b0382116158ec576157148354613e7f565b601f81116158aa575b50602090601f83116001146158485761574c9291869183610d875750508160011b915f199060031b1c19161790565b90555b8152600960205260408120908351906001600160401b038211615834576157768354613e7f565b601f81116157f2575b50602090601f83116001146157ae57906154e293949583610d875750508160011b915f199060031b1c19161790565b90601f198316848352818320925b8181106157da5750958360019596971061551457505050811b019055565b9192602060018192868b0151815501940192016157bc565b8281111561577f57615826908483526020832090601f850160051c906020861061582c57601f82910160051c039101615264565b5f61577f565b849150610e32565b634e487b7160e01b81526041600452602490fd5b8386528186209190601f198416875b818110615892575090846001959493921061587a575b505050811b01905561574f565b01515f1960f88460031b161c191690555f808061586d565b92936020600181928786015181550195019301615857565b8281111561571d576158de908487526020872090601f850160051c90602086106158e457601f82910160051c039101615264565b5f61571d565b889150610e32565b634e487b7160e01b85526041600452602485fd5b6001600160a01b03165f818152600a602052604090208251919350916001600160401b038211610e50576159348354613e7f565b601f8111615aaf575b50602090601f8311600114615a4c5761596c92915f9183610d875750508160011b915f199060031b1c19161790565b90555b5f52600b60205260405f2082516001600160401b038111610e50576159948254613e7f565b601f8111615a12575b506020601f82116001146159cd5781906154e29394955f92610d875750508160011b915f199060031b1c19161790565b601f19821690835f52805f20915f5b8181106159fa5750958360019596971061551457505050811b019055565b9192602060018192868b0151815501940192016159dc565b8181111561599d57615a4690835f5260205f2090601f840160051c9060208510610e4857601f82910160051c039101615264565b5f61599d565b90601f19831691845f52815f20925f5b818110615a975750908460019594939210615a7f575b505050811b01905561596f565b01515f1960f88460031b161c191690555f8080615a72565b92936020600181928786015181550195019301615a5c565b8281111561593d57615ae390845f5260205f2090601f850160051c9060208610610e4857601f82910160051c039101615264565b5f61593d565b63c4facfbf60e01b83526001600160a01b03166004526007602452604482fd5b63c4facfbf60e01b83526001600160a01b03166004526003602452604482fd5b929350505060018060a01b0381165f52600a602052615b4a60405f2061539a565b6001600160a01b03165f908152600b60205260409020613eef9061539a565b508151156156ba565b906020828203126112f25781516001600160401b0381116112f25761402092016140cb565b9160208201600460ff615ba98361538c565b1614615c4b5760ff615bbc60059261538c565b1614615bc9575050505f90565b5f615bd383615378565b604051639e5adaeb60e01b81526001600160a01b0391821660048201529485916024918391165afa9182156112c557614020935f93615c1f575b506108ff81604061127d930190613e4d565b61127d919350615c436108ff913d805f833e615c3b8183613c98565b810190615b72565b939150615c0d565b505f615c5683615378565b60405163b7af85d760e01b81526001600160a01b0391821660048201529485916024918391165afa9182156112c557614020935f93615ca2575b506108ff816040610906930190613e4d565b610906919350615cbe6108ff913d805f833e615c3b8183613c98565b939150615c90565b90915f615cd284615378565b60405163ad84ad1360e01b81526001600160a01b0391821660048201529384916024918391165afa9182156112c5575f92615d41575b508151158015615d2b575b6146d85761127d6108ff846060614020960190613e4d565b50615d396060840184613e4d565b905015615d13565b615d569192503d805f833e615c3b8183613c98565b905f615d08565b5f80916020815191016102035afa615d73615327565b90158015615d9e575b615d8957614bbb90615356565b634e6f9bdf60e11b5f5261020360045260245ffd5b50602081511415615d7c565b5f80916020815191016102075afa615dc0615327565b90158015615deb575b615dd657614bbb90615356565b634e6f9bdf60e11b5f5261020760045260245ffd5b50602081511415615dc956fecd32e76e8e1fee2c6f45e0ccbed91002a21c754eaaa76564a10aa391f628a2b5374d5630a5adaa53fb387074872486662e4eff70f8ef438306c8697c24e7016c
Nessuna coda di metadati CBOR — questo bytecode è stato compilato con cbor_metadata disattivato, l'impostazione che i nostri contratti fissano per l'invarianza degli indirizzi CREATE2.

disassemblato (prime 4,000 op)

pcopoperando
0000PUSH10x80
0002DUP1
0003PUSH10x40
0005MSTORE
0006PUSH10x04
0008CALLDATASIZE
0009LT
000aISZERO
000bPUSH20x0012
000eJUMPI
000fPUSH0
0010DUP1
0011REVERT
0012JUMPDEST
0013PUSH0
0014SWAP1
0015PUSH0
0016CALLDATALOAD
0017PUSH10xe0
0019SHR
001aSWAP1
001bDUP2
001cPUSH40x0408a87f
0021EQ
0022PUSH20x3af8
0025JUMPI
0026POP
0027DUP1
0028PUSH40x04525da8
002dEQ
002ePUSH20x3ad0
0031JUMPI
0032DUP1
0033PUSH40x0d641df0
0038EQ
0039PUSH20x3a04
003cJUMPI
003dDUP1
003ePUSH40x1479bc3a
0043EQ
0044PUSH20x392e
0047JUMPI
0048DUP1
0049PUSH40x15ba3c1d
004eEQ
004fPUSH20x3913
0052JUMPI
0053DUP1
0054PUSH40x1d4f1077
0059EQ
005aPUSH20x37d5
005dJUMPI
005eDUP1
005fPUSH40x1f394fb3
0064EQ
0065PUSH20x37a0
0068JUMPI
0069DUP1
006aPUSH40x1f9dc0bb
006fEQ
0070PUSH20x35b8
0073JUMPI
0074DUP1
0075PUSH40x209f8044
007aEQ
007bPUSH20x359d
007eJUMPI
007fDUP1
0080PUSH40x222e6f0e
0085EQ
0086PUSH20x356e
0089JUMPI
008aDUP1
008bPUSH40x228bf014
0090EQ
0091PUSH20x3553
0094JUMPI
0095DUP1
0096PUSH40x2741cb02
009bEQ
009cPUSH20x332c
009fJUMPI
00a0DUP1
00a1PUSH40x2ca02983
00a6EQ
00a7PUSH20x3282
00aaJUMPI
00abDUP1
00acPUSH40x3222383e
00b1EQ
00b2PUSH20x324a
00b5JUMPI
00b6DUP1
00b7PUSH40x32596d71
00bcEQ
00bdPUSH20x322d
00c0JUMPI
00c1DUP1
00c2PUSH40x342f6163
00c7EQ
00c8PUSH20x31bb
00cbJUMPI
00ccDUP1
00cdPUSH40x45e7e88e
00d2EQ
00d3PUSH20x3172
00d6JUMPI
00d7DUP1
00d8PUSH40x461de6c1
00ddEQ
00dePUSH20x3138
00e1JUMPI
00e2DUP1
00e3PUSH40x488429d9
00e8EQ
00e9PUSH20x30fe
00ecJUMPI
00edDUP1
00eePUSH40x4c4af1a4
00f3EQ
00f4PUSH20x2d6d
00f7JUMPI
00f8DUP1
00f9PUSH40x4ccf0005
00feEQ
00ffPUSH20x2cd4
0102JUMPI
0103DUP1
0104PUSH40x4f8d3216
0109EQ
010aPUSH20x2c60
010dJUMPI
010eDUP1
010fPUSH40x5a232a39
0114EQ
0115PUSH20x2c26
0118JUMPI
0119DUP1
011aPUSH40x5b882501
011fEQ
0120PUSH20x2af7
0123JUMPI
0124DUP1
0125PUSH40x5c97f4a2
012aEQ
012bPUSH20x2ad0
012eJUMPI
012fDUP1
0130PUSH40x61dbd452
0135EQ
0136PUSH20x2a96
0139JUMPI
013aDUP1
013bPUSH40x64d2218d
0140EQ
0141PUSH20x2a77
0144JUMPI
0145DUP1
0146PUSH40x70f64f0a
014bEQ
014cPUSH20x2a2c
014fJUMPI
0150DUP1
0151PUSH40x77e223b0
0156EQ
0157PUSH20x2a11
015aJUMPI
015bDUP1
015cPUSH40x86731755
0161EQ
0162PUSH20x29d7
0165JUMPI
0166DUP1
0167PUSH40x8c6820a9
016cEQ
016dPUSH20x1a74
0170JUMPI
0171DUP1
0172PUSH40x8cfc6aa0
0177EQ
0178PUSH20x1a42
017bJUMPI
017cDUP1
017dPUSH40x90ecddbf
0182EQ
0183PUSH20x1a01
0186JUMPI
0187DUP1
0188PUSH40x926590d6
018dEQ
018ePUSH20x1932
0191JUMPI
0192DUP1
0193PUSH40x94338765
0198EQ
0199PUSH20x18f0
019cJUMPI
019dDUP1
019ePUSH40x9aae87ad
01a3EQ
01a4PUSH20x18b6
01a7JUMPI
01a8DUP1
01a9PUSH40x9c11e31b
01aeEQ
01afPUSH20x189b
01b2JUMPI
01b3DUP1
01b4PUSH40x9d07ae09
01b9EQ
01baPUSH20x1787
01bdJUMPI
01beDUP1
01bfPUSH40x9e5adaeb
01c4EQ
01c5PUSH20x174f
01c8JUMPI
01c9DUP1
01caPUSH40x9f8a13d7
01cfEQ
01d0PUSH20x1722
01d3JUMPI
01d4DUP1
01d5PUSH40xa0c176c4
01daEQ
01dbPUSH20x16fd
01deJUMPI
01dfDUP1
01e0PUSH40xa760c295
01e5EQ
01e6PUSH20x16cb
01e9JUMPI
01eaDUP1
01ebPUSH40xa874eea5
01f0EQ
01f1PUSH20x16b0
01f4JUMPI
01f5DUP1
01f6PUSH40xad20445d
01fbEQ
01fcPUSH20x1696
01ffJUMPI
0200DUP1
0201PUSH40xad84ad13
0206EQ
0207PUSH20x165e
020aJUMPI
020bDUP1
020cPUSH40xb0538d5a
0211EQ
0212PUSH20x1643
0215JUMPI
0216DUP1
0217PUSH40xb16abc6c
021cEQ
021dPUSH20x160b
0220JUMPI
0221DUP1
0222PUSH40xb3c26283
0227EQ
0228PUSH20x15aa
022bJUMPI
022cDUP1
022dPUSH40xb7af85d7
0232EQ
0233PUSH20x155a
0236JUMPI
0237DUP1
0238PUSH40xbaadbb32
023dEQ
023ePUSH20x1520
0241JUMPI
0242DUP1
0243PUSH40xc0716729
0248EQ
0249PUSH20x1504
024cJUMPI
024dDUP1
024ePUSH40xc177a97e
0253EQ
0254PUSH20x14e1
0257JUMPI
0258DUP1
0259PUSH40xc5768fe4
025eEQ
025fPUSH20x14b9
0262JUMPI
0263DUP1
0264PUSH40xc6345626
0269EQ
026aPUSH20x12f6
026dJUMPI
026eDUP1
026fPUSH40xcbe6ebb4
0274EQ
0275PUSH20x0672
0278JUMPI
0279DUP1
027aPUSH40xce092d1c
027fEQ
0280PUSH20x0655
0283JUMPI
0284DUP1
0285PUSH40xde043e95
028aEQ
028bPUSH20x061a
028eJUMPI
028fDUP1
0290PUSH40xe0cd096a
0295EQ
0296PUSH20x05ee
0299JUMPI
029aDUP1
029bPUSH40xe14c465b
02a0EQ
02a1PUSH20x05d1
02a4JUMPI
02a5DUP1
02a6PUSH40xe4af29fc
02abEQ
02acPUSH20x05b3
02afJUMPI
02b0DUP1
02b1PUSH40xe51442e3
02b6EQ
02b7PUSH20x057b
02baJUMPI
02bbDUP1
02bcPUSH40xec964baa
02c1EQ
02c2PUSH20x04b0
02c5JUMPI
02c6DUP1
02c7PUSH40xef23f9f8
02ccEQ
02cdPUSH20x046f
02d0JUMPI
02d1DUP1
02d2PUSH40xf5778b03
02d7EQ
02d8PUSH20x0446
02dbJUMPI
02dcDUP1
02ddPUSH40xfce072f8
02e2EQ
02e3PUSH20x03e9
02e6JUMPI
02e7PUSH40xffd326f3
02ecEQ
02edPUSH20x02f4
02f0JUMPI
02f1PUSH0
02f2DUP1
02f3REVERT
02f4JUMPDEST
02f5CALLVALUE
02f6PUSH20x03e6
02f9JUMPI
02faPUSH10x40
02fcCALLDATASIZE
02fdPUSH10x03
02ffNOT
0300ADD
0301SLT
0302PUSH20x03e6
0305JUMPI
0306PUSH10x01
0308PUSH10x01
030aPUSH10x40
030cSHL
030dSUB
030ePUSH10x40
0310PUSH20x0317
0313PUSH20x3bfa
0316JUMP
0317JUMPDEST
0318SWAP3
0319PUSH20x0320
031cPUSH20x3b30
031fJUMP
0320JUMPDEST
0321SWAP4
0322DUP2
0323PUSH10x80
0325DUP5
0326MLOAD
0327PUSH20x032f
032aDUP2
032bPUSH20x3c61
032eJUMP
032fJUMPDEST
0330DUP3
0331DUP2
0332MSTORE
0333DUP3
0334PUSH10x20
0336DUP3
0337ADD
0338MSTORE
0339DUP3
033aDUP7
033bDUP3
033cADD
033dMSTORE
033eDUP3
033fPUSH10x60
0341DUP3
0342ADD
0343MSTORE
0344ADD
0345MSTORE
0346PUSH10x01
0348DUP1
0349PUSH10xa0
034bSHL
034cSUB
034dAND
034eDUP2
034fMSTORE
0350DUP1
0351PUSH10x20
0353MSTORE
0354KECCAK256
0355SWAP2
0356AND
0357PUSH0
0358MSTORE
0359PUSH10x20
035bMSTORE
035cPUSH10xa0
035ePUSH10x40
0360PUSH0
0361KECCAK256
0362PUSH10x40
0364MLOAD
0365PUSH20x036d
0368DUP2
0369PUSH20x3c61
036cJUMP
036dJUMPDEST
036ePUSH10x01
0370PUSH10x01
0372PUSH10x40
0374SHL
0375SUB
0376DUP3
0377SLOAD
0378SWAP2
0379PUSH10x01
037bPUSH10x01
037dPUSH10x80
037fSHL
0380SUB
0381NOT
0382DUP4
0383PUSH10x80
0385SHL
0386AND
0387SWAP4
0388DUP5
0389DUP3
038aMSTORE
038bPUSH10xff
038dPUSH10x20
038fDUP4
0390ADD
0391SWAP5
0392PUSH10x80
0394SHR
0395AND
0396DUP5
0397MSTORE
0398PUSH10xff
039aPUSH10x02
039cPUSH10x01
039eDUP4
039fADD
03a0SLOAD
03a1SWAP3
03a2PUSH10x40
03a4DUP6
03a5ADD
03a6SWAP4
03a7DUP5
03a8MSTORE
03a9ADD
03aaSLOAD
03abSWAP5
03acDUP2
03adPUSH10x80
03afPUSH10x60
03b1DUP7
03b2ADD
03b3SWAP6
03b4DUP8
03b5DUP10
03b6AND
03b7DUP8
03b8MSTORE
03b9ADD
03baSWAP7
03bbPUSH10x40
03bdSHR
03beAND
03bfISZERO
03c0ISZERO
03c1DUP7
03c2MSTORE
03c3PUSH10x40
03c5MLOAD
03c6SWAP7
03c7DUP8
03c8MSTORE
03c9MLOAD
03caAND
03cbPUSH10x20
03cdDUP7
03ceADD
03cfMSTORE
03d0MLOAD
03d1PUSH10x40
03d3DUP6
03d4ADD
03d5MSTORE
03d6MLOAD
03d7AND
03d8PUSH10x60
03daDUP4
03dbADD
03dcMSTORE
03ddMLOAD
03deISZERO
03dfISZERO
03e0PUSH10x80
03e2DUP3
03e3ADD
03e4MSTORE
03e5RETURN
03e6JUMPDEST
03e7DUP1
03e8REVERT
03e9JUMPDEST
03eaPOP
03ebCALLVALUE
03ecPUSH20x03e6
03efJUMPI
03f0PUSH10x20
03f2CALLDATASIZE
03f3PUSH10x03
03f5NOT
03f6ADD
03f7SLT
03f8PUSH20x03e6
03fbJUMPI
03fcPUSH10x04
03feCALLDATALOAD
03ffSWAP1
0400PUSH10x01
0402PUSH10x01
0404PUSH10x40
0406SHL
0407SUB
0408DUP3
0409GT
040aPUSH20x03e6
040dJUMPI
040eCALLDATASIZE
040fPUSH10x23
0411DUP4
0412ADD
0413SLT
0414ISZERO
0415PUSH20x03e6
0418JUMPI
0419PUSH10x20
041bPUSH20x0434
041ePUSH20x042f
0421CALLDATASIZE
0422PUSH10x04
0424DUP7
0425ADD
0426CALLDATALOAD
0427PUSH10x24
0429DUP8
042aADD
042bPUSH20x3cd4
042eJUMP
042fJUMPDEST
0430PUSH20x4701
0433JUMP
0434JUMPDEST
0435PUSH10x40
0437MLOAD
0438PUSH10x01
043aPUSH10x01
043cPUSH10xa0
043eSHL
043fSUB
0440SWAP1
0441SWAP2
0442AND
0443DUP2
0444MSTORE
0445RETURN
0446JUMPDEST
0447POP
0448CALLVALUE
0449PUSH20x03e6
044cJUMPI
044dDUP1
044ePUSH10x03
0450NOT
0451CALLDATASIZE
0452ADD
0453SLT
0454PUSH20x03e6
0457JUMPI
0458PUSH10x10
045aSLOAD
045bPUSH10x40
045dMLOAD
045ePUSH10x01
0460PUSH10x01
0462PUSH10xa0
0464SHL
0465SUB
0466SWAP1
0467SWAP2
0468AND
0469DUP2
046aMSTORE
046bPUSH10x20
046dSWAP1
046eRETURN
046fJUMPDEST
0470POP
0471CALLVALUE
0472PUSH20x03e6
0475JUMPI
0476PUSH10x20
0478CALLDATASIZE
0479PUSH10x03
047bNOT
047cADD
047dSLT
047ePUSH20x03e6
0481JUMPI
0482PUSH10x20
0484SWAP1
0485PUSH10x01
0487PUSH10x01
0489PUSH10xa0
048bSHL
048cSUB
048dPUSH20x0494
0490PUSH20x3bfa
0493JUMP
0494JUMPDEST
0495AND
0496DUP2
0497MSTORE
0498PUSH10x15
049aDUP3
049bMSTORE
049cPUSH10x40
049ePUSH10x01
04a0DUP1
04a1PUSH10xa0
04a3SHL
04a4SUB
04a5SWAP2
04a6KECCAK256
04a7SLOAD
04a8AND
04a9PUSH10x40
04abMLOAD
04acSWAP1
04adDUP2
04aeMSTORE
04afRETURN
04b0JUMPDEST
04b1POP
04b2CALLVALUE
04b3PUSH20x03e6
04b6JUMPI
04b7DUP1
04b8PUSH10x03
04baNOT
04bbCALLDATASIZE
04bcADD
04bdSLT
04bePUSH20x03e6
04c1JUMPI
04c2PUSH10x10
04c4SLOAD
04c5PUSH10x01
04c7PUSH10x01
04c9PUSH10xa0
04cbSHL
04ccSUB
04cdDUP2
04ceAND
04cfCALLER
04d0SUB
04d1PUSH20x0568
04d4JUMPI
04d5PUSH10xff
04d7DUP2
04d8PUSH10xa0
04daSHR
04dbAND
04dcPUSH20x0559
04dfJUMPI
04e0PUSH10x13
04e2SLOAD
04e3DUP1
04e4ISZERO
04e5PUSH20x054a
04e8JUMPI
04e9PUSH20x04f0
04ecPUSH20x3f57
04efJUMP
04f0JUMPDEST
04f1DUP2
04f2DUP2
04f3LT
04f4PUSH20x0534
04f7JUMPI
04f8POP
04f9POP
04faPUSH10x01
04fcPUSH10x01
04fePUSH10xa8
0500SHL
0501SUB
0502NOT
0503AND
0504PUSH10x01
0506PUSH10xa0
0508SHL
0509OR
050aPUSH10x10
050cSSTORE
050dCALLER
050ePUSH320xdd5280e26bdef754c085fd1fe40c0ab76df98368bb6014f85b49f8852a81be57
052fDUP3
0530DUP1
0531LOG2
0532DUP1
0533RETURN
0534JUMPDEST
0535PUSH40x04951c21
053aPUSH10xe1
053cSHL
053dDUP5
053eMSTORE
053fPUSH10x04
0541MSTORE
0542PUSH10x24
0544MSTORE
0545POP
0546PUSH10x44
0548SWAP1
0549REVERT
054aJUMPDEST
054bPUSH40x1de31ea5
0550PUSH10xe1
0552SHL
0553DUP4
0554MSTORE
0555PUSH10x04
0557DUP4
0558REVERT
0559JUMPDEST
055aPUSH40x0f80bc05
055fPUSH10xe3
0561SHL
0562DUP3
0563MSTORE
0564PUSH10x04
0566DUP3
0567REVERT
0568JUMPDEST
0569PUSH40x4a0bfec1
056ePUSH10xe0
0570SHL
0571DUP3
0572MSTORE
0573CALLER
0574PUSH10x04
0576MSTORE
0577PUSH10x24
0579DUP3
057aREVERT
057bJUMPDEST
057cPOP
057dCALLVALUE
057ePUSH20x03e6
0581JUMPI
0582PUSH10x20
0584CALLDATASIZE
0585PUSH10x03
0587NOT
0588ADD
0589SLT
058aPUSH20x03e6
058dJUMPI
058ePUSH10x20
0590PUSH20x059a
0593PUSH10x04
0595CALLDATALOAD
0596PUSH20x3d53
0599JUMP
059aJUMPDEST
059bSWAP1
059cSLOAD
059dPUSH10x40
059fMLOAD
05a0PUSH10x03
05a2SWAP3
05a3SWAP1
05a4SWAP3
05a5SHL
05a6SHR
05a7PUSH10x01
05a9PUSH10x01
05abPUSH10xa0
05adSHL
05aeSUB
05afAND
05b0DUP2
05b1MSTORE
05b2RETURN
05b3JUMPDEST
05b4POP
05b5CALLVALUE
05b6PUSH20x03e6
05b9JUMPI
05baDUP1
05bbPUSH10x03
05bdNOT
05beCALLDATASIZE
05bfADD
05c0SLT
05c1PUSH20x03e6
05c4JUMPI
05c5PUSH10x20
05c7PUSH10x0f
05c9SLOAD
05caPUSH10x40
05ccMLOAD
05cdSWAP1
05ceDUP2
05cfMSTORE
05d0RETURN
05d1JUMPDEST
05d2POP
05d3CALLVALUE
05d4PUSH20x03e6
05d7JUMPI
05d8DUP1
05d9PUSH10x03
05dbNOT
05dcCALLDATASIZE
05ddADD
05deSLT
05dfPUSH20x03e6
05e2JUMPI
05e3PUSH10x20
05e5PUSH10x40
05e7MLOAD
05e8PUSH20x0100
05ebDUP2
05ecMSTORE
05edRETURN
05eeJUMPDEST
05efPOP
05f0CALLVALUE
05f1PUSH20x03e6
05f4JUMPI
05f5PUSH10x20
05f7CALLDATASIZE
05f8PUSH10x03
05faNOT
05fbADD
05fcSLT
05fdPUSH20x03e6
0600JUMPI
0601PUSH10x20
0603PUSH20x0612
0606PUSH20x060d
0609PUSH20x3bfa
060cJUMP
060dJUMPDEST
060ePUSH20x44a9
0611JUMP
0612JUMPDEST
0613PUSH10x40
0615MLOAD
0616SWAP1
0617DUP2
0618MSTORE
0619RETURN
061aJUMPDEST
061bPOP
061cCALLVALUE
061dPUSH20x03e6
0620JUMPI
0621DUP1
0622PUSH10x03
0624NOT
0625CALLDATASIZE
0626ADD
0627SLT
0628PUSH20x03e6
062bJUMPI
062cPUSH10x20
062ePUSH10x40
0630MLOAD
0631PUSH320x664b0e72d3e91e88f8d9d2c0d916d51aff408110b8760394f6d677a5e6b24a2e
0652DUP2
0653MSTORE
0654RETURN
0655JUMPDEST
0656POP
0657CALLVALUE
0658PUSH20x03e6
065bJUMPI
065cDUP1
065dPUSH10x03
065fNOT
0660CALLDATASIZE
0661ADD
0662SLT
0663PUSH20x03e6
0666JUMPI
0667PUSH10x20
0669PUSH10x40
066bMLOAD
066cPUSH20x0200
066fDUP2
0670MSTORE
0671RETURN
0672JUMPDEST
0673CALLVALUE
0674PUSH20x12f2
0677JUMPI
0678PUSH20x0100
067bCALLDATASIZE
067cPUSH10x03
067eNOT
067fADD
0680SLT
0681PUSH20x12f2
0684JUMPI
0685PUSH20x068c
0688PUSH20x3bfa
068bJUMP
068cJUMPDEST
068dPUSH10x24
068fCALLDATALOAD
0690PUSH10x01
0692PUSH10x01
0694PUSH10x40
0696SHL
0697SUB
0698DUP2
0699GT
069aPUSH20x12f2
069dJUMPI
069ePUSH20x06ab
06a1SWAP1
06a2CALLDATASIZE
06a3SWAP1
06a4PUSH10x04
06a6ADD
06a7PUSH20x3c34
06aaJUMP
06abJUMPDEST
06acSWAP1
06adSWAP2
06aePUSH10x44
06b0CALLDATALOAD
06b1PUSH10x01
06b3PUSH10x01
06b5PUSH10x40
06b7SHL
06b8SUB
06b9DUP2
06baGT
06bbPUSH20x12f2
06beJUMPI
06bfPUSH20x06cc
06c2SWAP1
06c3CALLDATASIZE
06c4SWAP1
06c5PUSH10x04
06c7ADD
06c8PUSH20x3c34
06cbJUMP
06ccJUMPDEST
06cdPUSH10x64
06cfSWAP3
06d0SWAP2
06d1SWAP3
06d2CALLDATALOAD
06d3SWAP4
06d4PUSH10x01
06d6PUSH10x01
06d8PUSH10x40
06daSHL
06dbSUB
06dcDUP6
06ddGT
06dePUSH20x12f2
06e1JUMPI
06e2DUP5
06e3PUSH10x04
06e5ADD
06e6SWAP4
06e7PUSH10x40
06e9PUSH10x03
06ebNOT
06ecDUP8
06edCALLDATASIZE
06eeSUB
06efADD
06f0SLT
06f1PUSH20x12f2
06f4JUMPI
06f5PUSH10x84
06f7CALLDATALOAD
06f8SWAP6
06f9PUSH20x0700
06fcPUSH20x3b5c
06ffJUMP
0700JUMPDEST
0701SWAP6
0702PUSH20x0709
0705PUSH20x3b72
0708JUMP
0709JUMPDEST
070aSWAP4
070bPUSH10xe4
070dCALLDATALOAD
070eSWAP6
070fPUSH10x01
0711PUSH10x01
0713PUSH10x40
0715SHL
0716SUB
0717DUP8
0718GT
0719PUSH20x12f2
071cJUMPI
071dDUP10
071eSWAP7
071fPUSH20x072c
0722SWAP1
0723CALLDATASIZE
0724SWAP1
0725PUSH10x04
0727ADD
0728PUSH20x3b88
072bJUMP
072cJUMPDEST
072dSWAP3
072eSWAP1
072fSWAP7
0730ADDRESS
0731PUSH0
0732MSTORE
0733PUSH10x14
0735PUSH10x20
0737MSTORE
0738PUSH10x40
073aPUSH0
073bKECCAK256
073cSLOAD
073dPUSH10x01
073fPUSH10x01
0741PUSH10x40
0743SHL
0744SUB
0745AND
0746SWAP4
0747DUP14
0748DUP4
0749CALLDATASIZE
074aSWAP1
074bPUSH20x0753
074eSWAP3
074fPUSH20x3cd4
0752JUMP
0753JUMPDEST
0754DUP1
0755MLOAD
0756SWAP1
0757PUSH10x20
0759ADD
075aKECCAK256
075bSWAP9
075cDUP12
075dDUP14
075eCALLDATASIZE
075fPUSH20x0769
0762SWAP1
0763DUP9
0764DUP14
0765PUSH20x3cd4
0768JUMP
0769JUMPDEST
076aDUP1
076bMLOAD
076cSWAP1
076dPUSH10x20
076fADD
0770KECCAK256
0771SWAP13
0772PUSH10x40
0774MLOAD
0775DUP1
0776SWAP15
0777PUSH10x20
0779DUP3
077aADD
077bSWAP5
077cPUSH10x01
077ePUSH10xa0
0780SHL
0781PUSH10x01
0783SWAP1
0784SUB
0785AND
0786SWAP15
0787DUP16
0788DUP7
0789MSTORE
078aPUSH10x40
078cDUP4
078dADD
078eMSTORE
078fPUSH10x60
0791DUP3
0792ADD
0793MSTORE
0794PUSH10x80
0796ADD
0797MSTORE
0798PUSH10x01
079aPUSH10x01
079cPUSH10x40
079eSHL
079fSUB
07a0AND
07a1SWAP12
07a2DUP13
07a3PUSH10xa0
07a5DUP3
07a6ADD
07a7MSTORE
07a8PUSH10xa0
07aaDUP2
07abMSTORE
07acPUSH20x07b6
07afPUSH10xc0
07b1DUP3
07b2PUSH20x3c98
07b5JUMP
07b6JUMPDEST
07b7MLOAD
07b8SWAP1
07b9KECCAK256
07baSWAP3
07bbPUSH20x07c3
07beSWAP4
07bfPUSH20x495e
07c2JUMP
07c3JUMPDEST
07c4PUSH200x83d50624b34718978a0f118a95f7c6b6c9008c47
07d9DUP1
07daSWAP2
07dbPUSH10x40
07ddMLOAD
07deDUP1
07dfSWAP15
07e0DUP2
07e1SWAP3
07e2PUSH40x25f1701d
07e7PUSH10xe2
07e9SHL
07eaDUP4
07ebMSTORE
07ecPUSH10x04
07eeDUP4
07efADD
07f0SWAP2
07f1PUSH20x07f9
07f4SWAP3
07f5PUSH20x4303
07f8JUMP
07f9JUMPDEST
07faSUB
07fbDUP2
07fcGAS
07fdSWAP4
07fePUSH0
07ffSWAP5
0800DELEGATECALL
0801SWAP6
0802DUP7
0803ISZERO
0804PUSH20x12c5
0807JUMPI
0808PUSH20x0833
080bSWAP13
080cPUSH0
080dSWAP8
080ePUSH20x12d0
0811JUMPI
0812JUMPDEST
0813POP
0814SWAP1
0815PUSH0
0816SWAP3
0817SWAP2
0818PUSH10x40
081aMLOAD
081bDUP1
081cSWAP15
081dDUP2
081eSWAP5
081fDUP3
0820SWAP4
0821PUSH40xb389b84f
0826PUSH10xe0
0828SHL
0829DUP5
082aMSTORE
082bPUSH10x04
082dDUP5
082eADD
082fPUSH20x4303
0832JUMP
0833JUMPDEST
0834SUB
0835SWAP2
0836GAS
0837DELEGATECALL
0838SWAP10
0839DUP11
083aISZERO
083bPUSH20x12c5
083eJUMPI
083fPUSH0
0840SWAP11
0841PUSH20x12a1
0844JUMPI
0845JUMPDEST
0846POP
0847PUSH10x20
0849DUP5
084aADD
084bSWAP2
084cDUP3
084dMLOAD
084ePUSH10x20
0850DUP13
0851ADD
0852MLOAD
0853SWAP1
0854DUP2
0855DUP2
0856SUB
0857PUSH20x128c
085aJUMPI
085bPOP
085cPOP
085dPUSH20x0865
0860DUP6
0861PUSH20x527f
0864JUMP
0865JUMPDEST
0866PUSH20x086e
0869DUP12
086aPUSH20x527f
086dJUMP
086eJUMPDEST
086fDUP11
0870MLOAD
0871DUP6
0872MLOAD
0873SWAP1
0874PUSH10x40
0876MLOAD
0877SWAP1
0878PUSH10x20
087aDUP3
087bADD
087cSWAP3
087dPUSH320x6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f8168
089eDUP5
089fMSTORE
08a0CHAINID
08a1PUSH10x40
08a3DUP5
08a4ADD
08a5MSTORE
08a6ADDRESS
08a7PUSH10x60
08a9DUP5
08aaADD
08abMSTORE
08acPUSH10x80
08aeDUP4
08afADD
08b0MSTORE
08b1PUSH10xa0
08b3DUP3
08b4ADD
08b5MSTORE
08b6DUP4
08b7PUSH10xc0
08b9DUP3
08baADD
08bbMSTORE
08bcPUSH10xc0
08beDUP2
08bfMSTORE
08c0PUSH20x08ca
08c3PUSH10xe0
08c5DUP3
08c6PUSH20x3c98
08c9JUMP
08caJUMPDEST
08cbMLOAD
08ccSWAP1
08cdKECCAK256
08ceSWAP4
08cfPUSH10x40
08d1MLOAD
08d2SWAP5
08d3PUSH10x20
08d5DUP7
08d6ADD
08d7MSTORE
08d8PUSH10x20
08daDUP6
08dbMSTORE
08dcPUSH20x08e6
08dfPUSH10x40
08e1DUP7
08e2PUSH20x3c98
08e5JUMP
08e6JUMPDEST
08e7PUSH20x0160
08eaDUP7
08ebADD
08ecSWAP5
08edPUSH20x090c
08f0DUP7
08f1MLOAD
08f2DUP3
08f3PUSH20x0906
08f6PUSH20x08ff
08f9DUP8
08faDUP1
08fbPUSH20x3e4d
08feJUMP
08ffJUMPDEST
0900CALLDATASIZE
0901SWAP2
0902PUSH20x3cd4
0905JUMP
0906JUMPDEST
0907SWAP2
0908PUSH20x4b32
090bJUMP
090cJUMPDEST
090dISZERO
090eSWAP3
090fDUP4
0910ISZERO
0911PUSH20x1261
0914JUMPI
0915JUMPDEST
0916POP
0917POP
0918POP
0919PUSH20x124e
091cJUMPI
091dADDRESS
091ePUSH0
091fMSTORE
0920PUSH10x14
0922PUSH10x20
0924MSTORE
0925DUP1
0926PUSH10x01
0928PUSH10x01
092aPUSH10x40
092cSHL
092dSUB
092ePUSH10x40
0930PUSH0
0931KECCAK256
0932SLOAD
0933AND
0934EQ
0935PUSH20x121a
0938JUMPI
0939JUMPDEST
093aPOP
093bDUP3
093cMLOAD
093dSWAP7
093eDUP5
093fISZERO
0940PUSH20x1207
0943JUMPI
0944DUP4
0945MLOAD
0946PUSH0
0947MSTORE
0948PUSH10x0d
094aPUSH10x20
094cMSTORE
094dPUSH10xff
094fPUSH10x40
0951PUSH0
0952KECCAK256
0953SLOAD
0954AND
0955PUSH20x11f3
0958JUMPI
0959DUP4
095aMLOAD
095bPUSH0
095cSWAP1
095dDUP2
095eMSTORE
095fPUSH10x0c
0961PUSH10x20
0963MSTORE
0964PUSH10x40
0966SWAP1
0967KECCAK256
0968SLOAD
0969PUSH10x01
096bPUSH10x01
096dPUSH10xa0
096fSHL
0970SUB
0971AND
0972DUP1
0973ISZERO
0974ISZERO
0975DUP1
0976PUSH20x11e9
0979JUMPI
097aJUMPDEST
097bPUSH20x11d3
097eJUMPI
097fPOP
0980DUP5
0981PUSH0
0982MSTORE
0983PUSH10x02
0985PUSH10x20
0987MSTORE
0988PUSH10x40
098aPUSH0
098bKECCAK256
098cPUSH10x05
098eDUP2
098fADD
0990SWAP3
0991DUP4
0992SLOAD
0993PUSH10xff
0995DUP2
0996PUSH10xd8
0998SHR
0999AND
099aISZERO
099bPUSH0
099cEQ
099dPUSH20x1151
09a0JUMPI
09a1POP
09a2SWAP1
09a3PUSH10x0f
09a5SLOAD
09a6SWAP1
09a7PUSH10x01
09a9PUSH10x40
09abSHL
09acDUP3
09adLT
09aeISZERO
09afPUSH20x0e50
09b2JUMPI
09b3DUP12
09b4SWAP3
09b5PUSH20x09ee
09b8DUP12
09b9PUSH20x09ca
09bcDUP6
09bdPUSH10x01
09bfPUSH10x04
09c1SWAP8
09c2ADD
09c3PUSH10x0f
09c5SSTORE
09c6PUSH20x3d53
09c9JUMP
09caJUMPDEST
09cbDUP2
09ccSLOAD
09cdPUSH10x01
09cfPUSH10x01
09d1PUSH10xa0
09d3SHL
09d4SUB
09d5SWAP4
09d6DUP5
09d7AND
09d8PUSH10x03
09daSWAP3
09dbSWAP1
09dcSWAP3
09ddSHL
09deSWAP2
09dfDUP3
09e0SHL
09e1SWAP4
09e2SWAP1
09e3SWAP2
09e4SHL
09e5NOT
09e6AND
09e7SWAP2
09e8SWAP1
09e9SWAP2
09eaOR
09ebSWAP1
09ecSSTORE
09edJUMP
09eeJUMPDEST
09efDUP6
09f0SLOAD
09f1PUSH10xff
09f3PUSH10xd8
09f5SHL
09f6NOT
09f7AND
09f8PUSH10x01
09faPUSH10xd8
09fcSHL
09fdOR
09feDUP7
09ffSSTORE
0a00JUMPDEST
0a01DUP8
0a02MLOAD
0a03DUP3
0a04SSTORE
0a05DUP14
0a06MLOAD
0a07PUSH10x01
0a09DUP4
0a0aADD
0a0bSSTORE
0a0cMLOAD
0a0dPUSH10x02
0a0fDUP3
0a10ADD
0a11SSTORE
0a12PUSH10xc0
0a14DUP8
0a15ADD
0a16MLOAD
0a17PUSH10x03
0a19DUP3
0a1aADD
0a1bSSTORE
0a1cADD
0a1dSSTORE
0a1ePUSH10xe0
0a20DUP5
0a21ADD
0a22MLOAD
0a23DUP3
0a24SLOAD
0a25PUSH20x0100
0a28DUP7
0a29ADD
0a2aMLOAD
0a2bPUSH20x0120
0a2eDUP8
0a2fADD
0a30MLOAD
0a31PUSH20x0140
0a34DUP9
0a35ADD
0a36MLOAD
0a37PUSH10x01
0a39PUSH10x01
0a3bPUSH10xd0
0a3dSHL
0a3eSUB
0a3fNOT
0a40SWAP1
0a41SWAP4
0a42AND
0a43PUSH10xff
0a45SWAP5
0a46SWAP1
0a47SWAP5
0a48AND
0a49SWAP4
0a4aSWAP1
0a4bSWAP4
0a4cOR
0a4dPUSH10x08
0a4fSWAP2
0a50SWAP1
0a51SWAP2
0a52SHL
0a53PUSH20xff00
0a56AND
0a57OR
0a58PUSH10x10
0a5aSWAP3
0a5bSWAP1
0a5cSWAP3
0a5dSHL
0a5ePUSH100xffffffffffffffff0000
0a69AND
0a6aSWAP2
0a6bSWAP1
0a6cSWAP2
0a6dOR
0a6ePUSH10x50
0a70SWAP2
0a71SWAP1
0a72SWAP2
0a73SHL
0a74PUSH80xffffffffffffffff
0a7dPUSH10x50
0a7fSHL
0a80AND
0a81OR
0a82PUSH10x90
0a84SWAP2
0a85SWAP1
0a86SWAP2
0a87SHL
0a88PUSH80xffffffffffffffff
0a91PUSH10x90
0a93SHL
0a94AND
0a95OR
0a96SWAP1
0a97SSTORE
0a98DUP1
0a99MLOAD
0a9aPUSH10x01
0a9cPUSH10x01
0a9ePUSH10xa0
0aa0SHL
0aa1SUB
0aa2SWAP1
0aa3PUSH20x0aab
0aa6SWAP1
0aa7PUSH20x4701
0aaaJUMP
0aabJUMPDEST
0aacAND
0aadPUSH0
0aaeDUP2
0aafDUP2
0ab0MSTORE
0ab1PUSH10x15
0ab3PUSH10x20
0ab5MSTORE
0ab6PUSH10x40
0ab8SWAP1
0ab9KECCAK256
0abaSLOAD
0abbPUSH10x01
0abdPUSH10x01
0abfPUSH10xa0
0ac1SHL
0ac2SUB
0ac3AND
0ac4DUP1
0ac5ISZERO
0ac6ISZERO
0ac7DUP1
0ac8PUSH20x1147
0acbJUMPI
0accJUMPDEST
0acdPUSH20x1131
0ad0JUMPI
0ad1POP
0ad2DUP4
0ad3PUSH0
0ad4MSTORE
0ad5PUSH10x03
0ad7PUSH10x20
0ad9MSTORE
0adaPUSH20x0ae6
0addPUSH10x40
0adfPUSH0
0ae0KECCAK256
0ae1SLOAD
0ae2PUSH20x3e7f
0ae5JUMP
0ae6JUMPDEST
0ae7PUSH20x10e3
0aeaJUMPI
0aebJUMPDEST
0aecPUSH0
0aedMSTORE
0aeePUSH10x15
0af0PUSH10x20
0af2MSTORE
0af3PUSH10x40
0af5PUSH0
0af6KECCAK256
0af7DUP4
0af8PUSH10x01
0afaPUSH10x01
0afcPUSH10x60
0afeSHL
0affSUB
0b00PUSH10xa0
0b02SHL
0b03DUP3
0b04SLOAD
0b05AND
0b06OR
0b07SWAP1
0b08SSTORE
0b09MLOAD
0b0aDUP3
0b0bPUSH0
0b0cMSTORE
0b0dPUSH10x03
0b0fPUSH10x20
0b11MSTORE
0b12PUSH10x40
0b14PUSH0
0b15KECCAK256
0b16SWAP1
0b17DUP1
0b18MLOAD
0b19SWAP1
0b1aPUSH10x01
0b1cPUSH10x01
0b1ePUSH10x40
0b20SHL
0b21SUB
0b22DUP3
0b23GT
0b24PUSH20x0e50
0b27JUMPI
0b28PUSH20x0b31
0b2bDUP4
0b2cSLOAD
0b2dPUSH20x3e7f
0b30JUMP
0b31JUMPDEST
0b32PUSH10x1f
0b34DUP2
0b35GT
0b36PUSH20x10a9
0b39JUMPI
0b3aJUMPDEST
0b3bPOP
0b3cPUSH10x20
0b3eSWAP1
0b3fPUSH10x1f
0b41DUP4
0b42GT
0b43PUSH10x01
0b45EQ
0b46PUSH20x1046
0b49JUMPI
0b4aPUSH20x0b6a
0b4dSWAP3
0b4eSWAP2
0b4fPUSH0
0b50SWAP2
0b51DUP4
0b52PUSH20x0e64
0b55JUMPI
0b56JUMPDEST
0b57POP
0b58POP
0b59DUP2
0b5aPUSH10x01
0b5cSHL
0b5dSWAP2
0b5ePUSH0
0b5fNOT
0b60SWAP1
0b61PUSH10x03
0b63SHL
0b64SHR
0b65NOT
0b66AND
0b67OR
0b68SWAP1
0b69JUMP
0b6aJUMPDEST
0b6bSWAP1
0b6cSSTORE
0b6dJUMPDEST
0b6ePUSH20x0180
0b71DUP2
0b72ADD
0b73MLOAD
0b74DUP3
0b75PUSH0
0b76MSTORE
0b77PUSH10x04
0b79PUSH10x20
0b7bMSTORE
0b7cPUSH10x40
0b7ePUSH0
0b7fKECCAK256
0b80SWAP1
0b81DUP1
0b82MLOAD
0b83SWAP1
0b84PUSH10x01
0b86PUSH10x01
0b88PUSH10x40
0b8aSHL
0b8bSUB
0b8cDUP3
0b8dGT
0b8ePUSH20x0e50
0b91JUMPI
0b92PUSH20x0b9b
0b95DUP4
0b96SLOAD
0b97PUSH20x3e7f
0b9aJUMP
0b9bJUMPDEST
0b9cPUSH10x1f
0b9eDUP2
0b9fGT
0ba0PUSH20x100c
0ba3JUMPI
0ba4JUMPDEST
0ba5POP
0ba6PUSH10x20
0ba8SWAP1
0ba9PUSH10x1f
0babDUP4
0bacGT
0badPUSH10x01
0bafEQ
0bb0PUSH20x0fa9
0bb3JUMPI
0bb4PUSH20x0bd3
0bb7SWAP3
0bb8SWAP2
0bb9PUSH0
0bbaSWAP2
0bbbDUP4
0bbcPUSH20x0e64
0bbfJUMPI
0bc0POP
0bc1POP
0bc2DUP2
0bc3PUSH10x01
0bc5SHL
0bc6SWAP2
0bc7PUSH0
0bc8NOT
0bc9SWAP1
0bcaPUSH10x03
0bccSHL
0bcdSHR
0bceNOT
0bcfAND
0bd0OR
0bd1SWAP1
0bd2JUMP
0bd3JUMPDEST
0bd4SWAP1
0bd5SSTORE
0bd6JUMPDEST
0bd7PUSH20x0160
0bdaDUP8
0bdbADD
0bdcMLOAD
0bddDUP3
0bdePUSH0
0bdfMSTORE
0be0PUSH10x05
0be2PUSH10x20
0be4MSTORE
0be5PUSH10x40
0be7PUSH0
0be8KECCAK256
0be9SWAP1
0beaDUP1
0bebMLOAD
0becSWAP1
0bedPUSH10x01
0befPUSH10x01
0bf1PUSH10x40
0bf3SHL
0bf4SUB
0bf5DUP3
0bf6GT
0bf7PUSH20x0e50
0bfaJUMPI
0bfbPUSH20x0c04
0bfeDUP4
0bffSLOAD
0c00PUSH20x3e7f
0c03JUMP
0c04JUMPDEST
0c05PUSH10x1f
0c07DUP2
0c08GT
0c09PUSH20x0f6f
0c0cJUMPI
0c0dJUMPDEST
0c0ePOP
0c0fPUSH10x20
0c11SWAP1
0c12PUSH10x1f
0c14DUP4
0c15GT
0c16PUSH10x01
0c18EQ
0c19PUSH20x0f0c
0c1cJUMPI
0c1dPUSH20x0c3c
0c20SWAP3
0c21SWAP2
0c22PUSH0
0c23SWAP2
0c24DUP4
0c25PUSH20x0e64
0c28JUMPI
0c29POP
0c2aPOP
0c2bDUP2
0c2cPUSH10x01
0c2eSHL
0c2fSWAP2
0c30PUSH0
0c31NOT
0c32SWAP1
0c33PUSH10x03
0c35SHL
0c36SHR
0c37NOT
0c38AND
0c39OR
0c3aSWAP1
0c3bJUMP
0c3cJUMPDEST
0c3dSWAP1
0c3eSSTORE
0c3fJUMPDEST
0c40PUSH20x0180
0c43DUP8
0c44ADD
0c45MLOAD
0c46DUP3
0c47PUSH0
0c48MSTORE
0c49PUSH10x06
0c4bPUSH10x20
0c4dMSTORE
0c4ePUSH10x40
0c50PUSH0
0c51KECCAK256
0c52SWAP1
0c53DUP1
0c54MLOAD
0c55SWAP1
0c56PUSH10x01
0c58PUSH10x01
0c5aPUSH10x40
0c5cSHL
0c5dSUB
0c5eDUP3
0c5fGT
0c60PUSH20x0e50
0c63JUMPI
0c64PUSH20x0c6d
0c67DUP4
0c68SLOAD
0c69PUSH20x3e7f
0c6cJUMP
0c6dJUMPDEST
0c6ePUSH10x1f
0c70DUP2
0c71GT
0c72PUSH20x0ed2
0c75JUMPI
0c76JUMPDEST
0c77POP
0c78PUSH10x20
0c7aSWAP1
0c7bPUSH10x1f
0c7dDUP4
0c7eGT
0c7fPUSH10x01
0c81EQ
0c82PUSH20x0e6f
0c85JUMPI
0c86PUSH20x0ca5
0c89SWAP3
0c8aSWAP2
0c8bPUSH0
0c8cSWAP2
0c8dDUP4
0c8ePUSH20x0e64
0c91JUMPI
0c92POP
0c93POP
0c94DUP2
0c95PUSH10x01
0c97SHL
0c98SWAP2
0c99PUSH0
0c9aNOT
0c9bSWAP1
0c9cPUSH10x03
0c9eSHL
0c9fSHR
0ca0NOT
0ca1AND
0ca2OR
0ca3SWAP1
0ca4JUMP
0ca5JUMPDEST
0ca6SWAP1
0ca7SSTORE
0ca8JUMPDEST
0ca9PUSH20x01e0
0cacDUP2
0cadADD
0caeMLOAD
0cafSWAP7
0cb0DUP3
0cb1PUSH0
0cb2MSTORE
0cb3PUSH10x07
0cb5PUSH10x20
0cb7MSTORE
0cb8PUSH10x40
0cbaPUSH0
0cbbKECCAK256
0cbcDUP9
0cbdMLOAD
0cbePUSH10x01
0cc0PUSH10x01
0cc2PUSH10x40
0cc4SHL
0cc5SUB
0cc6DUP2
0cc7GT
0cc8PUSH20x0e50
0ccbJUMPI
0cccPUSH10x20
0cceSWAP10
0ccfPUSH20x0cd8
0cd2DUP4
0cd3SLOAD
0cd4PUSH20x3e7f
0cd7JUMP
0cd8JUMPDEST
0cd9PUSH10x1f
0cdbDUP2
0cdcGT
0cddPUSH20x0e0f
0ce0JUMPI
0ce1JUMPDEST
0ce2POP
0ce3DUP11
0ce4SWAP1
0ce5PUSH10x1f
0ce7DUP4
0ce8GT
0ce9PUSH10x01
0cebEQ
0cecPUSH20x0d92
0cefJUMPI
0cf0SWAP4
0cf1PUSH20x0612
0cf4SWAP10
0cf5SWAP11
0cf6SWAP4
0cf7PUSH20x0d2b
0cfaDUP5
0cfbPUSH0
0cfcMLOAD
0cfdPUSH10x20
0cffPUSH20x5df8
0d02PUSH0
0d03CODECOPY
0d04PUSH0
0d05MLOAD
0d06SWAP1
0d07PUSH0
0d08MSTORE
0d09SWAP9
0d0aSWAP6
0d0bPUSH20x0d59
0d0eSWAP6
0d0fPUSH10x40
0d11SWAP10
0d12PUSH0
0d13SWAP3
0d14PUSH20x0d87
0d17JUMPI
0d18POP
0d19POP
0d1aDUP2
0d1bPUSH10x01
0d1dSHL
0d1eSWAP2
0d1fPUSH0
0d20NOT
0d21SWAP1
0d22PUSH10x03
0d24SHL
0d25SHR
0d26NOT
0d27AND
0d28OR
0d29SWAP1
0d2aJUMP
0d2bJUMPDEST
0d2cSWAP1
0d2dSSTORE
0d2eJUMPDEST
0d2fPUSH20x0d45
0d32PUSH20x01a0
0d35DUP5
0d36ADD
0d37MLOAD
0d38PUSH20x01c0
0d3bDUP6
0d3cADD
0d3dMLOAD
0d3eSWAP1
0d3fPUSH0
0d40DUP13
0d41PUSH20x53e9
0d44JUMP
0d45JUMPDEST
0d46PUSH20x01c0
0d49PUSH20x01a0
0d4cDUP3
0d4dADD
0d4eMLOAD
0d4fSWAP2
0d50ADD
0d51MLOAD
0d52SWAP1
0d53PUSH0
0d54DUP11
0d55PUSH20x56ad
0d58JUMP
0d59JUMPDEST
0d5aDUP1
0d5bMLOAD
0d5cPUSH0
0d5dMSTORE
0d5ePUSH10x0c
0d60DUP11
0d61MSTORE
0d62DUP3
0d63PUSH0
0d64KECCAK256
0d65DUP6
0d66PUSH10x01
0d68PUSH10x01
0d6aPUSH10x60
0d6cSHL
0d6dSUB
0d6ePUSH10xa0
0d70SHL
0d71DUP3
0d72SLOAD
0d73AND
0d74OR
0d75SWAP1
0d76SSTORE
0d77MLOAD
0d78SWAP5
0d79DUP3
0d7aMLOAD
0d7bSWAP2
0d7cDUP3
0d7dMSTORE
0d7eDUP10
0d7fDUP3
0d80ADD
0d81MSTORE
0d82LOG3
0d83PUSH20x49db
0d86JUMP
0d87JUMPDEST
0d88ADD
0d89MLOAD
0d8aSWAP1
0d8bPOP
0d8cPUSH0
0d8dDUP1
0d8ePUSH20x0b56
0d91JUMP
0d92JUMPDEST
0d93SWAP1
0d94PUSH10x1f
0d96NOT
0d97DUP4
0d98AND
0d99SWAP2
0d9aDUP5
0d9bPUSH0
0d9cMSTORE
0d9dDUP2
0d9ePUSH0
0d9fKECCAK256
0da0SWAP3
0da1PUSH0
0da2JUMPDEST
0da3DUP2
0da4DUP2
0da5LT
0da6PUSH20x0df8
0da9JUMPI
0daaPOP
0dabDUP5
0dacPUSH20x0d59
0dafSWAP5
0db0PUSH10x40
0db2SWAP9
0db3SWAP5
0db4PUSH20x0612
0db7SWAP15
0db8SWAP16
0db9SWAP9
0dbaSWAP5
0dbbPUSH0
0dbcMLOAD
0dbdPUSH10x20
0dbfPUSH20x5df8
0dc2PUSH0
0dc3CODECOPY
0dc4PUSH0
0dc5MLOAD
0dc6SWAP1
0dc7PUSH0
0dc8MSTORE
0dc9SWAP12
0dcaSWAP9
0dcbPUSH10x01
0dcdSWAP6
0dceLT
0dcfPUSH20x0de0
0dd2JUMPI
0dd3JUMPDEST
0dd4POP
0dd5POP
0dd6POP
0dd7DUP2
0dd8SHL
0dd9ADD
0ddaSWAP1
0ddbSSTORE
0ddcPUSH20x0d2e
0ddfJUMP
0de0JUMPDEST
0de1ADD
0de2MLOAD
0de3PUSH0
0de4NOT
0de5PUSH10xf8
0de7DUP5
0de8PUSH10x03
0deaSHL
0debAND
0decSHR
0dedNOT
0deeAND
0defSWAP1
0df0SSTORE
0df1DUP16
0df2DUP1
0df3DUP1
0df4PUSH20x0dd3
0df7JUMP
0df8JUMPDEST
0df9SWAP3
0dfaSWAP4
0dfbDUP15
0dfcPUSH10x01
0dfeDUP2
0dffSWAP3
0e00DUP8
0e01DUP7
0e02ADD
0e03MLOAD
0e04DUP2
0e05SSTORE
0e06ADD
0e07SWAP6
0e08ADD
0e09SWAP4
0e0aADD
0e0bPUSH20x0da2
0e0eJUMP
0e0fJUMPDEST
0e10DUP3
0e11DUP2
0e12GT
0e13ISZERO
0e14PUSH20x0ce1
0e17JUMPI
0e18PUSH20x0e42
0e1bSWAP1
0e1cDUP5
0e1dPUSH0
0e1eMSTORE
0e1fDUP13
0e20PUSH0
0e21KECCAK256
0e22SWAP1
0e23PUSH10x1f
0e25DUP6
0e26ADD
0e27PUSH10x05
0e29SHR
0e2aSWAP1
0e2bDUP15
0e2cDUP7
0e2dLT
0e2ePUSH20x0e48
0e31JUMPI
0e32JUMPDEST
0e33PUSH10x1f
0e35DUP3
0e36SWAP2
0e37ADD
0e38PUSH10x05
0e3aSHR
0e3bSUB
0e3cSWAP2
0e3dADD
0e3ePUSH20x5264
0e41JUMP
0e42JUMPDEST
0e43DUP12
0e44PUSH20x0ce1
0e47JUMP
0e48JUMPDEST
0e49PUSH0
0e4aSWAP2
0e4bPOP
0e4cPUSH20x0e32
0e4fJUMP
0e50JUMPDEST
0e51PUSH40x4e487b71
0e56PUSH10xe0
0e58SHL
0e59PUSH0
0e5aMSTORE
0e5bPUSH10x41
0e5dPUSH10x04
0e5fMSTORE
0e60PUSH10x24
0e62PUSH0
0e63REVERT
0e64JUMPDEST
0e65ADD
0e66MLOAD
0e67SWAP1
0e68POP
0e69DUP12
0e6aDUP1
0e6bPUSH20x0b56
0e6eJUMP
0e6fJUMPDEST
0e70SWAP1
0e71PUSH10x1f
0e73NOT
0e74DUP4
0e75AND
0e76SWAP2
0e77DUP5
0e78PUSH0
0e79MSTORE
0e7aDUP2
0e7bPUSH0
0e7cKECCAK256
0e7dSWAP3
0e7ePUSH0
0e7fJUMPDEST
0e80DUP2
0e81DUP2
0e82LT
0e83PUSH20x0eba
0e86JUMPI
0e87POP
0e88SWAP1
0e89DUP5
0e8aPUSH10x01
0e8cSWAP6
0e8dSWAP5
0e8eSWAP4
0e8fSWAP3
0e90LT
0e91PUSH20x0ea2
0e94JUMPI
0e95JUMPDEST
0e96POP
0e97POP
0e98POP
0e99DUP2
0e9aSHL
0e9bADD
0e9cSWAP1
0e9dSSTORE
0e9ePUSH20x0ca8
0ea1JUMP
0ea2JUMPDEST
0ea3ADD
0ea4MLOAD
0ea5PUSH0
0ea6NOT
0ea7PUSH10xf8
0ea9DUP5
0eaaPUSH10x03
0eacSHL
0eadAND
0eaeSHR
0eafNOT
0eb0AND
0eb1SWAP1
0eb2SSTORE
0eb3DUP11
0eb4DUP1
0eb5DUP1
0eb6PUSH20x0e95
0eb9JUMP
0ebaJUMPDEST
0ebbSWAP3
0ebcSWAP4
0ebdPUSH10x20
0ebfPUSH10x01
0ec1DUP2
0ec2SWAP3
0ec3DUP8
0ec4DUP7
0ec5ADD
0ec6MLOAD
0ec7DUP2
0ec8SSTORE
0ec9ADD
0ecaSWAP6
0ecbADD
0eccSWAP4
0ecdADD
0ecePUSH20x0e7f
0ed1JUMP
0ed2JUMPDEST
0ed3DUP3
0ed4DUP2
0ed5GT
0ed6ISZERO
0ed7PUSH20x0c76
0edaJUMPI
0edbPUSH20x0f06
0edeSWAP1
0edfDUP5
0ee0PUSH0
0ee1MSTORE
0ee2PUSH10x20
0ee4PUSH0
0ee5KECCAK256
0ee6SWAP1
0ee7PUSH10x1f
0ee9DUP6
0eeaADD
0eebPUSH10x05
0eedSHR
0eeeSWAP1
0eefPUSH10x20
0ef1DUP7
0ef2LT
0ef3PUSH20x0e48
0ef6JUMPI
0ef7PUSH10x1f
0ef9DUP3
0efaSWAP2
0efbADD
0efcPUSH10x05
0efeSHR
0effSUB
0f00SWAP2
0f01ADD
0f02PUSH20x5264
0f05JUMP
0f06JUMPDEST
0f07DUP11
0f08PUSH20x0c76
0f0bJUMP
0f0cJUMPDEST
0f0dSWAP1
0f0ePUSH10x1f
0f10NOT
0f11DUP4
0f12AND
0f13SWAP2
0f14DUP5
0f15PUSH0
0f16MSTORE
0f17DUP2
0f18PUSH0
0f19KECCAK256
0f1aSWAP3
0f1bPUSH0
0f1cJUMPDEST
0f1dDUP2
0f1eDUP2
0f1fLT
0f20PUSH20x0f57
0f23JUMPI
0f24POP
0f25SWAP1
0f26DUP5
0f27PUSH10x01
0f29SWAP6
0f2aSWAP5
0f2bSWAP4
0f2cSWAP3
0f2dLT
0f2ePUSH20x0f3f
0f31JUMPI
0f32JUMPDEST
0f33POP
0f34POP
0f35POP
0f36DUP2
0f37SHL
0f38ADD
0f39SWAP1
0f3aSSTORE
0f3bPUSH20x0c3f
0f3eJUMP
0f3fJUMPDEST
0f40ADD
0f41MLOAD
0f42PUSH0
0f43NOT
0f44PUSH10xf8
0f46DUP5
0f47PUSH10x03
0f49SHL
0f4aAND
0f4bSHR
0f4cNOT
0f4dAND
0f4eSWAP1
0f4fSSTORE
0f50DUP11
0f51DUP1
0f52DUP1
0f53PUSH20x0f32
0f56JUMP
0f57JUMPDEST
0f58SWAP3
0f59SWAP4
0f5aPUSH10x20
0f5cPUSH10x01
0f5eDUP2
0f5fSWAP3
0f60DUP8
0f61DUP7
0f62ADD
0f63MLOAD
0f64DUP2
0f65SSTORE
0f66ADD
0f67SWAP6
0f68ADD
0f69SWAP4
0f6aADD
0f6bPUSH20x0f1c
0f6eJUMP
0f6fJUMPDEST
0f70DUP3
0f71DUP2
0f72GT
0f73ISZERO
0f74PUSH20x0c0d
0f77JUMPI
0f78PUSH20x0fa3
0f7bSWAP1
0f7cDUP5
0f7dPUSH0
0f7eMSTORE
0f7fPUSH10x20
0f81PUSH0
0f82KECCAK256
0f83SWAP1
0f84PUSH10x1f
0f86DUP6
0f87ADD
0f88PUSH10x05
0f8aSHR
0f8bSWAP1
0f8cPUSH10x20
0f8eDUP7
0f8fLT
0f90PUSH20x0e48
0f93JUMPI
0f94PUSH10x1f
0f96DUP3
0f97SWAP2
0f98ADD
0f99PUSH10x05
0f9bSHR
0f9cSUB
0f9dSWAP2
0f9eADD
0f9fPUSH20x5264
0fa2JUMP
0fa3JUMPDEST
0fa4DUP11
0fa5PUSH20x0c0d
0fa8JUMP
0fa9JUMPDEST
0faaSWAP1
0fabPUSH10x1f
0fadNOT
0faeDUP4
0fafAND
0fb0SWAP2
0fb1DUP5
0fb2PUSH0
0fb3MSTORE
0fb4DUP2
0fb5PUSH0
0fb6KECCAK256
0fb7SWAP3
0fb8PUSH0
0fb9JUMPDEST
0fbaDUP2
0fbbDUP2
0fbcLT
0fbdPUSH20x0ff4
0fc0JUMPI
0fc1POP
0fc2SWAP1
0fc3DUP5
0fc4PUSH10x01
0fc6SWAP6
0fc7SWAP5
0fc8SWAP4
0fc9SWAP3
0fcaLT
0fcbPUSH20x0fdc
0fceJUMPI
0fcfJUMPDEST
0fd0POP
0fd1POP
0fd2POP
0fd3DUP2
0fd4SHL
0fd5ADD
0fd6SWAP1
0fd7SSTORE
0fd8PUSH20x0bd6
0fdbJUMP
0fdcJUMPDEST
0fddADD
0fdeMLOAD
0fdfPUSH0
0fe0NOT
0fe1PUSH10xf8
0fe3DUP5
0fe4PUSH10x03
0fe6SHL
0fe7AND
0fe8SHR
0fe9NOT
0feaAND
0febSWAP1
0fecSSTORE
0fedDUP11
0feeDUP1
0fefDUP1
0ff0PUSH20x0fcf
0ff3JUMP
0ff4JUMPDEST
0ff5SWAP3
0ff6SWAP4
0ff7PUSH10x20
0ff9PUSH10x01
0ffbDUP2
0ffcSWAP3
0ffdDUP8
0ffeDUP7
0fffADD
1000MLOAD
1001DUP2
1002SSTORE
1003ADD
1004SWAP6
1005ADD
1006SWAP4
1007ADD
1008PUSH20x0fb9
100bJUMP
100cJUMPDEST
100dDUP3
100eDUP2
100fGT
1010ISZERO
1011PUSH20x0ba4
1014JUMPI
1015PUSH20x1040
1018SWAP1
1019DUP5
101aPUSH0
101bMSTORE
101cPUSH10x20
101ePUSH0
101fKECCAK256
1020SWAP1
1021PUSH10x1f
1023DUP6
1024ADD
1025PUSH10x05
1027SHR
1028SWAP1
1029PUSH10x20
102bDUP7
102cLT
102dPUSH20x0e48
1030JUMPI
1031PUSH10x1f
1033DUP3
1034SWAP2
1035ADD
1036PUSH10x05
1038SHR
1039SUB
103aSWAP2
103bADD
103cPUSH20x5264
103fJUMP
1040JUMPDEST
1041DUP11
1042PUSH20x0ba4
1045JUMP
1046JUMPDEST
1047SWAP1
1048PUSH10x1f
104aNOT
104bDUP4
104cAND
104dSWAP2
104eDUP5
104fPUSH0
1050MSTORE
1051DUP2
1052PUSH0
1053KECCAK256
1054SWAP3
1055PUSH0
1056JUMPDEST
1057DUP2
1058DUP2
1059LT
105aPUSH20x1091
105dJUMPI
105ePOP
105fSWAP1
1060DUP5
1061PUSH10x01
1063SWAP6
1064SWAP5
1065SWAP4
1066SWAP3
1067LT
1068PUSH20x1079
106bJUMPI
106cJUMPDEST
106dPOP
106ePOP
106fPOP
1070DUP2
1071SHL
1072ADD
1073SWAP1
1074SSTORE
1075PUSH20x0b6d
1078JUMP
1079JUMPDEST
107aADD
107bMLOAD
107cPUSH0
107dNOT
107ePUSH10xf8
1080DUP5
1081PUSH10x03
1083SHL
1084AND
1085SHR
1086NOT
1087AND
1088SWAP1
1089SSTORE
108aDUP11
108bDUP1
108cDUP1
108dPUSH20x106c
1090JUMP
1091JUMPDEST
1092SWAP3
1093SWAP4
1094PUSH10x20
1096PUSH10x01
1098DUP2
1099SWAP3
109aDUP8
109bDUP7
109cADD
109dMLOAD
109eDUP2
109fSSTORE
10a0ADD
10a1SWAP6
10a2ADD
10a3SWAP4
10a4ADD
10a5PUSH20x1056
10a8JUMP
10a9JUMPDEST
10aaDUP3
10abDUP2
10acGT
10adISZERO
10aePUSH20x0b3a
10b1JUMPI
10b2PUSH20x10dd
10b5SWAP1
10b6DUP5
10b7PUSH0
10b8MSTORE
10b9PUSH10x20
10bbPUSH0
10bcKECCAK256
10bdSWAP1
10bePUSH10x1f
10c0DUP6
10c1ADD
10c2PUSH10x05
10c4SHR
10c5SWAP1
10c6PUSH10x20
10c8DUP7
10c9LT
10caPUSH20x0e48
10cdJUMPI
10cePUSH10x1f
10d0DUP3
10d1SWAP2
10d2ADD
10d3PUSH10x05
10d5SHR
10d6SUB
10d7SWAP2
10d8ADD
10d9PUSH20x5264
10dcJUMP
10ddJUMPDEST
10deDUP11
10dfPUSH20x0b3a
10e2JUMP
10e3JUMPDEST
10e4DUP4
10e5PUSH0
10e6MSTORE
10e7PUSH10x03
10e9PUSH10x20
10ebMSTORE
10ecPUSH10x01
10eeDUP1
10efPUSH10xa0
10f1SHL
10f2SUB
10f3PUSH20x1101
10f6PUSH20x042f
10f9PUSH10x40
10fbPUSH0
10fcKECCAK256
10fdPUSH20x3eb7
1100JUMP
1101JUMPDEST
1102AND
1103DUP2
1104DUP2
1105SUB
1106PUSH20x1110
1109JUMPI
110aJUMPDEST
110bPOP
110cPUSH20x0aeb
110fJUMP
1110JUMPDEST
1111PUSH0
1112MSTORE
1113PUSH10x15
1115PUSH10x20
1117MSTORE
1118PUSH10x40
111aPUSH0
111bKECCAK256
111cPUSH10x01
111ePUSH10x01
1120PUSH10x60
1122SHL
1123SUB
1124PUSH10xa0
1126SHL
1127DUP2
1128SLOAD
1129AND
112aSWAP1
112bSSTORE
112cDUP10
112dPUSH20x110a
1130JUMP
1131JUMPDEST
1132SWAP1
1133PUSH40x13362de3
1138PUSH10xe0
113aSHL
113bPUSH0
113cMSTORE
113dPUSH10x04
113fMSTORE
1140PUSH10x24
1142MSTORE
1143PUSH10x44
1145PUSH0
1146REVERT
1147JUMPDEST
1148POP
1149DUP5
114aDUP2
114bEQ
114cISZERO
114dPUSH20x0acc
1150JUMP
1151JUMPDEST
1152PUSH10x01
1154PUSH10x01
1156PUSH10x40
1158SHL
1159SUB
115aDUP2
115bPUSH10x90
115dSHR
115eAND
115fDUP1
1160DUP11
1161GT
1162ISZERO
1163PUSH20x11bc
1166JUMPI
1167POP
1168PUSH10xd0
116aSHR
116bPUSH10xff
116dAND
116ePUSH20x11a8
1171JUMPI
1172DUP2
1173PUSH10x04
1175SWAP2
1176DUP13
1177SWAP4
1178SLOAD
1179DUP9
117aMLOAD
117bDUP2
117cSUB
117dPUSH20x1187
1180JUMPI
1181JUMPDEST
1182POP
1183PUSH20x0a00
1186JUMP
1187JUMPDEST
1188PUSH0
1189MSTORE
118aPUSH10x0c
118cPUSH10x20
118eMSTORE
118fPUSH10x40
1191PUSH0
1192KECCAK256
1193PUSH10x01
1195PUSH10x01
1197PUSH10x60
1199SHL
119aSUB
119bPUSH10xa0
119dSHL
119eDUP2
119fSLOAD
11a0AND
11a1SWAP1
11a2SSTORE
11a3DUP15
11a4PUSH20x1181
11a7JUMP
11a8JUMPDEST
11a9POP
11aaSLOAD
11abPUSH40xe41b98f7
11b0PUSH10xe0
11b2SHL
11b3PUSH0
11b4MSTORE
11b5PUSH10x04
11b7MSTORE
11b8PUSH10x24
11baPUSH0
11bbREVERT
11bcJUMPDEST
11bdDUP10
11beSWAP1
11bfPUSH40xdf5abcab
11c4PUSH10xe0
11c6SHL
11c7PUSH0
11c8MSTORE
11c9PUSH10x04
11cbMSTORE
11ccPUSH10x24
11ceMSTORE
11cfPUSH10x44
11d1PUSH0
11d2REVERT
11d3JUMPDEST
11d4DUP5
11d5MLOAD
11d6PUSH30x3bdaab
11daPUSH10xe5
11dcSHL
11ddPUSH0
11deMSTORE
11dfPUSH10x04
11e1MSTORE
11e2PUSH10x24
11e4MSTORE
11e5PUSH10x44
11e7PUSH0
11e8REVERT
11e9JUMPDEST
11eaPOP
11ebDUP6
11ecDUP2
11edEQ
11eeISZERO
11efPUSH20x097a
11f2JUMP
11f3JUMPDEST
11f4DUP4
11f5MLOAD
11f6PUSH40xe41b98f7
11fbPUSH10xe0
11fdSHL
11fePUSH0
11ffMSTORE
1200PUSH10x04
1202MSTORE
1203PUSH10x24
1205PUSH0
1206REVERT
1207JUMPDEST
1208DUP5
1209PUSH40x3131bf79
120ePUSH10xe2
1210SHL
1211PUSH0
1212MSTORE
1213PUSH10x04
1215MSTORE
1216PUSH10x24
1218PUSH0
1219REVERT
121aJUMPDEST
121bPUSH20x1223
121eSWAP1
121fPUSH20x3e2f
1222JUMP
1223JUMPDEST
1224ADDRESS
1225PUSH0
1226MSTORE
1227PUSH10x14
1229PUSH10x20
122bMSTORE
122cPUSH10x01
122ePUSH10x01
1230PUSH10x40
1232SHL
1233SUB
1234PUSH10x40
1236PUSH0
1237KECCAK256
1238SWAP2
1239AND
123aPUSH10x01
123cPUSH10x01
123ePUSH10x40
1240SHL
1241SUB
1242NOT
1243DUP3
1244SLOAD
1245AND
1246OR
1247SWAP1
1248SSTORE
1249DUP10
124aPUSH20x0939
124dJUMP
124eJUMPDEST
124fDUP5
1250PUSH40x9ae9f73b
1255PUSH10xe0
1257SHL
1258PUSH0
1259MSTORE
125aPUSH10x04
125cMSTORE
125dPUSH10x24
125fPUSH0
1260REVERT
1261JUMPDEST
1262PUSH20x1283
1265SWAP4
1266POP
1267PUSH20x08ff
126aPUSH20x127d
126dSWAP2
126ePUSH10x24
1270PUSH20x0180
1273DUP12
1274ADD
1275MLOAD
1276SWAP6
1277ADD
1278SWAP1
1279PUSH20x3e4d
127cJUMP
127dJUMPDEST
127eSWAP2
127fPUSH20x4bd9
1282JUMP
1283JUMPDEST
1284ISZERO
1285DUP12
1286DUP1
1287DUP1
1288PUSH20x0915
128bJUMP
128cJUMPDEST
128dPUSH40x88e08729
1292PUSH10xe0
1294SHL
1295PUSH0
1296MSTORE
1297PUSH10x04
1299MSTORE
129aPUSH10x24
129cMSTORE
129dPUSH10x44
129fPUSH0
12a0REVERT
12a1JUMPDEST
12a2PUSH20x12be
12a5SWAP2
12a6SWAP11
12a7POP
12a8RETURNDATASIZE
12a9DUP1
12aaPUSH0
12abDUP4
12acRETURNDATACOPY
12adPUSH20x12b6
12b0DUP2
12b1DUP4
12b2PUSH20x3c98
12b5JUMP
12b6JUMPDEST
12b7DUP2
12b8ADD
12b9SWAP1
12baPUSH20x4133
12bdJUMP
12beJUMPDEST
12bfSWAP9
12c0DUP11
12c1PUSH20x0845
12c4JUMP
12c5JUMPDEST
12c6PUSH10x40
12c8MLOAD
12c9RETURNDATASIZE
12caPUSH0
12cbDUP3
12ccRETURNDATACOPY
12cdRETURNDATASIZE
12ceSWAP1
12cfREVERT
12d0JUMPDEST
12d1PUSH0
12d2SWAP4
12d3SWAP3
12d4SWAP2
12d5SWAP8
12d6POP
12d7PUSH20x12e9
12daSWAP1
12dbRETURNDATASIZE
12dcDUP1
12ddDUP7
12deDUP4
12dfRETURNDATACOPY
12e0PUSH20x12b6
12e3DUP2
12e4DUP4
12e5PUSH20x3c98
12e8JUMP
12e9JUMPDEST
12eaSWAP7
12ebSWAP1
12ecSWAP2
12edSWAP3
12eePUSH20x0812
12f1JUMP
12f2JUMPDEST
12f3PUSH0
12f4DUP1
12f5REVERT
12f6JUMPDEST
12f7CALLVALUE
12f8PUSH20x12f2
12fbJUMPI
12fcPUSH10x20
12feCALLDATASIZE
12ffPUSH10x03
1301NOT
1302ADD
1303SLT
1304PUSH20x12f2
1307JUMPI
1308PUSH20x130f
130bPUSH20x3bfa
130eJUMP
130fJUMPDEST
1310PUSH10x40
1312MLOAD
1313PUSH20x131b
1316DUP2
1317PUSH20x3c7c
131aJUMP
131bJUMPDEST
131cPUSH0
131dDUP2
131eMSTORE
131fPUSH10x20
1321DUP2
1322ADD
1323PUSH0
1324SWAP1
1325MSTORE
1326PUSH10x40
1328DUP2
1329ADD
132aPUSH0
132bSWAP1
132cMSTORE
132dPUSH10x60
132fDUP2
1330ADD
1331PUSH0
1332SWAP1
1333MSTORE
1334PUSH10x80
1336DUP2
1337ADD
1338PUSH0
1339SWAP1
133aMSTORE
133bPUSH10xa0
133dDUP2
133eADD
133fPUSH0
1340SWAP1
1341MSTORE
1342PUSH10xc0
1344DUP2
1345ADD
1346PUSH0
1347SWAP1
1348MSTORE
1349PUSH10xe0
134bDUP2
134cADD
134dPUSH0
134eSWAP1
134fMSTORE
1350PUSH20x0100
1353DUP2
1354ADD
1355PUSH0
1356SWAP1
1357MSTORE
1358PUSH20x0120
135bDUP2
135cADD
135dPUSH0
135eSWAP1
135fMSTORE
1360PUSH20x0140
1363DUP2
1364ADD
1365PUSH0
1366SWAP1
1367MSTORE
1368PUSH20x0160
136bADD
136cPUSH0
136dSWAP1
136eMSTORE
136fPUSH10x01
1371PUSH10xa0
1373SHL
1374PUSH10x01
1376SWAP1
1377SUB
1378AND
1379PUSH0
137aMSTORE
137bPUSH10x02
137dPUSH10x20
137fMSTORE
1380PUSH10x40
1382PUSH0
1383KECCAK256
1384PUSH10x40
1386MLOAD
1387SWAP1
1388PUSH20x1390
138bDUP3
138cPUSH20x3c7c
138fJUMP
1390JUMPDEST
1391DUP1
1392SLOAD
1393SWAP2
1394DUP3
1395DUP2
1396MSTORE
1397PUSH10x01
1399DUP3
139aADD
139bSLOAD
139cPUSH10x20
139eDUP3
139fADD
13a0SWAP1
13a1DUP2
13a2MSTORE
13a3PUSH10x02
13a5DUP4
13a6ADD
13a7SLOAD
13a8PUSH10x40
13aaDUP4
13abADD
13acSWAP1
13adDUP2
13aeMSTORE
13afPUSH10x03
13b1DUP5
13b2ADD
13b3SLOAD
13b4PUSH10x60
13b6DUP5
13b7ADD
13b8SWAP1
13b9DUP2
13baMSTORE
13bbPUSH10x04
13bdDUP6
13beADD
13bfSLOAD
13c0SWAP5
13c1PUSH10x80
13c3DUP6
13c4ADD
13c5SWAP6
13c6DUP7
13c7MSTORE
13c8PUSH10x05
13caADD
13cbSLOAD
13ccSWAP5
13cdPUSH10xa0
13cfDUP6
13d0ADD
13d1PUSH10xff
13d3DUP8
13d4AND
13d5DUP2
13d6MSTORE
13d7PUSH10xc0
13d9DUP7
13daADD
13dbSWAP2
13dcDUP8
13ddPUSH10x08
13dfSHR
13e0PUSH10xff
13e2AND
13e3DUP4
13e4MSTORE
13e5PUSH10xe0
13e7DUP8
13e8ADD
13e9SWAP4
13eaDUP9
13ebPUSH10x10
13edSHR
13eePUSH10x01
13f0PUSH10x01
13f2PUSH10x40
13f4SHL
13f5SUB
13f6AND
13f7DUP6
13f8MSTORE
13f9PUSH20x0100
13fcDUP9
13fdADD
13feSWAP6
13ffDUP10
1400PUSH10x50
1402SHR
1403PUSH10x01
1405PUSH10x01
1407PUSH10x40
1409SHL
140aSUB
140bAND
140cDUP8
140dMSTORE
140ePUSH20x0120
1411DUP10
1412ADD
1413SWAP8
1414DUP11
1415PUSH10x90
1417SHR
1418PUSH10x01
141aPUSH10x01
141cPUSH10x40
141eSHL
141fSUB
1420AND
1421DUP10
1422MSTORE
1423PUSH20x0140
1426DUP11
1427ADD
1428SWAP10
1429DUP12
142aPUSH10xd0
142cSHR
142dPUSH10xff
142fAND
1430ISZERO
1431ISZERO
1432DUP12
1433MSTORE
1434PUSH20x0160
1437ADD
1438SWAP11
1439PUSH10xd8
143bSHR
143cPUSH10xff
143eAND
143fISZERO
1440ISZERO
1441DUP12
1442MSTORE
1443PUSH10x40
1445MLOAD
1446SWAP12
1447DUP13
1448MSTORE
1449MLOAD
144aPUSH10x20
144cDUP13
144dADD
144eMSTORE
144fMLOAD
1450PUSH10x40
1452DUP12
1453ADD
1454MSTORE
1455MLOAD
1456PUSH10x60
1458DUP11
1459ADD
145aMSTORE
145bMLOAD
145cPUSH10x80
145eDUP10
145fADD
1460MSTORE
1461MLOAD
1462PUSH10xff
1464AND
1465PUSH10xa0
1467DUP9
1468ADD
1469MSTORE
146aMLOAD
146bPUSH10xff
146dAND
146ePUSH10xc0
1470DUP8
1471ADD
1472MSTORE
1473MLOAD
1474PUSH10x01
1476PUSH10x01
1478PUSH10x40
147aSHL
147bSUB
147cAND
147dPUSH10xe0
147fDUP7
1480ADD
1481MSTORE
1482MLOAD
1483PUSH10x01
1485PUSH10x01
1487PUSH10x40
1489SHL
148aSUB
148bAND
148cPUSH20x0100
148fDUP6
1490ADD
1491MSTORE
1492MLOAD
1493PUSH10x01
1495PUSH10x01
1497PUSH10x40
1499SHL
149aSUB
149bAND
149cPUSH20x0120
149fDUP5
14a0ADD
14a1MSTORE
14a2MLOAD
14a3ISZERO
14a4ISZERO
14a5PUSH20x0140
14a8DUP4
14a9ADD
14aaMSTORE
14abMLOAD
14acISZERO
14adISZERO
14aePUSH20x0160
14b1DUP3
14b2ADD
14b3MSTORE
14b4PUSH20x0180
14b7SWAP1
14b8RETURN
14b9JUMPDEST
14baCALLVALUE
14bbPUSH20x12f2
14beJUMPI
14bfPUSH0
14c0CALLDATASIZE
14c1PUSH10x03
14c3NOT
14c4ADD
14c5SLT
14c6PUSH20x12f2
14c9JUMPI
14caPUSH10x12
14ccSLOAD
14cdPUSH10x40
14cfMLOAD
14d0PUSH10x01
14d2PUSH10x01
14d4PUSH10xa0
14d6SHL
14d7SUB
14d8SWAP1
14d9SWAP2
14daAND
14dbDUP2
14dcMSTORE
14ddPUSH10x20
14dfSWAP1
14e0RETURN
14e1JUMPDEST
14e2CALLVALUE
14e3PUSH20x12f2
14e6JUMPI
14e7PUSH10x20
14e9CALLDATASIZE
14eaPUSH10x03
14ecNOT
14edADD
14eeSLT
14efPUSH20x12f2
14f2JUMPI
14f3PUSH10x20
14f5PUSH20x0434
14f8PUSH20x14ff
14fbPUSH20x3bfa
14feJUMP
14ffJUMPDEST
1500PUSH20x43e2
1503JUMP
1504JUMPDEST
1505CALLVALUE
1506PUSH20x12f2
1509JUMPI
150aPUSH0
150bCALLDATASIZE
150cPUSH10x03
150eNOT
150fADD
1510SLT
1511PUSH20x12f2
1514JUMPI
1515PUSH10x20
1517PUSH10x40
1519MLOAD
151aPUSH20x0400
151dDUP2
151eMSTORE
151fRETURN
1520JUMPDEST
1521CALLVALUE
1522PUSH20x12f2
1525JUMPI
1526PUSH0
1527CALLDATASIZE
1528PUSH10x03
152aNOT
152bADD
152cSLT
152dPUSH20x12f2
1530JUMPI
1531PUSH10x20
1533PUSH10x40
1535MLOAD
1536PUSH320x6e3591285df1e4e62815fc41f5a94072fc4e3ab79993ede42c87f8995a9f8168
1557DUP2
1558MSTORE
1559RETURN
155aJUMPDEST
155bCALLVALUE
155cPUSH20x12f2
155fJUMPI
1560PUSH10x20
1562CALLDATASIZE
1563PUSH10x03
1565NOT
1566ADD
1567SLT
1568PUSH20x12f2
156bJUMPI
156cPUSH10x01
156ePUSH10x01
1570PUSH10xa0
1572SHL
1573SUB
1574PUSH20x157b
1577PUSH20x3bfa
157aJUMP
157bJUMPDEST
157cAND
157dPUSH0
157eMSTORE
157fPUSH10x03
1581PUSH10x20
1583MSTORE
1584PUSH20x15a6
1587PUSH20x1592
158aPUSH10x40
158cPUSH0
158dKECCAK256
158ePUSH20x3eb7
1591JUMP
1592JUMPDEST
1593PUSH10x40
1595MLOAD
1596SWAP2
1597DUP3
1598SWAP2
1599PUSH10x20
159bDUP4
159cMSTORE
159dPUSH10x20
159fDUP4
15a0ADD
15a1SWAP1
15a2PUSH20x3c10
15a5JUMP
15a6JUMPDEST
15a7SUB
15a8SWAP1
15a9RETURN
15aaJUMPDEST
15abCALLVALUE
15acPUSH20x12f2
15afJUMPI
15b0PUSH10x40
15b2CALLDATASIZE
15b3PUSH10x03
15b5NOT
15b6ADD
15b7SLT
15b8PUSH20x12f2
15bbJUMPI
15bcPUSH10x01
15bePUSH10x01
15c0PUSH10xa0
15c2SHL
15c3SUB
15c4PUSH20x15cb
15c7PUSH20x3bfa
15caJUMP
15cbJUMPDEST
15ccAND
15cdPUSH0
15ceMSTORE
15cfPUSH10x15
15d1PUSH10x20
15d3MSTORE
15d4PUSH10x20
15d6PUSH10x01
15d8DUP1
15d9PUSH10xa0
15dbSHL
15dcSUB
15ddPUSH10x40
15dfPUSH0
15e0KECCAK256
15e1SLOAD
15e2AND
15e3DUP1
15e4ISZERO
15e5ISZERO
15e6SWAP1
15e7DUP2
15e8PUSH20x15f7
15ebJUMPI
15ecJUMPDEST
15edPOP
15eePUSH10x40
15f0MLOAD
15f1SWAP1
15f2ISZERO
15f3ISZERO
15f4DUP2
15f5MSTORE
15f6RETURN
15f7JUMPDEST
15f8PUSH20x1605
15fbSWAP2
15fcPOP
15fdPUSH10x24
15ffCALLDATALOAD
1600SWAP1
1601PUSH20x4036
1604JUMP
1605JUMPDEST
1606DUP3
1607PUSH20x15ec
160aJUMP
160bJUMPDEST
160cCALLVALUE
160dPUSH20x12f2
1610JUMPI
1611PUSH10x20
1613CALLDATASIZE
1614PUSH10x03
1616NOT
1617ADD
1618SLT
1619PUSH20x12f2
161cJUMPI
161dPUSH10x01
161fPUSH10x01
1621PUSH10xa0
1623SHL
1624SUB
1625PUSH20x162c
1628PUSH20x3bfa
162bJUMP
162cJUMPDEST
162dAND
162ePUSH0
162fMSTORE
1630PUSH10x05
1632PUSH10x20
1634MSTORE
1635PUSH20x15a6
1638PUSH20x1592
163bPUSH10x40
163dPUSH0
163eKECCAK256
163fPUSH20x3eb7
1642JUMP
1643JUMPDEST
1644CALLVALUE
1645PUSH20x12f2
1648JUMPI
1649PUSH0
164aCALLDATASIZE
164bPUSH10x03
164dNOT
164eADD
164fSLT
1650PUSH20x12f2
1653JUMPI
1654PUSH10x20
1656PUSH10x40
1658MLOAD
1659PUSH10x10
165bDUP2
165cMSTORE
165dRETURN
165eJUMPDEST
165fCALLVALUE
1660PUSH20x12f2
1663JUMPI
1664PUSH10x20
1666CALLDATASIZE
1667PUSH10x03
1669NOT
166aADD
166bSLT
166cPUSH20x12f2
166fJUMPI
1670PUSH10x01
1672PUSH10x01
1674PUSH10xa0
1676SHL
1677SUB
1678PUSH20x167f
167bPUSH20x3bfa
167eJUMP
167fJUMPDEST
1680AND
1681PUSH0
1682MSTORE
1683PUSH10x07
1685PUSH10x20
1687MSTORE
1688PUSH20x15a6
168bPUSH20x1592
168ePUSH10x40
1690PUSH0
1691KECCAK256
1692PUSH20x3eb7
1695JUMP
1696JUMPDEST
1697CALLVALUE
1698PUSH20x12f2
169bJUMPI
169cPUSH0
169dCALLDATASIZE
169ePUSH10x03
16a0NOT
16a1ADD
16a2SLT
16a3PUSH20x12f2
16a6JUMPI
16a7PUSH10x20
16a9PUSH10x40
16abMLOAD
16acDUP2
16adDUP2
16aeMSTORE
16afRETURN
16b0JUMPDEST
16b1CALLVALUE
16b2PUSH20x12f2
16b5JUMPI
16b6PUSH0
16b7CALLDATASIZE
16b8PUSH10x03
16baNOT
16bbADD
16bcSLT
16bdPUSH20x12f2
16c0JUMPI
16c1PUSH10x20
16c3PUSH10x40
16c5MLOAD
16c6PUSH10x02
16c8DUP2
16c9MSTORE
16caRETURN
16cbJUMPDEST
16ccCALLVALUE
16cdPUSH20x12f2
16d0JUMPI
16d1PUSH10x20
16d3CALLDATASIZE
16d4PUSH10x03
16d6NOT
16d7ADD
16d8SLT
16d9PUSH20x12f2
16dcJUMPI
16ddPUSH10x04
16dfCALLDATALOAD
16e0PUSH0
16e1MSTORE
16e2PUSH10x0c
16e4PUSH10x20
16e6MSTORE
16e7PUSH10x20
16e9PUSH10x01
16ebDUP1
16ecPUSH10xa0
16eeSHL
16efSUB
16f0PUSH10x40
16f2PUSH0
16f3KECCAK256
16f4SLOAD
16f5AND
16f6PUSH10x40
16f8MLOAD
16f9SWAP1
16faDUP2
16fbMSTORE
16fcRETURN
16fdJUMPDEST
16feCALLVALUE
16ffPUSH20x12f2
1702JUMPI
1703PUSH0
1704CALLDATASIZE
1705PUSH10x03
1707NOT
1708ADD
1709SLT
170aPUSH20x12f2
170dJUMPI
170ePUSH10x20
1710PUSH10xff
1712PUSH10x10
1714SLOAD
1715PUSH10xa0
1717SHR
1718AND
1719PUSH10x40
171bMLOAD