Final Chainexplorer Φ₀ · Final Chain · 20678
cs

Kontrakt

0x617620cabf90b3ceabe00d8fb83626bdf55e3bd9

Adresa
0x617620cabf90b3ceabe00d8fb83626bdf55e3bd9
Druh
ověřený kontrakt FinalStateTrees
Zůstatek
0 vETH
Nonce
1
Kód
26,167 bajtů codehash 0x3c52c60717e521f4668716598930f107f1044b714513773e8fdc6c501f5710f0

strom účtů

Strom
1 · účty
Přítomen
žádný list
Klíč
0x734012e428963ccf4efad199b95cd90fd918fc8addc13847c8b2a78207e25cd6
Živý kořen
0xe7eb646dfe4cfe9c975fd970c7c565c8d96e5c1edae4440e5f6a2560325fb328
Tato adresa nemá ve stromu účtů list. Každý Final Wallet — včetně servisních identit — ho má, takže chybějící list znamená běžný účet, ne peněženku.
transakceudálostipřevody tokenůkontrakt

zdroj ověřeno

Kontrakt
FinalStateTrees přesná shoda · immutables maskovány
Kompilátor
v0.8.33+commit.64118f21
Optimalizátor
zapnut · 200 průchodů
Verze EVM
prague
Ověřeno
2026-09-07T05:00:39.580Z
Původ
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;
    }
}

contracts/finalchain/FinalStateTrees.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 {FinalIdentityRegistry} from "./FinalIdentityRegistry.sol";
import {FinalChainTime} from "./FinalChainTime.sol";
import {FinalPqQuorum} from "./FinalPqQuorum.sol";

/// @notice The one question `syncIdentities` asks the asset registry.
/// @dev An interface rather than an import of `FinalAssetRegistry`, which
///      imports this file: the registry is tree 6's writer and holds the trees
///      as an immutable, so the dependency runs that way and this is the one
///      read that runs the other.
interface IChainSource {
    function enabledChainRefs() external view returns (bytes32[] memory);
}

/// @notice The one question {FinalStateTrees.syncSlotKeyLeaves} asks the
///         slot-key registry: the leaf value for one member's slot — the
///         registry's own verdict, zero when the slot holds nothing usable.
interface ISlotKeySource {
    function slotKeyLeafOf(address member, uint64 slotIndex) external view returns (bytes32);
}

/// @notice The one question {FinalStateTrees.syncEndpointLeaves} asks the
///         endpoint registry: the leaf value for one tunnel endpoint — the
///         registry's own verdict (certificate hash, status, expiry, region),
///         zero when nothing is registered under the id.
interface IEndpointSource {
    function endpointLeafOf(bytes32 endpointId) external view returns (bytes32);
}

/**
 * @title FinalStateTrees
 * @notice The eight trees. Final Chain's state plane, and the source of truth
 *         every other chain projects from.
 *
 * @dev One tree per domain, because they change at unrelated cadences and a
 * combined tree invalidates every outstanding proof on every tick:
 *
 * | # | tree | holds | cadence |
 * |---|---|---|---|
 * | 1 | accounts | every Final Wallet's public state | per rotation / creation |
 * | 2 | phi | the PHI record: per (wallet, chain) balances, the lock, exposures | per publisher round |
 * | 3 | vasset | issued vAsset supply and backing, per (asset, chain) | per settlement |
 * | 4 | oracle | published prices and their inputs | ~10 s; 1 s for morph and fee assets |
 * | 5 | settlement | chain and asset registry roots | rarely |
 * | 6 | allowlist | assets, chains, policy, price sources, DEX deployments | rarely |
 * | 7 | intents | intent status, ring-keyed over the posting sequence | per posting |
 * | 8 | identity | the wallet-creation admission set, projected from the registry | per identity mutation |
 *
 * ## The hash shape is not a choice
 *
 * Leaves hash as `keccak256(0x00 ‖ leaf)` and internal nodes as
 * `keccak256(0x01 ‖ lo ‖ hi)` with the pair sorted. That is
 * `FinalMerkle.verifyTaggedSortedProof`, verbatim, which is what
 * `FinalWalletFactory.syncAccountState` and `FinalSettlement` already run on
 * every supported chain. A proof produced here is consumed there with no
 * translation and no contract change, and tree 1's leaf preimage is exactly
 * `FinalWalletFactory.accountStateLeafHash` — same fields, same order, the
 * `deployedChains` table `abi.encode`d like every other field.
 *
 * Getting this wrong is not a compile error anywhere. It is a root every chain
 * silently rejects, with nothing pointing at the cause.
 *
 * ## Positional slots under a sorted-pair tree
 *
 * Sorted pairs make a proof position-agnostic, which is why it carries no
 * direction bits. That does not stop the TREE from being positional, and here
 * it is: every key gets a permanent slot, so a single leaf update is `DEPTH`
 * hashes instead of a rebuild over every leaf. The verifier neither knows nor
 * needs to know that a slot exists.
 *
 * ## Branches (2026-09-04)
 *
 * The slot space of every tree is cut into `BRANCH_COUNT` branches by the top
 * `BRANCH_BITS` of the slot: a branch is a subtree with a permanent place, its
 * root is one internal node, and a leaf's path to the tree root passes through
 * it. Branches hold what belongs to the same domain but not to the same rows
 * — branch 0 is the owning service's CONFIGURATION on every tree, tree 8 adds
 * the owner → wallets index and the co-signers' slot keys beside the admission
 * set — and they are chosen over more trees because a branch shares its
 * tree's authority doors and writer, while a tree would need its own. A leaf
 * proves against its branch root with `BRANCH_DEPTH` siblings, against the
 * tree root with `DEPTH`, against the round root with `ROUND_DEPTH`: one path,
 * cut at three heights, one verifier.
 *
 * ## Rounds, and why the live roots are not the product
 *
 * `setLeaves` moves a tree. It does not publish one. A consumer that fetched
 * eight roots one at a time would get a price proof from one moment and a
 * roster proof from another, and something delisted in between would still
 * verify.
 *
 * `publishRound` snapshots all eight together, and folds them into ONE round
 * root — the tree roots as the level-`DEPTH` nodes of a depth-`ROUND_DEPTH`
 * tree, tree `t` at position `t` — so a single word commits to the whole
 * plane and any leaf in it proves against that word with four more siblings.
 * A round is the unit a consumer pins, and it is the only thing this contract
 * promises is contemporaneous. The execution chains keep anchoring per-tree
 * roots (identity, account state, registry roots): those must move at their
 * own cadence, not at the oracle's.
 */
contract FinalStateTrees {
    // ---------------------------------------------------------------- trees

    /// @notice Every Final Wallet's public state. The source of truth other
    /// chains copy through `syncAccountState`.
    uint8 public constant TREE_ACCOUNTS = 1;
    /// @notice The PHI record, per `(wallet, chain)`: balances, the lock, its
    /// terms, the exposures carved from it and the accrual between reconciliations.
    uint8 public constant TREE_PHI = 2;
    /// @notice vAsset supply and backing.
    uint8 public constant TREE_VASSET = 3;
    /// @notice Oracle prices and their inputs.
    uint8 public constant TREE_ORACLE = 4;
    /// @notice Settlement chain and asset registry roots.
    uint8 public constant TREE_SETTLEMENT = 5;
    /// @notice Which assets and chains are supported.
    uint8 public constant TREE_ALLOWLIST = 6;
    /// @notice Intent status, keyed by a RING over the posting sequence.
    /// @dev The search structure beside `FinalBundleLog`'s permanent record.
    /// Written only by `FinalIntentLog` through `treeWriter[7]` — the tree-1
    /// argument verbatim: the log verified the bond, the commitment, the
    /// approval and the consume itself, and a service quorum on top would be a
    /// censorship point over posting. Slots are permanent and intents are
    /// unbounded flow, so the log recycles keys modulo `CAPACITY`: the tree is
    /// an index with a ~1M-posting retention window, never the record.
    uint8 public constant TREE_INTENTS = 7;
    /// @notice The wallet-creation admission set — the identity leaves
    /// (`keccak256(DOMAIN_IDENTITY_LEAF ‖ serial ‖ keysHash)`) every execution
    /// chain's gateway verifies certificates against.
    /// @dev The root the gateways anchor as `currentIdentityRoot` — a
    /// CONTINUOUS root over this tree replaces the cold-set snapshot the
    /// retired `publishIdentityRoot.cjs` ceremony folded off-chain. Two
    /// feeders, one per identity plane, and NO quorum door for either:
    ///
    /// - SERVICE identities: {syncIdentityLeaves}, the permissionless
    ///   projection of `FinalIdentityRegistry`'s own verdict — the registry
    ///   calls it same-tx on every identity mutation, and anyone may call it
    ///   to retire a leaf whose standing lapsed by TIME (expiry moves no
    ///   registry storage, so only a projection pass can zero it).
    /// - USER identities: `treeWriter[8]` — `FinalAccountLedger`, which
    ///   computes the leaf from the genesis certificate fields it verified
    ///   under its opener quorum and writes it once at `openAccount`. A user
    ///   admission leaf is permanent by construction: the certificate IS the
    ///   address, rotation never changes it, and a post-rotation creation on
    ///   a new chain reads PUBLISHED account state (WAL-02), never the
    ///   certificate's genesis keys.
    ///
    /// A quorum of service signatures must not be able to state an identity
    /// neither ruler decided, so `setLeaves` refuses this tree outright.
    uint8 public constant TREE_IDENTITY = 8;
    /// @notice Count, for iteration. Trees are 1-indexed; 0 is not a tree.
    uint8 public constant TREE_COUNT = 8;

    /// @notice 2^24 slots per tree, laid out as 16 BRANCHES of 2^20: the top
    /// `BRANCH_BITS` of a slot name the branch, the rest its position in it.
    /// A million rows per branch is far past where this design gets replaced
    /// by Final Chain proper. Raising any of this later is a migration, not a
    /// parameter change: the depth is in every root.
    uint256 public constant DEPTH = 24;
    uint256 public constant BRANCH_BITS = 4;
    uint8 public constant BRANCH_COUNT = 16;
    /// @notice Height of a branch: a leaf proves against its branch root with
    /// this many siblings.
    uint256 public constant BRANCH_DEPTH = DEPTH - BRANCH_BITS;
    /// @notice Slots per branch.
    uint256 public constant BRANCH_CAPACITY = 1 << BRANCH_DEPTH;
    /// @notice Slots per tree, all branches together.
    uint256 public constant CAPACITY = 1 << DEPTH;
    /// @notice The round root is a tree over the tree roots — position `t`
    /// holds tree `t`'s root, positions 0 and 9..15 the empty tree — folded
    /// with the same node hash. It is literally the root of a depth-28 tree
    /// whose level-24 nodes are the eight tree roots, which is what lets one
    /// path prove a leaf against it.
    uint256 public constant FOREST_BITS = 4;
    uint256 public constant ROUND_DEPTH = DEPTH + FOREST_BITS;

    /// @notice Branch 0 of EVERY tree: the configuration of the service that
    /// owns the tree — key → one word, the VALUE stored so a contract on this
    /// chain reads it directly (`configValue`), the hash in the tree so it is
    /// provable wherever a round root is. Written only by {setConfig} under
    /// the configuration authority; every other door refuses the branch.
    uint8 public constant BRANCH_CONFIG = 0;
    /// @notice Branch 1 of every tree: the domain's own rows — accounts, PHI
    /// records, vAssets, prices, registry roots, the allowlist, the intent
    /// ring, the identity admission set.
    uint8 public constant BRANCH_MAIN = 1;
    /// @notice Tree 8, branch 2: the owner → wallets index. Key = the owner
    /// (`ownerIndexKeyFor`), leaf = {ownerIndexLeafHash} over the ledger's
    /// `walletsByOwner(owner)`. Written by tree 8's writer, the ledger, beside
    /// every open and every owner transfer — the tree is the search structure,
    /// the ledger holds the readable array it proves.
    uint8 public constant BRANCH_OWNER_INDEX = 2;
    /// @notice Tree 8, branch 3: the co-signers' per-slot KEM publics — a RING
    /// of `SLOT_KEY_RING` positions per member, projected from
    /// `slotKeySource` by {syncSlotKeyLeaves} exactly as identities are.
    uint8 public constant BRANCH_SLOT_KEYS = 3;
    /// @notice Tree 8, branch 4: the tunnel endpoints — the Final Node
    /// identities a wallet's FNP session terminates at. Key = the endpoint id
    /// (`endpointKeyFor`, the certificate's subject key id), leaf = the
    /// endpoint registry's verdict, projected from `endpointSource` by
    /// {syncEndpointLeaves} exactly as slot keys are. An execution chain never
    /// parses an endpoint certificate; it anchors this tree's root and a client
    /// proves the leaf against it (user ruling 2026-09-05).
    uint8 public constant BRANCH_ENDPOINTS = 4;
    /// @notice Slot-key positions per member. A slot index wraps modulo this,
    /// so the branch is an index over the recent slots and never fills; 1024
    /// members × 1024 positions is the branch exactly.
    uint64 public constant SLOT_KEY_RING = 1024;

    /// @dev Must equal `FinalWalletFactory.DOMAIN_ACCOUNT_STATE_LEAF`. Pinned
    /// by the cross-repo parity test; a field reordered on one side and not the
    /// other is a root every chain rejects.
    ///
    /// `v02`: `deployedChains` became the `(chainRef, account)` table. A v01
    /// leaf and a v02 leaf never share a domain, so a proof built against the
    /// retired plane cannot verify against this one by accident.
    bytes32 public constant DOMAIN_ACCOUNT_STATE_LEAF =
        keccak256("FINAL_ACCOUNT_STATE_LEAF_v02");

    bytes32 private constant ACTION_SET_LEAVES = keccak256("FinalStateTrees.setLeaves.v01");
    /// @dev Registrar-quorum actions, verified by the registry with this
    /// contract as the verifying contract. See `FinalIdentityRegistry.requireRegistrarQuorum`.
    bytes32 public constant ACTION_CONFIGURE_TREE = keccak256("FINAL_STATE_TREES_CONFIGURE_TREE_v01");
    bytes32 public constant ACTION_SET_TREE_WRITER = keccak256("FINAL_STATE_TREES_SET_TREE_WRITER_v01");
    bytes32 public constant ACTION_SET_CHAIN_SOURCE = keccak256("FINAL_STATE_TREES_SET_CHAIN_SOURCE_v01");
    bytes32 public constant ACTION_SET_SLOT_KEY_SOURCE = keccak256("FINAL_STATE_TREES_SET_SLOT_KEY_SOURCE_v01");
    bytes32 public constant ACTION_SET_ENDPOINT_SOURCE = keccak256("FINAL_STATE_TREES_SET_ENDPOINT_SOURCE_v01");
    bytes32 public constant ACTION_SEED_COUNTERS = keccak256("FINAL_STATE_TREES_SEED_COUNTERS_v01");
    bytes32 public constant ACTION_SET_TYPED_WRITER = keccak256("FINAL_STATE_TREES_SET_TYPED_WRITER_v01");
    bytes32 public constant ACTION_SET_CONFIG = keccak256("FINAL_STATE_TREES_SET_CONFIG_v01");

    /// @dev Key domains. Both are full-width hashes rather than the packed
    /// address they came from, which matters: an address key occupies only the
    /// low 160 bits, so a hashed key colliding with one needs ~2^96 work rather
    /// than a full collision. That is expensive but not comfortable, and the
    /// consequence would be a service identity landing in a wallet's slot.
    bytes32 private constant DOMAIN_ACCOUNT_KEY = keccak256("FinalStateTrees.key.account.v01");
    bytes32 private constant DOMAIN_IDENTITY_TREE_KEY = keccak256("FinalStateTrees.key.identity.v01");
    /// @dev Tree 8, branches 2 and 3, and branch 0 of every tree. Each is its
    ///      own domain so a key can never land in another branch's slot by
    ///      construction — `_set` refuses a key whose slot sits in a different
    ///      branch, and the domain is what makes that refusal unreachable.
    bytes32 private constant DOMAIN_OWNER_INDEX_KEY = keccak256("FinalStateTrees.key.ownerIndex.v01");
    bytes32 private constant DOMAIN_SLOT_KEY = keccak256("FinalStateTrees.key.slotKey.v01");
    bytes32 private constant DOMAIN_ENDPOINT_KEY = keccak256("FinalStateTrees.key.endpoint.v01");
    bytes32 private constant DOMAIN_CONFIG_KEY = keccak256("FinalStateTrees.key.config.v01");

    /// @notice Leaf domains for the owner index (tree 8, branch 2) and for
    /// configuration rows (branch 0 of every tree). The config leaf binds the
    /// tree too, so the same row in two trees is two different leaves.
    bytes32 public constant DOMAIN_OWNER_INDEX_LEAF = keccak256("FINAL_OWNER_INDEX_LEAF_v01");
    bytes32 public constant DOMAIN_CONFIG_LEAF = keccak256("FINAL_CONFIG_LEAF_v01");

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

    /// @notice The registry every signer is resolved through. Immutable so the
    /// quorum can never be pointed at a registry supplied in calldata.
    FinalIdentityRegistry public immutable registry;

    /// @notice Approvals required per tree.
    ///
    /// @dev Per-tree and not a scalar, because each tree is gated by a
    ///      DIFFERENT role — account co-signers, PHI, vAsset and oracle
    ///      publishers, registry publishers — so K is a property of that
    ///      tree's roster, not of the contract. All six read 2 today; that is
    ///      a deploy-time default, not an invariant, and collapsing them would
    ///      put the oracle roster's quorum on the account co-signers'.
    ///
    ///      The VALUE is a full word: it is a quantity compared against a live
    ///      member count, and every other threshold in the system is `uint256`.
    ///      The KEY is `uint8` because that is what a tree id is here — six
    ///      `uint8` constants, every parameter, every event, every error,
    ///      `_assertTree`, and the ten sibling mappings below. Widening it
    ///      would buy nothing (a narrow key is padded to 32 bytes before
    ///      hashing, so the slot is identical) and cost the getter's selector
    ///      on a contract that is live on both Final Chains.
    mapping(uint8 treeId => uint256) public threshold;
    /// @notice Role a signer must hold to write to a tree.
    mapping(uint8 treeId => uint256) public writerRole;

    /// Raw (untagged) leaf value by tree and slot.
    mapping(uint8 => mapping(uint256 => bytes32)) private _leaf;
    /// Internal nodes, levels 1..DEPTH. Level 0 is derived from `_leaf`.
    mapping(uint8 => mapping(uint256 => mapping(uint256 => bytes32))) private _node;
    /// Empty-subtree hash per level, computed once at construction — up to
    /// the round root's height, since the forest's empty positions are empty
    /// trees.
    bytes32[ROUND_DEPTH + 1] private _zero;

    /// Permanent slot for a key, 1-based so 0 means unassigned. The slot's top
    /// `BRANCH_BITS` are the branch the key lives in.
    mapping(uint8 => mapping(bytes32 => uint256)) private _slotPlusOne;
    /// The key a slot was handed to — the reverse of `_slotPlusOne`, so any
    /// branch enumerates on chain (`keyAt` over `0 .. branchSlotsUsed`) with
    /// no log window. One extra word per NEW key, never per update.
    mapping(uint8 => mapping(uint256 => bytes32)) private _keyAt;
    /// @notice Slots handed out per tree, all branches together.
    mapping(uint8 => uint256) public slotsUsed;
    /// Slots handed out per branch — the next position in it.
    mapping(uint8 => mapping(uint8 => uint256)) private _branchSlotsUsed;
    /// The VALUE behind a configuration row (branch 0), by tree and key.
    mapping(uint8 => mapping(bytes32 => bytes32)) private _configValue;

    /// @notice Live root per tree. Moves on every `setLeaves`.
    mapping(uint8 treeId => bytes32) public liveRoot;
    /// @notice Writes applied per tree, for change detection between rounds.
    mapping(uint8 treeId => uint64) public treeVersion;

    /// @notice A contemporaneous snapshot of all eight roots, and the one
    /// round root that folds them.
    struct Round {
        bytes32[TREE_COUNT + 1] roots;
        bytes32 roundRoot;
        uint64 blockNumber;
        uint64 timestamp;
    }

    /// @notice Published rounds, 1-indexed. Round 0 is "nothing published".
    mapping(uint64 => Round) private _rounds;
    /// @notice Highest published round.
    uint64 public round;
    /// Tree versions as of the last published round.
    mapping(uint8 => uint64) private _publishedVersion;

    /// @notice Per-tree nonce, bound into every quorum digest.
    mapping(uint8 treeId => uint64) public nonce;

    /**
     * @notice A CONTRACT allowed to write one tree without a quorum.
     *
     * @dev Exactly one per tree, and today exactly one exists: tree 1's is
     * `FinalAccountLedger`.
     *
     * This looks like a hole and is the opposite. The quorum on `setLeaves`
     * exists because a tree's writer is otherwise one key deciding what the
     * chain states. A writer contract is not a key — its rules are its
     * bytecode, it has no owner and no proxy, and tree 1's writer authorizes
     * every change by verifying the ACCOUNT HOLDER'S own post-quantum signature
     * in this chain's precompiles. That is strictly stronger evidence than a
     * K-of-N of our own services attesting to what they read.
     *
     * Keeping the quorum on top of it would be actively worse: our fleet could
     * then withhold approval from a user rotating a stolen key, which is a
     * censorship power over the exact operation the account plane exists to
     * make possible.
     *
     * The writer is set on the same bootstrap window as `configureTree` and can
     * be moved by a registrar afterwards — an immutable pointer would mean a
     * ledger upgrade abandons the tree it writes.
     */
    mapping(uint8 treeId => address) public treeWriter;

    /**
     * @notice Where `syncIdentities` reads the chain set from — the asset
     *         registry, which is also tree 6's writer.
     *
     * @dev A service identity is a Final Wallet whose address is the same on
     * every EVM chain, so its tree-1 `deployedChains` table is derivable: one
     * `(chainRef, itself)` row per chain the registry has enabled. The table
     * is DERIVED from state rather than supplied by the caller precisely so
     * that `syncIdentities` can stay permissionless — a caller-chosen table
     * would let anyone grant a service identity on a chain of their choosing.
     *
     * Unset (zero) means services carry an empty table and exist on Final
     * Chain alone, which is what a plane looks like before its registry is
     * seeded. Same configuration gate as `setTreeWriter`, because pointing this
     * at a different contract changes what every service leaf says.
     */
    address public chainSource;
    /// @notice Where {syncSlotKeyLeaves} reads the co-signers' slot keys from
    ///         — the slot-key registry, whose verdict tree 8's branch 3
    ///         projects. Same configuration gate as `chainSource`; unset means
    ///         the branch cannot be written.
    address public slotKeySource;
    /// @notice The endpoint registry whose verdict tree 8's branch 4 projects.
    address public endpointSource;
    /// @notice The one contract admitted to {writeTyped}: `FinalStateRecords`,
    ///         which holds the preimages behind trees 2, 3 and 4 and computes
    ///         their keys and hashes. Same configuration gate as `treeWriter`.
    address public typedWriter;

    // --------------------------------------------------------------- events

    event LeavesSet(uint8 indexed treeId, uint256 count, bytes32 newRoot, uint64 treeVersion);
    event RoundPublished(uint64 indexed round, uint64 blockNumber, uint64 timestamp);
    event TreeConfigured(uint8 indexed treeId, uint256 writerRole, uint256 threshold);
    event TreeWriterSet(uint8 indexed treeId, address writer);
    event ChainSourceSet(address source);
    event SlotKeySourceSet(address source);
    event EndpointSourceSet(address source);
    /// @notice A fresh plane took over the previous plane's counters.
    event CountersSeeded(uint64 round, uint64[] versions);
    event TypedWriterSet(address writer);
    event ConfigSet(uint8 indexed treeId, bytes32 indexed key, bytes32 value);

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

    error UnknownTree(uint8 treeId);
    error LengthMismatch(uint256 keys, uint256 leaves);
    error BranchFull(uint8 treeId, uint8 branch);
    error UnknownBranch(uint8 branch);
    /// @notice A key already holds a slot in another branch of this tree.
    error BranchMismatch(uint8 treeId, bytes32 key, uint8 have, uint8 want);
    /// @notice Branch 0 is written by `setConfig` alone.
    error ConfigBranchReserved(uint8 treeId);
    error SlotKeySourceUnset();
    error EndpointSourceUnset();
    /// @notice Counters can be seeded only into a plane that has published nothing.
    error NotFresh();
    error VersionCountMismatch(uint256 given);
    error TreeNotConfigured(uint8 treeId);
    error NothingToPublish();
    error UnknownKey(uint8 treeId, bytes32 key);
    error NotAuthorized(address caller);
    error NoRounds();
    error ThresholdUnreachable(uint8 treeId, uint256 live, uint256 required);
    /// @notice Trees 7 and 8 take no quorum writes — only their writer
    /// contract (and, for tree 8, the registry projection).
    error WriterOnlyTree(uint8 treeId);
    /// @notice `setLeaves` was called on a tree that has a typed writer.
    /// @dev Trees 2, 3 and 4 keep the leaf's preimage beside its hash so a
    ///      consumer can read the VALUE. An untyped write sets the hash and
    ///      cannot set the preimage — the pair would disagree, and the stored
    ///      value would look authoritative while committing to nothing. The
    ///      typed entrypoint is not a convenience over this one; it is the
    ///      only door.
    error TypedTreeOnly(uint8 treeId);
    /// @notice A `deployedChains` row names the zero chain or the zero account,
    ///         or repeats a chain. A table with either proves nothing about
    ///         where the account exists.
    error InvalidChainAccount(bytes32 chainRef, bytes32 account);

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

    /**
     * @param registry_ The identity registry. Every signer, key and role is
     *        resolved through it.
     * @dev The empty-subtree table is built here rather than as constants
     * because it depends on the tagging, and a constant table that drifted from
     * the tagging would produce roots nothing can verify — silently, since both
     * sides would still be self-consistent.
     */
    constructor(FinalIdentityRegistry registry_) {
        registry = registry_;

        // Level 0: the tagged hash of an empty (zero) leaf.
        _zero[0] = keccak256(abi.encodePacked(bytes1(0x00), bytes32(0)));
        for (uint256 l = 0; l < ROUND_DEPTH; l++) {
            // Both children equal, so the sort is a no-op and the order is
            // irrelevant — which is the only reason this table is one value per
            // level rather than one per position.
            _zero[l + 1] = keccak256(abi.encodePacked(bytes1(0x01), _zero[l], _zero[l]));
        }

        for (uint8 t = 1; t <= TREE_COUNT; t++) {
            liveRoot[t] = _zero[DEPTH];
        }
    }

    // ------------------------------------------------------- configuration

    /**
     * @dev The configuration gate: the registry's bootstrap admin alone while
     * its window is open, the sealed `ROLE_REGISTRAR` quorum afterwards. The
     * same window the registry uses, for the same reason — every roster has to
     * be installed by someone before it can install itself — and the same
     * quorum, because a threshold is membership by another name: whoever can
     * set K to one owns the tree.
     */
    function _requireConfigurationAuthority(
        bytes32 actionDomain,
        bytes32 payloadDigest,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) private {
        if (!registry.bootstrapSealed() && msg.sender == registry.bootstrapAdmin()) return;
        registry.requireRegistrarQuorum(actionDomain, payloadDigest, anchorBlock, approvals);
    }

    /**
     * @notice Set which role may write a tree and how many approvals it needs.
     * @param k Approvals a write needs; `0` leaves the tree unconfigured.
     * @param anchorBlock The registrars' roster anchor. Ignored during bootstrap.
     * @param approvals The sealed registrar quorum. Empty during bootstrap.
     */
    function configureTree(
        uint8 treeId,
        uint256 role,
        uint256 k,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _assertTree(treeId);
        _requireConfigurationAuthority(
            ACTION_CONFIGURE_TREE, keccak256(abi.encode(treeId, role, k)), anchorBlock, approvals
        );
        // Refuse a threshold nobody can meet. Register the members first; that
        // ordering is the point, not an inconvenience. A 4-of-5 configured
        // against three registered co-signers is a tree that reverts on every
        // write, and the revert names the threshold rather than the roster.
        if (k != 0) {
            uint256 live = registry.liveMemberCount(role);
            if (live < k) revert ThresholdUnreachable(treeId, live, k);
        }
        writerRole[treeId] = role;
        threshold[treeId] = k;
        emit TreeConfigured(treeId, role, k);
    }

    /**
     * @notice Point a tree at the contract allowed to write it directly.
     * @dev Same gate as `configureTree`, for the same reason. Setting it to the
     * zero address removes the path entirely and leaves the tree quorum-only.
     */
    function setTreeWriter(
        uint8 treeId,
        address writer,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _assertTree(treeId);
        _requireConfigurationAuthority(
            ACTION_SET_TREE_WRITER, keccak256(abi.encode(treeId, writer)), anchorBlock, approvals
        );
        treeWriter[treeId] = writer;
        emit TreeWriterSet(treeId, writer);
    }

    /**
     * @notice Point `syncIdentities` at the contract that knows the chain set.
     * @dev Same gate as `setTreeWriter`. Zero removes the source, after which
     * service leaves carry an empty `deployedChains` table.
     */
    function setChainSource(
        address source,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _requireConfigurationAuthority(
            ACTION_SET_CHAIN_SOURCE, keccak256(abi.encode(source)), anchorBlock, approvals
        );
        chainSource = source;
        emit ChainSourceSet(source);
    }

    /// @notice Point tree 8's branch 3 at the slot-key registry it projects.
    function setSlotKeySource(
        address source,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _requireConfigurationAuthority(
            ACTION_SET_SLOT_KEY_SOURCE, keccak256(abi.encode(source)), anchorBlock, approvals
        );
        slotKeySource = source;
        emit SlotKeySourceSet(source);
    }

    /// @notice Point tree 8's branch 4 at the endpoint registry it projects.
    function setEndpointSource(
        address source,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _requireConfigurationAuthority(
            ACTION_SET_ENDPOINT_SOURCE, keccak256(abi.encode(source)), anchorBlock, approvals
        );
        endpointSource = source;
        emit EndpointSourceSet(source);
    }

    /**
     * @notice Take over the previous plane's counters — one `treeVersion` per
     *         tree (index = treeId, 0 unused) and the published `round` — so a
     *         redeploy is monotonic for every consumer that compares them
     *         (rings, explorers, the round feed). NO-WIPE redeploy, ruled
     *         2026-09-03. Past rounds' roots stay on the old plane:
     *         `roundRootAt` below the seed answers zero.
     * @dev Configuration authority (bootstrap admin before the seal, registrar
     *      quorum after), and only while this plane has published nothing.
     */
    function seedCounters(
        uint64[] calldata versions,
        uint64 round_,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _requireConfigurationAuthority(
            ACTION_SEED_COUNTERS, keccak256(abi.encode(versions, round_)), anchorBlock, approvals
        );
        if (versions.length != TREE_COUNT + 1) revert VersionCountMismatch(versions.length);
        if (round != 0) revert NotFresh();
        for (uint8 t = 1; t <= TREE_COUNT; t++) {
            if (treeVersion[t] != 0) revert NotFresh();
        }
        for (uint8 t = 1; t <= TREE_COUNT; t++) {
            treeVersion[t] = versions[t];
        }
        round = round_;
        emit CountersSeeded(round_, versions);
    }

    /// @notice Install the records contract that writes the typed trees.
    function setTypedWriter(
        address writer,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _requireConfigurationAuthority(
            ACTION_SET_TYPED_WRITER, keccak256(abi.encode(writer)), anchorBlock, approvals
        );
        typedWriter = writer;
        emit TypedWriterSet(writer);
    }

    /**
     * @notice Write configuration rows into a tree's branch 0.
     * @param treeId The tree whose owning service the rows configure.
     * @param keys `configKey(name, sub)` per row.
     * @param values One word per row — a duration, a count, an address, a
     *        flag; the reader knows the shape from the name.
     *
     * @dev The configuration authority, not the tree's writer or quorum: a
     * tree's writer states what its domain verified, its quorum attests to
     * what it read, and neither is the authority over how the service that
     * feeds it is configured. The value is stored beside the hash so a
     * contract on this chain reads it in one call (`configValue`) while the
     * same row is provable off chain against the round root — one source for
     * the fleet, the contracts and the explorer, where the fleet's environment
     * used to be a second one.
     */
    function setConfig(
        uint8 treeId,
        bytes32[] calldata keys,
        bytes32[] calldata values,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _assertTree(treeId);
        if (keys.length != values.length || keys.length == 0) revert LengthMismatch(keys.length, values.length);
        _requireConfigurationAuthority(
            ACTION_SET_CONFIG, keccak256(abi.encode(treeId, keys, values)), anchorBlock, approvals
        );
        for (uint256 i = 0; i < keys.length; i++) {
            _configValue[treeId][keys[i]] = values[i];
            _set(treeId, BRANCH_CONFIG, keys[i], configLeafHash(treeId, keys[i], values[i]));
            emit ConfigSet(treeId, keys[i], values[i]);
        }
        _bump(treeId, keys.length);
    }

    // ------------------------------------------------------------- writing

    /**
     * @notice Write leaves into one branch of one tree under a PQ quorum.
     * @param treeId Which tree.
     * @param branch Which branch — never 0, which `setConfig` alone writes.
     * @param keys Domain keys — a wallet address for accounts, an asset id for
     *        the allowlist, whatever identifies a row in that domain. Each gets
     *        a permanent slot in the branch on first write.
     * @param leaves The raw (untagged) leaf values.
     * @param approvals At least `threshold[treeId]` of them, ascending by signer.
     *
     * @dev The digest binds the tree, its nonce, and the full batch. Binding the
     * nonce is what stops the same approved batch being replayed: without it,
     * an approval to set a price is an approval to set that price again at any
     * later block, which for an oracle is the whole attack.
     *
     * ML-DSA-87 is required rather than accepted. These are operational,
     * high-cadence writes — the transaction class — and leaving the choice open
     * would mean a break in either scheme takes the tree.
     */
    function setLeaves(
        uint8 treeId,
        uint8 branch,
        bytes32[] calldata keys,
        bytes32[] calldata leaves,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        _assertTree(treeId);
        _assertDataBranch(treeId, branch);
        if (treeId == TREE_PHI || treeId == TREE_VASSET || treeId == TREE_ORACLE) {
            revert TypedTreeOnly(treeId);
        }
        // Trees 7 and 8 have their own rulers and NO quorum path at all: an
        // intent's status is what `FinalIntentLog` verified, an identity is
        // what the registry or the ledger verified, and no set of service
        // signatures can make a different answer true.
        if (treeId == TREE_INTENTS || treeId == TREE_IDENTITY) revert WriterOnlyTree(treeId);
        if (keys.length != leaves.length) revert LengthMismatch(keys.length, leaves.length);
        uint256 k = threshold[treeId];
        if (k == 0) revert TreeNotConfigured(treeId);

        uint64 n = nonce[treeId];
        FinalPqQuorum.require_(
            registry,
            approvals,
            FinalPqQuorum.digest(
                address(this),
                ACTION_SET_LEAVES,
                anchorBlock,
                keccak256(abi.encode(treeId, branch, n, keys, leaves))
            ),
            writerRole[treeId],
            k,
            FinalPqQuorum.ALG_ML_DSA_87,
            anchorBlock,
            false
        );
        nonce[treeId] = n + 1;

        for (uint256 i = 0; i < keys.length; i++) {
            _set(treeId, branch, keys[i], leaves[i]);
        }

        _bump(treeId, keys.length);
    }

    /// @notice One chain an account exists on, and as what.
    /// @dev `chainRef` is the registry's CAIP-derived chain reference — the one
    ///      identifier that names an EVM chain and a non-EVM one alike — and
    ///      `account` is the wallet's account there, in that chain's own account
    ///      space (an EVM address right-aligned, a 32-byte key filling the
    ///      width). Field-for-field with `IWalletTypes.ChainAccount`.
    struct ChainAccount {
        bytes32 chainRef;
        bytes32 account;
    }

    /// @notice `FinalWalletFactory.AccountStateLeaf`, field for field.
    struct AccountStateLeaf {
        address wallet;
        bytes32 liveAccess;
        bytes32 liveTransaction;
        bytes32 recoveryAccess;
        bytes32 recoveryTransaction;
        /// @dev Active-stage encapsulation commitment and its pre-committed
        /// successor. Field-for-field with `FinalWalletFactory.AccountStateLeaf`;
        /// a field added on one side and not the other is a root every execution
        /// chain rejects, with nothing pointing at the cause.
        bytes32 liveKem;
        bytes32 recoveryKem;
        address owner;
        bool pqEnabled;
        bool frozen;
        /// @dev The chains this account exists on, and its account on each —
        /// including chains whose accounts are not EVM addresses. Decided HERE
        /// (set by the holder through the ledger) and enforced there: an
        /// execution chain refuses to create the account unless the table has a
        /// row for it, and a settlement toward a chain with no row is refused at
        /// the source. This is what a zero beneficiary resolves through; it
        /// replaced a bitmask over registry slots that could only say "may
        /// exist", never "as what".
        ChainAccount[] deployedChains;
        /// @dev Per-chain dormancy verdict, one bit per asset-registry chain
        /// slot. Keeps the slot space the bitmask had.
        uint32 dormantChains;
        uint64 version;
    }

    /**
     * @notice Write account state into tree 1 from the typed leaf.
     * @dev The typed form exists so the leaf preimage is built HERE rather than
     * by whoever assembles the calldata. Tree 1 is the source of truth for every
     * other chain, and `syncAccountState` will accept any 32 bytes that carry a
     * valid proof — so if the publisher chose the preimage, the publisher could
     * write an account state that no wallet record on this chain agrees with,
     * and the proof would still verify everywhere.
     *
     * **Sealed.** Tree 1 is membership: a leaf here is who an account is, on
     * every chain. So the round takes the hybrid class — each approval carries
     * the ML-DSA-87 vote AND the member's SLH-DSA seal — where the other trees
     * take the transaction class alone. A lattice break rewrites a price; it
     * does not rewrite an account.
     */
    function setAccountStates(
        AccountStateLeaf[] calldata leaves,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        uint256 k = threshold[TREE_ACCOUNTS];
        if (k == 0) revert TreeNotConfigured(TREE_ACCOUNTS);

        bytes32[] memory keys = new bytes32[](leaves.length);
        bytes32[] memory hashes = new bytes32[](leaves.length);
        for (uint256 i = 0; i < leaves.length; i++) {
            keys[i] = accountKeyFor(leaves[i].wallet);
            hashes[i] = accountStateLeafHash(leaves[i]);
        }

        uint64 n = nonce[TREE_ACCOUNTS];
        FinalPqQuorum.require_(
            registry,
            approvals,
            FinalPqQuorum.digest(
                address(this),
                ACTION_SET_LEAVES,
                anchorBlock,
                keccak256(abi.encode(TREE_ACCOUNTS, n, keys, hashes))
            ),
            writerRole[TREE_ACCOUNTS],
            k,
            FinalPqQuorum.ALG_ML_DSA_87,
            anchorBlock,
            true
        );
        nonce[TREE_ACCOUNTS] = n + 1;

        for (uint256 i = 0; i < leaves.length; i++) {
            _set(TREE_ACCOUNTS, BRANCH_MAIN, keys[i], hashes[i]);
        }

        _bump(TREE_ACCOUNTS, leaves.length);
    }

    /**
     * @notice Write account state into tree 1 from the contract that owns it.
     * @dev No quorum, and no nonce burned: `treeWriter[1]` is the ledger, and
     * the ledger already verified the holder's own signature before it called
     * here. See {treeWriter} for why adding a service quorum on top would be a
     * censorship power rather than a safeguard.
     *
     * Typed, exactly as `setAccountStates` is: the preimage is built HERE, so
     * even the writer contract cannot publish a leaf whose meaning no record on
     * this chain agrees with.
     */
    function setAccountStatesAsWriter(AccountStateLeaf[] calldata leaves) external {
        if (msg.sender != treeWriter[TREE_ACCOUNTS]) revert NotAuthorized(msg.sender);
        for (uint256 i = 0; i < leaves.length; i++) {
            _set(TREE_ACCOUNTS, BRANCH_MAIN, accountKeyFor(leaves[i].wallet), accountStateLeafHash(leaves[i]));
        }
        _bump(TREE_ACCOUNTS, leaves.length);
    }

    /**
     * @notice Write raw leaves into any tree from the contract that owns it.
     * @dev The generic sibling of {setAccountStatesAsWriter}, for a tree whose
     * writer is a contract rather than a service quorum. Same authorization —
     * `treeWriter[treeId]` and nothing else — and the same reasoning: the
     * writer has already verified whatever its domain requires, and layering a
     * quorum on top of a contract's own rules is a censorship power rather
     * than a safeguard.
     *
     * UNTYPED, unlike the account path, and that is the trade. Tree 1's
     * preimage is built here so even the ledger cannot publish a leaf whose
     * meaning no record agrees with; a generic writer supplies its own hash,
     * so the leaf means whatever that contract says it means. Acceptable only
     * because the writer is a specific contract this chain's operators
     * installed — its rules are its bytecode, it has no owner and no proxy —
     * and NOT acceptable for a role-gated key. Point `treeWriter` at a
     * contract, never at an EOA.
     */
    function setLeavesAsWriter(uint8 treeId, uint8 branch, bytes32[] calldata keys, bytes32[] calldata leaves)
        external
    {
        if (msg.sender != treeWriter[treeId]) revert NotAuthorized(msg.sender);
        _assertDataBranch(treeId, branch);
        if (keys.length != leaves.length) revert LengthMismatch(keys.length, leaves.length);
        for (uint256 i = 0; i < keys.length; i++) {
            _set(treeId, branch, keys[i], leaves[i]);
        }
        _bump(treeId, keys.length);
    }

    /// @notice The leaf hash `FinalWalletFactory.accountStateLeafHash` computes.
    /// @dev Identical `abi.encode`, identical field order, identical domain.
    /// Pinned against the factory by test. `deployedChains` rides through
    /// `abi.encode` like every other field — head offset, then length and
    /// rows — so the table is committed whole and in order.
    function accountStateLeafHash(AccountStateLeaf memory leaf) public pure returns (bytes32) {
        _assertChainAccounts(leaf.deployedChains);
        return keccak256(
            abi.encode(
                DOMAIN_ACCOUNT_STATE_LEAF,
                leaf.wallet,
                leaf.liveAccess,
                leaf.liveTransaction,
                leaf.recoveryAccess,
                leaf.recoveryTransaction,
                leaf.liveKem,
                leaf.recoveryKem,
                leaf.owner,
                leaf.pqEnabled,
                leaf.frozen,
                leaf.deployedChains,
                leaf.dormantChains,
                leaf.version
            )
        );
    }

    /// @dev A well-formed table: no zero chain, no zero account, no chain twice.
    ///      Checked where the leaf is hashed so no door — quorum, writer
    ///      contract, identity projection — can publish a table a resolver
    ///      would read two ways.
    function _assertChainAccounts(ChainAccount[] memory rows) private pure {
        for (uint256 i = 0; i < rows.length; i++) {
            if (rows[i].chainRef == bytes32(0) || rows[i].account == bytes32(0)) {
                revert InvalidChainAccount(rows[i].chainRef, rows[i].account);
            }
            for (uint256 j = 0; j < i; j++) {
                if (rows[j].chainRef == rows[i].chainRef) {
                    revert InvalidChainAccount(rows[i].chainRef, rows[i].account);
                }
            }
        }
    }

    /// @notice The account `wallet`'s published table names on `chainRef`, or
    ///         zero if it has no row there.
    /// @dev A convenience over `accountStateLeafHash`'s input for readers on
    /// this chain; execution chains answer the same question from their synced
    /// record (`FinalWalletFactory.addressOn`).
    function accountOn(AccountStateLeaf memory leaf, bytes32 chainRef) public pure returns (bytes32) {
        for (uint256 i = 0; i < leaf.deployedChains.length; i++) {
            if (leaf.deployedChains[i].chainRef == chainRef) return leaf.deployedChains[i].account;
        }
        return bytes32(0);
    }

    /**
     * @notice The typed trees' write door — `FinalStateRecords` alone.
     * @dev The quorum, the nonce and the write, shared by every typed record.
     * The records contract computed the keys and hashes from the structs it
     * stores; this contract admits nobody else to trees 2, 3 and 4
     * (`setLeaves` refuses them), so the value there can never drift from
     * the commitment here.
     *
     * The digest is byte-identical to `setLeaves`' over the same keys and
     * hashes, deliberately: the typed entrypoints choose the PREIMAGE, not the
     * authorization. A member recomputes one digest whichever door the batch
     * came through, and there is no second approval shape to get wrong.
     */
    function writeTyped(
        uint8 treeId,
        bytes32[] memory keys,
        bytes32[] memory hashes,
        uint64 anchorBlock,
        FinalPqQuorum.Approval[] calldata approvals
    ) external {
        if (msg.sender != typedWriter) revert NotAuthorized(msg.sender);
        uint256 k = threshold[treeId];
        if (k == 0) revert TreeNotConfigured(treeId);

        uint64 n = nonce[treeId];
        FinalPqQuorum.require_(
            registry,
            approvals,
            FinalPqQuorum.digest(
                address(this),
                ACTION_SET_LEAVES,
                anchorBlock,
                keccak256(abi.encode(treeId, n, keys, hashes))
            ),
            writerRole[treeId],
            k,
            FinalPqQuorum.ALG_ML_DSA_87,
            anchorBlock,
            false
        );
        nonce[treeId] = n + 1;

        for (uint256 i = 0; i < keys.length; i++) {
            _set(treeId, BRANCH_MAIN, keys[i], hashes[i]);
        }

        _bump(treeId, keys.length);
    }

    /**
     * @notice Snapshot every tree's root into a new round.
     * @dev Permissionless, deliberately. Every root being snapshotted was
     * already authorized by its tree's quorum, so this adds no authority — it
     * only fixes a moment. Requiring a signature would put a liveness
     * dependency in front of publication for no security gain.
     *
     * A round that would change nothing is refused, so the round number cannot
     * be advanced by anyone with gas to spend.
     */
    function publishRound() external returns (uint64 published) {
        bool changed;
        for (uint8 t = 1; t <= TREE_COUNT; t++) {
            if (treeVersion[t] != _publishedVersion[t]) {
                changed = true;
                break;
            }
        }
        if (!changed) revert NothingToPublish();

        published = round + 1;
        Round storage r = _rounds[published];
        for (uint8 t = 1; t <= TREE_COUNT; t++) {
            r.roots[t] = liveRoot[t];
            _publishedVersion[t] = treeVersion[t];
        }
        r.roundRoot = _foldForest(_forestLeaves(r.roots));
        r.blockNumber = uint64(block.number);
        // MILLISECONDS, like every instant on this chain.
        r.timestamp = FinalChainTime.nowMs();
        round = published;
        emit RoundPublished(published, r.blockNumber, r.timestamp);
    }

    // ---------------------------------------------------------------- views

    /// @notice Every root from one round. Index by the `TREE_*` constants;
    /// index 0 is unused.
    function rootsAt(uint64 which) external view returns (bytes32[TREE_COUNT + 1] memory) {
        return _rounds[which].roots;
    }

    /// @notice One tree's root at one round.
    function rootAt(uint64 which, uint8 treeId) external view returns (bytes32) {
        _assertTree(treeId);
        return _rounds[which].roots[treeId];
    }

    /// @notice The one word that commits to every tree at one round.
    function roundRootAt(uint64 which) external view returns (bytes32) {
        return _rounds[which].roundRoot;
    }

    /**
     * @notice The `FOREST_BITS` siblings that take a tree's root at one round
     *         up to that round's root — appended to `proofFor`, they make a
     *         leaf provable against `roundRootAt(which)` by the same verifier.
     */
    function roundProofFor(uint64 which, uint8 treeId) external view returns (bytes32[] memory path) {
        _assertTree(treeId);
        if (which == 0 || which > round) revert NoRounds();
        bytes32[] memory level = _forestLeaves(_rounds[which].roots);
        path = new bytes32[](FOREST_BITS);
        uint256 idx = treeId;
        uint256 n = level.length;
        for (uint256 l = 0; l < FOREST_BITS; l++) {
            path[l] = level[idx ^ 1];
            n >>= 1;
            for (uint256 i = 0; i < n; i++) {
                level[i] = _pair(level[2 * i], level[2 * i + 1]);
            }
            idx >>= 1;
        }
    }

    /// @notice The latest round's roots, with the block it was taken at.
    function latestRound()
        external
        view
        returns (uint64 which, bytes32[TREE_COUNT + 1] memory roots, uint64 blockNumber, uint64 timestamp)
    {
        which = round;
        if (which == 0) revert NoRounds();
        Round storage r = _rounds[which];
        return (which, r.roots, r.blockNumber, r.timestamp);
    }

    /// @notice The raw leaf stored for a key, and whether it has a slot.
    function leafOf(uint8 treeId, bytes32 key) external view returns (bytes32 leaf, bool present) {
        uint256 s = _slotPlusOne[treeId][key];
        if (s == 0) return (bytes32(0), false);
        return (_leaf[treeId][s - 1], true);
    }

    /// @notice The permanent slot for a key. Reverts if it has none. The
    /// slot's top `BRANCH_BITS` are its branch.
    function slotOf(uint8 treeId, bytes32 key) public view returns (uint256) {
        uint256 s = _slotPlusOne[treeId][key];
        if (s == 0) revert UnknownKey(treeId, key);
        return s - 1;
    }

    /// @notice The key a slot was handed to, or zero if it is still free —
    /// the enumeration every branch offers: slots `branch << BRANCH_DEPTH`
    /// through `+ branchSlotsUsed(treeId, branch) - 1`.
    function keyAt(uint8 treeId, uint256 slot) external view returns (bytes32) {
        return _keyAt[treeId][slot];
    }

    /// @notice Slots handed out in one branch.
    function branchSlotsUsed(uint8 treeId, uint8 branch) external view returns (uint256) {
        return _branchSlotsUsed[treeId][branch];
    }

    /// @notice One branch's root: the level-`BRANCH_DEPTH` node at its position.
    function branchRoot(uint8 treeId, uint8 branch) external view returns (bytes32) {
        _assertTree(treeId);
        _assertBranch(branch);
        return _nodeAt(treeId, BRANCH_DEPTH, branch);
    }

    /// @notice The first `BRANCH_DEPTH` siblings of `proofFor` — a proof
    /// against the leaf's branch root rather than the tree root.
    function branchProofFor(uint8 treeId, bytes32 key) external view returns (bytes32[] memory) {
        _assertTree(treeId);
        return _path(treeId, slotOf(treeId, key), BRANCH_DEPTH);
    }

    /// @notice A configuration row's value, and whether the row exists.
    function configValue(uint8 treeId, bytes32 key) external view returns (bytes32 value, bool present) {
        present = _slotPlusOne[treeId][key] != 0;
        value = _configValue[treeId][key];
    }

    /// @notice The branch-0 key of a configuration row: a name the owning
    /// service defines, and a sub-key (a chain reference, an asset, zero).
    function configKey(bytes32 name, bytes32 sub) public pure returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_CONFIG_KEY, name, sub));
    }

    /// @notice The leaf a configuration row hashes to.
    function configLeafHash(uint8 treeId, bytes32 key, bytes32 value) public pure returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_CONFIG_LEAF, treeId, key, value));
    }

    /// @notice The tree-8 branch-2 key an owner occupies.
    function ownerIndexKeyFor(address owner) public pure returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_OWNER_INDEX_KEY, owner));
    }

    /// @notice The owner-index leaf: a commitment to the ledger's ordered
    /// `walletsByOwner(owner)`.
    function ownerIndexLeafHash(address owner, address[] memory wallets) public pure returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_OWNER_INDEX_LEAF, owner, wallets));
    }

    /// @notice The tree-8 branch-3 key of one member's slot — a ring position.
    function slotKeyFor(address member, uint64 slotIndex) public pure returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_SLOT_KEY, member, slotIndex % SLOT_KEY_RING));
    }

    /**
     * @notice Project slot keys into tree 8's branch 3 — the co-signers'
     *         per-slot KEM publics the private option seals to.
     * @dev Permissionless, for {syncIdentityLeaves}' reason: the leaf VALUE
     * is `slotKeySource`'s own verdict (the registry verified the member's
     * signature when the key was published, and answers zero once the slot's
     * window has passed), so this adds no authority and only projects. The
     * registry calls it same-tx on publication; anyone may call it to retire a
     * slot that lapsed by time.
     */
    function syncSlotKeyLeaves(address member, uint64[] calldata slotIndexes) external {
        address source = slotKeySource;
        if (source == address(0)) revert SlotKeySourceUnset();
        for (uint256 i = 0; i < slotIndexes.length; i++) {
            _set(
                TREE_IDENTITY,
                BRANCH_SLOT_KEYS,
                slotKeyFor(member, slotIndexes[i]),
                ISlotKeySource(source).slotKeyLeafOf(member, slotIndexes[i])
            );
        }
        _bump(TREE_IDENTITY, slotIndexes.length);
    }

    /// @notice The tree-8 branch-4 key of one tunnel endpoint.
    function endpointKeyFor(bytes32 endpointId) public pure returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_ENDPOINT_KEY, endpointId));
    }

    /**
     * @notice Project tunnel endpoints into tree 8's branch 4.
     * @dev Permissionless, for {syncSlotKeyLeaves}' reason: the leaf VALUE is
     * `endpointSource`'s own verdict — the registry admitted the certificate
     * under the registrar quorum with the holder's proof of possession, and
     * answers the revoked status once it is revoked — so this adds no authority
     * and only projects. The registry calls it same-tx on registration and
     * revocation; anyone may call it to re-project.
     */
    function syncEndpointLeaves(bytes32[] calldata endpointIds) external {
        address source = endpointSource;
        if (source == address(0)) revert EndpointSourceUnset();
        for (uint256 i = 0; i < endpointIds.length; i++) {
            _set(
                TREE_IDENTITY,
                BRANCH_ENDPOINTS,
                endpointKeyFor(endpointIds[i]),
                IEndpointSource(source).endpointLeafOf(endpointIds[i])
            );
        }
        _bump(TREE_IDENTITY, endpointIds.length);
    }

    /**
     * @notice The sibling path for a key, ready for
     *         `FinalMerkle.verifyTaggedSortedProof` on any chain.
     * @dev A view, so the backend fetches a proof with one `eth_call` instead of
     * rebuilding the tree off chain. Rebuilding is where a divergence between
     * what the chain holds and what a service believes it holds would come
     * from, and this removes the second implementation entirely.
     */
    function proofFor(uint8 treeId, bytes32 key) external view returns (bytes32[] memory) {
        _assertTree(treeId);
        return _path(treeId, slotOf(treeId, key), DEPTH);
    }

    /// @notice The empty-subtree hash at a level. Level `DEPTH` is the root of
    /// a tree with nothing in it.
    function emptyRoot(uint256 level) external view returns (bytes32) {
        return _zero[level];
    }

    /// @notice The tree-1 key a wallet occupies.
    function accountKeyFor(address wallet) public pure returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_ACCOUNT_KEY, wallet));
    }

    /**
     * @notice Copy a registered identity into tree 1 as an account-state leaf.
     * @dev Services are Final Wallets, so a service's leaf is the SAME leaf a
     * user's wallet gets — `FinalWalletFactory.AccountStateLeaf`, four key
     * commitments and all. There is no second shape and no second domain,
     * which is what lets every chain that already consumes account state
     * consume a co-signer's identity with no contract change.
     *
     * `owner` is the account itself: a service wallet is its own owner, having
     * no separate holder to speak for it.
     *
     * Permissionless, and for the same reason `publishRound` is: every fact it
     * writes was already authorized when it entered the registry, so this adds
     * no authority and only projects. Gating it would put a liveness dependency
     * in front of publishing a revocation, which is the one thing that must
     * never wait.
     */
    function syncIdentities(address[] calldata accounts) external {
        // One table for the batch: a service is its own canonical address on
        // every enabled chain, so the rows differ only in `account`.
        bytes32[] memory chainRefs = _enabledChainRefs();
        for (uint256 i = 0; i < accounts.length; i++) {
            address who = accounts[i];
            FinalIdentityRegistry.Identity memory id = registry.identityOf(who);
            if (!id.registered) revert UnknownKey(TREE_ACCOUNTS, accountKeyFor(who));
            (bytes32 la, bytes32 lt, bytes32 ra, bytes32 rt) = registry.keyCommitments(who);
            (bytes32 lk, bytes32 rk) = registry.kemCommitments(who);
            ChainAccount[] memory table = new ChainAccount[](chainRefs.length);
            for (uint256 c = 0; c < chainRefs.length; c++) {
                table[c] = ChainAccount({chainRef: chainRefs[c], account: bytes32(uint256(uint160(who)))});
            }
            AccountStateLeaf memory leaf = AccountStateLeaf({
                wallet: who,
                liveAccess: la,
                liveTransaction: lt,
                recoveryAccess: ra,
                recoveryTransaction: rt,
                liveKem: lk,
                recoveryKem: rk,
                // A service reaches every chain the registry has enabled, at
                // its own address, and is never dormant: dormancy measures an
                // ABSENT holder, and these identities have no holder to be
                // absent.
                deployedChains: table,
                dormantChains: 0,
                owner: who,
                // Every identity here is PQ by construction — there is no other
                // kind of key in this registry.
                pqEnabled: true,
                // Revocation is a leaf that CHANGES, not one that disappears.
                // A consumer holding an old proof gets a stale `false`, which is
                // why the round is the thing to pin.
                frozen: id.revoked,
                version: id.version
            });
            _set(TREE_ACCOUNTS, BRANCH_MAIN, accountKeyFor(who), accountStateLeafHash(leaf));
        }
        _bump(TREE_ACCOUNTS, accounts.length);
    }

    /// @notice The tree-8 slot key an identity occupies.
    function identityKeyFor(address account) public pure returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_IDENTITY_TREE_KEY, account));
    }

    /**
     * @notice Project identities into tree 8 — the wallet-creation admission
     *         set whose live root every execution chain anchors as its
     *         `currentIdentityRoot`.
     *
     * @dev The leaf VALUE is the registry's own verdict —
     * `FinalIdentityRegistry.identityTreeLeafOf`: the execution chains'
     * identity leaf while the identity stands, zero once it does not. Derived
     * there rather than here because every input (serial, the six key
     * commitments, standing, the CA depth pair) is registry storage, and this
     * contract sits against EIP-170 while the registry does not.
     *
     * Permissionless, for exactly {syncIdentities}' reason: every fact
     * written here was authorized when it entered the registry, so this adds
     * no authority and only projects. The registry itself calls it same-tx on
     * every identity mutation (register, rotate, roles, revoke, LMS-key ops),
     * which is what makes the root CONTINUOUS; the open door additionally lets
     * anyone retire a leaf whose standing lapsed by TIME — expiry moves no
     * registry storage, so no mutation hook can ever fire for it.
     *
     * There is no quorum door and no writer seat (both raw doors refuse this
     * tree), so the strongest thing any caller can do here is copy the
     * registry's own verdict.
     */
    function syncIdentityLeaves(address[] calldata accounts) external {
        for (uint256 i = 0; i < accounts.length; i++) {
            _set(TREE_IDENTITY, BRANCH_MAIN, identityKeyFor(accounts[i]), registry.identityTreeLeafOf(accounts[i]));
        }
        _bump(TREE_IDENTITY, accounts.length);
    }

    /**
     * @notice Per-tree quorum health: can each configured tree still be written?
     * @dev A threshold above the live member count is not a strict quorum, it is
     * a tree that reverts forever with nothing naming the roster as the cause.
     * `configureTree` refuses to create that state, but revocation can arrive at
     * it later — revocation must never be blocked on quorum arithmetic, so the
     * check has to be something monitoring reads rather than something the
     * contract enforces after the fact.
     */
    function quorumHealth()
        external
        view
        returns (uint256[] memory live, uint256[] memory required, bool[] memory ok)
    {
        live = new uint256[](TREE_COUNT + 1);
        required = new uint256[](TREE_COUNT + 1);
        ok = new bool[](TREE_COUNT + 1);
        for (uint8 t = 1; t <= TREE_COUNT; t++) {
            required[t] = threshold[t];
            live[t] = required[t] == 0 ? 0 : registry.liveMemberCount(writerRole[t]);
            ok[t] = required[t] == 0 || live[t] >= required[t];
        }
    }

    // -------------------------------------------------------------- internal

    /// @dev The enabled chain references `chainSource` knows, or none if it is
    ///      unset. Read through the narrow interface so this contract need not
    ///      import the registry that imports it.
    function _enabledChainRefs() private view returns (bytes32[] memory) {
        address source = chainSource;
        if (source == address(0)) return new bytes32[](0);
        return IChainSource(source).enabledChainRefs();
    }

    function _assertTree(uint8 treeId) private pure {
        if (treeId == 0 || treeId > TREE_COUNT) revert UnknownTree(treeId);
    }

    function _assertBranch(uint8 branch) private pure {
        if (branch >= BRANCH_COUNT) revert UnknownBranch(branch);
    }

    /// @dev A branch a quorum or a writer may write: any but the config branch.
    function _assertDataBranch(uint8 treeId, uint8 branch) private pure {
        _assertBranch(branch);
        if (branch == BRANCH_CONFIG) revert ConfigBranchReserved(treeId);
    }

    /// @dev Version + event, the tail of every write door.
    function _bump(uint8 treeId, uint256 count) private {
        uint64 v = treeVersion[treeId] + 1;
        treeVersion[treeId] = v;
        emit LeavesSet(treeId, count, liveRoot[treeId], v);
    }

    /// @dev `keccak256(0x01 ‖ lo ‖ hi)`, the pair sorted — the one node hash.
    function _pair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        (bytes32 lo, bytes32 hi) = a < b ? (a, b) : (b, a);
        return keccak256(abi.encodePacked(bytes1(0x01), lo, hi));
    }

    /// @dev The sibling path from a slot up `height` levels.
    function _path(uint8 treeId, uint256 idx, uint256 height) private view returns (bytes32[] memory path) {
        path = new bytes32[](height);
        for (uint256 l = 0; l < height; l++) {
            path[l] = _nodeAt(treeId, l, idx ^ 1);
            idx >>= 1;
        }
    }

    /// @dev The forest's leaves: the tree roots at their positions, the
    ///      empty tree at the rest.
    function _forestLeaves(bytes32[TREE_COUNT + 1] memory roots) private view returns (bytes32[] memory level) {
        level = new bytes32[](1 << FOREST_BITS);
        for (uint256 p = 0; p < level.length; p++) {
            level[p] = (p >= 1 && p <= TREE_COUNT) ? roots[p] : _zero[DEPTH];
        }
    }

    /// @dev Fold a power-of-two level to its root, in place.
    function _foldForest(bytes32[] memory level) private pure returns (bytes32) {
        for (uint256 n = level.length; n > 1; n >>= 1) {
            for (uint256 i = 0; i < n / 2; i++) {
                level[i] = _pair(level[2 * i], level[2 * i + 1]);
            }
        }
        return level[0];
    }

    function _set(uint8 treeId, uint8 branch, bytes32 key, bytes32 leaf) private {
        uint256 s = _slotPlusOne[treeId][key];
        uint256 idx;
        if (s == 0) {
            uint256 used = _branchSlotsUsed[treeId][branch];
            if (used >= BRANCH_CAPACITY) revert BranchFull(treeId, branch);
            idx = (uint256(branch) << BRANCH_DEPTH) | used;
            _branchSlotsUsed[treeId][branch] = used + 1;
            slotsUsed[treeId] += 1;
            _slotPlusOne[treeId][key] = idx + 1;
            _keyAt[treeId][idx] = key;
        } else {
            idx = s - 1;
            uint8 have = uint8(idx >> BRANCH_DEPTH);
            if (have != branch) revert BranchMismatch(treeId, key, have, branch);
        }

        _leaf[treeId][idx] = leaf;

        bytes32 cursor = keccak256(abi.encodePacked(bytes1(0x00), leaf));
        for (uint256 l = 0; l < DEPTH; l++) {
            cursor = _pair(cursor, _nodeAt(treeId, l, idx ^ 1));
            idx >>= 1;
            _node[treeId][l + 1][idx] = cursor;
        }
        liveRoot[treeId] = cursor;
    }

    /// @dev Level 0 is derived from the leaf store rather than duplicated into
    /// `_node`, so there is one place a leaf lives and no way for the two to
    /// disagree. Unset positions fall through to the empty-subtree hash.
    function _nodeAt(uint8 treeId, uint256 level, uint256 index) private view returns (bytes32) {
        if (level == 0) {
            return keccak256(abi.encodePacked(bytes1(0x00), _leaf[treeId][index]));
        }
        bytes32 v = _node[treeId][level][index];
        return v == bytes32(0) ? _zero[level] : v;
    }
}

abi

[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "registry_",
        "type": "address",
        "internalType": "contract FinalIdentityRegistry"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "ACTION_CONFIGURE_TREE",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ACTION_SEED_COUNTERS",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ACTION_SET_CHAIN_SOURCE",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ACTION_SET_CONFIG",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ACTION_SET_ENDPOINT_SOURCE",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ACTION_SET_SLOT_KEY_SOURCE",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ACTION_SET_TREE_WRITER",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ACTION_SET_TYPED_WRITER",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_BITS",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_CAPACITY",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_CONFIG",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_COUNT",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_DEPTH",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_ENDPOINTS",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_MAIN",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_OWNER_INDEX",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BRANCH_SLOT_KEYS",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "CAPACITY",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "DEPTH",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "DOMAIN_ACCOUNT_STATE_LEAF",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "DOMAIN_CONFIG_LEAF",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "DOMAIN_OWNER_INDEX_LEAF",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "FOREST_BITS",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ROUND_DEPTH",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "SLOT_KEY_RING",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_ACCOUNTS",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_ALLOWLIST",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_COUNT",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_IDENTITY",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_INTENTS",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_ORACLE",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_PHI",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_SETTLEMENT",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "TREE_VASSET",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "accountKeyFor",
    "inputs": [
      {
        "name": "wallet",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "accountOn",
    "inputs": [
      {
        "name": "leaf",
        "type": "tuple",
        "internalType": "struct FinalStateTrees.AccountStateLeaf",
        "components": [
          {
            "name": "wallet",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "liveAccess",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "liveTransaction",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryAccess",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryTransaction",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "liveKem",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryKem",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "owner",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "pqEnabled",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "frozen",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "deployedChains",
            "type": "tuple[]",
            "internalType": "struct FinalStateTrees.ChainAccount[]",
            "components": [
              {
                "name": "chainRef",
                "type": "bytes32",
                "internalType": "bytes32"
              },
              {
                "name": "account",
                "type": "bytes32",
                "internalType": "bytes32"
              }
            ]
          },
          {
            "name": "dormantChains",
            "type": "uint32",
            "internalType": "uint32"
          },
          {
            "name": "version",
            "type": "uint64",
            "internalType": "uint64"
          }
        ]
      },
      {
        "name": "chainRef",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "accountStateLeafHash",
    "inputs": [
      {
        "name": "leaf",
        "type": "tuple",
        "internalType": "struct FinalStateTrees.AccountStateLeaf",
        "components": [
          {
            "name": "wallet",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "liveAccess",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "liveTransaction",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryAccess",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryTransaction",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "liveKem",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryKem",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "owner",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "pqEnabled",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "frozen",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "deployedChains",
            "type": "tuple[]",
            "internalType": "struct FinalStateTrees.ChainAccount[]",
            "components": [
              {
                "name": "chainRef",
                "type": "bytes32",
                "internalType": "bytes32"
              },
              {
                "name": "account",
                "type": "bytes32",
                "internalType": "bytes32"
              }
            ]
          },
          {
            "name": "dormantChains",
            "type": "uint32",
            "internalType": "uint32"
          },
          {
            "name": "version",
            "type": "uint64",
            "internalType": "uint64"
          }
        ]
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "branchProofFor",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "branchRoot",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "branch",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "branchSlotsUsed",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "branch",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "chainSource",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "configKey",
    "inputs": [
      {
        "name": "name",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "sub",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "configLeafHash",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "value",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "configValue",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "value",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "present",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "configureTree",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "role",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "k",
        "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": "emptyRoot",
    "inputs": [
      {
        "name": "level",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "endpointKeyFor",
    "inputs": [
      {
        "name": "endpointId",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "endpointSource",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "identityKeyFor",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "keyAt",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "slot",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "latestRound",
    "inputs": [],
    "outputs": [
      {
        "name": "which",
        "type": "uint64",
        "internalType": "uint64"
      },
      {
        "name": "roots",
        "type": "bytes32[9]",
        "internalType": "bytes32[9]"
      },
      {
        "name": "blockNumber",
        "type": "uint64",
        "internalType": "uint64"
      },
      {
        "name": "timestamp",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "leafOf",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "leaf",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "present",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "liveRoot",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "nonce",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ownerIndexKeyFor",
    "inputs": [
      {
        "name": "owner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "ownerIndexLeafHash",
    "inputs": [
      {
        "name": "owner",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "wallets",
        "type": "address[]",
        "internalType": "address[]"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "proofFor",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "publishRound",
    "inputs": [],
    "outputs": [
      {
        "name": "published",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "quorumHealth",
    "inputs": [],
    "outputs": [
      {
        "name": "live",
        "type": "uint256[]",
        "internalType": "uint256[]"
      },
      {
        "name": "required",
        "type": "uint256[]",
        "internalType": "uint256[]"
      },
      {
        "name": "ok",
        "type": "bool[]",
        "internalType": "bool[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "registry",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract FinalIdentityRegistry"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "rootAt",
    "inputs": [
      {
        "name": "which",
        "type": "uint64",
        "internalType": "uint64"
      },
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "rootsAt",
    "inputs": [
      {
        "name": "which",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32[9]",
        "internalType": "bytes32[9]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "round",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "roundProofFor",
    "inputs": [
      {
        "name": "which",
        "type": "uint64",
        "internalType": "uint64"
      },
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "path",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "roundRootAt",
    "inputs": [
      {
        "name": "which",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "seedCounters",
    "inputs": [
      {
        "name": "versions",
        "type": "uint64[]",
        "internalType": "uint64[]"
      },
      {
        "name": "round_",
        "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": "setAccountStates",
    "inputs": [
      {
        "name": "leaves",
        "type": "tuple[]",
        "internalType": "struct FinalStateTrees.AccountStateLeaf[]",
        "components": [
          {
            "name": "wallet",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "liveAccess",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "liveTransaction",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryAccess",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryTransaction",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "liveKem",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryKem",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "owner",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "pqEnabled",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "frozen",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "deployedChains",
            "type": "tuple[]",
            "internalType": "struct FinalStateTrees.ChainAccount[]",
            "components": [
              {
                "name": "chainRef",
                "type": "bytes32",
                "internalType": "bytes32"
              },
              {
                "name": "account",
                "type": "bytes32",
                "internalType": "bytes32"
              }
            ]
          },
          {
            "name": "dormantChains",
            "type": "uint32",
            "internalType": "uint32"
          },
          {
            "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": "setAccountStatesAsWriter",
    "inputs": [
      {
        "name": "leaves",
        "type": "tuple[]",
        "internalType": "struct FinalStateTrees.AccountStateLeaf[]",
        "components": [
          {
            "name": "wallet",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "liveAccess",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "liveTransaction",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryAccess",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryTransaction",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "liveKem",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "recoveryKem",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "owner",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "pqEnabled",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "frozen",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "deployedChains",
            "type": "tuple[]",
            "internalType": "struct FinalStateTrees.ChainAccount[]",
            "components": [
              {
                "name": "chainRef",
                "type": "bytes32",
                "internalType": "bytes32"
              },
              {
                "name": "account",
                "type": "bytes32",
                "internalType": "bytes32"
              }
            ]
          },
          {
            "name": "dormantChains",
            "type": "uint32",
            "internalType": "uint32"
          },
          {
            "name": "version",
            "type": "uint64",
            "internalType": "uint64"
          }
        ]
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "setChainSource",
    "inputs": [
      {
        "name": "source",
        "type": "address",
        "internalType": "address"
      },
      {
        "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": "setConfig",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "keys",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      },
      {
        "name": "values",
        "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": "setEndpointSource",
    "inputs": [
      {
        "name": "source",
        "type": "address",
        "internalType": "address"
      },
      {
        "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": "setLeaves",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "branch",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "keys",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      },
      {
        "name": "leaves",
        "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": "setLeavesAsWriter",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "branch",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "keys",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      },
      {
        "name": "leaves",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "setSlotKeySource",
    "inputs": [
      {
        "name": "source",
        "type": "address",
        "internalType": "address"
      },
      {
        "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": "setTreeWriter",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "writer",
        "type": "address",
        "internalType": "address"
      },
      {
        "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": "setTypedWriter",
    "inputs": [
      {
        "name": "writer",
        "type": "address",
        "internalType": "address"
      },
      {
        "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": "slotKeyFor",
    "inputs": [
      {
        "name": "member",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "slotIndex",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "type": "function",
    "name": "slotKeySource",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "slotOf",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "slotsUsed",
    "inputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "syncEndpointLeaves",
    "inputs": [
      {
        "name": "endpointIds",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "syncIdentities",
    "inputs": [
      {
        "name": "accounts",
        "type": "address[]",
        "internalType": "address[]"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "syncIdentityLeaves",
    "inputs": [
      {
        "name": "accounts",
        "type": "address[]",
        "internalType": "address[]"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "syncSlotKeyLeaves",
    "inputs": [
      {
        "name": "member",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "slotIndexes",
        "type": "uint64[]",
        "internalType": "uint64[]"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "threshold",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "treeVersion",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint64",
        "internalType": "uint64"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "treeWriter",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "typedWriter",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "writeTyped",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "keys",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      },
      {
        "name": "hashes",
        "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": "writerRole",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "event",
    "name": "ChainSourceSet",
    "inputs": [
      {
        "name": "source",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "ConfigSet",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "indexed": true,
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      },
      {
        "name": "value",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "CountersSeeded",
    "inputs": [
      {
        "name": "round",
        "type": "uint64",
        "indexed": false,
        "internalType": "uint64"
      },
      {
        "name": "versions",
        "type": "uint64[]",
        "indexed": false,
        "internalType": "uint64[]"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "EndpointSourceSet",
    "inputs": [
      {
        "name": "source",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "LeavesSet",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "indexed": true,
        "internalType": "uint8"
      },
      {
        "name": "count",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "newRoot",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      },
      {
        "name": "treeVersion",
        "type": "uint64",
        "indexed": false,
        "internalType": "uint64"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "RoundPublished",
    "inputs": [
      {
        "name": "round",
        "type": "uint64",
        "indexed": true,
        "internalType": "uint64"
      },
      {
        "name": "blockNumber",
        "type": "uint64",
        "indexed": false,
        "internalType": "uint64"
      },
      {
        "name": "timestamp",
        "type": "uint64",
        "indexed": false,
        "internalType": "uint64"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "SlotKeySourceSet",
    "inputs": [
      {
        "name": "source",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "TreeConfigured",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "indexed": true,
        "internalType": "uint8"
      },
      {
        "name": "writerRole",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "threshold",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "TreeWriterSet",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "indexed": true,
        "internalType": "uint8"
      },
      {
        "name": "writer",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "TypedWriterSet",
    "inputs": [
      {
        "name": "writer",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "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": "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": "BranchFull",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "branch",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  },
  {
    "type": "error",
    "name": "BranchMismatch",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "have",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "want",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  },
  {
    "type": "error",
    "name": "ConfigBranchReserved",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  },
  {
    "type": "error",
    "name": "EndpointSourceUnset",
    "inputs": []
  },
  {
    "type": "error",
    "name": "InvalidChainAccount",
    "inputs": [
      {
        "name": "chainRef",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "account",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ]
  },
  {
    "type": "error",
    "name": "LengthMismatch",
    "inputs": [
      {
        "name": "keys",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "leaves",
        "type": "uint256",
        "internalType": "uint256"
      }
    ]
  },
  {
    "type": "error",
    "name": "NoRounds",
    "inputs": []
  },
  {
    "type": "error",
    "name": "NotAuthorized",
    "inputs": [
      {
        "name": "caller",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "type": "error",
    "name": "NotFresh",
    "inputs": []
  },
  {
    "type": "error",
    "name": "NothingToPublish",
    "inputs": []
  },
  {
    "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": "SlotKeySourceUnset",
    "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": "ThresholdUnreachable",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "live",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "required",
        "type": "uint256",
        "internalType": "uint256"
      }
    ]
  },
  {
    "type": "error",
    "name": "TreeNotConfigured",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  },
  {
    "type": "error",
    "name": "TypedTreeOnly",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  },
  {
    "type": "error",
    "name": "UnknownBranch",
    "inputs": [
      {
        "name": "branch",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  },
  {
    "type": "error",
    "name": "UnknownKey",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "key",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ]
  },
  {
    "type": "error",
    "name": "UnknownTree",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  },
  {
    "type": "error",
    "name": "VersionCountMismatch",
    "inputs": [
      {
        "name": "given",
        "type": "uint256",
        "internalType": "uint256"
      }
    ]
  },
  {
    "type": "error",
    "name": "WriterOnlyTree",
    "inputs": [
      {
        "name": "treeId",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  },
  {
    "type": "error",
    "name": "WrongAlgorithm",
    "inputs": [
      {
        "name": "signer",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "got",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "required",
        "type": "uint8",
        "internalType": "uint8"
      }
    ]
  }
]

čtení kontraktu

bytecode · 26,167 bajtů

0x60806040526004361015610011575f80fd5b5f3560e01c806301e8a3a71461362957806302ffb43a146135ac57806306ff9a011461324857806307465c211461320557806308699369146131ca5780630acd12b51461314d5780630b543248146131135780630dc85691146130f857806310229c9b146130be57806311cb42a614613084578063146ca5311461305e578063158f59b614613023578063159735ac14612fe9578063177b99da14612fc25780631bc6dca714612f915780631c37f8be14612be65780631cb78cf614612b835780631ed23e2514612b455780631ff3149514612b2a5780632198527014612af05780632211d9eb14612a73578063227cab8714612995578063227d5d7a1461261557806324a47334146125ed57806325030f27146125c55780632737f33e146124a5578063361792081461246b578063389023001461244d5780633fe9eec5146123d0578063427fbf5a146123b257806345763154146123945780634d686726146121a15780634e4d143c146121635780634e74a4041461211b578063563a6c071461210057806357c45d9c146120d85780635807879d1461209e578063614a5c3414612067578063668a0f0214611fe45780636a404030146103f75780636b6a26821461104f5780636d2e5ca014611faa5780637203b80714611b8d57806376e058a81461078d57806379fee94a1461104f5780637a9730d714611b535780637b10399914611b0f5780637d211cba1461197f57806380d78ea914611709578063825c45cd146116d757806382edfbd9146116b457806387a2d8ae1461167a57806387bba45c1461162357806387c283451461160857806389106905146115625780638c3712dc1461104f5780638ca9a268146115095780638e808c09146114de578063915ed0bf146114b657806398366e351461149b5780639ca3c9c4146113e7578063a12af04d14611054578063a2f2d26a1461104f578063a7b0e4d01461101a578063ab50578e14610e4e578063abf1570d14610d53578063ac81af2f14610cf7578063ae1be12814610cbd578063b31ec19e14610b7e578063b563586714610b44578063b7e7e92e14610b3f578063bab8378214610b3f578063c034768614610b22578063c161552b1461090d578063c1dac0711461070e578063c2b9f48e146108a6578063c5eeeb391461088b578063cf3add5d14610859578063d2430e0e146107ad578063d5284ee314610792578063d8dea2bf1461078d578063e0236d3714610759578063e0b180f71461073f578063e9750fcf14610713578063eaff2dda1461070e578063eb75174b146106f2578063ec0bb2491461046d578063f501cff31461043b578063f5951975146103fc5763fd1d0819146103f7575f80fd5b613d9a565b346104375760203660031901126104375760ff610417613ad7565b165f52602b60205260206001600160401b0360405f205416604051908152f35b5f80fd5b346104375760203660031901126104375760ff610456613ad7565b165f526023602052602060405f2054604051908152f35b34610437575f366003190112610437575f60015b60ff8116600881116106eb57805f5260276020526001600160401b0360405f205416905f52602a6020526001600160401b0360405f205416036104cc576104c790613feb565b610481565b505060015b156106dc576001600160401b036104eb8160295416613e52565b16805f52602860205260405f209060015b60ff8116906008821161057e5781610579925f52602660205260405f20546105248387614120565b819291549060031b91821b915f19901b1916179055805f5260276020526001600160401b0360405f205416905f52602a6020526001600160401b0360405f2091166001600160401b0319825416179055613feb565b6104fc565b505061059161058c8361404d565b61612d565b9182515b6001811161065e575082511561064a577f5a33796ad97fb8cf3289d33093f3deb1797b5bcf1e795e716194046784ca4d3c6040600a84936020809701516009820155016001600160401b03804316166001600160401b0319825416178155805467ffffffffffffffff60401b42841b169067ffffffffffffffff60401b1916178155836001600160401b03196029541617602955546001600160401b038251918181168352831c1686820152a2604051908152f35b634e487b7160e01b5f52603260045260245ffd5b5f5b8160011c8110610673575060011c610595565b600181901b906001600160ff1b03811681036106c8576106938287613e70565b5191600181018091116106c8576001926106b06106b79289613e70565b51906161a9565b6106c18288613e70565b5201610660565b634e487b7160e01b5f52601160045260245ffd5b630eba0e1b60e21b5f5260045ffd5b50506104d1565b34610437575f3660031901126104375760206040516104008152f35b613e13565b3461043757602036600319011261043757600435601d81101561064a5760209060040154604051908152f35b34610437575f3660031901126104375760206040515f8152f35b346104375760403660031901126104375760206107856107776139eb565b61077f613a57565b906143e4565b604051908152f35b613dd0565b34610437575f36600319011261043757602060405160148152f35b34610437576040366003190112610437576107c6613ad7565b6107cf81615f46565b6107db60243582613e84565b90604051916103206107ed8185613b3c565b60188452601f19013660208501375f905b60188210610820576040516020808252819061081c90820187613d40565b0390f35b8061083060018093188486615f96565b61083a8487613e70565b52811c9101906107fe565b634e487b7160e01b5f52604160045260245ffd5b346104375760203660031901126104375760ff610874613ad7565b165f526001602052602060405f2054604051908152f35b34610437575f366003190112610437576020604051601c8152f35b34610437576040366003190112610437576020604051818101907ff12bce0649327409848ceec32acf20a7e21971869cbf30f9d92357bbe85565b782526004356040820152602435606082015260608152610902608082613b3c565b519020604051908152f35b34610437576060366003190112610437576004356001600160401b0381116104375761093d9036906004016139bb565b90610946613a57565b906044356001600160401b038111610437576109669036906004016139bb565b909260015f525f60205260405f20548015610b0e5761098486613fb9565b9461098e87613fb9565b945f5b888110610ab857505091610a5091610a56949360015f52602b6020526001600160401b0360405f20541694610a1a866109f0610a108c610a028d60405194859360208501976001895260408601526080606086015260a0850190613d40565b838103601f1901608085015290613d40565b03601f198101835282613b3c565b5190208530615a89565b9060015f52600160205260405f2054927f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6615dac565b50613e52565b60015f52602b6020526001600160401b0360405f2091166001600160401b03198254161790555f5b838110610a9057610a8e84614951565b005b80610ab2610aa060019386613e70565b51610aab8386613e70565b5190614436565b01610a7e565b80610ad6610ad1610acc6001948d8761417d565b613e3e565b613f70565b610ae0828b613e70565b52610afd610af836610af3848e8861417d565b613bde565b6141a0565b610b07828a613e70565b5201610991565b6347c987b560e11b5f52600160045260245ffd5b34610437575f366003190112610437576020604051621000008152f35b613deb565b34610437575f3660031901126104375760206040517fb3b4169a40f71a0b1b279bcd6443676c80bdb4ebe0b1db4478b68793211743918152f35b3461043757604036600319011261043757610b97613a2b565b6001600160401b03610ba7613ae7565b91610bb183615f46565b1680158015610ca9575b610c9a575f526028602052610bd561058c60405f2061404d565b9060ff60405191610be760a084613b3c565b6004835260803660208501371682515f905b60048210610c17576040516020808252819061081c90820187613d40565b610c246001841886613e70565b51610c2f8386613e70565b5260011c915f5b838110610c4c5750600190811c91019091610bf9565b600181901b906001600160ff1b03811681036106c857610c6c8288613e70565b5191600181018091116106c8576001926106b0610c89928a613e70565b610c938289613e70565b5201610c36565b6312c6ca0560e21b5f5260045ffd5b506001600160401b03602954168111610bbb565b34610437575f3660031901126104375760206040517faac3f59af44d7d33ac1e055e30909d4c9da73d4ab294df533f38b5e23d376e618152f35b3461043757604036600319011261043757610d10613ad7565b60ff165f81815260216020908152604080832060243580855290835281842054948452602583528184209084528252918290205482519081529215159083015290f35b3461043757608036600319011261043757610d6c613ad7565b610d74613ae7565b906044356001600160401b03811161043757610d949036906004016139bb565b91906064356001600160401b03811161043757610db59036906004016139bb565b9460ff84165f52602c60205260018060a01b0360405f2054163303610e3b57610dde8185616102565b858503610e24575f5b858110610df857610a8e8686614996565b80610e1e610e096001938988613e2e565b35610e15838b88613e2e565b359085896146e0565b01610de7565b85856355c5b3e360e11b5f5260045260245260445ffd5b634a0bfec160e01b5f523360045260245ffd5b34610437576080366003190112610437576004356001600160401b03811161043757610e7e9036906004016139bb565b90610e87613a57565b90610e90613a41565b606435906001600160401b03821161043757610eb3610ef99236906004016139bb565b91604051602081019060408252610ef1816001600160401b03610eda606083018d8c6143a3565b9a16998a604083015203601f198101835282613b3c565b51902061588a565b6008198301611007576001600160401b0360295416610f485760015b60ff811660088111610f57575f5260276020526001600160401b0360405f205416610f4857610f4390613feb565b610f15565b63dc63d81f60e01b5f5260045ffd5b82858560015b60ff811660088111610fb0579081610f81610f7c610fab948789613e2e565b613ecb565b905f5260276020526001600160401b0360405f2091166001600160401b0319825416179055613feb565b610f5d565b50506110027f15fd51992bd825f38af10ca1ec217ca34f12b24fa98401404df53e3052deac4893826001600160401b0319602954161760295560405193849384526040602085015260408401916143a3565b0390a1005b82632c9979d160e11b5f5260045260245ffd5b34610437576020366003190112610437576004356001600160401b03811161043757610785610af86020923690600401613bde565b613db5565b346104375760a03660031901126104375761106d613ad7565b6024356001600160401b0381116104375761108c9036906004016139bb565b906044356001600160401b038111610437576110ac9036906004016139bb565b6110b4613a15565b6084356001600160401b038111610437576110d39036906004016139bb565b916110dd88615f46565b8387148015906113df575b6113c8579161113b918794938960405161113381610a026111208d60ff6020850197169c8d88526060604086015260808501916140b3565b828103601f190160608401528a8d6140b3565b51902061568b565b5f5b85811061114e57610a8e8688614996565b611159818386613e2e565b35835f52602560205260405f20611171838989613e2e565b355f5260205260405f2055611187818787613e2e565b356111aa611196838989613e2e565b356111a2848689613e2e565b35908a61412f565b845f52602160205260405f20825f5260205260405f205480155f1461137e5750845f52602460205260405f205f805260205260405f205491621000008310156113675782600181018082116106c857875f52602460205260405f205f80526020528060405f2055875f52602360205260405f20805490600182018092116106c85755875f52602160205260405f20835f5260205260405f2055865f52602260205260405f20905f5260205260405f20555b845f52600260205260405f20825f526020528060405f205560405160208101915f83526001830152611299816021840103601f198101835282613b3c565b519020905f5b6018811061130357505090600191845f52602660205260405f20556112c5818888613e2e565b35847f2e0f0f49c5675cc0ca3b0631934e93ab749ffc07928459dd09921848e5cc2f5460206112f585888b613e2e565b35604051908152a30161113d565b90916113249061131e6001859b9798999a969b18848d615f96565b906161a9565b9160011c90865f52600360205260405f209060018101918282116106c8576001925f5260205260405f20835f526020528360405f2055019792969594939761129f565b85635633a85b60e01b5f526004525f60245260445ffd5b5f198101929083116106c85760ff8360141c16908161139e57505061125b565b60849187916040519263431ddf3560e11b84526004840152602483015260448201525f6064820152fd5b83876355c5b3e360e11b5f5260045260245260445ffd5b5086156110e8565b34610437576020366003190112610437576004356001600160401b038111610437576114179036906004016139bb565b60015f52602c6020527fa1f88ee5f5d946e3956f6291445d84cd8aea2bf6c57f4f4ac349f7a338882643549091906001600160a01b03163303610e3b575f5b82811061146657610a8e83614951565b8061149561147d610ad1610acc600195888861417d565b61148f610af836610af3868a8a61417d565b90614436565b01611456565b34610437575f36600319011261043757602060405160188152f35b34610437575f36600319011261043757602e546040516001600160a01b039091168152602090f35b346104375760603660031901126104375760206107856114fc613ad7565b604435906024359061412f565b3461043757602036600319011261043757611522613a2b565b6001600160401b03610120918260405161153c8282613b3c565b369037165f52602860205261155360405f2061404d565b6115606040518092613d73565bf35b346104375760403660031901126104375761157b613ad7565b61158481615f46565b61159060243582613e84565b60149061159d6014613b5d565b926115ab6040519485613b3c565b60148452601f196115bc6014613b5d565b013660208601375f915b8383106115e3576040516020808252819061081c90820188613d40565b806115f360018093188585615f96565b6115fd8588613e70565b52811c9201916115c6565b34610437575f36600319011261043757602060405160058152f35b3461043757604036600319011261043757602061166b611641613a2b565b6001600160401b03611651613ae7565b9161165b83615f46565b165f526028835260405f20614120565b90549060031b1c604051908152f35b34610437575f3660031901126104375760206040517f152e47491922dd015e08e657ae4de24ad07106d14eb7be07faeb91afcf3974468152f35b346104375760203660031901126104375760206107856116d26139eb565b6140d7565b346104375760203660031901126104375760ff6116f2613ad7565b165f526026602052602060405f2054604051908152f35b346104375760c036600319011261043757611722613ad7565b61172a613ae7565b906044356001600160401b0381116104375761174a9036906004016139bb565b91906064356001600160401b0381116104375761176b9036906004016139bb565b946084356001600160401b03811681036104375760a4356001600160401b0381116104375761179e9036906004016139bb565b916117a887615f46565b6117b28488616102565b60ff871692600284148015611975575b801561196b575b6119585760078414801561194e575b61193b5789890361192457835f525f60205260405f205490811561191157908291889493868c898f8c90845f52602b60205260405f20546001600160401b03169a6040519485946020860197885260ff1660408601528c60608601526080850160a0905260c085019061184a926140b3565b90601f198483030160a0850152611860926140b3565b03601f19810182526118729082613b3c565b51902061187f9130615a89565b865f52600160205260405f2054917f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6956118b896615b1f565b506118c290613e52565b905f52602b60205260405f20906001600160401b03166001600160401b03198254161790555f5b8581106118fa57610a8e8686614996565b8061190b610e096001938988613e2e565b016118e9565b846347c987b560e11b5f5260045260245ffd5b89896355c5b3e360e11b5f5260045260245260445ffd5b8363c84507ef60e01b5f5260045260245ffd5b50600884146117d8565b8363634a044d60e01b5f5260045260245ffd5b50600484146117c9565b50600384146117c2565b346104375760a036600319011261043757611998613ad7565b6024356044356119a6613a15565b608435906001600160401b038211610437576119c9611a069236906004016139bb565b916119d387615f46565b6040519660ff60208901911697888252876040820152866060820152606081526119fe608082613b3c565b51902061548c565b80611a57575b7fb1cc83d4c4d6db2c85b34c0c804011de9082f750e50dfb4b610e18ae00f5a5fa91604091845f52600160205281835f2055845f525f60205280835f205582519182526020820152a2005b60405163342f616360e01b8152600481018390526020816024817f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03165afa908115611b04575f91611ad2575b50818110611ab95750611a0c565b83634c02ba4760e01b5f5260045260245260445260645ffd5b90506020813d602011611afc575b81611aed60209383613b3c565b81010312610437575184611aab565b3d9150611ae0565b6040513d5f823e3d90fd5b34610437575f366003190112610437576040517f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03168152602090f35b34610437575f3660031901126104375760206040517f542f7edce941e119d877e833115e3acf72a5682040fe3ef9793073cc52d81aa38152f35b34610437576020366003190112610437576004356001600160401b03811161043757611bbd9036906004016139bb565b611bc5616022565b5f92907f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b0316905b838510611c0457610a8e84614951565b611c14610acc8686869796613e2e565b60405163631a2b1360e11b81526001600160a01b038216600482018190529096909390919061018088602481845afa978815611b04575f98611eac575b5061016088015115611e8c57604051632ca0298360e01b81526004810186905297608089602481855afa978815611b04575f925f995f9b5f91611e40575b5060405191634932c86b60e11b8352896004840152604083602481845afa938415611b04575f935f95611e06575b5089519b611cca8d613b5d565b9c806040519e8f90611cdc9082613b3c565b52601f1990611cea90613b5d565b018d5f5b828110611de1575050505f5b8b51811015611d465780611d3f8f928f938f94611d1983600197613e70565b519060405191611d2883613b05565b82526020820152611d398383613e70565b52613e70565b5001611cfa565b509c9a611dd29561148f97929f9560209995949e94611dd89b9760019e9f6001600160401b03610120610140890151151598015116986040519d8e611d8a81613b20565b888152015260408d015260608c015260808b015260a08a015260c089015260e0880152896101008801526101208701526101408601525f610160860152610180850152613f70565b916141a0565b01939190611bf4565b602091828260405192611df384613b05565b5f84525f83850152010152018e90611cee565b935093506040833d8211611e38575b81611e2260409383613b3c565b810103126104375760208351930151938e611cbd565b3d9150611e15565b9a5050925098506080883d8211611e84575b81611e5f60809383613b3c565b8101031261043757875198602089015192606060408b01519a01519a93999a8c611c8f565b3d9150611e52565b611e9582613f70565b632e20d67b60e21b5f52600160045260245260445ffd5b909750610180813d8211611fa2575b81611ec96101809383613b3c565b81010312610437576040519061018082018281106001600160401b0382111761084557611f9591610160916040528051845260208101516020850152604081015160408501526060810151606085015260808101516080850152611f2f60a08201614084565b60a0850152611f4060c08201614084565b60c0850152611f5160e08201614092565b60e0850152611f636101008201614092565b610100850152611f766101208201614092565b610120850152611f8961014082016140a6565b610140850152016140a6565b6101608201529688611c51565b3d9150611ebb565b34610437575f3660031901126104375760206040517fc561565714be00c45be6d4b28308576249863ebb92c68dc8e98d29fb610dc4b38152f35b34610437575f366003190112610437576101206040516120048282613b3c565b3690376001600160401b03602954168015610c9a5780610180915f5260286020526001600160401b0360405f20612052612042600a8301549261404d565b6040519485526020850190613d73565b81811661014084015260401c16610160820152f35b346104375760403660031901126104375761208c612083613ad7565b60243590613ffc565b60408051928352901515602083015290f35b34610437575f3660031901126104375760206040517f836450d8e30ee99baf6ed58424d195ad05faa0b1d95c004a6c9ec2b6ad97c9278152f35b34610437575f36600319011261043757602f546040516001600160a01b039091168152602090f35b34610437575f36600319011261043757602060405160068152f35b3461043757604036600319011261043757612134613ad7565b60ff61213e613ae7565b91165f52602460205260ff60405f2091165f52602052602060405f2054604051908152f35b346104375760403660031901126104375760ff61217e613ad7565b165f52602260205260405f206024355f52602052602060405f2054604051908152f35b34610437575f3660031901126104375760096121bc81613fb9565b6121c582613fb9565b916121e86121d282613b5d565b916121e06040519384613b3c565b808352613b5d565b602082019290601f190136843760015b60ff8116906008821161232a57815f525f60205260405f205461221b8388613e70565b526122268287613e70565b5161228e57612264915f5b61223b8286613e70565b526122468188613e70565b5115908115612269575b61225a9086613e70565b9015159052613feb565b6121f8565b905061225a6122788286613e70565b51612283838a613e70565b511115919050612250565b815f52600160205260405f2054916040519263342f616360e01b8452600484015260208360248160018060a01b037f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6165afa8015611b04575f906122f7575b6122649350612231565b506020833d8211612322575b8161231060209383613b3c565b810103126104375761226492516122ed565b3d9150612303565b50509190602061235861234b95604051968796606088526060880190613d40565b9086820384880152613d40565b91848303604086015251918281520191905f5b818110612379575050500390f35b8251151584528594506020938401939092019160010161236b565b34610437576020366003190112610437576020610785610ad16139eb565b34610437575f36600319011261043757602060405163010000008152f35b34610437577fd8f017452a759bc07cbb79d60579ec3cecaa3669358342450ad9f05b73bc79bb602061243261240436613a81565b91604094939451948686019060018060a01b03169586825287815261242a604082613b3c565b51902061528d565b602d80546001600160a01b03191682179055604051908152a1005b34610437576020366003190112610437576020610785600435613f2b565b34610437575f3660031901126104375760206040517f9c57ef476e1983208f620f721cb1c8eb1297e8e0315ae8704308bcddc11b864e8152f35b34610437576040366003190112610437576124be6139eb565b6024356001600160401b0381116104375736602382011215610437578060040135916124e983613b5d565b916124f76040519384613b3c565b838352602083016024819560051b8301019136831161043757602401905b8282106125ad5750505060405190602082019260808301917f9c57ef476e1983208f620f721cb1c8eb1297e8e0315ae8704308bcddc11b864e855260018060a01b031660408401526060808401525180915260a0820193905f5b81811061258e575050506109028160209403601f198101835282613b3c565b82516001600160a01b031686526020958601959092019160010161256f565b602080916125ba84613a01565b815201910190612515565b34610437575f366003190112610437576030546040516001600160a01b039091168152602090f35b34610437575f36600319011261043757602d546040516001600160a01b039091168152602090f35b34610437576020366003190112610437576004356001600160401b038111610437576126459036906004016139bb565b602f546001600160a01b03168015612986575f5b82811061266957610a8e836148d4565b61267d612677828587613e2e565b35613f2b565b612688828587613e2e565b60405163199bed7f60e31b815290356004820152602081602481875afa908115611b04575f91612955575b505f8281525f5160206166175f395f51905f52602052604090205480612907575060045f527f283ad1fa537f2bde05287a438aa5a837917793fc3ef421ca997bcba10fcd57b16020527f047a21767b7c5d5a12dfe7c6be69a385865569b06021ac7f1b00f46477afc2b25491621000008310156128ee5782624000001792600181018091116106c8577f047a21767b7c5d5a12dfe7c6be69a385865569b06021ac7f1b00f46477afc2b25560085f5260236020527f43a8d743eedbf634542f2d1c3f99f7ad101c7c0bf66c8cf9de364b937bf7df06805460018101919082106106c85755600183018084116106c8575f8281525f5160206166175f395f51905f5260209081526040808320939093558582527f5e00771a5085b4c5e233dc09115168874841c99f1dfe99703953d33b7f3b6342905220555b5f8281527f3a5ea591190eeb3f8fcdced843c78df04ec0dfd42f5510375207515664fa0a7560209081526040808320849055519081019182526021808201939093529182529061283c604182613b3c565b519020905f5b6018811061287f57505060085f5260266020527f893f0d4f231966a90dbf6a0fe2dd3e922956d447e6b926303382e9d7b307677d55600101612659565b90916128949061131e60018518846008615f96565b60085f52600360205291600190811c91908101907f85aaa47b6dc46495bb8824fad4583769726fea36efd831a35556690b830a8fbe8282116106c8576001925f5260205260405f20835f526020528360405f205501612842565b635633a85b60e01b5f526008600452600460245260445ffd5b5f198101929083116106c85760ff8360141c16600481036129295750506127eb565b608492506040519163431ddf3560e11b8352600860048401526024830152604482015260046064820152fd5b90506020813d821161297e575b8161296f60209383613b3c565b810103126104375751866126b3565b3d9150612962565b6369a6654760e01b5f5260045ffd5b34610437576080366003190112610437576129ae613ad7565b6024356001600160a01b03811690819003610437576129cb613a41565b606435916001600160401b03831161043757612a48602092612a127f2f12afbcfd1c846e7bf38fef7cea10a352d3f458df65ab4a7929118629ee90319536906004016139bb565b91612a1c88615f46565b6040519760ff878a0191169889825286604082015260408152612a40606082613b3c565b51902061508e565b835f52602c825260405f20816bffffffffffffffffffffffff60a01b825416179055604051908152a2005b34610437577f593d7758afd689d5da8fa98f5f5530f41c3fd3a1b6a9dcbab3c5d151c1c4585a6020612ad5612aa736613a81565b91604094939451948686019060018060a01b031695868252878152612acd604082613b3c565b519020614e8f565b602f80546001600160a01b03191682179055604051908152a1005b346104375760203660031901126104375760ff612b0b613ad7565b165f52602c602052602060018060a01b0360405f205416604051908152f35b34610437575f36600319011261043757602060405160078152f35b34610437576040366003190112610437576004356001600160401b03811161043757610785612b7a6020923690600401613bde565b60243590613edf565b34610437576020366003190112610437576020612b9e6139eb565b604080517fd9678eabd6141fe616532b9df9bfdaedfa09cd81dd3444e4a52f5d3dc6e8872a8482019081526001600160a01b0390931681830152908152610902606082613b3c565b3461043757604036600319011261043757612bff6139eb565b6024356001600160401b03811161043757612c1e9036906004016139bb565b602e549091906001600160a01b03168015612f82576001600160a01b03841693905f5b848110612c5157610a8e856148d4565b612c68612c62610f7c838888613e2e565b846143e4565b612c76610f7c838888613e2e565b6001600160401b0360405191635f9ae5b160e11b8352896004840152166024820152602081604481875afa908115611b04575f91612f51575b505f8281525f5160206166175f395f51905f52602052604090205480612f03575060035f527f283ad1fa537f2bde05287a438aa5a837917793fc3ef421ca997bcba10fcd57b16020527f60b74b20978d70a33d68c23dbf133a6f1d674a2d6ae4da0e5cee3942f6eea5d5549162100000831015612eea5782623000001792600181018091116106c8577f60b74b20978d70a33d68c23dbf133a6f1d674a2d6ae4da0e5cee3942f6eea5d55560085f5260236020527f43a8d743eedbf634542f2d1c3f99f7ad101c7c0bf66c8cf9de364b937bf7df06805460018101919082106106c85755600183018084116106c8575f8281525f5160206166175f395f51905f5260209081526040808320939093558582527f5e00771a5085b4c5e233dc09115168874841c99f1dfe99703953d33b7f3b6342905220555b5f8281527f3a5ea591190eeb3f8fcdced843c78df04ec0dfd42f5510375207515664fa0a75602090815260408083208490555190810191825260218082019390935291825290612e38604182613b3c565b519020905f5b60188110612e7b57505060085f5260266020527f893f0d4f231966a90dbf6a0fe2dd3e922956d447e6b926303382e9d7b307677d55600101612c41565b9091612e909061131e60018518846008615f96565b60085f52600360205291600190811c91908101907f85aaa47b6dc46495bb8824fad4583769726fea36efd831a35556690b830a8fbe8282116106c8576001925f5260205260405f20835f526020528360405f205501612e3e565b635633a85b60e01b5f526008600452600360245260445ffd5b5f198101929083116106c85760ff8360141c1660038103612f25575050612de7565b608492506040519163431ddf3560e11b8352600860048401526024830152604482015260036064820152fd5b90506020813d8211612f7a575b81612f6b60209383613b3c565b81010312610437575188612caf565b3d9150612f5e565b63203af9db60e21b5f5260045ffd5b346104375760203660031901126104375760ff612fac613ad7565b165f525f602052602060405f2054604051908152f35b34610437576040366003190112610437576020610785612fe0613ad7565b60243590613e84565b34610437575f3660031901126104375760206040517fab207f95fe9e89b4f6a8f6c3858d54419e156a214e15ec645aea4c7af360a3798152f35b34610437576020366003190112610437576001600160401b03613044613a2b565b165f5260286020526020600960405f200154604051908152f35b34610437575f3660031901126104375760206001600160401b0360295416604051908152f35b34610437575f3660031901126104375760206040517fef5acc774d80331391f411254261fe2c337fecc96ad255d82e8fa47fd593be8c8152f35b34610437575f3660031901126104375760206040517f90783c5900b0eac80aa32145b2a36cc816556e13d82957ee229c86e3300038388152f35b34610437575f36600319011261043757602060405160108152f35b34610437575f3660031901126104375760206040517fead07c0f33d1424ea40cdc26b2fad68b761e496797bd0b5c1cf8bb7ed4b53c7e8152f35b34610437577f706d7e47c9f939c28bd8639e54f60248454a51a12c6ad98e93f2872fcba91d3d60206131af61318136613a81565b91604094939451948686019060018060a01b0316958682528781526131a7604082613b3c565b519020614c90565b602e80546001600160a01b03191682179055604051908152a1005b346104375760203660031901126104375760ff6131e5613ad7565b165f52602760205260206001600160401b0360405f205416604051908152f35b34610437576040366003190112610437576020610785613223613ad7565b601460ff61322f613ae7565b61323884615f46565b61324181615f75565b1691615f96565b346104375760a036600319011261043757613261613ad7565b6024356001600160401b03811161043757613280903690600401613b74565b6044356001600160401b0381116104375761329f903690600401613b74565b6132a7613a15565b6084356001600160401b038111610437576132c69036906004016139bb565b6030549192916001600160a01b03163303610e3b5760ff861692835f525f60205260405f20549182156119115792610a0292610a509261337d95875f52602b602052613348888b610a108c6001600160401b0360405f2054169a8b916109f06040519586946020860198895260408601526080606086015260a0850190613d40565b90885f52600160205260405f2054927f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6615b1f565b815f52602b6020526001600160401b0360405f2091166001600160401b03198254161790555f5b83518110156135a0576133b78185613e70565b516133c28285613e70565b51835f52602160205260405f20825f5260205260405f205480155f146135525750835f52602460205260405f2060015f5260205260405f2054916210000083101561353a5782621000001792600181018091116106c857855f52602460205260405f2060015f5260205260405f2055845f52602360205260405f20805490600182018092116106c85755600183018084116106c857855f52602160205260405f20825f5260205260405f2055845f52602260205260405f20835f5260205260405f20555b835f52600260205260405f20825f526020528060405f205560405160208101915f835260018301526134c4816021840103601f198101835282613b3c565b519020905f5b601881106134ea5750505f838152602660205260409020556001016133a4565b90916134fe9061131e60018518848b615f96565b9160011c90845f52600360205260405f209060018101918282116106c8576001925f5260205260405f20835f526020528360405f2055016134ca565b84635633a85b60e01b5f52600452600160245260445ffd5b5f198101929083116106c85760ff8360141c169060018203613575575050613486565b60849186916040519263431ddf3560e11b845260048401526024830152604482015260016064820152fd5b610a8e85855190614996565b34610437577f26ef7b9e972363cd493eb2075cde9bf177b455f7cb5847106e9b9dc4a0d4ab1a602061360e6135e036613a81565b91604094939451948686019060018060a01b031695868252878152613606604082613b3c565b519020614a2e565b603080546001600160a01b03191682179055604051908152a1005b34610437576020366003190112610437576004356001600160401b038111610437576136599036906004016139bb565b7f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03165f5b82811061369557610a8e836148d4565b6136a66116d2610acc838688613e2e565b6136b4610acc838688613e2e565b60405163706684b560e11b81526001600160a01b039091166004820152602081602481875afa908115611b04575f9161398a575b505f8281525f5160206166175f395f51905f5260205260409020548061393c575060015f527f283ad1fa537f2bde05287a438aa5a837917793fc3ef421ca997bcba10fcd57b16020527fe061722b09a2ecd9d97bd330fdeaac5798cacff901ec69463e71197dd549320e5491621000008310156139235782621000001792600181018091116106c8577fe061722b09a2ecd9d97bd330fdeaac5798cacff901ec69463e71197dd549320e5560085f5260236020527f43a8d743eedbf634542f2d1c3f99f7ad101c7c0bf66c8cf9de364b937bf7df06805460018101919082106106c85755600183018084116106c8575f8281525f5160206166175f395f51905f5260209081526040808320939093558582527f5e00771a5085b4c5e233dc09115168874841c99f1dfe99703953d33b7f3b6342905220555b5f8281527f3a5ea591190eeb3f8fcdced843c78df04ec0dfd42f5510375207515664fa0a75602090815260408083208490555190810191825260218082019390935291825290613871604182613b3c565b519020905f5b601881106138b457505060085f5260266020527f893f0d4f231966a90dbf6a0fe2dd3e922956d447e6b926303382e9d7b307677d55600101613685565b90916138c99061131e60018518846008615f96565b60085f52600360205291600190811c91908101907f85aaa47b6dc46495bb8824fad4583769726fea36efd831a35556690b830a8fbe8282116106c8576001925f5260205260405f20835f526020528360405f205501613877565b635633a85b60e01b5f526008600452600160245260445ffd5b5f198101929083116106c85760ff8360141c166001810361395e575050613820565b608492506040519163431ddf3560e11b8352600860048401526024830152604482015260016064820152fd5b90506020813d82116139b3575b816139a460209383613b3c565b810103126104375751866136e8565b3d9150613997565b9181601f84011215610437578235916001600160401b038311610437576020808501948460051b01011161043757565b600435906001600160a01b038216820361043757565b35906001600160a01b038216820361043757565b606435906001600160401b038216820361043757565b600435906001600160401b038216820361043757565b604435906001600160401b038216820361043757565b602435906001600160401b038216820361043757565b35906001600160401b038216820361043757565b6060600319820112610437576004356001600160a01b038116810361043757916024356001600160401b03811681036104375791604435906001600160401b03821161043757613ad3916004016139bb565b9091565b6004359060ff8216820361043757565b6024359060ff8216820361043757565b359060ff8216820361043757565b604081019081106001600160401b0382111761084557604052565b6101a081019081106001600160401b0382111761084557604052565b90601f801991011681019081106001600160401b0382111761084557604052565b6001600160401b0381116108455760051b60200190565b9080601f83011215610437578135613b8b81613b5d565b92613b996040519485613b3c565b81845260208085019260051b82010192831161043757602001905b828210613bc15750505090565b8135815260209182019101613bb4565b3590811515820361043757565b9190916101a0818403126104375760405190613bf982613b20565b8193613c0482613a01565b83526020820135602084015260408201356040840152606082013560608401526080820135608084015260a082013560a084015260c082013560c0840152613c4e60e08301613a01565b60e0840152613c606101008301613bd1565b610100840152613c736101208301613bd1565b6101208401526101408201356001600160401b03811161043757820181601f8201121561043757803590613ca682613b5d565b92613cb46040519485613b3c565b82845260208085019360061b8301019181831161043757602001925b828410613d0f57505050506101408301526101608101359063ffffffff8216820361043757610180613d0a91819361016086015201613a6d565b910152565b6040848303126104375760206040918251613d2981613b05565b863581528287013583820152815201930192613cd0565b90602080835192838152019201905f5b818110613d5d5750505090565b8251845260209384019390920191600101613d50565b905f905b60098210613d8457505050565b6020806001928551815201930191019091613d77565b34610437575f36600319011261043757602060405160018152f35b34610437575f36600319011261043757602060405160048152f35b34610437575f36600319011261043757602060405160028152f35b34610437575f36600319011261043757602060405160088152f35b919082039182116106c857565b34610437575f36600319011261043757602060405160038152f35b919081101561064a5760051b0190565b356001600160a01b03811681036104375790565b6001600160401b036001911601906001600160401b0382116106c857565b805182101561064a5760209160051b010190565b60ff1690815f52602160205260405f20815f5260205260405f2054918215613eb65750505f1981019081116106c85790565b632e20d67b60e21b5f5260045260245260445ffd5b356001600160401b03811681036104375790565b905f5b610140830180518051831015613f2257613efd838592613e70565b515114613f0d5750600101613ee2565b60209350613f1c925051613e70565b51015190565b50505050505f90565b60405160208101917f30fee2ec51f2fcc72d20be52fafccddf7595a2e510b15522cc3dd9c6d2876d288352604082015260408152613f6a606082613b3c565b51902090565b604080517fa2572d39d0c41aac68a98ce8ad7420eb365bd2c32bdc0a255a610cb7b5a9ed4e602082019081526001600160a01b0390931681830152908152613f6a606082613b3c565b90613fc382613b5d565b613fd06040519182613b3c565b8281528092613fe1601f1991613b5d565b0190602036910137565b60ff1660ff81146106c85760010190565b60ff1690815f52602160205260405f20905f5260205260405f2054908115614045575f52600260205260405f20905f1981019081116106c8575f5260205260405f205490600190565b50505f905f90565b60405191905f835b6009821061406e5750505061406c61012083613b3c565b565b6001602081928554815201930191019091614055565b519060ff8216820361043757565b51906001600160401b038216820361043757565b5190811515820361043757565b81835290916001600160fb1b0383116104375760209260051b809284830137010190565b604080517ff2b4f1826737aad869e8646b0419dc175b9a080139565dda7a94ab3fe7f0a670602082019081526001600160a01b0390931681830152908152613f6a606082613b3c565b600982101561064a5701905f90565b916040519160ff60208401947fef5acc774d80331391f411254261fe2c337fecc96ad255d82e8fa47fd593be8c86521660408401526060830152608082015260808152613f6a60a082613b3c565b919081101561064a5760051b8101359061019e1981360301821215610437570190565b906101408201908151925f5b845181101561425d576141bf8186613e70565b5151158015614247575b614236575f5b8181106141df57506001016141ac565b6141e98187613e70565b51516141f58388613e70565b515114614204576001016141cf565b85602061421d846142158185613e70565b515193613e70565b5101519063483f415960e01b5f5260045260245260445ffd5b602061421d82614215889485613e70565b5060206142548287613e70565b510151156141c9565b50909250600160a01b6001900381511660208201519260408301519060608401519360808101519060a08101519260c0820151600160a01b6001900360e084015116610100840151151591610120850151151593519661016086015163ffffffff169561018001516001600160401b0316966040519b8c9a60208c019e8f9d8d6101e081019f7fead07c0f33d1424ea40cdc26b2fad68b761e496797bd0b5c1cf8bb7ed4b53c7e90526040015260608d015260808c015260a08b015260c08a015260e089015261010088015261012087015261014086015261016085015261018084016101c090528251809552610200840192602001945f5b81811061437e5750506101a08401526101c083015203601f1981018352613f6a915082613b3c565b8651805186526020908101518187015290960195879550604090940193600101614356565b916020908281520191905f5b8181106143bc5750505090565b9091926020806001926001600160401b036143d688613a6d565b1681520194019291016143af565b906103ff6040519160208301937fa56f176426c90fc7bbc7bd6d8689a2e8373d36cd1a29b66b49d92f6b28f6d91f855260018060a01b0316604084015216606082015260608152613f6a608082613b3c565b5f8181527f2b59c9df127166d3570f589f0cb7377a6b175795e70ab275ebf42fa16c0a23f4602052604090205490919080614692575060015f527fbbbb3b1da0cb0951f34c5e9db4606f934b7367b5284f29163e9e6fe67e1e97d66020527fb9055aa4fe7e8c3a3a353dc4f908acb9bd2b7c1238f64f569ef733969e23c0bf5491621000008310156146795782621000001792600181018091116106c85760015f8190527fb9055aa4fe7e8c3a3a353dc4f908acb9bd2b7c1238f64f569ef733969e23c0bf9190915560236020527fb361aea33a0348d043deace4a562cb920ac10508397ad80f12dfe9a2a063e04780549182019182106106c85755600183018084116106c8575f8281527f2b59c9df127166d3570f589f0cb7377a6b175795e70ab275ebf42fa16c0a23f460209081526040808320939093558582527fe39b43e4224876d80510ac9d8f190663bcce357e28a4aec26f3bf2e600bb40ec905220555b5f8281527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e06020908152604080832084905551908101918252602180820193909352918252906145ea604182613b3c565b519020905f5b6018811061462757505060015f5260266020527f5db1dfd2ced311e642486f0e890a646498a1bdee1ad9403b0a73141af8a4c41155565b909161463c9061131e60018518846001615f96565b9160011c9060015f52600360205260405f209060018101918282116106c8576001925f5260205260405f20835f526020528360405f2055016145f0565b635633a85b60e01b5f526001600452600160245260445ffd5b5f198101929083116106c85760ff8360141c16600181036146b4575050614599565b608492506040519163431ddf3560e11b8352600160048401526024830152604482015260016064820152fd5b909260ff821692835f52602160205260405f20815f5260205260405f205480155f146148815750835f52602460205260405f2060ff86165f5260205260405f2054621000008110156148675780630ff000008760141b161795600182018092116106c857855f52602460205260ff60405f2091165f5260205260405f2055835f52602360205260405f20805490600182018092116106c85755600185018086116106c857845f52602160205260405f20825f5260205260405f2055835f52602260205260405f20855f5260205260405f20559290925b825f52600260205260405f20825f526020528060405f205560405160208101915f835260018301526147f4816021840103601f198101835282613b3c565b519020925f5b60188110614813575050505f52602660205260405f2055565b9091936148289061131e600187188486615f96565b9360011c90835f52600360205260405f209060018101918282116106c8576001925f5260205260405f20835f526020528560405f2055019190916147fa565b60ff8686635633a85b60e01b5f526004521660245260445ffd5b5f198101959086116106c85760ff808760141c169116918282036148aa575050509290926147b6565b9185916084936040519363431ddf3560e11b85526004850152602484015260448301526064820152fd5b7ffd2b1f10be3fd33a76b3ae76c4ce4fb0ddce0d992ae04bfb27367ad94623a0056060600892835f5260276020526001600160401b036149198160405f205416613e52565b855f52602760205260405f208282168319825416179055855f52602660205260405f20546040519384526020840152166040820152a2565b7ffd2b1f10be3fd33a76b3ae76c4ce4fb0ddce0d992ae04bfb27367ad94623a0056060600192835f5260276020526001600160401b036149198160405f205416613e52565b606060ff7ffd2b1f10be3fd33a76b3ae76c4ce4fb0ddce0d992ae04bfb27367ad94623a005921692835f5260276020526001600160401b036149198160405f205416613e52565b9035601e19823603018112156104375701602081359101916001600160401b03821161043757813603831361043757565b908060209392818452848401375f828201840152601f01601f1916010190565b6040516328305db160e21b815291937f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03169392909190602081600481885afa908115611b04575f91614c56575b501580614bde575b614bd757833b156104375791816001600160401b0395936040519687956322f3f44760e11b875260848701927faac3f59af44d7d33ac1e055e30909d4c9da73d4ab294df533f38b5e23d376e6160048901526024880152166044860152608060648601525260a4830160a48360051b85010192825f90607e1981360301935b838310614b385750505050505091815f81819503925af18015611b0457614b2e5750565b5f61406c91613b3c565b919395909294965060a319898203018252863586811215610437576001916020918291614bc5918701906001600160a01b03614b7383613a01565b16815260ff614b83858401613af7565b1684820152614bb7614bac614b9b60408501856149dd565b608060408601526080850191614a0e565b9260608101906149dd565b916060818503910152614a0e565b98019201930190939188969593614b0a565b5050505050565b5060405163f5778b0360e01b8152602081600481885afa908115611b04575f91614c14575b506001600160a01b03163314614a8b565b90506020813d602011614c4e575b81614c2f60209383613b3c565b8101031261043757516001600160a01b0381168103610437575f614c03565b3d9150614c22565b90506020813d602011614c88575b81614c7160209383613b3c565b8101031261043757614c82906140a6565b5f614a83565b3d9150614c64565b6040516328305db160e21b815291937f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03169392909190602081600481885afa908115611b04575f91614e55575b501580614ddd575b614bd757833b156104375791816001600160401b0395936040519687956322f3f44760e11b875260848701927f152e47491922dd015e08e657ae4de24ad07106d14eb7be07faeb91afcf39744660048901526024880152166044860152608060648601525260a4830160a48360051b85010192825f90607e1981360301935b838310614d905750505050505091815f81819503925af18015611b0457614b2e5750565b919395909294965060a319898203018252863586811215610437576001916020918291614dcb918701906001600160a01b03614b7383613a01565b98019201930190939188969593614d6c565b5060405163f5778b0360e01b8152602081600481885afa908115611b04575f91614e13575b506001600160a01b03163314614ced565b90506020813d602011614e4d575b81614e2e60209383613b3c565b8101031261043757516001600160a01b0381168103610437575f614e02565b3d9150614e21565b90506020813d602011614e87575b81614e7060209383613b3c565b8101031261043757614e81906140a6565b5f614ce5565b3d9150614e63565b6040516328305db160e21b815291937f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03169392909190602081600481885afa908115611b04575f91615054575b501580614fdc575b614bd757833b156104375791816001600160401b0395936040519687956322f3f44760e11b875260848701927fb3b4169a40f71a0b1b279bcd6443676c80bdb4ebe0b1db4478b687932117439160048901526024880152166044860152608060648601525260a4830160a48360051b85010192825f90607e1981360301935b838310614f8f5750505050505091815f81819503925af18015611b0457614b2e5750565b919395909294965060a319898203018252863586811215610437576001916020918291614fca918701906001600160a01b03614b7383613a01565b98019201930190939188969593614f6b565b5060405163f5778b0360e01b8152602081600481885afa908115611b04575f91615012575b506001600160a01b03163314614eec565b90506020813d60201161504c575b8161502d60209383613b3c565b8101031261043757516001600160a01b0381168103610437575f615001565b3d9150615020565b90506020813d602011615086575b8161506f60209383613b3c565b8101031261043757615080906140a6565b5f614ee4565b3d9150615062565b6040516328305db160e21b815291937f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03169392909190602081600481885afa908115611b04575f91615253575b5015806151db575b614bd757833b156104375791816001600160401b0395936040519687956322f3f44760e11b875260848701927fc561565714be00c45be6d4b28308576249863ebb92c68dc8e98d29fb610dc4b360048901526024880152166044860152608060648601525260a4830160a48360051b85010192825f90607e1981360301935b83831061518e5750505050505091815f81819503925af18015611b0457614b2e5750565b919395909294965060a3198982030182528635868112156104375760019160209182916151c9918701906001600160a01b03614b7383613a01565b9801920193019093918896959361516a565b5060405163f5778b0360e01b8152602081600481885afa908115611b04575f91615211575b506001600160a01b031633146150eb565b90506020813d60201161524b575b8161522c60209383613b3c565b8101031261043757516001600160a01b0381168103610437575f615200565b3d915061521f565b90506020813d602011615285575b8161526e60209383613b3c565b810103126104375761527f906140a6565b5f6150e3565b3d9150615261565b6040516328305db160e21b815291937f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03169392909190602081600481885afa908115611b04575f91615452575b5015806153da575b614bd757833b156104375791816001600160401b0395936040519687956322f3f44760e11b875260848701927fab207f95fe9e89b4f6a8f6c3858d54419e156a214e15ec645aea4c7af360a37960048901526024880152166044860152608060648601525260a4830160a48360051b85010192825f90607e1981360301935b83831061538d5750505050505091815f81819503925af18015611b0457614b2e5750565b919395909294965060a3198982030182528635868112156104375760019160209182916153c8918701906001600160a01b03614b7383613a01565b98019201930190939188969593615369565b5060405163f5778b0360e01b8152602081600481885afa908115611b04575f91615410575b506001600160a01b031633146152ea565b90506020813d60201161544a575b8161542b60209383613b3c565b8101031261043757516001600160a01b0381168103610437575f6153ff565b3d915061541e565b90506020813d602011615484575b8161546d60209383613b3c565b810103126104375761547e906140a6565b5f6152e2565b3d9150615460565b6040516328305db160e21b815291937f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03169392909190602081600481885afa908115611b04575f91615651575b5015806155d9575b614bd757833b156104375791816001600160401b0395936040519687956322f3f44760e11b875260848701927f542f7edce941e119d877e833115e3acf72a5682040fe3ef9793073cc52d81aa360048901526024880152166044860152608060648601525260a4830160a48360051b85010192825f90607e1981360301935b83831061558c5750505050505091815f81819503925af18015611b0457614b2e5750565b919395909294965060a3198982030182528635868112156104375760019160209182916155c7918701906001600160a01b03614b7383613a01565b98019201930190939188969593615568565b5060405163f5778b0360e01b8152602081600481885afa908115611b04575f9161560f575b506001600160a01b031633146154e9565b90506020813d602011615649575b8161562a60209383613b3c565b8101031261043757516001600160a01b0381168103610437575f6155fe565b3d915061561d565b90506020813d602011615683575b8161566c60209383613b3c565b810103126104375761567d906140a6565b5f6154e1565b3d915061565f565b6040516328305db160e21b815291937f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03169392909190602081600481885afa908115611b04575f91615850575b5015806157d8575b614bd757833b156104375791816001600160401b0395936040519687956322f3f44760e11b875260848701927f836450d8e30ee99baf6ed58424d195ad05faa0b1d95c004a6c9ec2b6ad97c92760048901526024880152166044860152608060648601525260a4830160a48360051b85010192825f90607e1981360301935b83831061578b5750505050505091815f81819503925af18015611b0457614b2e5750565b919395909294965060a3198982030182528635868112156104375760019160209182916157c6918701906001600160a01b03614b7383613a01565b98019201930190939188969593615767565b5060405163f5778b0360e01b8152602081600481885afa908115611b04575f9161580e575b506001600160a01b031633146156e8565b90506020813d602011615848575b8161582960209383613b3c565b8101031261043757516001600160a01b0381168103610437575f6157fd565b3d915061581c565b90506020813d602011615882575b8161586b60209383613b3c565b810103126104375761587c906140a6565b5f6156e0565b3d915061585e565b6040516328305db160e21b815291937f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03169392909190602081600481885afa908115611b04575f91615a4f575b5015806159d7575b614bd757833b156104375791816001600160401b0395936040519687956322f3f44760e11b875260848701927f90783c5900b0eac80aa32145b2a36cc816556e13d82957ee229c86e33000383860048901526024880152166044860152608060648601525260a4830160a48360051b85010192825f90607e1981360301935b83831061598a5750505050505091815f81819503925af18015611b0457614b2e5750565b919395909294965060a3198982030182528635868112156104375760019160209182916159c5918701906001600160a01b03614b7383613a01565b98019201930190939188969593615966565b5060405163f5778b0360e01b8152602081600481885afa908115611b04575f91615a0d575b506001600160a01b031633146158e7565b90506020813d602011615a47575b81615a2860209383613b3c565b8101031261043757516001600160a01b0381168103610437575f6159fc565b3d9150615a1b565b90506020813d602011615a81575b81615a6a60209383613b3c565b8101031261043757615a7b906140a6565b5f6158df565b3d9150615a5d565b916001600160401b036040519260208401947fd850f5df47b124511e8e6ec99cf1a0beaf7c6237eff0a31305ce53d85f312675865246604086015260018060a01b031660608501527f334d6e4047ccc9c7ecf59f8a99399ea767bd51509ace69f651bd4f5a35a9e3af60808501521660a083015260c082015260c08152613f6a60e082613b3c565b3560ff811681036104375790565b94939195965f978515615d9d576001600160401b0316438111615d8757610258615b498243613e06565b11615d71575060405193602085015260208452615b67604085613b3c565b5f945f985b888a1015615d47578960051b840135607e198536030181121561043757840196615b9588613e3e565b6001600160a01b039182169116811015615d1b5750615bb387613e3e565b96615bf4602087615bc384613e3e565b604051632e4bfa5160e11b81526001600160a01b039091166004820152602481019190915291829081906044820190565b03816001600160a01b038e165afa908115611b04575f91615ce2575b5015615cbb576020810190600460ff615c2884615b11565b1603615c8857615c3988828c6162c6565b15615c545750505f1981146106c8576001998a019901615b6c565b90615c69615c6360ff93613e3e565b91615b11565b9063bbf82ba360e01b5f5260018060a01b03166004521660245260445ffd5b90615c97615c6360ff93613e3e565b9063587548c360e11b5f5260018060a01b031660045216602452600460445260645ffd5b615cc58691613e3e565b63ae8bb03960e01b5f5260018060a01b031660045260245260445ffd5b90506020813d8211615d13575b81615cfc60209383613b3c565b8101031261043757615d0d906140a6565b5f615c10565b3d9150615cef565b615d2488613e3e565b6311641feb60e21b5f9081526004929092526001600160a01b0316602452604490fd5b98509550955050505050808310615d5b5750565b826305bc216760e51b5f5260045260245260445ffd5b630ed38fd160e41b5f526004524360245260445ffd5b637b51505560e01b5f526004524360245260445ffd5b631fc460bf60e11b5f5260045ffd5b92939195965f978615615d9d576001600160401b0316438111615d8757610258615dd68243613e06565b11615d71575060405194602086015260208552615df4604086613b3c565b5f955f985b888a1015615d47578960051b840135607e198536030181121561043757840197615e2289613e3e565b6001600160a01b039182169116811015615f3d5750615e4088613e3e565b97615e50602087615bc384613e3e565b03816001600160a01b038c165afa908115611b04575f91615f04575b5015615cbb5760208101600460ff615e8383615b11565b1603615ef657615e9489838a6162c6565b15615ee85750615ea588828961640b565b15615ebf57505f1981146106c8576001998a019901615df9565b615ec890613e3e565b63c082266360e01b5f9081526001600160a01b0391909116600452602490fd5b615c69615c6360ff93613e3e565b615c97615c6360ff93613e3e565b90506020813d8211615f35575b81615f1e60209383613b3c565b8101031261043757615f2f906140a6565b5f615e6c565b3d9150615f11565b615d2489613e3e565b60ff1680158015615f6b575b615f595750565b6322160ff360e11b5f5260045260245ffd5b5060088111615f52565b60ff166010811015615f845750565b633984d6f560e11b5f5260045260245ffd5b9190918215615fe05760ff165f52600360205260405f20825f5260205260405f20905f5260205260405f205480155f14615fdb5750601d81101561064a576004015490565b905090565b60ff919250165f52600260205260405f20905f5260205260405f205460405160208101915f83526001830152613f6a816021840103601f198101835282613b3c565b602d546001600160a01b031680156160e9575f600491604051928380926375bbb9c160e11b82525afa908115611b04575f9161605c575090565b90503d805f833e61606d8183613b3c565b810190602081830312610437578051906001600160401b03821161043757019080601f830112156104375781516160a381613b5d565b926160b16040519485613b3c565b81845260208085019260051b82010192831161043757602001905b8282106160d95750505090565b81518152602091820191016160cc565b506040516160f8602082613b3c565b5f81525f36813790565b9060ff9061610f81615f75565b16156161185750565b60ff906294926560e31b5f521660045260245ffd5b9060405161022061613e8183613b3c565b60108252601f1901366020830137805f5b82518110156161a357600181101580616198575b1561618d57600981101561064a578060019160051b8601515b6161868286613e70565b520161614f565b600190601c5461617c565b506008811115616163565b50925050565b818110156161dc57905b604051906020820192600160f81b84526021830152604182015260418152613f6a606182613b3c565b6161b3565b6001600160401b03811161084557601f01601f191660200190565b602081830312610437578051906001600160401b038211610437570181601f820112156104375780519061622f826161e1565b9261623d6040519485613b3c565b8284526020838301011161043757815f9260208093018386015e8301015290565b903590601e198136030182121561043757018035906001600160401b0382116104375760200191813603831361043757565b92919261629c826161e1565b916162aa6040519384613b3c565b829481845281830111610437578281602093845f960137010152565b9160208201600460ff6162d883615b11565b161461638a5760ff6162eb600592615b11565b16146162f8575050505f90565b5f61630283613e3e565b604051639e5adaeb60e01b81526001600160a01b0391821660048201529485916024918391165afa918215611b045761635b935f9361635e575b5061634e81604061635593019061625e565b3691616290565b91616592565b90565b61635591935061638261634e913d805f833e61637a8183613b3c565b8101906161fc565b93915061633c565b505f61639583613e3e565b60405163b7af85d760e01b81526001600160a01b0391821660048201529485916024918391165afa918215611b045761635b935f936163e7575b5061634e8160406163e193019061625e565b916164a9565b6163e191935061640361634e913d805f833e61637a8183613b3c565b9391506163cf565b90915f61641784613e3e565b60405163ad84ad1360e01b81526001600160a01b0391821660048201529384916024918391165afa918215611b04575f9261648d575b508151158015616477575b6164705761635561634e84606061635b96019061625e565b5050505f90565b50616485606084018461625e565b905015616458565b6164a29192503d805f833e61637a8183613b3c565b905f61644d565b610a20815114801590616585575b61647057602061650a5f948286958160405195869481808701998051918291018b5e8601908282018b8152815193849201905e010190878252805192839101825e0185815203601f198101835282613b3c565b51906102045afa3d1561657e573d616521816161e1565b9061652f6040519283613b3c565b81523d5f602083013e5b81616572575b81616548575090565b9050602081519101519060208110616561575b50151590565b5f199060200360031b1b165f61655b565b8051602014915061653f565b6060616539565b50611213835114156164b7565b6040815114801590616609575b6164705760206165f25f948286958160405195869481808701998051918291018b5e8601908282018b8152815193849201905e010190878252805192839101825e0185815203601f198101835282613b3c565b51906102055afa3d1561657e573d616521816161e1565b506174608351141561659f56fe71de0cfd3838c45926d39cfb530d525827c4d229159c994c3c2a83c184e8fb49
Žádný ocas metadat CBOR — tento bytecode byl sestaven s vypnutým cbor_metadata, nastavením, které naše kontrakty fixují kvůli neměnnosti adres CREATE2.

disassemblace (prvních 4,000 op)

pcopoperand
0000PUSH10x80
0002PUSH10x40
0004MSTORE
0005PUSH10x04
0007CALLDATASIZE
0008LT
0009ISZERO
000aPUSH20x0011
000dJUMPI
000ePUSH0
000fDUP1
0010REVERT
0011JUMPDEST
0012PUSH0
0013CALLDATALOAD
0014PUSH10xe0
0016SHR
0017DUP1
0018PUSH40x01e8a3a7
001dEQ
001ePUSH20x3629
0021JUMPI
0022DUP1
0023PUSH40x02ffb43a
0028EQ
0029PUSH20x35ac
002cJUMPI
002dDUP1
002ePUSH40x06ff9a01
0033EQ
0034PUSH20x3248
0037JUMPI
0038DUP1
0039PUSH40x07465c21
003eEQ
003fPUSH20x3205
0042JUMPI
0043DUP1
0044PUSH40x08699369
0049EQ
004aPUSH20x31ca
004dJUMPI
004eDUP1
004fPUSH40x0acd12b5
0054EQ
0055PUSH20x314d
0058JUMPI
0059DUP1
005aPUSH40x0b543248
005fEQ
0060PUSH20x3113
0063JUMPI
0064DUP1
0065PUSH40x0dc85691
006aEQ
006bPUSH20x30f8
006eJUMPI
006fDUP1
0070PUSH40x10229c9b
0075EQ
0076PUSH20x30be
0079JUMPI
007aDUP1
007bPUSH40x11cb42a6
0080EQ
0081PUSH20x3084
0084JUMPI
0085DUP1
0086PUSH40x146ca531
008bEQ
008cPUSH20x305e
008fJUMPI
0090DUP1
0091PUSH40x158f59b6
0096EQ
0097PUSH20x3023
009aJUMPI
009bDUP1
009cPUSH40x159735ac
00a1EQ
00a2PUSH20x2fe9
00a5JUMPI
00a6DUP1
00a7PUSH40x177b99da
00acEQ
00adPUSH20x2fc2
00b0JUMPI
00b1DUP1
00b2PUSH40x1bc6dca7
00b7EQ
00b8PUSH20x2f91
00bbJUMPI
00bcDUP1
00bdPUSH40x1c37f8be
00c2EQ
00c3PUSH20x2be6
00c6JUMPI
00c7DUP1
00c8PUSH40x1cb78cf6
00cdEQ
00cePUSH20x2b83
00d1JUMPI
00d2DUP1
00d3PUSH40x1ed23e25
00d8EQ
00d9PUSH20x2b45
00dcJUMPI
00ddDUP1
00dePUSH40x1ff31495
00e3EQ
00e4PUSH20x2b2a
00e7JUMPI
00e8DUP1
00e9PUSH40x21985270
00eeEQ
00efPUSH20x2af0
00f2JUMPI
00f3DUP1
00f4PUSH40x2211d9eb
00f9EQ
00faPUSH20x2a73
00fdJUMPI
00feDUP1
00ffPUSH40x227cab87
0104EQ
0105PUSH20x2995
0108JUMPI
0109DUP1
010aPUSH40x227d5d7a
010fEQ
0110PUSH20x2615
0113JUMPI
0114DUP1
0115PUSH40x24a47334
011aEQ
011bPUSH20x25ed
011eJUMPI
011fDUP1
0120PUSH40x25030f27
0125EQ
0126PUSH20x25c5
0129JUMPI
012aDUP1
012bPUSH40x2737f33e
0130EQ
0131PUSH20x24a5
0134JUMPI
0135DUP1
0136PUSH40x36179208
013bEQ
013cPUSH20x246b
013fJUMPI
0140DUP1
0141PUSH40x38902300
0146EQ
0147PUSH20x244d
014aJUMPI
014bDUP1
014cPUSH40x3fe9eec5
0151EQ
0152PUSH20x23d0
0155JUMPI
0156DUP1
0157PUSH40x427fbf5a
015cEQ
015dPUSH20x23b2
0160JUMPI
0161DUP1
0162PUSH40x45763154
0167EQ
0168PUSH20x2394
016bJUMPI
016cDUP1
016dPUSH40x4d686726
0172EQ
0173PUSH20x21a1
0176JUMPI
0177DUP1
0178PUSH40x4e4d143c
017dEQ
017ePUSH20x2163
0181JUMPI
0182DUP1
0183PUSH40x4e74a404
0188EQ
0189PUSH20x211b
018cJUMPI
018dDUP1
018ePUSH40x563a6c07
0193EQ
0194PUSH20x2100
0197JUMPI
0198DUP1
0199PUSH40x57c45d9c
019eEQ
019fPUSH20x20d8
01a2JUMPI
01a3DUP1
01a4PUSH40x5807879d
01a9EQ
01aaPUSH20x209e
01adJUMPI
01aeDUP1
01afPUSH40x614a5c34
01b4EQ
01b5PUSH20x2067
01b8JUMPI
01b9DUP1
01baPUSH40x668a0f02
01bfEQ
01c0PUSH20x1fe4
01c3JUMPI
01c4DUP1
01c5PUSH40x6a404030
01caEQ
01cbPUSH20x03f7
01ceJUMPI
01cfDUP1
01d0PUSH40x6b6a2682
01d5EQ
01d6PUSH20x104f
01d9JUMPI
01daDUP1
01dbPUSH40x6d2e5ca0
01e0EQ
01e1PUSH20x1faa
01e4JUMPI
01e5DUP1
01e6PUSH40x7203b807
01ebEQ
01ecPUSH20x1b8d
01efJUMPI
01f0DUP1
01f1PUSH40x76e058a8
01f6EQ
01f7PUSH20x078d
01faJUMPI
01fbDUP1
01fcPUSH40x79fee94a
0201EQ
0202PUSH20x104f
0205JUMPI
0206DUP1
0207PUSH40x7a9730d7
020cEQ
020dPUSH20x1b53
0210JUMPI
0211DUP1
0212PUSH40x7b103999
0217EQ
0218PUSH20x1b0f
021bJUMPI
021cDUP1
021dPUSH40x7d211cba
0222EQ
0223PUSH20x197f
0226JUMPI
0227DUP1
0228PUSH40x80d78ea9
022dEQ
022ePUSH20x1709
0231JUMPI
0232DUP1
0233PUSH40x825c45cd
0238EQ
0239PUSH20x16d7
023cJUMPI
023dDUP1
023ePUSH40x82edfbd9
0243EQ
0244PUSH20x16b4
0247JUMPI
0248DUP1
0249PUSH40x87a2d8ae
024eEQ
024fPUSH20x167a
0252JUMPI
0253DUP1
0254PUSH40x87bba45c
0259EQ
025aPUSH20x1623
025dJUMPI
025eDUP1
025fPUSH40x87c28345
0264EQ
0265PUSH20x1608
0268JUMPI
0269DUP1
026aPUSH40x89106905
026fEQ
0270PUSH20x1562
0273JUMPI
0274DUP1
0275PUSH40x8c3712dc
027aEQ
027bPUSH20x104f
027eJUMPI
027fDUP1
0280PUSH40x8ca9a268
0285EQ
0286PUSH20x1509
0289JUMPI
028aDUP1
028bPUSH40x8e808c09
0290EQ
0291PUSH20x14de
0294JUMPI
0295DUP1
0296PUSH40x915ed0bf
029bEQ
029cPUSH20x14b6
029fJUMPI
02a0DUP1
02a1PUSH40x98366e35
02a6EQ
02a7PUSH20x149b
02aaJUMPI
02abDUP1
02acPUSH40x9ca3c9c4
02b1EQ
02b2PUSH20x13e7
02b5JUMPI
02b6DUP1
02b7PUSH40xa12af04d
02bcEQ
02bdPUSH20x1054
02c0JUMPI
02c1DUP1
02c2PUSH40xa2f2d26a
02c7EQ
02c8PUSH20x104f
02cbJUMPI
02ccDUP1
02cdPUSH40xa7b0e4d0
02d2EQ
02d3PUSH20x101a
02d6JUMPI
02d7DUP1
02d8PUSH40xab50578e
02ddEQ
02dePUSH20x0e4e
02e1JUMPI
02e2DUP1
02e3PUSH40xabf1570d
02e8EQ
02e9PUSH20x0d53
02ecJUMPI
02edDUP1
02eePUSH40xac81af2f
02f3EQ
02f4PUSH20x0cf7
02f7JUMPI
02f8DUP1
02f9PUSH40xae1be128
02feEQ
02ffPUSH20x0cbd
0302JUMPI
0303DUP1
0304PUSH40xb31ec19e
0309EQ
030aPUSH20x0b7e
030dJUMPI
030eDUP1
030fPUSH40xb5635867
0314EQ
0315PUSH20x0b44
0318JUMPI
0319DUP1
031aPUSH40xb7e7e92e
031fEQ
0320PUSH20x0b3f
0323JUMPI
0324DUP1
0325PUSH40xbab83782
032aEQ
032bPUSH20x0b3f
032eJUMPI
032fDUP1
0330PUSH40xc0347686
0335EQ
0336PUSH20x0b22
0339JUMPI
033aDUP1
033bPUSH40xc161552b
0340EQ
0341PUSH20x090d
0344JUMPI
0345DUP1
0346PUSH40xc1dac071
034bEQ
034cPUSH20x070e
034fJUMPI
0350DUP1
0351PUSH40xc2b9f48e
0356EQ
0357PUSH20x08a6
035aJUMPI
035bDUP1
035cPUSH40xc5eeeb39
0361EQ
0362PUSH20x088b
0365JUMPI
0366DUP1
0367PUSH40xcf3add5d
036cEQ
036dPUSH20x0859
0370JUMPI
0371DUP1
0372PUSH40xd2430e0e
0377EQ
0378PUSH20x07ad
037bJUMPI
037cDUP1
037dPUSH40xd5284ee3
0382EQ
0383PUSH20x0792
0386JUMPI
0387DUP1
0388PUSH40xd8dea2bf
038dEQ
038ePUSH20x078d
0391JUMPI
0392DUP1
0393PUSH40xe0236d37
0398EQ
0399PUSH20x0759
039cJUMPI
039dDUP1
039ePUSH40xe0b180f7
03a3EQ
03a4PUSH20x073f
03a7JUMPI
03a8DUP1
03a9PUSH40xe9750fcf
03aeEQ
03afPUSH20x0713
03b2JUMPI
03b3DUP1
03b4PUSH40xeaff2dda
03b9EQ
03baPUSH20x070e
03bdJUMPI
03beDUP1
03bfPUSH40xeb75174b
03c4EQ
03c5PUSH20x06f2
03c8JUMPI
03c9DUP1
03caPUSH40xec0bb249
03cfEQ
03d0PUSH20x046d
03d3JUMPI
03d4DUP1
03d5PUSH40xf501cff3
03daEQ
03dbPUSH20x043b
03deJUMPI
03dfDUP1
03e0PUSH40xf5951975
03e5EQ
03e6PUSH20x03fc
03e9JUMPI
03eaPUSH40xfd1d0819
03efEQ
03f0PUSH20x03f7
03f3JUMPI
03f4PUSH0
03f5DUP1
03f6REVERT
03f7JUMPDEST
03f8PUSH20x3d9a
03fbJUMP
03fcJUMPDEST
03fdCALLVALUE
03fePUSH20x0437
0401JUMPI
0402PUSH10x20
0404CALLDATASIZE
0405PUSH10x03
0407NOT
0408ADD
0409SLT
040aPUSH20x0437
040dJUMPI
040ePUSH10xff
0410PUSH20x0417
0413PUSH20x3ad7
0416JUMP
0417JUMPDEST
0418AND
0419PUSH0
041aMSTORE
041bPUSH10x2b
041dPUSH10x20
041fMSTORE
0420PUSH10x20
0422PUSH10x01
0424PUSH10x01
0426PUSH10x40
0428SHL
0429SUB
042aPUSH10x40
042cPUSH0
042dKECCAK256
042eSLOAD
042fAND
0430PUSH10x40
0432MLOAD
0433SWAP1
0434DUP2
0435MSTORE
0436RETURN
0437JUMPDEST
0438PUSH0
0439DUP1
043aREVERT
043bJUMPDEST
043cCALLVALUE
043dPUSH20x0437
0440JUMPI
0441PUSH10x20
0443CALLDATASIZE
0444PUSH10x03
0446NOT
0447ADD
0448SLT
0449PUSH20x0437
044cJUMPI
044dPUSH10xff
044fPUSH20x0456
0452PUSH20x3ad7
0455JUMP
0456JUMPDEST
0457AND
0458PUSH0
0459MSTORE
045aPUSH10x23
045cPUSH10x20
045eMSTORE
045fPUSH10x20
0461PUSH10x40
0463PUSH0
0464KECCAK256
0465SLOAD
0466PUSH10x40
0468MLOAD
0469SWAP1
046aDUP2
046bMSTORE
046cRETURN
046dJUMPDEST
046eCALLVALUE
046fPUSH20x0437
0472JUMPI
0473PUSH0
0474CALLDATASIZE
0475PUSH10x03
0477NOT
0478ADD
0479SLT
047aPUSH20x0437
047dJUMPI
047ePUSH0
047fPUSH10x01
0481JUMPDEST
0482PUSH10xff
0484DUP2
0485AND
0486PUSH10x08
0488DUP2
0489GT
048aPUSH20x06eb
048dJUMPI
048eDUP1
048fPUSH0
0490MSTORE
0491PUSH10x27
0493PUSH10x20
0495MSTORE
0496PUSH10x01
0498PUSH10x01
049aPUSH10x40
049cSHL
049dSUB
049ePUSH10x40
04a0PUSH0
04a1KECCAK256
04a2SLOAD
04a3AND
04a4SWAP1
04a5PUSH0
04a6MSTORE
04a7PUSH10x2a
04a9PUSH10x20
04abMSTORE
04acPUSH10x01
04aePUSH10x01
04b0PUSH10x40
04b2SHL
04b3SUB
04b4PUSH10x40
04b6PUSH0
04b7KECCAK256
04b8SLOAD
04b9AND
04baSUB
04bbPUSH20x04cc
04beJUMPI
04bfPUSH20x04c7
04c2SWAP1
04c3PUSH20x3feb
04c6JUMP
04c7JUMPDEST
04c8PUSH20x0481
04cbJUMP
04ccJUMPDEST
04cdPOP
04cePOP
04cfPUSH10x01
04d1JUMPDEST
04d2ISZERO
04d3PUSH20x06dc
04d6JUMPI
04d7PUSH10x01
04d9PUSH10x01
04dbPUSH10x40
04ddSHL
04deSUB
04dfPUSH20x04eb
04e2DUP2
04e3PUSH10x29
04e5SLOAD
04e6AND
04e7PUSH20x3e52
04eaJUMP
04ebJUMPDEST
04ecAND
04edDUP1
04eePUSH0
04efMSTORE
04f0PUSH10x28
04f2PUSH10x20
04f4MSTORE
04f5PUSH10x40
04f7PUSH0
04f8KECCAK256
04f9SWAP1
04faPUSH10x01
04fcJUMPDEST
04fdPUSH10xff
04ffDUP2
0500AND
0501SWAP1
0502PUSH10x08
0504DUP3
0505GT
0506PUSH20x057e
0509JUMPI
050aDUP2
050bPUSH20x0579
050eSWAP3
050fPUSH0
0510MSTORE
0511PUSH10x26
0513PUSH10x20
0515MSTORE
0516PUSH10x40
0518PUSH0
0519KECCAK256
051aSLOAD
051bPUSH20x0524
051eDUP4
051fDUP8
0520PUSH20x4120
0523JUMP
0524JUMPDEST
0525DUP2
0526SWAP3
0527SWAP2
0528SLOAD
0529SWAP1
052aPUSH10x03
052cSHL
052dSWAP2
052eDUP3
052fSHL
0530SWAP2
0531PUSH0
0532NOT
0533SWAP1
0534SHL
0535NOT
0536AND
0537OR
0538SWAP1
0539SSTORE
053aDUP1
053bPUSH0
053cMSTORE
053dPUSH10x27
053fPUSH10x20
0541MSTORE
0542PUSH10x01
0544PUSH10x01
0546PUSH10x40
0548SHL
0549SUB
054aPUSH10x40
054cPUSH0
054dKECCAK256
054eSLOAD
054fAND
0550SWAP1
0551PUSH0
0552MSTORE
0553PUSH10x2a
0555PUSH10x20
0557MSTORE
0558PUSH10x01
055aPUSH10x01
055cPUSH10x40
055eSHL
055fSUB
0560PUSH10x40
0562PUSH0
0563KECCAK256
0564SWAP2
0565AND
0566PUSH10x01
0568PUSH10x01
056aPUSH10x40
056cSHL
056dSUB
056eNOT
056fDUP3
0570SLOAD
0571AND
0572OR
0573SWAP1
0574SSTORE
0575PUSH20x3feb
0578JUMP
0579JUMPDEST
057aPUSH20x04fc
057dJUMP
057eJUMPDEST
057fPOP
0580POP
0581PUSH20x0591
0584PUSH20x058c
0587DUP4
0588PUSH20x404d
058bJUMP
058cJUMPDEST
058dPUSH20x612d
0590JUMP
0591JUMPDEST
0592SWAP2
0593DUP3
0594MLOAD
0595JUMPDEST
0596PUSH10x01
0598DUP2
0599GT
059aPUSH20x065e
059dJUMPI
059ePOP
059fDUP3
05a0MLOAD
05a1ISZERO
05a2PUSH20x064a
05a5JUMPI
05a6PUSH320x5a33796ad97fb8cf3289d33093f3deb1797b5bcf1e795e716194046784ca4d3c
05c7PUSH10x40
05c9PUSH10x0a
05cbDUP5
05ccSWAP4
05cdPUSH10x20
05cfDUP1
05d0SWAP8
05d1ADD
05d2MLOAD
05d3PUSH10x09
05d5DUP3
05d6ADD
05d7SSTORE
05d8ADD
05d9PUSH10x01
05dbPUSH10x01
05ddPUSH10x40
05dfSHL
05e0SUB
05e1DUP1
05e2NUMBER
05e3AND
05e4AND
05e5PUSH10x01
05e7PUSH10x01
05e9PUSH10x40
05ebSHL
05ecSUB
05edNOT
05eeDUP3
05efSLOAD
05f0AND
05f1OR
05f2DUP2
05f3SSTORE
05f4DUP1
05f5SLOAD
05f6PUSH80xffffffffffffffff
05ffPUSH10x40
0601SHL
0602TIMESTAMP
0603DUP5
0604SHL
0605AND
0606SWAP1
0607PUSH80xffffffffffffffff
0610PUSH10x40
0612SHL
0613NOT
0614AND
0615OR
0616DUP2
0617SSTORE
0618DUP4
0619PUSH10x01
061bPUSH10x01
061dPUSH10x40
061fSHL
0620SUB
0621NOT
0622PUSH10x29
0624SLOAD
0625AND
0626OR
0627PUSH10x29
0629SSTORE
062aSLOAD
062bPUSH10x01
062dPUSH10x01
062fPUSH10x40
0631SHL
0632SUB
0633DUP3
0634MLOAD
0635SWAP2
0636DUP2
0637DUP2
0638AND
0639DUP4
063aMSTORE
063bDUP4
063cSHR
063dAND
063eDUP7
063fDUP3
0640ADD
0641MSTORE
0642LOG2
0643PUSH10x40
0645MLOAD
0646SWAP1
0647DUP2
0648MSTORE
0649RETURN
064aJUMPDEST
064bPUSH40x4e487b71
0650PUSH10xe0
0652SHL
0653PUSH0
0654MSTORE
0655PUSH10x32
0657PUSH10x04
0659MSTORE
065aPUSH10x24
065cPUSH0
065dREVERT
065eJUMPDEST
065fPUSH0
0660JUMPDEST
0661DUP2
0662PUSH10x01
0664SHR
0665DUP2
0666LT
0667PUSH20x0673
066aJUMPI
066bPOP
066cPUSH10x01
066eSHR
066fPUSH20x0595
0672JUMP
0673JUMPDEST
0674PUSH10x01
0676DUP2
0677SWAP1
0678SHL
0679SWAP1
067aPUSH10x01
067cPUSH10x01
067ePUSH10xff
0680SHL
0681SUB
0682DUP2
0683AND
0684DUP2
0685SUB
0686PUSH20x06c8
0689JUMPI
068aPUSH20x0693
068dDUP3
068eDUP8
068fPUSH20x3e70
0692JUMP
0693JUMPDEST
0694MLOAD
0695SWAP2
0696PUSH10x01
0698DUP2
0699ADD
069aDUP1
069bSWAP2
069cGT
069dPUSH20x06c8
06a0JUMPI
06a1PUSH10x01
06a3SWAP3
06a4PUSH20x06b0
06a7PUSH20x06b7
06aaSWAP3
06abDUP10
06acPUSH20x3e70
06afJUMP
06b0JUMPDEST
06b1MLOAD
06b2SWAP1
06b3PUSH20x61a9
06b6JUMP
06b7JUMPDEST
06b8PUSH20x06c1
06bbDUP3
06bcDUP9
06bdPUSH20x3e70
06c0JUMP
06c1JUMPDEST
06c2MSTORE
06c3ADD
06c4PUSH20x0660
06c7JUMP
06c8JUMPDEST
06c9PUSH40x4e487b71
06cePUSH10xe0
06d0SHL
06d1PUSH0
06d2MSTORE
06d3PUSH10x11
06d5PUSH10x04
06d7MSTORE
06d8PUSH10x24
06daPUSH0
06dbREVERT
06dcJUMPDEST
06ddPUSH40x0eba0e1b
06e2PUSH10xe2
06e4SHL
06e5PUSH0
06e6MSTORE
06e7PUSH10x04
06e9PUSH0
06eaREVERT
06ebJUMPDEST
06ecPOP
06edPOP
06eePUSH20x04d1
06f1JUMP
06f2JUMPDEST
06f3CALLVALUE
06f4PUSH20x0437
06f7JUMPI
06f8PUSH0
06f9CALLDATASIZE
06faPUSH10x03
06fcNOT
06fdADD
06feSLT
06ffPUSH20x0437
0702JUMPI
0703PUSH10x20
0705PUSH10x40
0707MLOAD
0708PUSH20x0400
070bDUP2
070cMSTORE
070dRETURN
070eJUMPDEST
070fPUSH20x3e13
0712JUMP
0713JUMPDEST
0714CALLVALUE
0715PUSH20x0437
0718JUMPI
0719PUSH10x20
071bCALLDATASIZE
071cPUSH10x03
071eNOT
071fADD
0720SLT
0721PUSH20x0437
0724JUMPI
0725PUSH10x04
0727CALLDATALOAD
0728PUSH10x1d
072aDUP2
072bLT
072cISZERO
072dPUSH20x064a
0730JUMPI
0731PUSH10x20
0733SWAP1
0734PUSH10x04
0736ADD
0737SLOAD
0738PUSH10x40
073aMLOAD
073bSWAP1
073cDUP2
073dMSTORE
073eRETURN
073fJUMPDEST
0740CALLVALUE
0741PUSH20x0437
0744JUMPI
0745PUSH0
0746CALLDATASIZE
0747PUSH10x03
0749NOT
074aADD
074bSLT
074cPUSH20x0437
074fJUMPI
0750PUSH10x20
0752PUSH10x40
0754MLOAD
0755PUSH0
0756DUP2
0757MSTORE
0758RETURN
0759JUMPDEST
075aCALLVALUE
075bPUSH20x0437
075eJUMPI
075fPUSH10x40
0761CALLDATASIZE
0762PUSH10x03
0764NOT
0765ADD
0766SLT
0767PUSH20x0437
076aJUMPI
076bPUSH10x20
076dPUSH20x0785
0770PUSH20x0777
0773PUSH20x39eb
0776JUMP
0777JUMPDEST
0778PUSH20x077f
077bPUSH20x3a57
077eJUMP
077fJUMPDEST
0780SWAP1
0781PUSH20x43e4
0784JUMP
0785JUMPDEST
0786PUSH10x40
0788MLOAD
0789SWAP1
078aDUP2
078bMSTORE
078cRETURN
078dJUMPDEST
078ePUSH20x3dd0
0791JUMP
0792JUMPDEST
0793CALLVALUE
0794PUSH20x0437
0797JUMPI
0798PUSH0
0799CALLDATASIZE
079aPUSH10x03
079cNOT
079dADD
079eSLT
079fPUSH20x0437
07a2JUMPI
07a3PUSH10x20
07a5PUSH10x40
07a7MLOAD
07a8PUSH10x14
07aaDUP2
07abMSTORE
07acRETURN
07adJUMPDEST
07aeCALLVALUE
07afPUSH20x0437
07b2JUMPI
07b3PUSH10x40
07b5CALLDATASIZE
07b6PUSH10x03
07b8NOT
07b9ADD
07baSLT
07bbPUSH20x0437
07beJUMPI
07bfPUSH20x07c6
07c2PUSH20x3ad7
07c5JUMP
07c6JUMPDEST
07c7PUSH20x07cf
07caDUP2
07cbPUSH20x5f46
07ceJUMP
07cfJUMPDEST
07d0PUSH20x07db
07d3PUSH10x24
07d5CALLDATALOAD
07d6DUP3
07d7PUSH20x3e84
07daJUMP
07dbJUMPDEST
07dcSWAP1
07ddPUSH10x40
07dfMLOAD
07e0SWAP2
07e1PUSH20x0320
07e4PUSH20x07ed
07e7DUP2
07e8DUP6
07e9PUSH20x3b3c
07ecJUMP
07edJUMPDEST
07eePUSH10x18
07f0DUP5
07f1MSTORE
07f2PUSH10x1f
07f4NOT
07f5ADD
07f6CALLDATASIZE
07f7PUSH10x20
07f9DUP6
07faADD
07fbCALLDATACOPY
07fcPUSH0
07fdSWAP1
07feJUMPDEST
07ffPUSH10x18
0801DUP3
0802LT
0803PUSH20x0820
0806JUMPI
0807PUSH10x40
0809MLOAD
080aPUSH10x20
080cDUP1
080dDUP3
080eMSTORE
080fDUP2
0810SWAP1
0811PUSH20x081c
0814SWAP1
0815DUP3
0816ADD
0817DUP8
0818PUSH20x3d40
081bJUMP
081cJUMPDEST
081dSUB
081eSWAP1
081fRETURN
0820JUMPDEST
0821DUP1
0822PUSH20x0830
0825PUSH10x01
0827DUP1
0828SWAP4
0829XOR
082aDUP5
082bDUP7
082cPUSH20x5f96
082fJUMP
0830JUMPDEST
0831PUSH20x083a
0834DUP5
0835DUP8
0836PUSH20x3e70
0839JUMP
083aJUMPDEST
083bMSTORE
083cDUP2
083dSHR
083eSWAP2
083fADD
0840SWAP1
0841PUSH20x07fe
0844JUMP
0845JUMPDEST
0846PUSH40x4e487b71
084bPUSH10xe0
084dSHL
084ePUSH0
084fMSTORE
0850PUSH10x41
0852PUSH10x04
0854MSTORE
0855PUSH10x24
0857PUSH0
0858REVERT
0859JUMPDEST
085aCALLVALUE
085bPUSH20x0437
085eJUMPI
085fPUSH10x20
0861CALLDATASIZE
0862PUSH10x03
0864NOT
0865ADD
0866SLT
0867PUSH20x0437
086aJUMPI
086bPUSH10xff
086dPUSH20x0874
0870PUSH20x3ad7
0873JUMP
0874JUMPDEST
0875AND
0876PUSH0
0877MSTORE
0878PUSH10x01
087aPUSH10x20
087cMSTORE
087dPUSH10x20
087fPUSH10x40
0881PUSH0
0882KECCAK256
0883SLOAD
0884PUSH10x40
0886MLOAD
0887SWAP1
0888DUP2
0889MSTORE
088aRETURN
088bJUMPDEST
088cCALLVALUE
088dPUSH20x0437
0890JUMPI
0891PUSH0
0892CALLDATASIZE
0893PUSH10x03
0895NOT
0896ADD
0897SLT
0898PUSH20x0437
089bJUMPI
089cPUSH10x20
089ePUSH10x40
08a0MLOAD
08a1PUSH10x1c
08a3DUP2
08a4MSTORE
08a5RETURN
08a6JUMPDEST
08a7CALLVALUE
08a8PUSH20x0437
08abJUMPI
08acPUSH10x40
08aeCALLDATASIZE
08afPUSH10x03
08b1NOT
08b2ADD
08b3SLT
08b4PUSH20x0437
08b7JUMPI
08b8PUSH10x20
08baPUSH10x40
08bcMLOAD
08bdDUP2
08beDUP2
08bfADD
08c0SWAP1
08c1PUSH320xf12bce0649327409848ceec32acf20a7e21971869cbf30f9d92357bbe85565b7
08e2DUP3
08e3MSTORE
08e4PUSH10x04
08e6CALLDATALOAD
08e7PUSH10x40
08e9DUP3
08eaADD
08ebMSTORE
08ecPUSH10x24
08eeCALLDATALOAD
08efPUSH10x60
08f1DUP3
08f2ADD
08f3MSTORE
08f4PUSH10x60
08f6DUP2
08f7MSTORE
08f8PUSH20x0902
08fbPUSH10x80
08fdDUP3
08fePUSH20x3b3c
0901JUMP
0902JUMPDEST
0903MLOAD
0904SWAP1
0905KECCAK256
0906PUSH10x40
0908MLOAD
0909SWAP1
090aDUP2
090bMSTORE
090cRETURN
090dJUMPDEST
090eCALLVALUE
090fPUSH20x0437
0912JUMPI
0913PUSH10x60
0915CALLDATASIZE
0916PUSH10x03
0918NOT
0919ADD
091aSLT
091bPUSH20x0437
091eJUMPI
091fPUSH10x04
0921CALLDATALOAD
0922PUSH10x01
0924PUSH10x01
0926PUSH10x40
0928SHL
0929SUB
092aDUP2
092bGT
092cPUSH20x0437
092fJUMPI
0930PUSH20x093d
0933SWAP1
0934CALLDATASIZE
0935SWAP1
0936PUSH10x04
0938ADD
0939PUSH20x39bb
093cJUMP
093dJUMPDEST
093eSWAP1
093fPUSH20x0946
0942PUSH20x3a57
0945JUMP
0946JUMPDEST
0947SWAP1
0948PUSH10x44
094aCALLDATALOAD
094bPUSH10x01
094dPUSH10x01
094fPUSH10x40
0951SHL
0952SUB
0953DUP2
0954GT
0955PUSH20x0437
0958JUMPI
0959PUSH20x0966
095cSWAP1
095dCALLDATASIZE
095eSWAP1
095fPUSH10x04
0961ADD
0962PUSH20x39bb
0965JUMP
0966JUMPDEST
0967SWAP1
0968SWAP3
0969PUSH10x01
096bPUSH0
096cMSTORE
096dPUSH0
096ePUSH10x20
0970MSTORE
0971PUSH10x40
0973PUSH0
0974KECCAK256
0975SLOAD
0976DUP1
0977ISZERO
0978PUSH20x0b0e
097bJUMPI
097cPUSH20x0984
097fDUP7
0980PUSH20x3fb9
0983JUMP
0984JUMPDEST
0985SWAP5
0986PUSH20x098e
0989DUP8
098aPUSH20x3fb9
098dJUMP
098eJUMPDEST
098fSWAP5
0990PUSH0
0991JUMPDEST
0992DUP9
0993DUP2
0994LT
0995PUSH20x0ab8
0998JUMPI
0999POP
099aPOP
099bSWAP2
099cPUSH20x0a50
099fSWAP2
09a0PUSH20x0a56
09a3SWAP5
09a4SWAP4
09a5PUSH10x01
09a7PUSH0
09a8MSTORE
09a9PUSH10x2b
09abPUSH10x20
09adMSTORE
09aePUSH10x01
09b0PUSH10x01
09b2PUSH10x40
09b4SHL
09b5SUB
09b6PUSH10x40
09b8PUSH0
09b9KECCAK256
09baSLOAD
09bbAND
09bcSWAP5
09bdPUSH20x0a1a
09c0DUP7
09c1PUSH20x09f0
09c4PUSH20x0a10
09c7DUP13
09c8PUSH20x0a02
09cbDUP14
09ccPUSH10x40
09ceMLOAD
09cfSWAP5
09d0DUP6
09d1SWAP4
09d2PUSH10x20
09d4DUP6
09d5ADD
09d6SWAP8
09d7PUSH10x01
09d9DUP10
09daMSTORE
09dbPUSH10x40
09ddDUP7
09deADD
09dfMSTORE
09e0PUSH10x80
09e2PUSH10x60
09e4DUP7
09e5ADD
09e6MSTORE
09e7PUSH10xa0
09e9DUP6
09eaADD
09ebSWAP1
09ecPUSH20x3d40
09efJUMP
09f0JUMPDEST
09f1DUP4
09f2DUP2
09f3SUB
09f4PUSH10x1f
09f6NOT
09f7ADD
09f8PUSH10x80
09faDUP6
09fbADD
09fcMSTORE
09fdSWAP1
09fePUSH20x3d40
0a01JUMP
0a02JUMPDEST
0a03SUB
0a04PUSH10x1f
0a06NOT
0a07DUP2
0a08ADD
0a09DUP4
0a0aMSTORE
0a0bDUP3
0a0cPUSH20x3b3c
0a0fJUMP
0a10JUMPDEST
0a11MLOAD
0a12SWAP1
0a13KECCAK256
0a14DUP6
0a15ADDRESS
0a16PUSH20x5a89
0a19JUMP
0a1aJUMPDEST
0a1bSWAP1
0a1cPUSH10x01
0a1ePUSH0
0a1fMSTORE
0a20PUSH10x01
0a22PUSH10x20
0a24MSTORE
0a25PUSH10x40
0a27PUSH0
0a28KECCAK256
0a29SLOAD
0a2aSWAP3
0a2bPUSH320x000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6
0a4cPUSH20x5dac
0a4fJUMP
0a50JUMPDEST
0a51POP
0a52PUSH20x3e52
0a55JUMP
0a56JUMPDEST
0a57PUSH10x01
0a59PUSH0
0a5aMSTORE
0a5bPUSH10x2b
0a5dPUSH10x20
0a5fMSTORE
0a60PUSH10x01
0a62PUSH10x01
0a64PUSH10x40
0a66SHL
0a67SUB
0a68PUSH10x40
0a6aPUSH0
0a6bKECCAK256
0a6cSWAP2
0a6dAND
0a6ePUSH10x01
0a70PUSH10x01
0a72PUSH10x40
0a74SHL
0a75SUB
0a76NOT
0a77DUP3
0a78SLOAD
0a79AND
0a7aOR
0a7bSWAP1
0a7cSSTORE
0a7dPUSH0
0a7eJUMPDEST
0a7fDUP4
0a80DUP2
0a81LT
0a82PUSH20x0a90
0a85JUMPI
0a86PUSH20x0a8e
0a89DUP5
0a8aPUSH20x4951
0a8dJUMP
0a8eJUMPDEST
0a8fSTOP
0a90JUMPDEST
0a91DUP1
0a92PUSH20x0ab2
0a95PUSH20x0aa0
0a98PUSH10x01
0a9aSWAP4
0a9bDUP7
0a9cPUSH20x3e70
0a9fJUMP
0aa0JUMPDEST
0aa1MLOAD
0aa2PUSH20x0aab
0aa5DUP4
0aa6DUP7
0aa7PUSH20x3e70
0aaaJUMP
0aabJUMPDEST
0aacMLOAD
0aadSWAP1
0aaePUSH20x4436
0ab1JUMP
0ab2JUMPDEST
0ab3ADD
0ab4PUSH20x0a7e
0ab7JUMP
0ab8JUMPDEST
0ab9DUP1
0abaPUSH20x0ad6
0abdPUSH20x0ad1
0ac0PUSH20x0acc
0ac3PUSH10x01
0ac5SWAP5
0ac6DUP14
0ac7DUP8
0ac8PUSH20x417d
0acbJUMP
0accJUMPDEST
0acdPUSH20x3e3e
0ad0JUMP
0ad1JUMPDEST
0ad2PUSH20x3f70
0ad5JUMP
0ad6JUMPDEST
0ad7PUSH20x0ae0
0adaDUP3
0adbDUP12
0adcPUSH20x3e70
0adfJUMP
0ae0JUMPDEST
0ae1MSTORE
0ae2PUSH20x0afd
0ae5PUSH20x0af8
0ae8CALLDATASIZE
0ae9PUSH20x0af3
0aecDUP5
0aedDUP15
0aeeDUP9
0aefPUSH20x417d
0af2JUMP
0af3JUMPDEST
0af4PUSH20x3bde
0af7JUMP
0af8JUMPDEST
0af9PUSH20x41a0
0afcJUMP
0afdJUMPDEST
0afePUSH20x0b07
0b01DUP3
0b02DUP11
0b03PUSH20x3e70
0b06JUMP
0b07JUMPDEST
0b08MSTORE
0b09ADD
0b0aPUSH20x0991
0b0dJUMP
0b0eJUMPDEST
0b0fPUSH40x47c987b5
0b14PUSH10xe1
0b16SHL
0b17PUSH0
0b18MSTORE
0b19PUSH10x01
0b1bPUSH10x04
0b1dMSTORE
0b1ePUSH10x24
0b20PUSH0
0b21REVERT
0b22JUMPDEST
0b23CALLVALUE
0b24PUSH20x0437
0b27JUMPI
0b28PUSH0
0b29CALLDATASIZE
0b2aPUSH10x03
0b2cNOT
0b2dADD
0b2eSLT
0b2fPUSH20x0437
0b32JUMPI
0b33PUSH10x20
0b35PUSH10x40
0b37MLOAD
0b38PUSH30x100000
0b3cDUP2
0b3dMSTORE
0b3eRETURN
0b3fJUMPDEST
0b40PUSH20x3deb
0b43JUMP
0b44JUMPDEST
0b45CALLVALUE
0b46PUSH20x0437
0b49JUMPI
0b4aPUSH0
0b4bCALLDATASIZE
0b4cPUSH10x03
0b4eNOT
0b4fADD
0b50SLT
0b51PUSH20x0437
0b54JUMPI
0b55PUSH10x20
0b57PUSH10x40
0b59MLOAD
0b5aPUSH320xb3b4169a40f71a0b1b279bcd6443676c80bdb4ebe0b1db4478b6879321174391
0b7bDUP2
0b7cMSTORE
0b7dRETURN
0b7eJUMPDEST
0b7fCALLVALUE
0b80PUSH20x0437
0b83JUMPI
0b84PUSH10x40
0b86CALLDATASIZE
0b87PUSH10x03
0b89NOT
0b8aADD
0b8bSLT
0b8cPUSH20x0437
0b8fJUMPI
0b90PUSH20x0b97
0b93PUSH20x3a2b
0b96JUMP
0b97JUMPDEST
0b98PUSH10x01
0b9aPUSH10x01
0b9cPUSH10x40
0b9eSHL
0b9fSUB
0ba0PUSH20x0ba7
0ba3PUSH20x3ae7
0ba6JUMP
0ba7JUMPDEST
0ba8SWAP2
0ba9PUSH20x0bb1
0bacDUP4
0badPUSH20x5f46
0bb0JUMP
0bb1JUMPDEST
0bb2AND
0bb3DUP1
0bb4ISZERO
0bb5DUP1
0bb6ISZERO
0bb7PUSH20x0ca9
0bbaJUMPI
0bbbJUMPDEST
0bbcPUSH20x0c9a
0bbfJUMPI
0bc0PUSH0
0bc1MSTORE
0bc2PUSH10x28
0bc4PUSH10x20
0bc6MSTORE
0bc7PUSH20x0bd5
0bcaPUSH20x058c
0bcdPUSH10x40
0bcfPUSH0
0bd0KECCAK256
0bd1PUSH20x404d
0bd4JUMP
0bd5JUMPDEST
0bd6SWAP1
0bd7PUSH10xff
0bd9PUSH10x40
0bdbMLOAD
0bdcSWAP2
0bddPUSH20x0be7
0be0PUSH10xa0
0be2DUP5
0be3PUSH20x3b3c
0be6JUMP
0be7JUMPDEST
0be8PUSH10x04
0beaDUP4
0bebMSTORE
0becPUSH10x80
0beeCALLDATASIZE
0befPUSH10x20
0bf1DUP6
0bf2ADD
0bf3CALLDATACOPY
0bf4AND
0bf5DUP3
0bf6MLOAD
0bf7PUSH0
0bf8SWAP1
0bf9JUMPDEST
0bfaPUSH10x04
0bfcDUP3
0bfdLT
0bfePUSH20x0c17
0c01JUMPI
0c02PUSH10x40
0c04MLOAD
0c05PUSH10x20
0c07DUP1
0c08DUP3
0c09MSTORE
0c0aDUP2
0c0bSWAP1
0c0cPUSH20x081c
0c0fSWAP1
0c10DUP3
0c11ADD
0c12DUP8
0c13PUSH20x3d40
0c16JUMP
0c17JUMPDEST
0c18PUSH20x0c24
0c1bPUSH10x01
0c1dDUP5
0c1eXOR
0c1fDUP7
0c20PUSH20x3e70
0c23JUMP
0c24JUMPDEST
0c25MLOAD
0c26PUSH20x0c2f
0c29DUP4
0c2aDUP7
0c2bPUSH20x3e70
0c2eJUMP
0c2fJUMPDEST
0c30MSTORE
0c31PUSH10x01
0c33SHR
0c34SWAP2
0c35PUSH0
0c36JUMPDEST
0c37DUP4
0c38DUP2
0c39LT
0c3aPUSH20x0c4c
0c3dJUMPI
0c3ePOP
0c3fPUSH10x01
0c41SWAP1
0c42DUP2
0c43SHR
0c44SWAP2
0c45ADD
0c46SWAP1
0c47SWAP2
0c48PUSH20x0bf9
0c4bJUMP
0c4cJUMPDEST
0c4dPUSH10x01
0c4fDUP2
0c50SWAP1
0c51SHL
0c52SWAP1
0c53PUSH10x01
0c55PUSH10x01
0c57PUSH10xff
0c59SHL
0c5aSUB
0c5bDUP2
0c5cAND
0c5dDUP2
0c5eSUB
0c5fPUSH20x06c8
0c62JUMPI
0c63PUSH20x0c6c
0c66DUP3
0c67DUP9
0c68PUSH20x3e70
0c6bJUMP
0c6cJUMPDEST
0c6dMLOAD
0c6eSWAP2
0c6fPUSH10x01
0c71DUP2
0c72ADD
0c73DUP1
0c74SWAP2
0c75GT
0c76PUSH20x06c8
0c79JUMPI
0c7aPUSH10x01
0c7cSWAP3
0c7dPUSH20x06b0
0c80PUSH20x0c89
0c83SWAP3
0c84DUP11
0c85PUSH20x3e70
0c88JUMP
0c89JUMPDEST
0c8aPUSH20x0c93
0c8dDUP3
0c8eDUP10
0c8fPUSH20x3e70
0c92JUMP
0c93JUMPDEST
0c94MSTORE
0c95ADD
0c96PUSH20x0c36
0c99JUMP
0c9aJUMPDEST
0c9bPUSH40x12c6ca05
0ca0PUSH10xe2
0ca2SHL
0ca3PUSH0
0ca4MSTORE
0ca5PUSH10x04
0ca7PUSH0
0ca8REVERT
0ca9JUMPDEST
0caaPOP
0cabPUSH10x01
0cadPUSH10x01
0cafPUSH10x40
0cb1SHL
0cb2SUB
0cb3PUSH10x29
0cb5SLOAD
0cb6AND
0cb7DUP2
0cb8GT
0cb9PUSH20x0bbb
0cbcJUMP
0cbdJUMPDEST
0cbeCALLVALUE
0cbfPUSH20x0437
0cc2JUMPI
0cc3PUSH0
0cc4CALLDATASIZE
0cc5PUSH10x03
0cc7NOT
0cc8ADD
0cc9SLT
0ccaPUSH20x0437
0ccdJUMPI
0ccePUSH10x20
0cd0PUSH10x40
0cd2MLOAD
0cd3PUSH320xaac3f59af44d7d33ac1e055e30909d4c9da73d4ab294df533f38b5e23d376e61
0cf4DUP2
0cf5MSTORE
0cf6RETURN
0cf7JUMPDEST
0cf8CALLVALUE
0cf9PUSH20x0437
0cfcJUMPI
0cfdPUSH10x40
0cffCALLDATASIZE
0d00PUSH10x03
0d02NOT
0d03ADD
0d04SLT
0d05PUSH20x0437
0d08JUMPI
0d09PUSH20x0d10
0d0cPUSH20x3ad7
0d0fJUMP
0d10JUMPDEST
0d11PUSH10xff
0d13AND
0d14PUSH0
0d15DUP2
0d16DUP2
0d17MSTORE
0d18PUSH10x21
0d1aPUSH10x20
0d1cSWAP1
0d1dDUP2
0d1eMSTORE
0d1fPUSH10x40
0d21DUP1
0d22DUP4
0d23KECCAK256
0d24PUSH10x24
0d26CALLDATALOAD
0d27DUP1
0d28DUP6
0d29MSTORE
0d2aSWAP1
0d2bDUP4
0d2cMSTORE
0d2dDUP2
0d2eDUP5
0d2fKECCAK256
0d30SLOAD
0d31SWAP5
0d32DUP5
0d33MSTORE
0d34PUSH10x25
0d36DUP4
0d37MSTORE
0d38DUP2
0d39DUP5
0d3aKECCAK256
0d3bSWAP1
0d3cDUP5
0d3dMSTORE
0d3eDUP3
0d3fMSTORE
0d40SWAP2
0d41DUP3
0d42SWAP1
0d43KECCAK256
0d44SLOAD
0d45DUP3
0d46MLOAD
0d47SWAP1
0d48DUP2
0d49MSTORE
0d4aSWAP3
0d4bISZERO
0d4cISZERO
0d4dSWAP1
0d4eDUP4
0d4fADD
0d50MSTORE
0d51SWAP1
0d52RETURN
0d53JUMPDEST
0d54CALLVALUE
0d55PUSH20x0437
0d58JUMPI
0d59PUSH10x80
0d5bCALLDATASIZE
0d5cPUSH10x03
0d5eNOT
0d5fADD
0d60SLT
0d61PUSH20x0437
0d64JUMPI
0d65PUSH20x0d6c
0d68PUSH20x3ad7
0d6bJUMP
0d6cJUMPDEST
0d6dPUSH20x0d74
0d70PUSH20x3ae7
0d73JUMP
0d74JUMPDEST
0d75SWAP1
0d76PUSH10x44
0d78CALLDATALOAD
0d79PUSH10x01
0d7bPUSH10x01
0d7dPUSH10x40
0d7fSHL
0d80SUB
0d81DUP2
0d82GT
0d83PUSH20x0437
0d86JUMPI
0d87PUSH20x0d94
0d8aSWAP1
0d8bCALLDATASIZE
0d8cSWAP1
0d8dPUSH10x04
0d8fADD
0d90PUSH20x39bb
0d93JUMP
0d94JUMPDEST
0d95SWAP2
0d96SWAP1
0d97PUSH10x64
0d99CALLDATALOAD
0d9aPUSH10x01
0d9cPUSH10x01
0d9ePUSH10x40
0da0SHL
0da1SUB
0da2DUP2
0da3GT
0da4PUSH20x0437
0da7JUMPI
0da8PUSH20x0db5
0dabSWAP1
0dacCALLDATASIZE
0dadSWAP1
0daePUSH10x04
0db0ADD
0db1PUSH20x39bb
0db4JUMP
0db5JUMPDEST
0db6SWAP5
0db7PUSH10xff
0db9DUP5
0dbaAND
0dbbPUSH0
0dbcMSTORE
0dbdPUSH10x2c
0dbfPUSH10x20
0dc1MSTORE
0dc2PUSH10x01
0dc4DUP1
0dc5PUSH10xa0
0dc7SHL
0dc8SUB
0dc9PUSH10x40
0dcbPUSH0
0dccKECCAK256
0dcdSLOAD
0dceAND
0dcfCALLER
0dd0SUB
0dd1PUSH20x0e3b
0dd4JUMPI
0dd5PUSH20x0dde
0dd8DUP2
0dd9DUP6
0ddaPUSH20x6102
0dddJUMP
0ddeJUMPDEST
0ddfDUP6
0de0DUP6
0de1SUB
0de2PUSH20x0e24
0de5JUMPI
0de6PUSH0
0de7JUMPDEST
0de8DUP6
0de9DUP2
0deaLT
0debPUSH20x0df8
0deeJUMPI
0defPUSH20x0a8e
0df2DUP7
0df3DUP7
0df4PUSH20x4996
0df7JUMP
0df8JUMPDEST
0df9DUP1
0dfaPUSH20x0e1e
0dfdPUSH20x0e09
0e00PUSH10x01
0e02SWAP4
0e03DUP10
0e04DUP9
0e05PUSH20x3e2e
0e08JUMP
0e09JUMPDEST
0e0aCALLDATALOAD
0e0bPUSH20x0e15
0e0eDUP4
0e0fDUP12
0e10DUP9
0e11PUSH20x3e2e
0e14JUMP
0e15JUMPDEST
0e16CALLDATALOAD
0e17SWAP1
0e18DUP6
0e19DUP10
0e1aPUSH20x46e0
0e1dJUMP
0e1eJUMPDEST
0e1fADD
0e20PUSH20x0de7
0e23JUMP
0e24JUMPDEST
0e25DUP6
0e26DUP6
0e27PUSH40x55c5b3e3
0e2cPUSH10xe1
0e2eSHL
0e2fPUSH0
0e30MSTORE
0e31PUSH10x04
0e33MSTORE
0e34PUSH10x24
0e36MSTORE
0e37PUSH10x44
0e39PUSH0
0e3aREVERT
0e3bJUMPDEST
0e3cPUSH40x4a0bfec1
0e41PUSH10xe0
0e43SHL
0e44PUSH0
0e45MSTORE
0e46CALLER
0e47PUSH10x04
0e49MSTORE
0e4aPUSH10x24
0e4cPUSH0
0e4dREVERT
0e4eJUMPDEST
0e4fCALLVALUE
0e50PUSH20x0437
0e53JUMPI
0e54PUSH10x80
0e56CALLDATASIZE
0e57PUSH10x03
0e59NOT
0e5aADD
0e5bSLT
0e5cPUSH20x0437
0e5fJUMPI
0e60PUSH10x04
0e62CALLDATALOAD
0e63PUSH10x01
0e65PUSH10x01
0e67PUSH10x40
0e69SHL
0e6aSUB
0e6bDUP2
0e6cGT
0e6dPUSH20x0437
0e70JUMPI
0e71PUSH20x0e7e
0e74SWAP1
0e75CALLDATASIZE
0e76SWAP1
0e77PUSH10x04
0e79ADD
0e7aPUSH20x39bb
0e7dJUMP
0e7eJUMPDEST
0e7fSWAP1
0e80PUSH20x0e87
0e83PUSH20x3a57
0e86JUMP
0e87JUMPDEST
0e88SWAP1
0e89PUSH20x0e90
0e8cPUSH20x3a41
0e8fJUMP
0e90JUMPDEST
0e91PUSH10x64
0e93CALLDATALOAD
0e94SWAP1
0e95PUSH10x01
0e97PUSH10x01
0e99PUSH10x40
0e9bSHL
0e9cSUB
0e9dDUP3
0e9eGT
0e9fPUSH20x0437
0ea2JUMPI
0ea3PUSH20x0eb3
0ea6PUSH20x0ef9
0ea9SWAP3
0eaaCALLDATASIZE
0eabSWAP1
0eacPUSH10x04
0eaeADD
0eafPUSH20x39bb
0eb2JUMP
0eb3JUMPDEST
0eb4SWAP2
0eb5PUSH10x40
0eb7MLOAD
0eb8PUSH10x20
0ebaDUP2
0ebbADD
0ebcSWAP1
0ebdPUSH10x40
0ebfDUP3
0ec0MSTORE
0ec1PUSH20x0ef1
0ec4DUP2
0ec5PUSH10x01
0ec7PUSH10x01
0ec9PUSH10x40
0ecbSHL
0eccSUB
0ecdPUSH20x0eda
0ed0PUSH10x60
0ed2DUP4
0ed3ADD
0ed4DUP14
0ed5DUP13
0ed6PUSH20x43a3
0ed9JUMP
0edaJUMPDEST
0edbSWAP11
0edcAND
0eddSWAP10
0edeDUP11
0edfPUSH10x40
0ee1DUP4
0ee2ADD
0ee3MSTORE
0ee4SUB
0ee5PUSH10x1f
0ee7NOT
0ee8DUP2
0ee9ADD
0eeaDUP4
0eebMSTORE
0eecDUP3
0eedPUSH20x3b3c
0ef0JUMP
0ef1JUMPDEST
0ef2MLOAD
0ef3SWAP1
0ef4KECCAK256
0ef5PUSH20x588a
0ef8JUMP
0ef9JUMPDEST
0efaPUSH10x08
0efcNOT
0efdDUP4
0efeADD
0effPUSH20x1007
0f02JUMPI
0f03PUSH10x01
0f05PUSH10x01
0f07PUSH10x40
0f09SHL
0f0aSUB
0f0bPUSH10x29
0f0dSLOAD
0f0eAND
0f0fPUSH20x0f48
0f12JUMPI
0f13PUSH10x01
0f15JUMPDEST
0f16PUSH10xff
0f18DUP2
0f19AND
0f1aPUSH10x08
0f1cDUP2
0f1dGT
0f1ePUSH20x0f57
0f21JUMPI
0f22PUSH0
0f23MSTORE
0f24PUSH10x27
0f26PUSH10x20
0f28MSTORE
0f29PUSH10x01
0f2bPUSH10x01
0f2dPUSH10x40
0f2fSHL
0f30SUB
0f31PUSH10x40
0f33PUSH0
0f34KECCAK256
0f35SLOAD
0f36AND
0f37PUSH20x0f48
0f3aJUMPI
0f3bPUSH20x0f43
0f3eSWAP1
0f3fPUSH20x3feb
0f42JUMP
0f43JUMPDEST
0f44PUSH20x0f15
0f47JUMP
0f48JUMPDEST
0f49PUSH40xdc63d81f
0f4ePUSH10xe0
0f50SHL
0f51PUSH0
0f52MSTORE
0f53PUSH10x04
0f55PUSH0
0f56REVERT
0f57JUMPDEST
0f58DUP3
0f59DUP6
0f5aDUP6
0f5bPUSH10x01
0f5dJUMPDEST
0f5ePUSH10xff
0f60DUP2
0f61AND
0f62PUSH10x08
0f64DUP2
0f65GT
0f66PUSH20x0fb0
0f69JUMPI
0f6aSWAP1
0f6bDUP2
0f6cPUSH20x0f81
0f6fPUSH20x0f7c
0f72PUSH20x0fab
0f75SWAP5
0f76DUP8
0f77DUP10
0f78PUSH20x3e2e
0f7bJUMP
0f7cJUMPDEST
0f7dPUSH20x3ecb
0f80JUMP
0f81JUMPDEST
0f82SWAP1
0f83PUSH0
0f84MSTORE
0f85PUSH10x27
0f87PUSH10x20
0f89MSTORE
0f8aPUSH10x01
0f8cPUSH10x01
0f8ePUSH10x40
0f90SHL
0f91SUB
0f92PUSH10x40
0f94PUSH0
0f95KECCAK256
0f96SWAP2
0f97AND
0f98PUSH10x01
0f9aPUSH10x01
0f9cPUSH10x40
0f9eSHL
0f9fSUB
0fa0NOT
0fa1DUP3
0fa2SLOAD
0fa3AND
0fa4OR
0fa5SWAP1
0fa6SSTORE
0fa7PUSH20x3feb
0faaJUMP
0fabJUMPDEST
0facPUSH20x0f5d
0fafJUMP
0fb0JUMPDEST
0fb1POP
0fb2POP
0fb3PUSH20x1002
0fb6PUSH320x15fd51992bd825f38af10ca1ec217ca34f12b24fa98401404df53e3052deac48
0fd7SWAP4
0fd8DUP3
0fd9PUSH10x01
0fdbPUSH10x01
0fddPUSH10x40
0fdfSHL
0fe0SUB
0fe1NOT
0fe2PUSH10x29
0fe4SLOAD
0fe5AND
0fe6OR
0fe7PUSH10x29
0fe9SSTORE
0feaPUSH10x40
0fecMLOAD
0fedSWAP4
0feeDUP5
0fefSWAP4
0ff0DUP5
0ff1MSTORE
0ff2PUSH10x40
0ff4PUSH10x20
0ff6DUP6
0ff7ADD
0ff8MSTORE
0ff9PUSH10x40
0ffbDUP5
0ffcADD
0ffdSWAP2
0ffePUSH20x43a3
1001JUMP
1002JUMPDEST
1003SUB
1004SWAP1
1005LOG1
1006STOP
1007JUMPDEST
1008DUP3
1009PUSH40x2c9979d1
100ePUSH10xe1
1010SHL
1011PUSH0
1012MSTORE
1013PUSH10x04
1015MSTORE
1016PUSH10x24
1018PUSH0
1019REVERT
101aJUMPDEST
101bCALLVALUE
101cPUSH20x0437
101fJUMPI
1020PUSH10x20
1022CALLDATASIZE
1023PUSH10x03
1025NOT
1026ADD
1027SLT
1028PUSH20x0437
102bJUMPI
102cPUSH10x04
102eCALLDATALOAD
102fPUSH10x01
1031PUSH10x01
1033PUSH10x40
1035SHL
1036SUB
1037DUP2
1038GT
1039PUSH20x0437
103cJUMPI
103dPUSH20x0785
1040PUSH20x0af8
1043PUSH10x20
1045SWAP3
1046CALLDATASIZE
1047SWAP1
1048PUSH10x04
104aADD
104bPUSH20x3bde
104eJUMP
104fJUMPDEST
1050PUSH20x3db5
1053JUMP
1054JUMPDEST
1055CALLVALUE
1056PUSH20x0437
1059JUMPI
105aPUSH10xa0
105cCALLDATASIZE
105dPUSH10x03
105fNOT
1060ADD
1061SLT
1062PUSH20x0437
1065JUMPI
1066PUSH20x106d
1069PUSH20x3ad7
106cJUMP
106dJUMPDEST
106ePUSH10x24
1070CALLDATALOAD
1071PUSH10x01
1073PUSH10x01
1075PUSH10x40
1077SHL
1078SUB
1079DUP2
107aGT
107bPUSH20x0437
107eJUMPI
107fPUSH20x108c
1082SWAP1
1083CALLDATASIZE
1084SWAP1
1085PUSH10x04
1087ADD
1088PUSH20x39bb
108bJUMP
108cJUMPDEST
108dSWAP1
108ePUSH10x44
1090CALLDATALOAD
1091PUSH10x01
1093PUSH10x01
1095PUSH10x40
1097SHL
1098SUB
1099DUP2
109aGT
109bPUSH20x0437
109eJUMPI
109fPUSH20x10ac
10a2SWAP1
10a3CALLDATASIZE
10a4SWAP1
10a5PUSH10x04
10a7ADD
10a8PUSH20x39bb
10abJUMP
10acJUMPDEST
10adPUSH20x10b4
10b0PUSH20x3a15
10b3JUMP
10b4JUMPDEST
10b5PUSH10x84
10b7CALLDATALOAD
10b8PUSH10x01
10baPUSH10x01
10bcPUSH10x40
10beSHL
10bfSUB
10c0DUP2
10c1GT
10c2PUSH20x0437
10c5JUMPI
10c6PUSH20x10d3
10c9SWAP1
10caCALLDATASIZE
10cbSWAP1
10ccPUSH10x04
10ceADD
10cfPUSH20x39bb
10d2JUMP
10d3JUMPDEST
10d4SWAP2
10d5PUSH20x10dd
10d8DUP9
10d9PUSH20x5f46
10dcJUMP
10ddJUMPDEST
10deDUP4
10dfDUP8
10e0EQ
10e1DUP1
10e2ISZERO
10e3SWAP1
10e4PUSH20x13df
10e7JUMPI
10e8JUMPDEST
10e9PUSH20x13c8
10ecJUMPI
10edSWAP2
10eePUSH20x113b
10f1SWAP2
10f2DUP8
10f3SWAP5
10f4SWAP4
10f5DUP10
10f6PUSH10x40
10f8MLOAD
10f9PUSH20x1133
10fcDUP2
10fdPUSH20x0a02
1100PUSH20x1120
1103DUP14
1104PUSH10xff
1106PUSH10x20
1108DUP6
1109ADD
110aSWAP8
110bAND
110cSWAP13
110dDUP14
110eDUP9
110fMSTORE
1110PUSH10x60
1112PUSH10x40
1114DUP7
1115ADD
1116MSTORE
1117PUSH10x80
1119DUP6
111aADD
111bSWAP2
111cPUSH20x40b3
111fJUMP
1120JUMPDEST
1121DUP3
1122DUP2
1123SUB
1124PUSH10x1f
1126NOT
1127ADD
1128PUSH10x60
112aDUP5
112bADD
112cMSTORE
112dDUP11
112eDUP14
112fPUSH20x40b3
1132JUMP
1133JUMPDEST
1134MLOAD
1135SWAP1
1136KECCAK256
1137PUSH20x568b
113aJUMP
113bJUMPDEST
113cPUSH0
113dJUMPDEST
113eDUP6
113fDUP2
1140LT
1141PUSH20x114e
1144JUMPI
1145PUSH20x0a8e
1148DUP7
1149DUP9
114aPUSH20x4996
114dJUMP
114eJUMPDEST
114fPUSH20x1159
1152DUP2
1153DUP4
1154DUP7
1155PUSH20x3e2e
1158JUMP
1159JUMPDEST
115aCALLDATALOAD
115bDUP4
115cPUSH0
115dMSTORE
115ePUSH10x25
1160PUSH10x20
1162MSTORE
1163PUSH10x40
1165PUSH0
1166KECCAK256
1167PUSH20x1171
116aDUP4
116bDUP10
116cDUP10
116dPUSH20x3e2e
1170JUMP
1171JUMPDEST
1172CALLDATALOAD
1173PUSH0
1174MSTORE
1175PUSH10x20
1177MSTORE
1178PUSH10x40
117aPUSH0
117bKECCAK256
117cSSTORE
117dPUSH20x1187
1180DUP2
1181DUP8
1182DUP8
1183PUSH20x3e2e
1186JUMP
1187JUMPDEST
1188CALLDATALOAD
1189PUSH20x11aa
118cPUSH20x1196
118fDUP4
1190DUP10
1191DUP10
1192PUSH20x3e2e
1195JUMP
1196JUMPDEST
1197CALLDATALOAD
1198PUSH20x11a2
119bDUP5
119cDUP7
119dDUP10
119ePUSH20x3e2e
11a1JUMP
11a2JUMPDEST
11a3CALLDATALOAD
11a4SWAP1
11a5DUP11
11a6PUSH20x412f
11a9JUMP
11aaJUMPDEST
11abDUP5
11acPUSH0
11adMSTORE
11aePUSH10x21
11b0PUSH10x20
11b2MSTORE
11b3PUSH10x40
11b5PUSH0
11b6KECCAK256
11b7DUP3
11b8PUSH0
11b9MSTORE
11baPUSH10x20
11bcMSTORE
11bdPUSH10x40
11bfPUSH0
11c0KECCAK256
11c1SLOAD
11c2DUP1
11c3ISZERO
11c4PUSH0
11c5EQ
11c6PUSH20x137e
11c9JUMPI
11caPOP
11cbDUP5
11ccPUSH0
11cdMSTORE
11cePUSH10x24
11d0PUSH10x20
11d2MSTORE
11d3PUSH10x40
11d5PUSH0
11d6KECCAK256
11d7PUSH0
11d8DUP1
11d9MSTORE
11daPUSH10x20
11dcMSTORE
11ddPUSH10x40
11dfPUSH0
11e0KECCAK256
11e1SLOAD
11e2SWAP2
11e3PUSH30x100000
11e7DUP4
11e8LT
11e9ISZERO
11eaPUSH20x1367
11edJUMPI
11eeDUP3
11efPUSH10x01
11f1DUP2
11f2ADD
11f3DUP1
11f4DUP3
11f5GT
11f6PUSH20x06c8
11f9JUMPI
11faDUP8
11fbPUSH0
11fcMSTORE
11fdPUSH10x24
11ffPUSH10x20
1201MSTORE
1202PUSH10x40
1204PUSH0
1205KECCAK256
1206PUSH0
1207DUP1
1208MSTORE
1209PUSH10x20
120bMSTORE
120cDUP1
120dPUSH10x40
120fPUSH0
1210KECCAK256
1211SSTORE
1212DUP8
1213PUSH0
1214MSTORE
1215PUSH10x23
1217PUSH10x20
1219MSTORE
121aPUSH10x40
121cPUSH0
121dKECCAK256
121eDUP1
121fSLOAD
1220SWAP1
1221PUSH10x01
1223DUP3
1224ADD
1225DUP1
1226SWAP3
1227GT
1228PUSH20x06c8
122bJUMPI
122cSSTORE
122dDUP8
122ePUSH0
122fMSTORE
1230PUSH10x21
1232PUSH10x20
1234MSTORE
1235PUSH10x40
1237PUSH0
1238KECCAK256
1239DUP4
123aPUSH0
123bMSTORE
123cPUSH10x20
123eMSTORE
123fPUSH10x40
1241PUSH0
1242KECCAK256
1243SSTORE
1244DUP7
1245PUSH0
1246MSTORE
1247PUSH10x22
1249PUSH10x20
124bMSTORE
124cPUSH10x40
124ePUSH0
124fKECCAK256
1250SWAP1
1251PUSH0
1252MSTORE
1253PUSH10x20
1255MSTORE
1256PUSH10x40
1258PUSH0
1259KECCAK256
125aSSTORE
125bJUMPDEST
125cDUP5
125dPUSH0
125eMSTORE
125fPUSH10x02
1261PUSH10x20
1263MSTORE
1264PUSH10x40
1266PUSH0
1267KECCAK256
1268DUP3
1269PUSH0
126aMSTORE
126bPUSH10x20
126dMSTORE
126eDUP1
126fPUSH10x40
1271PUSH0
1272KECCAK256
1273SSTORE
1274PUSH10x40
1276MLOAD
1277PUSH10x20
1279DUP2
127aADD
127bSWAP2
127cPUSH0
127dDUP4
127eMSTORE
127fPUSH10x01
1281DUP4
1282ADD
1283MSTORE
1284PUSH20x1299
1287DUP2
1288PUSH10x21
128aDUP5
128bADD
128cSUB
128dPUSH10x1f
128fNOT
1290DUP2
1291ADD
1292DUP4
1293MSTORE
1294DUP3
1295PUSH20x3b3c
1298JUMP
1299JUMPDEST
129aMLOAD
129bSWAP1
129cKECCAK256
129dSWAP1
129ePUSH0
129fJUMPDEST
12a0PUSH10x18
12a2DUP2
12a3LT
12a4PUSH20x1303
12a7JUMPI
12a8POP
12a9POP
12aaSWAP1
12abPUSH10x01
12adSWAP2
12aeDUP5
12afPUSH0
12b0MSTORE
12b1PUSH10x26
12b3PUSH10x20
12b5MSTORE
12b6PUSH10x40
12b8PUSH0
12b9KECCAK256
12baSSTORE
12bbPUSH20x12c5
12beDUP2
12bfDUP9
12c0DUP9
12c1PUSH20x3e2e
12c4JUMP
12c5JUMPDEST
12c6CALLDATALOAD
12c7DUP5
12c8PUSH320x2e0f0f49c5675cc0ca3b0631934e93ab749ffc07928459dd09921848e5cc2f54
12e9PUSH10x20
12ebPUSH20x12f5
12eeDUP6
12efDUP9
12f0DUP12
12f1PUSH20x3e2e
12f4JUMP
12f5JUMPDEST
12f6CALLDATALOAD
12f7PUSH10x40
12f9MLOAD
12faSWAP1
12fbDUP2
12fcMSTORE
12fdLOG3
12feADD
12ffPUSH20x113d
1302JUMP
1303JUMPDEST
1304SWAP1
1305SWAP2
1306PUSH20x1324
1309SWAP1
130aPUSH20x131e
130dPUSH10x01
130fDUP6
1310SWAP12
1311SWAP8
1312SWAP9
1313SWAP10
1314SWAP11
1315SWAP7
1316SWAP12
1317XOR
1318DUP5
1319DUP14
131aPUSH20x5f96
131dJUMP
131eJUMPDEST
131fSWAP1
1320PUSH20x61a9
1323JUMP
1324JUMPDEST
1325SWAP2
1326PUSH10x01
1328SHR
1329SWAP1
132aDUP7
132bPUSH0
132cMSTORE
132dPUSH10x03
132fPUSH10x20
1331MSTORE
1332PUSH10x40
1334PUSH0
1335KECCAK256
1336SWAP1
1337PUSH10x01
1339DUP2
133aADD
133bSWAP2
133cDUP3
133dDUP3
133eGT
133fPUSH20x06c8
1342JUMPI
1343PUSH10x01
1345SWAP3
1346PUSH0
1347MSTORE
1348PUSH10x20
134aMSTORE
134bPUSH10x40
134dPUSH0
134eKECCAK256
134fDUP4
1350PUSH0
1351MSTORE
1352PUSH10x20
1354MSTORE
1355DUP4
1356PUSH10x40
1358PUSH0
1359KECCAK256
135aSSTORE
135bADD
135cSWAP8
135dSWAP3
135eSWAP7
135fSWAP6
1360SWAP5
1361SWAP4
1362SWAP8
1363PUSH20x129f
1366JUMP
1367JUMPDEST
1368DUP6
1369PUSH40x5633a85b
136ePUSH10xe0
1370SHL
1371PUSH0
1372MSTORE
1373PUSH10x04
1375MSTORE
1376PUSH0
1377PUSH10x24
1379MSTORE
137aPUSH10x44
137cPUSH0
137dREVERT
137eJUMPDEST
137fPUSH0
1380NOT
1381DUP2
1382ADD
1383SWAP3
1384SWAP1
1385DUP4
1386GT
1387PUSH20x06c8
138aJUMPI
138bPUSH10xff
138dDUP4
138ePUSH10x14
1390SHR
1391AND
1392SWAP1
1393DUP2
1394PUSH20x139e
1397JUMPI
1398POP
1399POP
139aPUSH20x125b
139dJUMP
139eJUMPDEST
139fPUSH10x84
13a1SWAP2
13a2DUP8
13a3SWAP2
13a4PUSH10x40
13a6MLOAD
13a7SWAP3
13a8PUSH40x431ddf35
13adPUSH10xe1
13afSHL
13b0DUP5
13b1MSTORE
13b2PUSH10x04
13b4DUP5
13b5ADD
13b6MSTORE
13b7PUSH10x24
13b9DUP4
13baADD
13bbMSTORE
13bcPUSH10x44
13beDUP3
13bfADD
13c0MSTORE
13c1PUSH0
13c2PUSH10x64
13c4DUP3
13c5ADD
13c6MSTORE
13c7REVERT
13c8JUMPDEST
13c9DUP4
13caDUP8
13cbPUSH40x55c5b3e3
13d0PUSH10xe1
13d2SHL
13d3PUSH0
13d4MSTORE
13d5PUSH10x04
13d7MSTORE
13d8PUSH10x24
13daMSTORE
13dbPUSH10x44
13ddPUSH0
13deREVERT
13dfJUMPDEST
13e0POP
13e1DUP7
13e2ISZERO
13e3PUSH20x10e8
13e6JUMP
13e7JUMPDEST
13e8CALLVALUE
13e9PUSH20x0437
13ecJUMPI
13edPUSH10x20
13efCALLDATASIZE
13f0PUSH10x03
13f2NOT
13f3ADD
13f4SLT
13f5PUSH20x0437
13f8JUMPI
13f9PUSH10x04
13fbCALLDATALOAD
13fcPUSH10x01
13fePUSH10x01
1400PUSH10x40
1402SHL
1403SUB
1404DUP2
1405GT
1406PUSH20x0437
1409JUMPI
140aPUSH20x1417
140dSWAP1
140eCALLDATASIZE
140fSWAP1
1410PUSH10x04
1412ADD
1413PUSH20x39bb
1416JUMP
1417JUMPDEST
1418PUSH10x01
141aPUSH0
141bMSTORE
141cPUSH10x2c
141ePUSH10x20
1420MSTORE
1421PUSH320xa1f88ee5f5d946e3956f6291445d84cd8aea2bf6c57f4f4ac349f7a338882643
1442SLOAD
1443SWAP1
1444SWAP2
1445SWAP1
1446PUSH10x01
1448PUSH10x01
144aPUSH10xa0
144cSHL
144dSUB
144eAND
144fCALLER
1450SUB
1451PUSH20x0e3b
1454JUMPI
1455PUSH0
1456JUMPDEST
1457DUP3
1458DUP2
1459LT
145aPUSH20x1466
145dJUMPI
145ePUSH20x0a8e
1461DUP4
1462PUSH20x4951
1465JUMP
1466JUMPDEST
1467DUP1
1468PUSH20x1495
146bPUSH20x147d
146ePUSH20x0ad1
1471PUSH20x0acc
1474PUSH10x01
1476SWAP6
1477DUP9
1478DUP9
1479PUSH20x417d
147cJUMP
147dJUMPDEST
147ePUSH20x148f
1481PUSH20x0af8
1484CALLDATASIZE
1485PUSH20x0af3
1488DUP7
1489DUP11
148aDUP11
148bPUSH20x417d
148eJUMP
148fJUMPDEST
1490SWAP1
1491PUSH20x4436
1494JUMP
1495JUMPDEST
1496ADD
1497PUSH20x1456
149aJUMP
149bJUMPDEST
149cCALLVALUE
149dPUSH20x0437
14a0JUMPI
14a1PUSH0
14a2CALLDATASIZE
14a3PUSH10x03
14a5NOT
14a6ADD
14a7SLT
14a8PUSH20x0437
14abJUMPI
14acPUSH10x20
14aePUSH10x40
14b0MLOAD
14b1PUSH10x18
14b3DUP2
14b4MSTORE
14b5RETURN
14b6JUMPDEST
14b7CALLVALUE
14b8PUSH20x0437
14bbJUMPI
14bcPUSH0
14bdCALLDATASIZE
14bePUSH10x03
14c0NOT
14c1ADD
14c2SLT
14c3PUSH20x0437
14c6JUMPI
14c7PUSH10x2e
14c9SLOAD
14caPUSH10x40
14ccMLOAD
14cdPUSH10x01
14cfPUSH10x01
14d1PUSH10xa0
14d3SHL
14d4SUB
14d5SWAP1
14d6SWAP2
14d7AND
14d8DUP2
14d9MSTORE
14daPUSH10x20
14dcSWAP1
14ddRETURN
14deJUMPDEST
14dfCALLVALUE
14e0PUSH20x0437
14e3JUMPI
14e4PUSH10x60
14e6CALLDATASIZE
14e7PUSH10x03
14e9NOT
14eaADD
14ebSLT
14ecPUSH20x0437
14efJUMPI
14f0PUSH10x20
14f2PUSH20x0785
14f5PUSH20x14fc
14f8PUSH20x3ad7
14fbJUMP
14fcJUMPDEST
14fdPUSH10x44
14ffCALLDATALOAD
1500SWAP1
1501PUSH10x24
1503CALLDATALOAD
1504SWAP1
1505PUSH20x412f
1508JUMP
1509JUMPDEST
150aCALLVALUE
150bPUSH20x0437
150eJUMPI
150fPUSH10x20
1511CALLDATASIZE
1512PUSH10x03
1514NOT
1515ADD
1516SLT
1517PUSH20x0437
151aJUMPI
151bPUSH20x1522
151ePUSH20x3a2b
1521JUMP
1522JUMPDEST
1523PUSH10x01
1525PUSH10x01
1527PUSH10x40
1529SHL
152aSUB
152bPUSH20x0120
152eSWAP2
152fDUP3
1530PUSH10x40
1532MLOAD
1533PUSH20x153c
1536DUP3
1537DUP3
1538PUSH20x3b3c
153bJUMP
153cJUMPDEST
153dCALLDATASIZE
153eSWAP1
153fCALLDATACOPY
1540AND
1541PUSH0
1542MSTORE
1543PUSH10x28
1545PUSH10x20
1547MSTORE
1548PUSH20x1553
154bPUSH10x40
154dPUSH0
154eKECCAK256
154fPUSH20x404d
1552JUMP
1553JUMPDEST
1554PUSH20x1560
1557PUSH10x40
1559MLOAD
155aDUP1
155bSWAP3
155cPUSH20x3d73
155fJUMP
1560JUMPDEST
1561RETURN
1562JUMPDEST
1563CALLVALUE
1564PUSH20x0437
1567JUMPI
1568PUSH10x40
156aCALLDATASIZE
156bPUSH10x03
156dNOT
156eADD
156fSLT
1570PUSH20x0437
1573JUMPI
1574PUSH20x157b
1577PUSH20x3ad7
157aJUMP
157bJUMPDEST
157cPUSH20x1584
157fDUP2
1580PUSH20x5f46
1583JUMP
1584JUMPDEST
1585PUSH20x1590
1588PUSH10x24
158aCALLDATALOAD
158bDUP3
158cPUSH20x3e84
158fJUMP
1590JUMPDEST
1591PUSH10x14
1593SWAP1
1594PUSH20x159d
1597PUSH10x14
1599PUSH20x3b5d
159cJUMP
159dJUMPDEST
159eSWAP3
159fPUSH20x15ab
15a2PUSH10x40
15a4MLOAD
15a5SWAP5
15a6DUP6
15a7PUSH20x3b3c
15aaJUMP
15abJUMPDEST
15acPUSH10x14
15aeDUP5
15afMSTORE
15b0PUSH10x1f
15b2NOT
15b3PUSH20x15bc
15b6PUSH10x14
15b8PUSH20x3b5d
15bbJUMP
15bcJUMPDEST
15bdADD
15beCALLDATASIZE
15bfPUSH10x20
15c1DUP7
15c2ADD
15c3CALLDATACOPY
15c4PUSH0
15c5SWAP2
15c6JUMPDEST
15c7DUP4
15c8DUP4
15c9LT
15caPUSH20x15e3
15cdJUMPI
15cePUSH10x40
15d0MLOAD
15d1PUSH10x20
15d3DUP1
15d4DUP3
15d5MSTORE
15d6DUP2
15d7SWAP1
15d8PUSH20x081c
15dbSWAP1
15dcDUP3
15ddADD
15deDUP9
15dfPUSH20x3d40
15e2JUMP
15e3JUMPDEST
15e4DUP1
15e5PUSH20x15f3
15e8PUSH10x01
15eaDUP1
15ebSWAP4
15ecXOR
15edDUP6
15eeDUP6
15efPUSH20x5f96
15f2JUMP
15f3JUMPDEST
15f4PUSH20x15fd
15f7DUP6
15f8DUP9
15f9PUSH20x3e70
15fcJUMP
15fdJUMPDEST
15feMSTORE
15ffDUP2
1600SHR
1601SWAP3
1602ADD
1603SWAP2
1604PUSH20x15c6
1607JUMP
1608JUMPDEST
1609CALLVALUE
160aPUSH20x0437
160dJUMPI
160ePUSH0
160fCALLDATASIZE
1610PUSH10x03
1612NOT
1613ADD
1614SLT
1615PUSH20x0437
1618JUMPI
1619PUSH10x20
161bPUSH10x40
161dMLOAD
161ePUSH10x05
1620DUP2
1621MSTORE
1622RETURN
1623JUMPDEST
1624CALLVALUE
1625PUSH20x0437
1628JUMPI
1629PUSH10x40
162bCALLDATASIZE
162cPUSH10x03
162eNOT
162fADD
1630SLT
1631PUSH20x0437
1634JUMPI
1635PUSH10x20
1637PUSH20x166b
163aPUSH20x1641
163dPUSH20x3a2b
1640JUMP
1641JUMPDEST
1642PUSH10x01
1644PUSH10x01
1646PUSH10x40
1648SHL
1649SUB
164aPUSH20x1651
164dPUSH20x3ae7
1650JUMP
1651JUMPDEST
1652SWAP2
1653PUSH20x165b
1656DUP4
1657PUSH20x5f46
165aJUMP
165bJUMPDEST
165cAND
165dPUSH0
165eMSTORE
165fPUSH10x28
1661DUP4
1662MSTORE
1663PUSH10x40
1665PUSH0
1666KECCAK256
1667PUSH20x4120
166aJUMP
166bJUMPDEST
166cSWAP1
166dSLOAD
166eSWAP1
166fPUSH10x03
1671SHL
1672SHR
1673PUSH10x40
1675MLOAD
1676SWAP1
1677DUP2
1678MSTORE
1679RETURN
167aJUMPDEST
167bCALLVALUE
167cPUSH20x0437
167fJUMPI
1680PUSH0
1681CALLDATASIZE
1682PUSH10x03
1684NOT
1685ADD
1686SLT
1687PUSH20x0437
168aJUMPI
168bPUSH10x20
168dPUSH10x40
168fMLOAD
1690PUSH320x152e47491922dd015e08e657ae4de24ad07106d14eb7be07faeb91afcf397446
16b1DUP2
16b2MSTORE
16b3RETURN
16b4JUMPDEST
16b5CALLVALUE
16b6PUSH20x0437
16b9JUMPI
16baPUSH10x20
16bcCALLDATASIZE
16bdPUSH10x03
16bfNOT
16c0ADD
16c1SLT
16c2PUSH20x0437
16c5JUMPI
16c6PUSH10x20
16c8PUSH20x0785
16cbPUSH20x16d2
16cePUSH20x39eb
16d1JUMP
16d2JUMPDEST
16d3PUSH20x40d7
16d6JUMP
16d7JUMPDEST
16d8CALLVALUE
16d9PUSH20x0437
16dcJUMPI
16ddPUSH10x20
16dfCALLDATASIZE
16e0PUSH10x03
16e2NOT
16e3ADD
16e4SLT
16e5PUSH20x0437
16e8JUMPI
16e9PUSH10xff
16ebPUSH20x16f2
16eePUSH20x3ad7
16f1JUMP
16f2JUMPDEST
16f3AND
16f4PUSH0
16f5MSTORE
16f6PUSH10x26
16f8PUSH10x20
16faMSTORE
16fbPUSH10x20
16fdPUSH10x40
16ffPUSH0
1700KECCAK256
1701SLOAD
1702PUSH10x40
1704MLOAD
1705SWAP1
1706DUP2
1707MSTORE
1708RETURN
1709JUMPDEST
170aCALLVALUE
170bPUSH20x0437
170eJUMPI
170fPUSH10xc0
1711CALLDATASIZE
1712PUSH10x03
1714NOT
1715ADD
1716SLT
1717PUSH20x0437
171aJUMPI
171bPUSH20x1722
171ePUSH20x3ad7
1721JUMP
1722JUMPDEST
1723PUSH20x172a
1726PUSH20x3ae7
1729JUMP
172aJUMPDEST
172bSWAP1
172cPUSH10x44
172eCALLDATALOAD
172fPUSH10x01
1731PUSH10x01
1733PUSH10x40
1735SHL
1736SUB
1737DUP2
1738GT
1739PUSH20x0437
173cJUMPI
173dPUSH20x174a
1740SWAP1
1741CALLDATASIZE
1742SWAP1
1743PUSH10x04
1745ADD
1746PUSH20x39bb
1749JUMP
174aJUMPDEST
174bSWAP2
174cSWAP1
174dPUSH10x64
174fCALLDATALOAD
1750PUSH10x01
1752PUSH10x01
1754PUSH10x40
1756SHL
1757SUB
1758DUP2
1759GT
175aPUSH20x0437
175dJUMPI
175ePUSH20x176b
1761SWAP1
1762CALLDATASIZE
1763SWAP1
1764PUSH10x04
1766ADD
1767PUSH20x39bb
176aJUMP
176bJUMPDEST
176cSWAP5
176dPUSH10x84
176fCALLDATALOAD
1770PUSH10x01
1772PUSH10x01
1774PUSH10x40
1776SHL
1777SUB
1778DUP2
1779AND
177aDUP2
177bSUB
177cPUSH20x0437
177fJUMPI
1780PUSH10xa4
1782CALLDATALOAD
1783PUSH10x01
1785PUSH10x01
1787PUSH10x40
1789SHL
178aSUB
178bDUP2
178cGT
178dPUSH20x0437
1790JUMPI
1791PUSH20x179e
1794SWAP1
1795CALLDATASIZE
1796SWAP1
1797PUSH10x04
1799ADD
179aPUSH20x39bb
179dJUMP
179eJUMPDEST
179fSWAP2
17a0PUSH20x17a8
17a3DUP8
17a4PUSH20x5f46
17a7JUMP
17a8JUMPDEST
17a9PUSH20x17b2
17acDUP5
17adDUP9
17aePUSH20x6102
17b1JUMP
17b2JUMPDEST
17b3PUSH10xff
17b5DUP8
17b6AND
17b7SWAP3
17b8PUSH10x02
17baDUP5
17bbEQ
17bcDUP1
17bdISZERO
17bePUSH20x1975
17c1JUMPI
17c2JUMPDEST
17c3DUP1
17c4ISZERO
17c5PUSH20x196b
17c8JUMPI
17c9JUMPDEST
17caPUSH20x1958
17cdJUMPI
17cePUSH10x07
17d0DUP5
17d1EQ
17d2DUP1
17d3ISZERO
17d4PUSH20x194e
17d7JUMPI
17d8JUMPDEST
17d9PUSH20x193b
17dcJUMPI
17ddDUP10
17deDUP10
17dfSUB
17e0PUSH20x1924
17e3JUMPI
17e4DUP4
17e5PUSH0
17e6MSTORE
17e7PUSH0
17e8PUSH10x20
17eaMSTORE
17ebPUSH10x40
17edPUSH0
17eeKECCAK256
17efSLOAD
17f0SWAP1
17f1DUP2
17f2ISZERO
17f3PUSH20x1911
17f6JUMPI
17f7SWAP1
17f8DUP3
17f9SWAP2
17faDUP9
17fbSWAP5
17fcSWAP4
17fdDUP7
17feDUP13
17ffDUP10
1800DUP16
1801DUP13
1802SWAP1
1803DUP5
1804PUSH0
1805MSTORE
1806PUSH10x2b
1808PUSH10x20
180aMSTORE
180bPUSH10x40
180dPUSH0
180eKECCAK256
180fSLOAD
1810PUSH10x01
1812PUSH10x01
1814PUSH10x40
1816SHL
1817SUB
1818AND
1819SWAP11
181aPUSH10x40
181cMLOAD
181dSWAP5
181eDUP6
181fSWAP5
1820PUSH10x20
1822DUP7
1823ADD
1824SWAP8
1825DUP9
1826MSTORE
1827PUSH10xff
1829AND
182aPUSH10x40
182cDUP7
182dADD
182eMSTORE
182fDUP13
1830PUSH10x60
1832DUP7
1833ADD
1834MSTORE
1835PUSH10x80
1837DUP6
1838ADD
1839PUSH10xa0
183bSWAP1
183cMSTORE
183dPUSH10xc0
183fDUP6
1840ADD
1841SWAP1
1842PUSH20x184a
1845SWAP3
1846PUSH20x40b3
1849JUMP
184aJUMPDEST
184bSWAP1
184cPUSH10x1f
184eNOT
184fDUP5
1850DUP4
1851SUB
1852ADD
1853PUSH10xa0
1855DUP6
1856ADD
1857MSTORE
1858PUSH20x1860
185bSWAP3
185cPUSH20x40b3
185fJUMP
1860JUMPDEST
1861SUB
1862PUSH10x1f
1864NOT
1865DUP2
1866ADD
1867DUP3
1868MSTORE
1869PUSH20x1872
186cSWAP1
186dDUP3
186ePUSH20x3b3c
1871JUMP
1872JUMPDEST
1873MLOAD
1874SWAP1
1875KECCAK256
1876PUSH20x187f
1879SWAP2
187aADDRESS
187bPUSH20x5a89
187eJUMP
187fJUMPDEST
1880DUP7
1881PUSH0
1882MSTORE
1883PUSH10x01
1885PUSH10x20
1887MSTORE
1888PUSH10x40
188aPUSH0
188bKECCAK256
188cSLOAD
188dSWAP2
188ePUSH320x000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6
18afSWAP6
18b0PUSH20x18b8
18b3SWAP7
18b4PUSH20x5b1f
18b7JUMP
18b8JUMPDEST
18b9POP
18baPUSH20x18c2
18bdSWAP1
18bePUSH20x3e52
18c1JUMP
18c2JUMPDEST
18c3SWAP1
18c4PUSH0
18c5MSTORE
18c6PUSH10x2b
18c8PUSH10x20
18caMSTORE
18cbPUSH10x40
18cdPUSH0
18ceKECCAK256
18cfSWAP1
18d0PUSH10x01
18d2PUSH10x01
18d4PUSH10x40
18d6SHL
18d7SUB
18d8AND
18d9PUSH10x01
18dbPUSH10x01
18ddPUSH10x40
18dfSHL
18e0SUB
18e1NOT
18e2DUP3
18e3SLOAD
18e4AND
18e5OR
18e6SWAP1
18e7SSTORE
18e8PUSH0
18e9JUMPDEST
18eaDUP6
18ebDUP2
18ecLT
18edPUSH20x18fa
18f0JUMPI
18f1PUSH20x0a8e
18f4DUP7
18f5DUP7
18f6PUSH20x4996
18f9JUMP
18faJUMPDEST
18fbDUP1
18fcPUSH20x190b
18ffPUSH20x0e09
1902PUSH10x01
1904SWAP4
1905DUP10
1906DUP9
1907PUSH20x3e2e
190aJUMP
190bJUMPDEST
190cADD
190dPUSH20x18e9
1910JUMP
1911JUMPDEST
1912DUP5
1913PUSH40x47c987b5
1918PUSH10xe1
191aSHL
191bPUSH0
191cMSTORE
191dPUSH10x04
191fMSTORE
1920PUSH10x24
1922PUSH0
1923REVERT
1924JUMPDEST
1925DUP10
1926DUP10
1927PUSH40x55c5b3e3
192cPUSH10xe1
192eSHL
192fPUSH0
1930MSTORE
1931PUSH10x04
1933MSTORE
1934PUSH10x24
1936MSTORE
1937PUSH10x44
1939PUSH0
193aREVERT
193bJUMPDEST
193cDUP4
193dPUSH40xc84507ef
1942PUSH10xe0
1944SHL
1945PUSH0
1946MSTORE
1947PUSH10x04
1949MSTORE
194aPUSH10x24
194cPUSH0
194dREVERT
194eJUMPDEST
194fPOP
1950PUSH10x08
1952DUP5