Kontrakt
0xf5c6689d42285c9cd157e784c00d601bc7d526de
- Adresa
- 0xf5c6689d42285c9cd157e784c00d601bc7d526de
- Druh
- ověřený kontrakt FinalAssetRegistry
- Zůstatek
- 0 vETH
- Nonce
- 1
- Kód
- 27,628 bajtů codehash 0xcbdb3c160d07a2f7bb292d2814a16eefc5040c68e4171375bfc7e005addaf16d
strom účtů
- Strom
- 1 · účty
- Přítomen
- žádný list
- Klíč
- 0x6cfb63f6f5f00add530ad8c85167e2b55e6ae6f97652e7c2468cf59c6bffe722
- Ž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.
zdroj ověřeno
- Kontrakt
- FinalAssetRegistry 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:09:33.677Z
- Původ
- preverify-final-chain (forge artifact, bytecode compared against live code)
contracts/finalchain/FinalAssetRegistry.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";
import {FinalPqQuorum} from "./FinalPqQuorum.sol";
import {FinalStateTrees, IChainSource} from "./FinalStateTrees.sol";
/**
* @title FinalAssetRegistry
* @notice Which networks we settle on, and which assets exist on them. On
* Final Chain, as state — not as a list in a process.
*
* ## What this replaces
*
* Three hardcoded rosters, each authoritative for something and none of them a
* record anyone could prove:
*
* | was | where | how it failed |
* |---|---|---|
* | the supported chain set | `chainRegistry.js` + a JS Merkle fold | the root a chain verified against was folded in `log2(n)`; the chain's own verifier is `verifyTaggedSortedProof`. Two implementations of one construction. |
* | the vAsset roster | `buildAssetRegistry(records)` over a caller-supplied array | whoever called it decided the set. "The publisher should pass the right list" is not a control. |
* | the PHI morphable roster | `PHI_MORPH_ASSETS`, an env var | unset on every environment, so five publishers idled; set wrong, and they would have priced against pools nobody checked. |
* | the monitored fee assets | `MONITORED_ASSETS`, a literal with MAINNET defaults | applied silently on Sepolia, where those pool addresses have no code, so the test fleet sampled reserves that could never exist. |
*
* Each was a different shape of the same mistake: a set that decides what the
* protocol will accept, held somewhere that cannot be proven, versioned or
* audited, and readable only by the process that happened to hold it.
*
* ## The tree IS the roster
*
* An entry here is state. It is seeded once, mutated by `add` and `remove`
* under a quorum, and every mutation writes tree 6 in the same transaction —
* so there is no window in which the record and the root disagree, and no
* second copy for them to disagree with.
*
* `remove` is a tombstone, not a deletion, and that is deliberate twice over.
* `FinalStateTrees` gives every key a PERMANENT slot on first write, so a
* deletion is not available to implement. It is also the wrong thing to want:
* a consumer asking "is this asset supported?" needs a provable NO, and an
* absent leaf proves nothing — it is indistinguishable from a leaf that was
* never published, from a chain that is behind, and from a proof built against
* the wrong epoch. `enabled=false` is a leaf, and a leaf can be proven.
*
* ## Two trees, and they are not redundant
*
* **Tree 6 holds the rows.** One leaf per chain and per asset, carrying every
* attribute a consumer needs. That is what makes membership provable on a chain
* that cannot read this one.
*
* **Tree 5 holds the two registry roots** that `FinalSettlement.syncChain` and
* `registerAsset` verify against. Those are not tree 6's root: they are roots
* over the chain leaves alone and the asset leaves alone, in the sorted-pair
* tagged shape `FinalMerkle.verifyTaggedSortedProof` runs — a different
* construction from tree 6's fixed depth-20 slotted tree, over a different leaf
* set. Publishing tree 6's root as a registry root would produce a value that
* verifies nothing, with both sides internally consistent.
*
* They are computed HERE, on chain, for the reason every other root on this
* chain is: the alternative is a second implementation in JavaScript, and a
* divergence between two folders of one construction presents as a proof that
* verifies nowhere with nothing pointing at the cause.
*
* ## Tree 6 is ALL of the protocol's configuration
*
* Six key kinds. Kinds 0–2 are the rosters (chains, assets, assets on chains),
* kind 3 is every numeric policy, and kinds 4 and 5 are the price plane's
* wiring: one row per price VENUE with that venue's own ticker and, for a DEX
* pool, every TWAP term of that pool; and one row per DEX protocol deployment
* per chain, so a pool is verified against its factory by a chain read and the
* treasury's router is a reference rather than an address of its own. A chain
* row is the complete protocol description of a chain — base asset, finality
* rule, block timer, multicall, gas model. With this, no service reads an
* environment variable for anything but its own RPC and keys; the RPC stays
* off the tree by rule, because where one operator's fleet reads a chain is
* not a protocol fact and tree 6 is public state.
*
* ## Gas
*
* Re-folding both registries on every mutation is O(n log n) hashes. That is
* affordable because this is our own chain, the cadence is "rarely", and the
* sets are tens of entries. Do not carry the pattern to a chain where a fold is
* paid by a user.
*/
contract FinalAssetRegistry is IChainSource {
// ------------------------------------------------------------- constants
/// @dev Chain-registry leaf space. Byte-for-byte what `FinalSettlement`
/// hashes, because a root computed under a different domain verifies
/// nowhere and the mismatch is invisible until a proof is spent.
bytes32 internal constant DOMAIN_CHAIN_LEAF = keccak256("FINAL_CHAIN_REGISTRY_LEAF_v01");
/// @dev Asset-registry leaf space. Disjoint from the chain space so a chain
/// record can never be replayed as an asset record.
bytes32 internal constant DOMAIN_ASSET_LEAF = keccak256("FINAL_ASSET_REGISTRY_LEAF_v01");
/// @dev Tree 6 key and leaf spaces. Separate from the registry spaces
/// above: tree 6 answers "is this supported", the registries answer "what
/// are its attributes", and one leaf must not satisfy the other's proof.
bytes32 internal constant DOMAIN_ALLOWLIST_KEY = keccak256("FINAL_ALLOWLIST_KEY_v01");
bytes32 internal constant DOMAIN_ALLOWLIST_LEAF = keccak256("FINAL_ALLOWLIST_LEAF_v01");
/// @dev Tree 5 key and leaf spaces.
bytes32 internal constant DOMAIN_REGISTRY_ROOT_KEY = keccak256("FINAL_REGISTRY_ROOT_KEY_v01");
bytes32 internal constant DOMAIN_REGISTRY_ROOT_LEAF = keccak256("FINAL_REGISTRY_ROOT_LEAF_v01");
bytes32 internal constant ACTION_SEED = keccak256("FinalAssetRegistry.seed.v01");
bytes32 internal constant ACTION_MUTATE = keccak256("FinalAssetRegistry.mutate.v01");
/// @dev A DISTINCT action, sharing `mutate`'s nonce. Distinct so a batch
/// approved for one door cannot be replayed through the other; shared
/// nonce so the two write paths are totally ordered against each other
/// rather than each advancing a counter the other cannot see.
bytes32 internal constant ACTION_SET_POLICY = keccak256("FinalAssetRegistry.setPolicy.v01");
bytes32 internal constant ACTION_SET_CHAIN_TERMS = keccak256("FinalAssetRegistry.setChainTerms.v01");
/// @dev Kinds 4 and 5, same arrangement: their own door, the shared nonce.
bytes32 internal constant ACTION_SET_SOURCES = keccak256("FinalAssetRegistry.setSources.v01");
uint8 internal constant TREE_SETTLEMENT = 5;
uint8 internal constant TREE_ALLOWLIST = 6;
/// @dev The branch every registry row lives in — `FinalStateTrees.BRANCH_MAIN`,
/// pinned by test. Branch 0 of both trees is the configuration branch.
uint8 internal constant BRANCH_MAIN_ID = 1;
/// @dev Which registry a tree-5 leaf is the root of.
uint8 internal constant REGISTRY_CHAIN = 0;
uint8 internal constant REGISTRY_ASSET = 1;
/// @dev Byte-for-byte `FinalSettlement`'s. A chain reference derived under
/// a different domain here would name a chain nothing else can find.
bytes32 internal constant DOMAIN_CHAIN_REF = keccak256("FINAL_CHAIN_REF_v01");
bytes32 public constant CAIP_NAMESPACE_EIP155 = keccak256("eip155");
// ----------------------------------------------------------------- types
/// @notice What an asset is FOR. An asset can be more than one.
///
/// @dev A bitfield rather than an enum, because the three uses are
/// independent and an asset commonly has two of them. Modelling it as an
/// enum forced a "BOTH" member the day the second combination appeared,
/// and a third use makes that combinatorial.
///
/// - `USE_SETTLE` — bridgeable; may be issued as a vAsset.
/// - `USE_MORPH` — may be morphed by the PHI ledger.
/// - `USE_FEE` — accepted as a gateway fee token and priced by the
/// oracle. This is the roster `MONITORED_ASSETS` was.
/// - `USE_REFILL` — autosold to base crypto.
/// - `USE_SETTLE_DENIED` — excluded from bridging.
///
/// Scope is not uniform. `USE_MORPH` and `USE_SETTLE_DENIED` are properties
/// of the ASSET and live on the global row; `USE_FEE` and `USE_REFILL`
/// differ by network and live on the per-chain row. `GLOBAL_USE_MASK` and
/// `CHAIN_USE_MASK` are enforced, so a bit cannot be set where nothing would
/// read it.
uint8 internal constant USE_SETTLE = 1;
uint8 internal constant USE_MORPH = 2;
uint8 internal constant USE_FEE = 4;
/// @dev Autosold to base crypto on this chain. **Separate from `USE_FEE` on
/// purpose**: accepting an asset as payment and dumping it for base
/// crypto are different decisions, and they stop matching the first
/// time we accept a fee token we do not want to sell.
uint8 internal constant USE_REFILL = 8;
/// @dev Excluded from bridging. A DENY bit, not an allow bit — the asset
/// registry is default-allow minus exclusions, so absence means
/// bridgeable and only an explicit leaf can say otherwise. An absent
/// leaf proves nothing, which is why exclusion has to be written down
/// rather than inferred from a missing row.
uint8 internal constant USE_SETTLE_DENIED = 16;
/// @dev Bits meaningful on the GLOBAL asset row (kind 1). Properties of the
/// asset itself, true wherever it is.
uint8 internal constant GLOBAL_USE_MASK = USE_SETTLE | USE_MORPH | USE_SETTLE_DENIED;
/// @dev Bits meaningful on the PER-CHAIN row (kind 2). Properties of the
/// asset ON a network.
uint8 internal constant CHAIN_USE_MASK = USE_FEE | USE_REFILL;
/// @notice `ChainEntry.role` values.
uint8 public constant CHAIN_ROLE_FULL = 0;
uint8 public constant CHAIN_ROLE_OBSERVED = 1;
/// @notice Bounds of `AssetChainEntry.maxLeveragePct` when set — the same
/// numbers as `FinalStateRecords.MIN_LEVERAGE_PCT` / `MAX_LEVERAGE_PCT`
/// (a cap outside the records' own bound would never bind).
uint16 public constant LEVERAGE_CAP_MIN_PCT = 100;
uint16 public constant LEVERAGE_CAP_MAX_PCT = 500;
/// @notice How a chain's finality is decided. Zero is unset.
uint8 public constant FINALITY_TAG_FINALIZED = 1;
uint8 public constant FINALITY_CONFIRMATIONS = 2;
uint8 public constant FINALITY_L2_SETTLED = 3;
/// @notice How a chain prices gas. Zero is unset.
uint8 public constant GAS_MODEL_EIP1559 = 1;
uint8 public constant GAS_MODEL_LEGACY = 2;
uint8 public constant GAS_MODEL_L2_WITH_L1_FEE = 3;
/// @dev `vmKind` values. Zero is refused on an enabled row: a chain whose
/// execution model nobody named is one every consumer would guess at,
/// and the guesses would all be "EVM" right up until the first chain
/// that is not. Extend by number; never renumber.
uint8 public constant VM_EVM = 1;
uint8 public constant VM_SVM = 2;
uint8 public constant VM_MOVE = 3;
/// @notice DEX protocol families a kind-5 row may describe. Zero is unset.
/// @dev The READ SHAPE of a pool — v2 reserves, v3 `slot0` + `observe`, v4
/// StateView — comes from this, never from the pool row.
uint8 public constant PROTOCOL_UNISWAP_V2 = 1;
uint8 public constant PROTOCOL_UNISWAP_V3 = 2;
uint8 public constant PROTOCOL_UNISWAP_V4 = 3;
uint8 public constant PROTOCOL_VELODROME = 4;
uint8 public constant PROTOCOL_CURVE = 5;
/// @notice The tree-4 refresh a roster row gets by default, and the one a
/// morph or fee asset gets. Milliseconds.
/// @dev A stale price on a morphable or fee asset is a free option against
/// the collateral or the float, so those refresh every second and are
/// refused after three of their own ticks. Everything else keeps the
/// round.
uint32 public constant PRICE_CADENCE_DEFAULT_MS = 10_000;
uint32 public constant PRICE_MAX_AGE_DEFAULT_MS = 120_000;
uint32 public constant PRICE_CADENCE_FAST_MS = 1_000;
uint32 public constant PRICE_MAX_AGE_FAST_MS = 3_000;
/// @notice One network we settle on — the complete protocol description of
/// a chain.
///
/// @dev Everything a service needs to READ a chain correctly is here, so
/// none of it is a per-process setting: base asset, finality rule, the
/// block timer, multicall, the gas model. Only the RPC endpoint stays out,
/// by rule — it is where one operator's fleet reads the chain, resolved by
/// convention from `chainRef`, not a fact about the chain.
struct ChainEntry {
/// @dev CAIP-style reference, not an EIP-155 id — the registry spans
/// non-EVM namespaces and an integer chain id cannot name those.
bytes32 chainRef;
/// @dev The two halves `chainRef` is the hash OF, carried so the entry
/// is self-describing.
///
/// A hash cannot be inverted, so a consumer holding only
/// `chainRef` cannot learn which chain it names — it can only
/// re-hash candidates from a list it already has, which is the
/// list this registry exists to replace. Carrying the preimage is
/// what makes "read the chain set from the tree" a complete answer
/// instead of a lookup that still needs the old array.
///
/// Checked on write: `chainRefFor(namespace, reference)` must
/// equal `chainRef`. An entry claiming a reference it does not
/// hash to would send every consumer to the wrong chain with a
/// correct-looking proof.
bytes32 caipNamespace;
bytes32 caipReference;
/// @dev `FinalSettlement` on that chain, widened to 32 bytes.
bytes32 settlement;
/// @dev Matches `FinalSettlement.AccountSpace`.
uint8 accountSpace;
/// @dev Asset id of the gas / base asset — what fees are priced in.
/// Zero while the chain's assets are not yet seeded: the chain row
/// has to exist before an asset can name it as origin.
bytes32 nativeAsset;
/// @dev Asset id of its wrapped form — the pool leg.
bytes32 wrappedNative;
/// @dev `FINALITY_*`.
uint8 finalityKind;
/// @dev Confirmations for `FINALITY_CONFIRMATIONS`; zero for a tag rule.
uint64 finalityParam;
/// @dev The chain's block timer — the cadence of everything read per
/// block. Every DEX / TWAP source on the chain is read once per
/// block, the window → blocks conversion uses it, and the RPC
/// health probe paces on it.
uint32 blockTimeMs;
/// @dev Read the gas price every N blocks (1 = every block).
uint16 gasReadBlocks;
/// @dev `eth_feeHistory` window the gas quote is composed over.
uint16 gasHistoryBlocks;
/// @dev Multicall3 on that chain, widened.
bytes32 multicall;
/// @dev `GAS_MODEL_*`.
uint8 gasModel;
/// @dev For an L2: the chain whose L1 fee component applies, else zero.
bytes32 l1ChainRef;
/// @dev `FinalGateway` on that chain, widened to 32 bytes. On EVM
/// chains it is today the same CREATE2 address everywhere, which
/// is exactly the assumption a non-EVM chain breaks — so the
/// registry carries it per chain and a consumer reads it here
/// rather than deriving it. Zero while the chain's gateway is not
/// yet deployed, same rule as `settlement`.
bytes32 gateway;
/// @dev `VM_*` — the chain's execution model. What a consumer branches
/// on to pick an adapter; `caipNamespace` names the namespace and
/// this names the machine, and on eip155 they must agree (VM_EVM).
uint8 vmKind;
bool enabled;
/// @dev Monotonic per entry. What makes a stale proof refusable rather
/// than merely old, and what a consumer's ring compares against.
uint64 epoch;
/// @dev The height the fleet's resyncs start from on this chain: nothing
/// of ours exists below it, so no window is scanned below it (ruled
/// 2026-09-03, "fill with blanks before even trying to resync").
/// 0 = unknown, scan from the cursor as before.
uint64 startHeight;
/// @dev `CHAIN_ROLE_FULL` (0): settlement, accounts, fees — `settlement`
/// required. `CHAIN_ROLE_OBSERVED` (1): the fleet READS it — gas per
/// block time, marks — but settles nothing there and needs no
/// contracts of ours on it (ruled 2026-09-04: the test chains the
/// oracles cover without a wallet deployment).
uint8 role;
}
/// @notice One asset, with every attribute a remote chain cannot read.
///
/// @dev `decimals`, `name` and `symbol` are here for the reason
/// `FinalSettlement.AssetLeaf` carries them: the token lives somewhere
/// else, so a chain registering it cannot check them, and a
/// caller-supplied `decimals` on a permissionless entrypoint is a
/// mint-multiplier attack no on-chain check could catch.
///
/// The venue an asset is priced against is NOT here any more: it is a
/// kind-4 row per venue, because the median needs several and each has its
/// own ticker and terms.
struct AssetEntry {
bytes32 originChainRef;
bytes32 originToken;
uint8 decimals;
string name;
string symbol;
/// @dev `USE_*` bitfield.
uint8 uses;
/// @dev How often this asset's tree-4 row is republished, and how old a
/// price may be before the banded agreement refuses it. Properties
/// of the asset, inherited by its kind-4 venues, so no service
/// decides on its own how fresh a price is. Zero on write selects
/// the default for the asset's uses.
uint32 priceCadenceMs;
uint32 maxAgeMs;
bool enabled;
uint64 epoch;
}
/// @notice How a price venue is reached.
///
/// @dev An enum and not a bool, because "DEX or CEX" was the question until
/// the first aggregator, and a third answer should not need a second
/// field. `None` is the zero value so an unset venue is legible rather
/// than reading as a DEX at address zero.
enum VenueKind {
None,
Dex,
Cex,
Aggregator
}
/// @notice One asset's policy on ONE chain.
///
/// @dev `uses` carries only `CHAIN_USE_MASK` bits; the global row carries
/// the rest. The venue that used to sit here is a kind-4 row.
struct AssetChainEntry {
bytes32 assetId;
bytes32 chainRef;
/// @dev `USE_FEE` and/or `USE_REFILL`. Other bits are refused.
uint8 uses;
bool enabled;
uint64 epoch;
/// @dev Leverage ceiling for morphs on THIS asset on THIS chain, in
/// percent (300 = 3×); 0 = no per-chain cap, the records' global
/// bound alone applies. Enforced by `FinalStateRecords.setPhiAccounts`
/// through `leverageCapPct` (ruled 2026-09-04: "leverage can be set
/// per asset per chain, also in asset lists on FC").
uint16 maxLeveragePct;
}
/// @notice One price source for one asset — kind 4.
///
/// @dev One row per VENUE, because the median wants several and every DEX
/// pool has its own right TWAP terms: a deep mainnet pool tolerates a long
/// window, a thin L2 pool needs a shorter one and a liquidity floor. All
/// of it was a roster file, a set of constants and fourteen environment
/// variables; now it is a leaf, and the median reads every enabled row for
/// the asset. `symbol` is the ticker THIS venue uses, so an alias
/// (WETH-vs-ETH) is a field rather than a code path.
struct PriceSourceEntry {
bytes32 assetId;
/// @dev Zero for a CEX row — a CEX is on no chain.
bytes32 chainRef;
/// @dev Distinguishes rows for one asset on one chain.
bytes32 venueId;
/// @dev `VenueKind`.
uint8 venueKind;
/// @dev The pool address, widened, or the CEX id.
bytes32 venue;
/// @dev The kind-5 deployment a DEX pool is verified against
/// (`factory.getPool(token0, token1, fee) == venue`). Zero on a CEX.
bytes32 protocolId;
/// @dev The ticker this venue quotes the asset under.
bytes32 symbol;
/// @dev What the venue quotes against — an asset id.
bytes32 quoteAsset;
/// @dev Weight in the composition, basis points of the total.
uint16 weight;
// DEX rows only — the TWAP terms of THIS pool. Zero on a CEX row.
/// @dev The pool's token ordering. `slot0` always prices token0 in
/// token1, and reading it the wrong way round is a well-formed
/// wrong price at full weight.
bytes32 baseToken;
bytes32 quoteToken;
bool baseIsToken0;
/// @dev The window `observe` / the cumulative pair spans, and the
/// shortest window still accepted before the row reports
/// unavailable rather than a price.
uint32 twapWindowSeconds;
uint32 twapMinWindowSeconds;
/// @dev Active liquidity below which the row is skipped — a v3 pool's
/// depth is its in-range L, not its balances.
uint128 minLiquidity;
/// @dev Spot-vs-TWAP spread above which the row is refused as manipulated.
uint16 maxSpotDeviationBps;
bool enabled;
uint64 epoch;
}
/// @notice One DEX protocol's deployment on one chain — kind 5.
///
/// @dev What reading or verifying a pool needs and a pool row cannot carry:
/// the factory that proves a pool address is genuine, the quoter for a
/// quote, the router the treasury swaps through, the position manager.
/// Per protocol per chain, so the addresses hardcoded in the oracle and
/// treasury workers go, and the treasury's swap target becomes a reference
/// to `router` here — still gated on the gateway's own allowlist, because
/// the tree names it and the contract gates it.
struct DexProtocolEntry {
bytes32 chainRef;
bytes32 protocolId;
/// @dev `PROTOCOL_*`.
uint8 protocolKind;
bytes32 factory;
bytes32 quoter;
bytes32 router;
bytes32 positionManager;
bool enabled;
uint64 epoch;
}
// ----------------------------------------------------------------- state
FinalIdentityRegistry public immutable registry;
FinalStateTrees public immutable trees;
/// @dev Registrar-quorum action, verified by the registry with this
/// contract as the verifying contract.
bytes32 public constant ACTION_CONFIGURE = keccak256("FINAL_ASSET_REGISTRY_CONFIGURE_v01");
/// @dev Bootstrap admin, cleared by `seal`. Mirrors the registry's window.
address public admin;
/// @dev Which role may mutate, and how many approvals it takes.
uint256 public publisherRole;
uint256 public threshold;
/// @dev Replay domain for mutations. Bound into every digest.
uint64 public nonce;
bytes32[] internal _chainRefs;
/**
* @notice One global policy scalar — kind 3.
*
* @dev The fourth key kind, for the numbers that are POLICY and are not a
* property of a chain or an asset: a treasury swap ceiling, a subsidy
* budget. They lived in environment variables, which makes a limit
* something an operator can change alone, silently, per revision — and a
* limit nobody had to agree to is not a limit, it is a default.
*
* A scalar rather than a typed field per parameter, because the alternative
* is a contract change for every new bound, and a contract change is the
* one thing that must not be the price of tightening a limit. The units are
* the caller's: `TREASURY_SWAP_MAX_WEI_PER_TICK` is wei, and the name says
* so. A consumer that reads the wrong parameter reads a number of the wrong
* magnitude, which is why the reader is one shared module and not a
* `getUint` at each call site.
*
* `enabled` is a tombstone. A retired bound must prove it was retired: an
* absent leaf proves nothing, and a consumer that treats absence as
* "unlimited" is exactly the failure a policy row exists to prevent.
*/
struct PolicyEntry {
bytes32 paramId;
uint256 value;
bool enabled;
uint64 epoch;
}
/// @notice Per-chain EXECUTION TERMS — kind 7. What a leg on this chain
/// executes under and what its fee quote is bounded by. Operator-
/// tunable: a newer record (epoch) supersedes, never a deploy
/// (ruled 2026-09-04: "default lane plus other available variants on
/// a given chain should be there in the tree, changeable there per
/// chain"). A zero numeric field means "the fee schedule's default".
struct ChainTermsEntry {
bytes32 chainRef;
/// @dev The lane a leg on this chain takes unless the intent names
/// another AVAILABLE one. Lane ids and their meaning are the
/// intent plane's table (arch/intent-plane.md): 0 = the relayer-
/// staged PQ envelope through the chain's gateway.
uint8 defaultLane;
/// @dev Bitmask of the lanes available on this chain (bit i = lane i);
/// must include `defaultLane`. Any bit is storable — a new lane is a
/// record, not a registry redeploy.
uint16 lanes;
/// @dev How long a fee quote for this chain stays valid, seconds.
uint32 quoteWindowSeconds;
/// @dev Per-chain float premium on top of cost, basis points.
uint16 floatPremiumBps;
/// @dev Admission floor for a leg on this chain, USD micros.
uint64 admissionFloorUsdMicros;
/// @dev Legs one intent may carry on this chain; 0 = no per-chain cap.
uint16 maxLegsPerIntent;
bool enabled;
uint64 epoch;
}
mapping(bytes32 chainRef => ChainEntry) internal _chains;
mapping(bytes32 chainRef => bool) internal _chainKnown;
bytes32[] internal _assetIds;
mapping(bytes32 assetId => AssetEntry) internal _assets;
mapping(bytes32 assetId => bool) internal _assetKnown;
/// @dev Per-chain policy and venue wiring, `assetId -> chainRef -> entry`.
/// Sparse on purpose: an asset with no row on a chain is simply not a
/// fee or refill asset there, which is the common case.
mapping(bytes32 assetId => mapping(bytes32 chainRef => AssetChainEntry)) internal _assetChains;
/// @dev Kind 3 — a global policy scalar, keyed by a name.
mapping(bytes32 paramId => PolicyEntry) internal _policy;
mapping(bytes32 paramId => bool) internal _policyKnown;
bytes32[] internal _policyIds;
mapping(bytes32 chainRef => ChainTermsEntry) internal _chainTerms;
/// @dev Kind 4 — price sources, keyed by their tree-6 key so one mapping
/// serves rows on a chain and CEX rows alike; `_sourceKeys` is the
/// iteration order and `_sourceKeysFor[assetId]` the per-asset view.
mapping(bytes32 key => PriceSourceEntry) internal _sources;
mapping(bytes32 key => bool) internal _sourceKnown;
bytes32[] internal _sourceKeys;
mapping(bytes32 assetId => bytes32[]) internal _sourceKeysFor;
/// @dev Kind 5 — DEX protocol deployments, `chainRef -> protocolId -> entry`.
mapping(bytes32 chainRef => mapping(bytes32 protocolId => DexProtocolEntry)) internal _protocols;
mapping(bytes32 chainRef => bytes32[]) internal _protocolIdsOn;
/// @notice The roots `FinalSettlement` consumes. Recomputed on every write.
bytes32 public chainRegistryRoot;
bytes32 public assetRegistryRoot;
/// @notice Bumped on every mutation; published beside each root so a
/// consumer can tell which moment a proof was built against.
uint64 public registryEpoch;
// ---------------------------------------------------------------- events
event RegistryConfigured(uint256 role, uint256 threshold);
event ChainSet(bytes32 indexed chainRef, bool enabled, uint64 epoch);
event AssetSet(bytes32 indexed assetId, uint8 uses, bool enabled, uint64 epoch);
event AssetChainSet(
bytes32 indexed assetId, bytes32 indexed chainRef, uint8 uses, bool enabled, uint64 epoch
);
event PolicySet(bytes32 indexed paramId, uint256 value, bool enabled, uint64 epoch);
event ChainTermsSet(
bytes32 indexed chainRef, uint8 defaultLane, uint16 lanes, uint32 quoteWindowSeconds, uint16 floatPremiumBps, uint64 epoch
);
event PriceSourceSet(
bytes32 indexed assetId, bytes32 indexed chainRef, bytes32 indexed venueId, bool enabled, uint64 epoch
);
event DexProtocolSet(bytes32 indexed chainRef, bytes32 indexed protocolId, bool enabled, uint64 epoch);
event RootsPublished(bytes32 chainRoot, bytes32 assetRoot, uint64 epoch);
event Sealed();
// ---------------------------------------------------------------- errors
error NotAdmin(address caller);
/// @notice The default lane is not among the chain's available lanes.
error LaneNotAvailable(bytes32 chainRef, uint8 defaultLane, uint16 lanes);
/// @notice The epoch can be seeded only into a registry that holds nothing.
error NotFresh();
/// @notice A fresh registry took over the previous registry's epoch.
event EpochSeeded(uint64 epoch);
error AlreadySealed();
error ThresholdUnreachable(uint256 live, uint256 asked);
error NotConfigured();
error UnknownChain(bytes32 chainRef);
error UnknownAsset(bytes32 assetId);
error EmptyBatch();
error ChainNotRegistered(bytes32 chainRef);
error ChainRefMismatch(bytes32 claimed, bytes32 derived);
error AssetNotRegistered(bytes32 assetId);
/// @dev A use bit set in the wrong scope — a global bit on a per-chain row
/// or the reverse. Refused rather than stored, because a bit nothing
/// reads still looks accepted to the operator who set it.
error UseBitOutOfScope(uint8 uses);
/// @dev A kind-4 row is malformed for its venue kind: a CEX row on a chain
/// or with a protocol, a DEX row with no chain, no pool, no protocol,
/// or a protocol the chain has no row for, a zero weight, a zero window.
error InvalidPriceSource(bytes32 assetId, bytes32 chainRef, bytes32 venueId);
/// @dev A kind-5 row names no protocol, no factory, or an unknown kind.
error InvalidDexProtocol(bytes32 chainRef, bytes32 protocolId);
error VmKindMismatch(bytes32 chainRef, uint8 vmKind);
/// @dev A chain row's protocol description is incomplete for an enabled
/// chain: no block timer, no finality rule, no gas model.
error IncompleteChainDescription(bytes32 chainRef);
/// @notice `role` is not one of the `CHAIN_ROLE_*` values.
error InvalidChainRole(bytes32 chainRef, uint8 role);
/// @notice An enabled FULL chain named no settlement contract.
error SettlementRequired(bytes32 chainRef);
/// @notice A per-chain leverage cap outside `[LEVERAGE_CAP_MIN_PCT, LEVERAGE_CAP_MAX_PCT]`.
error LeverageCapOutOfRange(bytes32 assetId, bytes32 chainRef, uint16 maxLeveragePct);
// ----------------------------------------------------------- constructor
/**
* @dev The precompile probe is the point of having a constructor. A
* registry deployed where ML-DSA cannot be verified would accept no quorum
* it was ever given, and the first symptom would be a roster nobody can
* change.
*/
constructor(FinalIdentityRegistry registry_, FinalStateTrees trees_, address admin_) {
FinalChainPrecompiles.assertAvailable();
registry = registry_;
trees = trees_;
admin = admin_;
}
// ------------------------------------------------------------- bootstrap
/**
* @notice Set which role may mutate the registry, and how many approvals.
* @dev Bootstrap only. A threshold above the live member count is refused
* rather than stored: that is not a strict quorum, it is a registry that
* reverts on every write with the revert naming the threshold rather than
* the roster.
*/
function configure(
uint256 role,
uint256 k,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
// The admin alone while this contract's window is open; the sealed
// `ROLE_REGISTRAR` quorum afterwards, exactly as on the registry and
// the trees. Before the quorum path existed, `seal()` froze this
// configuration forever — a publisher set that could never re-threshold.
if (msg.sender != admin) {
registry.requireRegistrarQuorum(
ACTION_CONFIGURE, keccak256(abi.encode(role, k)), anchorBlock, approvals
);
}
if (k != 0) {
uint256 live = registry.liveMemberCount(role);
if (live < k) revert ThresholdUnreachable(live, k);
}
publisherRole = role;
threshold = k;
emit RegistryConfigured(role, k);
}
/// @notice Close the bootstrap window. One way.
function seal() external {
if (msg.sender != admin) revert NotAdmin(msg.sender);
admin = address(0);
emit Sealed();
}
/**
* @notice Take over the previous registry's `registryEpoch`, so the first
* rows this registry publishes carry an epoch every consumer's
* ring already accepts as newer. NO-WIPE redeploy, ruled
* 2026-09-03. Bootstrap admin only, only while empty.
*/
function seedEpoch(uint64 epoch_) external {
if (msg.sender != admin) revert NotAdmin(msg.sender);
if (registryEpoch != 0 || _chainRefs.length != 0 || _assetIds.length != 0) revert NotFresh();
registryEpoch = epoch_;
emit EpochSeeded(epoch_);
}
// -------------------------------------------------------------- mutation
/**
* @notice Add or update chains and assets, and remove them by disabling.
*
* @dev One entrypoint for add, update and remove, because they are the same
* write. A separate `remove` would be a second path to the same storage
* with its own quorum check to get wrong, and "removed" here is a field
* rather than an absence.
*
* The digest binds the nonce AND the full batch. Binding only the batch
* would make an approval to enable an asset an approval to re-enable it at
* any later block — which, for a roster that gates settlement, is the whole
* attack.
*
* An asset whose `originChainRef` names a chain this registry does not hold
* is refused. The asset registry's default-allow is bounded by the chain
* set's default-deny, and that only holds if the reference resolves.
*/
function mutate(
ChainEntry[] calldata chainUpdates,
AssetEntry[] calldata assetUpdates,
AssetChainEntry[] calldata assetChainUpdates,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
uint256 k = threshold;
if (k == 0) revert NotConfigured();
if (chainUpdates.length == 0 && assetUpdates.length == 0 && assetChainUpdates.length == 0) {
revert EmptyBatch();
}
uint64 n = nonce;
FinalPqQuorum.require_(
registry,
approvals,
FinalPqQuorum.digest(
address(this),
ACTION_MUTATE,
anchorBlock,
keccak256(abi.encode(n, chainUpdates, assetUpdates, assetChainUpdates))
),
publisherRole,
k,
FinalPqQuorum.ALG_ML_DSA_87,
anchorBlock,
false
);
nonce = n + 1;
uint64 e = registryEpoch + 1;
registryEpoch = e;
// One extra slot per kind-2 row: the fee-asset cadence rule below may
// republish that row's kind-1 leaf in the same write. Unused slots are
// trimmed before the writer call.
uint256 rows = chainUpdates.length + assetUpdates.length + 2 * assetChainUpdates.length;
bytes32[] memory keys = new bytes32[](rows);
bytes32[] memory leaves = new bytes32[](rows);
uint256 w;
for (uint256 i = 0; i < chainUpdates.length; i++) {
ChainEntry calldata c = chainUpdates[i];
// The reference must hash to the ref it is filed under. Unchecked,
// an entry could name Ethereum and carry Polygon's preimage — every
// proof would verify and every consumer would go to the wrong
// chain, which is the failure a hash-only entry cannot even have
// because it cannot be read at all.
if (chainRefFor(c.caipNamespace, c.caipReference) != c.chainRef) {
revert ChainRefMismatch(c.chainRef, chainRefFor(c.caipNamespace, c.caipReference));
}
// An enabled chain must be readable: the oracles pace on the block
// timer, the settlement lane waits on the finality rule, the fee
// quote needs the gas model, and every adapter branches on the VM
// kind. A tombstone may leave them zero.
if (c.enabled && (c.blockTimeMs == 0 || c.finalityKind == 0 || c.gasModel == 0 || c.vmKind == 0)) {
revert IncompleteChainDescription(c.chainRef);
}
if (c.role > CHAIN_ROLE_OBSERVED) revert InvalidChainRole(c.chainRef, c.role);
// A FULL chain settles; an OBSERVED one is only read, so it may
// name no contracts of ours at all.
if (c.enabled && c.role == CHAIN_ROLE_FULL && c.settlement == bytes32(0)) {
revert SettlementRequired(c.chainRef);
}
// The namespace and the machine must agree where the namespace
// decides it: an eip155 row claiming any other VM would send every
// adapter to the wrong codepath with a self-consistent row.
if (c.caipNamespace == keccak256("eip155") && c.vmKind != VM_EVM) {
revert VmKindMismatch(c.chainRef, c.vmKind);
}
if (c.l1ChainRef != bytes32(0) && !_chainKnown[c.l1ChainRef]) revert ChainNotRegistered(c.l1ChainRef);
if (!_chainKnown[c.chainRef]) {
_chainKnown[c.chainRef] = true;
_chainRefs.push(c.chainRef);
}
// Calldata straight into storage, the epoch stamped there, the leaf
// hashed from storage: no memory copy of a seventeen-field struct.
ChainEntry storage sc = _chains[c.chainRef];
sc.chainRef = c.chainRef;
sc.caipNamespace = c.caipNamespace;
sc.caipReference = c.caipReference;
sc.settlement = c.settlement;
sc.accountSpace = c.accountSpace;
sc.nativeAsset = c.nativeAsset;
sc.wrappedNative = c.wrappedNative;
sc.finalityKind = c.finalityKind;
sc.finalityParam = c.finalityParam;
sc.blockTimeMs = c.blockTimeMs;
sc.gasReadBlocks = c.gasReadBlocks;
sc.gasHistoryBlocks = c.gasHistoryBlocks;
sc.multicall = c.multicall;
sc.gasModel = c.gasModel;
sc.l1ChainRef = c.l1ChainRef;
sc.gateway = c.gateway;
sc.vmKind = c.vmKind;
sc.enabled = c.enabled;
sc.epoch = e;
sc.startHeight = c.startHeight;
sc.role = c.role;
keys[w] = allowlistKeyForChain(c.chainRef);
leaves[w] = _allowlistLeafForChain(sc);
w++;
emit ChainSet(c.chainRef, c.enabled, e);
}
for (uint256 i = 0; i < assetUpdates.length; i++) {
AssetEntry memory a = assetUpdates[i];
if (!_chainKnown[a.originChainRef]) revert ChainNotRegistered(a.originChainRef);
if (a.uses & ~GLOBAL_USE_MASK != 0) revert UseBitOutOfScope(a.uses);
// The cadence is a property of the asset's USES, chosen here rather
// than by each publisher: a morphable asset is what liquidations
// mark against, so it refreshes every second whether or not the
// operator remembered to say so. A fee asset is per chain (kind 2),
// which is why the kind-2 loop below applies the same rule.
if (a.priceCadenceMs == 0) {
a.priceCadenceMs = (a.uses & USE_MORPH) != 0 ? PRICE_CADENCE_FAST_MS : PRICE_CADENCE_DEFAULT_MS;
}
if (a.maxAgeMs == 0) {
a.maxAgeMs = (a.uses & USE_MORPH) != 0 ? PRICE_MAX_AGE_FAST_MS : PRICE_MAX_AGE_DEFAULT_MS;
}
a.epoch = e;
bytes32 id = assetIdFor(a.originChainRef, a.originToken);
if (!_assetKnown[id]) {
_assetKnown[id] = true;
_assetIds.push(id);
}
_assets[id] = a;
keys[w] = allowlistKeyForAsset(id);
leaves[w] = _allowlistLeafForAsset(id, a);
w++;
emit AssetSet(id, a.uses, a.enabled, e);
}
for (uint256 i = 0; i < assetChainUpdates.length; i++) {
AssetChainEntry memory ac = assetChainUpdates[i];
// Both halves must exist. A per-chain row naming an asset the
// registry does not know, or a chain it does not settle on, is a
// policy statement about nothing — and it would sit in the tree
// looking authoritative.
if (!_chainKnown[ac.chainRef]) revert ChainNotRegistered(ac.chainRef);
if (!_assetKnown[ac.assetId]) revert AssetNotRegistered(ac.assetId);
// One scope per bit. A global bit arriving on a per-chain row would
// be stored and never read, which is worse than a refusal: the
// operator sees it accepted.
if (ac.uses & ~CHAIN_USE_MASK != 0) revert UseBitOutOfScope(ac.uses);
if (
ac.maxLeveragePct != 0
&& (ac.maxLeveragePct < LEVERAGE_CAP_MIN_PCT || ac.maxLeveragePct > LEVERAGE_CAP_MAX_PCT)
) revert LeverageCapOutOfRange(ac.assetId, ac.chainRef, ac.maxLeveragePct);
ac.epoch = e;
_assetChains[ac.assetId][ac.chainRef] = ac;
keys[w] = allowlistKeyForAssetOnChain(ac.assetId, ac.chainRef);
leaves[w] = _allowlistLeafForAssetOnChain(ac);
w++;
emit AssetChainSet(ac.assetId, ac.chainRef, ac.uses, ac.enabled, e);
// A fee asset anywhere is a fast asset everywhere: the paymaster
// converts at its price, so the global row's cadence tightens the
// first time a chain accepts it as a fee token. Written as its own
// leaf so the kind-1 proof moves with the fact.
if (ac.enabled && (ac.uses & USE_FEE) != 0) {
AssetEntry storage ga = _assets[ac.assetId];
if (ga.priceCadenceMs > PRICE_CADENCE_FAST_MS || ga.maxAgeMs > PRICE_MAX_AGE_FAST_MS) {
ga.priceCadenceMs = PRICE_CADENCE_FAST_MS;
ga.maxAgeMs = PRICE_MAX_AGE_FAST_MS;
ga.epoch = e;
keys[w] = allowlistKeyForAsset(ac.assetId);
leaves[w] = _allowlistLeafForAsset(ac.assetId, ga);
w++;
emit AssetSet(ac.assetId, ga.uses, ga.enabled, e);
}
}
}
// Trim the unused tail so the writer sees exactly the rows written.
assembly ("memory-safe") {
mstore(keys, w)
mstore(leaves, w)
}
trees.setLeavesAsWriter(TREE_ALLOWLIST, BRANCH_MAIN_ID, keys, leaves);
_republishRoots(e);
}
/**
* @notice Set or retire price sources (kind 4) and DEX protocol deployments
* (kind 5).
*
* @dev Its own door for the reasons `setPolicy` is: `mutate`'s digest binds
* its three arrays, and neither kind is copied to other chains, so the
* registry roots in tree 5 must not move for them. Shared nonce, distinct
* action.
*
* Protocols are applied before sources within the batch, so a pool and the
* deployment it is verified against can land in one write.
*/
function setSources(
DexProtocolEntry[] calldata protocolUpdates,
PriceSourceEntry[] calldata sourceUpdates,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
uint256 k = threshold;
if (k == 0) revert NotConfigured();
if (protocolUpdates.length == 0 && sourceUpdates.length == 0) revert EmptyBatch();
uint64 n = nonce;
FinalPqQuorum.require_(
registry,
approvals,
FinalPqQuorum.digest(
address(this),
ACTION_SET_SOURCES,
anchorBlock,
keccak256(abi.encode(n, protocolUpdates, sourceUpdates))
),
publisherRole,
k,
FinalPqQuorum.ALG_ML_DSA_87,
anchorBlock,
false
);
nonce = n + 1;
uint64 e = registryEpoch + 1;
registryEpoch = e;
uint256 rows = protocolUpdates.length + sourceUpdates.length;
bytes32[] memory keys = new bytes32[](rows);
bytes32[] memory leaves = new bytes32[](rows);
uint256 w;
for (uint256 i = 0; i < protocolUpdates.length; i++) {
DexProtocolEntry calldata p = protocolUpdates[i];
if (!_chainKnown[p.chainRef]) revert ChainNotRegistered(p.chainRef);
if (p.protocolId == bytes32(0) || (p.enabled && (p.protocolKind == 0 || p.factory == bytes32(0)))) {
revert InvalidDexProtocol(p.chainRef, p.protocolId);
}
DexProtocolEntry storage sp = _protocols[p.chainRef][p.protocolId];
if (sp.epoch == 0) _protocolIdsOn[p.chainRef].push(p.protocolId);
sp.chainRef = p.chainRef;
sp.protocolId = p.protocolId;
sp.protocolKind = p.protocolKind;
sp.factory = p.factory;
sp.quoter = p.quoter;
sp.router = p.router;
sp.positionManager = p.positionManager;
sp.enabled = p.enabled;
sp.epoch = e;
keys[w] = allowlistKeyForProtocol(p.chainRef, p.protocolId);
leaves[w] = _allowlistLeafForProtocol(sp);
w++;
emit DexProtocolSet(p.chainRef, p.protocolId, p.enabled, e);
}
for (uint256 i = 0; i < sourceUpdates.length; i++) {
PriceSourceEntry calldata s = sourceUpdates[i];
if (!_assetKnown[s.assetId]) revert AssetNotRegistered(s.assetId);
_assertPriceSource(s);
bytes32 key = allowlistKeyForSource(s.assetId, s.chainRef, s.venueId);
if (!_sourceKnown[key]) {
_sourceKnown[key] = true;
_sourceKeys.push(key);
_sourceKeysFor[s.assetId].push(key);
}
PriceSourceEntry storage ss = _sources[key];
ss.assetId = s.assetId;
ss.chainRef = s.chainRef;
ss.venueId = s.venueId;
ss.venueKind = s.venueKind;
ss.venue = s.venue;
ss.protocolId = s.protocolId;
ss.symbol = s.symbol;
ss.quoteAsset = s.quoteAsset;
ss.weight = s.weight;
ss.baseToken = s.baseToken;
ss.quoteToken = s.quoteToken;
ss.baseIsToken0 = s.baseIsToken0;
ss.twapWindowSeconds = s.twapWindowSeconds;
ss.twapMinWindowSeconds = s.twapMinWindowSeconds;
ss.minLiquidity = s.minLiquidity;
ss.maxSpotDeviationBps = s.maxSpotDeviationBps;
ss.enabled = s.enabled;
ss.epoch = e;
keys[w] = key;
leaves[w] = _allowlistLeafForSource(ss);
w++;
emit PriceSourceSet(s.assetId, s.chainRef, s.venueId, s.enabled, e);
}
trees.setLeavesAsWriter(TREE_ALLOWLIST, BRANCH_MAIN_ID, keys, leaves);
}
/// @dev The shape rules of a kind-4 row, by venue kind. A tombstone
/// (`enabled == false`) only needs its identity.
function _assertPriceSource(PriceSourceEntry calldata s) private view {
if (s.venueId == bytes32(0)) revert InvalidPriceSource(s.assetId, s.chainRef, s.venueId);
if (!s.enabled) return;
if (s.weight == 0 || s.venue == bytes32(0)) revert InvalidPriceSource(s.assetId, s.chainRef, s.venueId);
if (s.venueKind == uint8(VenueKind.Cex)) {
// A CEX is on no chain and verifies against no factory.
if (s.chainRef != bytes32(0) || s.protocolId != bytes32(0)) {
revert InvalidPriceSource(s.assetId, s.chainRef, s.venueId);
}
return;
}
if (s.venueKind != uint8(VenueKind.Dex) && s.venueKind != uint8(VenueKind.Aggregator)) {
revert InvalidPriceSource(s.assetId, s.chainRef, s.venueId);
}
// A pool is on a chain the registry knows, verified against a
// deployment that chain has a row for, with a real window.
if (!_chainKnown[s.chainRef]) revert ChainNotRegistered(s.chainRef);
DexProtocolEntry storage p = _protocols[s.chainRef][s.protocolId];
if (s.protocolId == bytes32(0) || !p.enabled) revert InvalidPriceSource(s.assetId, s.chainRef, s.venueId);
if (s.baseToken == bytes32(0) || s.quoteToken == bytes32(0)) {
revert InvalidPriceSource(s.assetId, s.chainRef, s.venueId);
}
if (s.twapWindowSeconds == 0 || s.twapMinWindowSeconds == 0 || s.twapMinWindowSeconds > s.twapWindowSeconds) {
revert InvalidPriceSource(s.assetId, s.chainRef, s.venueId);
}
}
/**
* @notice Set or retire global policy scalars — tree 6, kind 3.
*
* @dev Deliberately NOT part of `mutate`. Two reasons, and the second is
* the load-bearing one:
*
* - `mutate`'s digest binds its three arrays, so widening it would
* invalidate every approval shape already in use for no gain here; and
* - policy scalars are not copied to other chains. `mutate` ends by
* republishing the chain and asset roots into tree 5, which is what
* `syncChain` copies. A swap ceiling has no business advancing that
* root: every chain would see a new registry epoch to catch up to, for a
* number none of them read.
*
* The nonce is shared with `mutate` so the two paths are totally ordered,
* and the action is distinct so an approval for one cannot be replayed
* through the other.
*/
function setPolicy(
PolicyEntry[] calldata updates,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
uint256 k = threshold;
if (k == 0) revert NotConfigured();
if (updates.length == 0) revert EmptyBatch();
uint64 n = nonce;
FinalPqQuorum.require_(
registry,
approvals,
FinalPqQuorum.digest(
address(this), ACTION_SET_POLICY, anchorBlock, keccak256(abi.encode(n, updates))
),
publisherRole,
k,
FinalPqQuorum.ALG_ML_DSA_87,
anchorBlock,
false
);
nonce = n + 1;
uint64 e = registryEpoch + 1;
registryEpoch = e;
bytes32[] memory keys = new bytes32[](updates.length);
bytes32[] memory leaves = new bytes32[](updates.length);
for (uint256 i = 0; i < updates.length; i++) {
PolicyEntry memory p = updates[i];
p.epoch = e;
if (!_policyKnown[p.paramId]) {
_policyKnown[p.paramId] = true;
_policyIds.push(p.paramId);
}
_policy[p.paramId] = p;
keys[i] = allowlistKeyForPolicy(p.paramId);
leaves[i] = _allowlistLeafForPolicy(p);
emit PolicySet(p.paramId, p.value, p.enabled, e);
}
trees.setLeavesAsWriter(TREE_ALLOWLIST, BRANCH_MAIN_ID, keys, leaves);
}
/**
* @notice Publish per-chain execution terms (kind 7) — the default lane,
* the available lanes and the fee-quote bounds a leg on that chain
* runs under. The same quorum as every other row; a newer epoch
* supersedes the old record, which is how these change.
*/
function setChainTerms(
ChainTermsEntry[] calldata updates,
uint64 anchorBlock,
FinalPqQuorum.Approval[] calldata approvals
) external {
uint256 k = threshold;
if (k == 0) revert NotConfigured();
if (updates.length == 0) revert EmptyBatch();
uint64 n = nonce;
FinalPqQuorum.require_(
registry,
approvals,
FinalPqQuorum.digest(
address(this), ACTION_SET_CHAIN_TERMS, anchorBlock, keccak256(abi.encode(n, updates))
),
publisherRole,
k,
FinalPqQuorum.ALG_ML_DSA_87,
anchorBlock,
false
);
nonce = n + 1;
uint64 e = registryEpoch + 1;
registryEpoch = e;
bytes32[] memory keys = new bytes32[](updates.length);
bytes32[] memory leaves = new bytes32[](updates.length);
for (uint256 i = 0; i < updates.length; i++) {
ChainTermsEntry memory t = updates[i];
if (!_chainKnown[t.chainRef]) revert ChainNotRegistered(t.chainRef);
if (t.lanes & (uint16(1) << t.defaultLane) == 0) revert LaneNotAvailable(t.chainRef, t.defaultLane, t.lanes);
t.epoch = e;
_chainTerms[t.chainRef] = t;
keys[i] = allowlistKeyForChainTerms(t.chainRef);
leaves[i] = _allowlistLeafForChainTerms(t);
emit ChainTermsSet(t.chainRef, t.defaultLane, t.lanes, t.quoteWindowSeconds, t.floatPremiumBps, e);
}
trees.setLeavesAsWriter(TREE_ALLOWLIST, BRANCH_MAIN_ID, keys, leaves);
}
/**
* @dev Recompute both registry roots and write them into tree 5.
*
* Both are folded over the ENABLED entries only. A disabled entry is a
* tombstone in tree 6, where a consumer needs to prove the negative; it is
* not a member of the registry a settlement contract copies from, and
* including it would make `syncChain` able to copy a revoked chain.
*/
function _republishRoots(uint64 e) private {
chainRegistryRoot = _foldChains();
assetRegistryRoot = _foldAssets();
bytes32[] memory keys = new bytes32[](2);
bytes32[] memory leaves = new bytes32[](2);
keys[0] = registryRootKey(REGISTRY_CHAIN);
leaves[0] = keccak256(
abi.encode(DOMAIN_REGISTRY_ROOT_LEAF, REGISTRY_CHAIN, chainRegistryRoot, e)
);
keys[1] = registryRootKey(REGISTRY_ASSET);
leaves[1] = keccak256(
abi.encode(DOMAIN_REGISTRY_ROOT_LEAF, REGISTRY_ASSET, assetRegistryRoot, e)
);
trees.setLeavesAsWriter(TREE_SETTLEMENT, BRANCH_MAIN_ID, keys, leaves);
emit RootsPublished(chainRegistryRoot, assetRegistryRoot, e);
}
// ------------------------------------------------------------------ keys
/// @dev `assetIdFor` as `FinalSettlement` derives it. Not a stored field:
/// a record carrying its own id could disagree with its own contents.
function assetIdFor(bytes32 originChainRef, bytes32 originToken) public pure returns (bytes32) {
return keccak256(abi.encode(originChainRef, originToken));
}
function allowlistKeyForChain(bytes32 chainRef) public pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_ALLOWLIST_KEY, uint8(0), chainRef));
}
function allowlistKeyForAsset(bytes32 assetId) public pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_ALLOWLIST_KEY, uint8(1), assetId));
}
/// @notice One asset's wiring on one chain — kind 2.
///
/// @dev The third key kind, because paymaster policy and venue integration
/// differ by network while morphability and bridging do not. There is
/// no inheritance from the global row: a bit is meaningful in exactly
/// one scope, so a consumer never has to implement a fallback rule that
/// another consumer might implement differently.
/// @notice One asset's per-chain wiring, or a zeroed entry if it has none.
function assetOnChain(bytes32 assetId, bytes32 chainRef)
external
view
returns (AssetChainEntry memory)
{
return _assetChains[assetId][chainRef];
}
/// @notice The morph leverage ceiling for `assetId` on EVM chain `chainId`,
/// in percent; 0 when the asset has no per-chain cap there.
/// `ILeverageCapSource` for `FinalStateRecords`.
function leverageCapPct(bytes32 assetId, uint64 chainId) external view returns (uint16) {
return _assetChains[assetId][evmChainRef(chainId)].maxLeveragePct;
}
function allowlistKeyForAssetOnChain(bytes32 assetId, bytes32 chainRef)
public
pure
returns (bytes32)
{
return keccak256(abi.encode(DOMAIN_ALLOWLIST_KEY, uint8(2), assetId, chainRef));
}
/// @notice One global policy scalar's tree-6 key — kind 3.
function allowlistKeyForPolicy(bytes32 paramId) public pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_ALLOWLIST_KEY, uint8(3), paramId));
}
/// @notice Kind 7: the chain's execution terms.
function allowlistKeyForChainTerms(bytes32 chainRef) public pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_ALLOWLIST_KEY, uint8(7), chainRef));
}
/// @notice One price source's tree-6 key — kind 4.
function allowlistKeyForSource(bytes32 assetId, bytes32 chainRef, bytes32 venueId)
public
pure
returns (bytes32)
{
return keccak256(abi.encode(DOMAIN_ALLOWLIST_KEY, uint8(4), assetId, chainRef, venueId));
}
/// @notice One DEX protocol deployment's tree-6 key — kind 5.
function allowlistKeyForProtocol(bytes32 chainRef, bytes32 protocolId) public pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_ALLOWLIST_KEY, uint8(5), chainRef, protocolId));
}
function registryRootKey(uint8 which) public pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_REGISTRY_ROOT_KEY, which));
}
// ----------------------------------------------------------- leaf hashes
/// @dev Kind 0, v2: the complete chain description. Pinned against the
/// backend's `chainLeafHash` by test.
function _allowlistLeafForChain(ChainEntry storage c) private view returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_ALLOWLIST_LEAF, uint8(0), c.chainRef, c.caipNamespace, c.caipReference,
c.settlement, c.accountSpace,
c.nativeAsset, c.wrappedNative, c.finalityKind, c.finalityParam,
c.blockTimeMs, c.gasReadBlocks, c.gasHistoryBlocks, c.multicall, c.gasModel, c.l1ChainRef,
c.gateway, c.vmKind,
c.enabled, c.epoch,
c.startHeight, c.role
)
);
}
/// @dev Kind 1, v2: `pool` / `poolQuote` moved to kind 4; the tree-4
/// cadence and staleness joined.
function _allowlistLeafForAsset(bytes32 id, AssetEntry memory a) private pure returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_ALLOWLIST_LEAF, uint8(1), id, a.originChainRef, a.originToken,
a.decimals, keccak256(bytes(a.name)), keccak256(bytes(a.symbol)),
a.uses, a.priceCadenceMs, a.maxAgeMs, a.enabled, a.epoch
)
);
}
/// @dev Kind 2, v2: policy only — the venue is a kind-4 row.
function _allowlistLeafForAssetOnChain(AssetChainEntry memory e) private pure returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_ALLOWLIST_LEAF, uint8(2), e.assetId, e.chainRef, e.uses, e.enabled, e.epoch, e.maxLeveragePct
)
);
}
function _allowlistLeafForPolicy(PolicyEntry memory p) private pure returns (bytes32) {
return keccak256(
abi.encode(DOMAIN_ALLOWLIST_LEAF, uint8(3), p.paramId, p.value, p.enabled, p.epoch)
);
}
function _allowlistLeafForChainTerms(ChainTermsEntry memory t) private pure returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_ALLOWLIST_LEAF, uint8(7), t.chainRef, t.defaultLane, t.lanes, t.quoteWindowSeconds,
t.floatPremiumBps, t.admissionFloorUsdMicros, t.maxLegsPerIntent, t.enabled, t.epoch
)
);
}
/// @dev Kind 4: identity, venue, composition weight, then every TWAP term
/// of the pool. Encoded in two halves because one `abi.encode` over
/// twenty words is the same bytes and this reads as the leaf it is.
function _allowlistLeafForSource(PriceSourceEntry storage s) private view returns (bytes32) {
return keccak256(
bytes.concat(
abi.encode(
DOMAIN_ALLOWLIST_LEAF, uint8(4), s.assetId, s.chainRef, s.venueId, s.venueKind, s.venue,
s.protocolId, s.symbol, s.quoteAsset, s.weight
),
abi.encode(
s.baseToken, s.quoteToken, s.baseIsToken0, s.twapWindowSeconds, s.twapMinWindowSeconds,
s.minLiquidity, s.maxSpotDeviationBps, s.enabled, s.epoch
)
)
);
}
/// @dev Kind 5.
function _allowlistLeafForProtocol(DexProtocolEntry storage p) private view returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_ALLOWLIST_LEAF, uint8(5), p.chainRef, p.protocolId, p.protocolKind,
p.factory, p.quoter, p.router, p.positionManager, p.enabled, p.epoch
)
);
}
/// @dev `FinalSettlement.ChainLeaf`, field for field and in order.
function chainLeafHash(ChainEntry memory c) public pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_CHAIN_LEAF, c.chainRef, c.settlement, c.accountSpace, c.epoch));
}
/// @dev `FinalSettlement.assetLeafHash`, byte for byte.
///
/// `name` and `symbol` are HASHED, because that is what the consumer does.
/// This encoded them as raw strings — with a comment asserting the
/// opposite — so `assetRegistryRoot` was folded over leaves no
/// `registerAsset` proof could verify against on any settlement chain.
/// Both sides were internally consistent and no proof verified anywhere,
/// while the root on the publishing chain looked perfectly healthy.
/// `AssetLeafParity.t.sol` pins the two against each other.
///
/// Not the same encoding as `_allowlistLeafForAsset`, deliberately: tree 6
/// commits to the full registry entry, this commits to the six fields
/// `FinalSettlement` copies.
function assetLeafHash(AssetEntry memory a) public pure returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_ASSET_LEAF, a.originChainRef, a.originToken,
a.decimals, keccak256(bytes(a.name)), keccak256(bytes(a.symbol)), a.epoch
)
);
}
// -------------------------------------------------------------- the fold
//
// Sorted-pair, tagged, promoting an odd node — `FinalMerkle`'s shape, which
// is what `FinalSettlement.verifyTaggedSortedProof` runs. Not tree 6's
// shape, and the two must not be confused: a fixed depth-20 slotted tree
// over the same leaves lands on a different root while both sides look
// right.
function _foldChains() private view returns (bytes32) {
uint256 n = _chainRefs.length;
bytes32[] memory hashes = new bytes32[](n);
uint256 m;
for (uint256 i = 0; i < n; i++) {
ChainEntry memory c = _chains[_chainRefs[i]];
if (!c.enabled) continue;
hashes[m++] = chainLeafHash(c);
}
return _fold(hashes, m);
}
function _foldAssets() private view returns (bytes32) {
uint256 n = _assetIds.length;
bytes32[] memory hashes = new bytes32[](n);
uint256 m;
for (uint256 i = 0; i < n; i++) {
AssetEntry memory a = _assets[_assetIds[i]];
if (!a.enabled) continue;
hashes[m++] = assetLeafHash(a);
}
return _fold(hashes, m);
}
/**
* @dev Fold `m` leaves of `hashes` into a sorted-pair tagged root.
*
* Leaves are sorted first, so the root is a function of the SET rather than
* of insertion order — two registries holding the same entries must publish
* the same root however they got there.
*
* An empty set folds to zero rather than to a hash of nothing. A consumer
* comparing against zero can tell "no registry published" from "a registry
* that happens to be empty"; a hash cannot be distinguished from a real
* root without knowing the construction.
*/
function _fold(bytes32[] memory hashes, uint256 m) private pure returns (bytes32) {
if (m == 0) return bytes32(0);
// Insertion sort. `m` is tens of entries and the alternative is a
// quicksort's worst case on an adversarially ordered set, which here
// would be a set an operator chose.
for (uint256 i = 1; i < m; i++) {
bytes32 x = hashes[i];
uint256 j = i;
while (j > 0 && hashes[j - 1] > x) {
hashes[j] = hashes[j - 1];
j--;
}
hashes[j] = x;
}
// Tag every leaf once, then pair upward.
for (uint256 i = 0; i < m; i++) {
hashes[i] = keccak256(abi.encodePacked(bytes1(0x00), hashes[i]));
}
uint256 len = m;
while (len > 1) {
uint256 w;
for (uint256 i = 0; i < len; i += 2) {
if (i + 1 == len) {
// Odd node promoted, not paired with itself: hashing a node
// with its own value makes a one-element layer collide with
// a two-element layer holding it twice.
hashes[w++] = hashes[i];
continue;
}
(bytes32 lo, bytes32 hi) =
hashes[i] < hashes[i + 1] ? (hashes[i], hashes[i + 1]) : (hashes[i + 1], hashes[i]);
hashes[w++] = keccak256(abi.encodePacked(bytes1(0x01), lo, hi));
}
len = w;
}
return hashes[0];
}
// ----------------------------------------------------------------- views
/// @notice One policy scalar. `enabled == false` is a RETIRED bound, and a
/// never-written one is zeroed — which is why the flag is returned and not
/// inferred from the value. Zero is a legitimate ceiling.
function policyOf(bytes32 paramId) external view returns (PolicyEntry memory) {
return _policy[paramId];
}
function policyCount() external view returns (uint256) {
return _policyIds.length;
}
function policyAt(uint256 i) external view returns (PolicyEntry memory) {
return _policy[_policyIds[i]];
}
/// @notice A chain's execution terms, or a zeroed entry when none were published.
function chainTermsOf(bytes32 chainRef) external view returns (ChainTermsEntry memory) {
return _chainTerms[chainRef];
}
function chainCount() external view returns (uint256) {
return _chainRefs.length;
}
function assetCount() external view returns (uint256) {
return _assetIds.length;
}
function chainAt(uint256 i) external view returns (ChainEntry memory) {
return _chains[_chainRefs[i]];
}
function assetAt(uint256 i) external view returns (AssetEntry memory) {
return _assets[_assetIds[i]];
}
function chainOf(bytes32 chainRef) external view returns (ChainEntry memory) {
if (!_chainKnown[chainRef]) revert UnknownChain(chainRef);
return _chains[chainRef];
}
function assetOf(bytes32 assetId) external view returns (AssetEntry memory) {
if (!_assetKnown[assetId]) revert UnknownAsset(assetId);
return _assets[assetId];
}
/**
* @notice Every enabled asset carrying `use`.
* @dev The roster a publisher reads instead of an environment variable. It
* is a view over state rather than a list handed to a process, so two
* publishers cannot disagree about what the set is.
*/
function assetsFor(uint8 use) external view returns (AssetEntry[] memory out) {
uint256 n = _assetIds.length;
AssetEntry[] memory buf = new AssetEntry[](n);
uint256 m;
for (uint256 i = 0; i < n; i++) {
AssetEntry memory a = _assets[_assetIds[i]];
if (a.enabled && (a.uses & use) != 0) buf[m++] = a;
}
out = new AssetEntry[](m);
for (uint256 i = 0; i < m; i++) out[i] = buf[i];
}
/**
* @notice Every asset carrying `use` ON `chainRef`, with its venue wiring.
*
* @dev The per-chain counterpart to `assetsFor`, and the roster a paymaster
* actually needs: `USE_FEE` and `USE_REFILL` live only on the per-chain row,
* so asking `assetsFor(USE_FEE)` would return nothing however many chains
* accept the asset. Both the global row and the per-chain row must be
* enabled — a globally revoked asset is revoked everywhere, and leaving that
* to each consumer to remember is how a disabled asset stays spendable on
* one chain.
*/
function assetsOnChainFor(bytes32 chainRef, uint8 use)
external
view
returns (AssetChainEntry[] memory out)
{
uint256 n = _assetIds.length;
AssetChainEntry[] memory buf = new AssetChainEntry[](n);
uint256 m;
for (uint256 i = 0; i < n; i++) {
bytes32 assetId = _assetIds[i];
if (!_assets[assetId].enabled) continue;
AssetChainEntry memory ac = _assetChains[assetId][chainRef];
if (ac.enabled && (ac.uses & use) != 0) buf[m++] = ac;
}
out = new AssetChainEntry[](m);
for (uint256 i = 0; i < m; i++) out[i] = buf[i];
}
/// @notice `keccak(DOMAIN_CHAIN_REF, namespace, reference)`, mirroring
/// `FinalSettlement.chainRefFor` byte for byte.
function chainRefFor(bytes32 namespace, bytes32 caipRef) public pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_CHAIN_REF, namespace, caipRef));
}
/// @notice The chain reference for an EVM chain id.
function evmChainRef(uint256 chainId) public pure returns (bytes32) {
return chainRefFor(CAIP_NAMESPACE_EIP155, bytes32(chainId));
}
/// @notice Every enabled chain's reference — what `FinalStateTrees.syncIdentities`
/// builds a service identity's `deployedChains` table from, and
/// what a reader enumerates the chain set by (`chainOf` per ref).
/// @dev Refs rather than rows: a seventeen-field row array is an ABI
/// encoder this contract has no room for, and every reader multicalls
/// anyway.
function enabledChainRefs() external view override returns (bytes32[] memory out) {
uint256 n = _chainRefs.length;
bytes32[] memory buf = new bytes32[](n);
uint256 m;
for (uint256 i = 0; i < n; i++) {
if (_chains[_chainRefs[i]].enabled) buf[m++] = _chainRefs[i];
}
out = new bytes32[](m);
for (uint256 i = 0; i < m; i++) out[i] = buf[i];
}
// ------------------------------------------------------ kinds 4 and 5, views
/// @notice One price source, or a zeroed entry if it has none.
function priceSourceOf(bytes32 assetId, bytes32 chainRef, bytes32 venueId)
external
view
returns (PriceSourceEntry memory)
{
return _sources[allowlistKeyForSource(assetId, chainRef, venueId)];
}
/// @notice One price source by its tree-6 key.
function priceSourceByKey(bytes32 key) external view returns (PriceSourceEntry memory) {
return _sources[key];
}
/**
* @notice The tree-6 keys of every price-source row for an asset, tombstones
* included — the roster the median reads, one venue per key, with
* the venue's own ticker and terms behind `priceSourceByKey`.
* @dev Keys rather than rows, for the encoder's sake; a reader multicalls
* the rows and drops `enabled == false` and rows of a disabled asset.
*/
function priceSourceKeysFor(bytes32 assetId) external view returns (bytes32[] memory) {
return _sourceKeysFor[assetId];
}
/// @notice Every price-source key ever written, in write order.
function priceSourceKeys() external view returns (bytes32[] memory) {
return _sourceKeys;
}
/// @notice One DEX protocol deployment, or a zeroed entry if the chain has none.
function dexProtocolOf(bytes32 chainRef, bytes32 protocolId) external view returns (DexProtocolEntry memory) {
return _protocols[chainRef][protocolId];
}
/// @notice Every DEX protocol id ever written for a chain — what can be
/// read there, behind `dexProtocolOf`. Replaces a literal set of
/// chain ids in the oracle.
function dexProtocolIdsOn(bytes32 chainRef) external view returns (bytes32[] memory) {
return _protocolIdsOn[chainRef];
}
}
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"
},
{
"name": "trees_",
"type": "address",
"internalType": "contract FinalStateTrees"
},
{
"name": "admin_",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "ACTION_CONFIGURE",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "CAIP_NAMESPACE_EIP155",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "CHAIN_ROLE_FULL",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "CHAIN_ROLE_OBSERVED",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "FINALITY_CONFIRMATIONS",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "FINALITY_L2_SETTLED",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "FINALITY_TAG_FINALIZED",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "GAS_MODEL_EIP1559",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "GAS_MODEL_L2_WITH_L1_FEE",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "GAS_MODEL_LEGACY",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "LEVERAGE_CAP_MAX_PCT",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint16",
"internalType": "uint16"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "LEVERAGE_CAP_MIN_PCT",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint16",
"internalType": "uint16"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PRICE_CADENCE_DEFAULT_MS",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PRICE_CADENCE_FAST_MS",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PRICE_MAX_AGE_DEFAULT_MS",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PRICE_MAX_AGE_FAST_MS",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PROTOCOL_CURVE",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PROTOCOL_UNISWAP_V2",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PROTOCOL_UNISWAP_V3",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PROTOCOL_UNISWAP_V4",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "PROTOCOL_VELODROME",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "VM_EVM",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "VM_MOVE",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "VM_SVM",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "uint8"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "admin",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "allowlistKeyForAsset",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "allowlistKeyForAssetOnChain",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "allowlistKeyForChain",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "allowlistKeyForChainTerms",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "allowlistKeyForPolicy",
"inputs": [
{
"name": "paramId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "allowlistKeyForProtocol",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "allowlistKeyForSource",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "assetAt",
"inputs": [
{
"name": "i",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.AssetEntry",
"components": [
{
"name": "originChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "originToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "decimals",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "name",
"type": "string",
"internalType": "string"
},
{
"name": "symbol",
"type": "string",
"internalType": "string"
},
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "priceCadenceMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "maxAgeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "assetCount",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "assetIdFor",
"inputs": [
{
"name": "originChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "originToken",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "assetLeafHash",
"inputs": [
{
"name": "a",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.AssetEntry",
"components": [
{
"name": "originChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "originToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "decimals",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "name",
"type": "string",
"internalType": "string"
},
{
"name": "symbol",
"type": "string",
"internalType": "string"
},
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "priceCadenceMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "maxAgeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "assetOf",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.AssetEntry",
"components": [
{
"name": "originChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "originToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "decimals",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "name",
"type": "string",
"internalType": "string"
},
{
"name": "symbol",
"type": "string",
"internalType": "string"
},
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "priceCadenceMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "maxAgeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "assetOnChain",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.AssetChainEntry",
"components": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "maxLeveragePct",
"type": "uint16",
"internalType": "uint16"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "assetRegistryRoot",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "assetsFor",
"inputs": [
{
"name": "use",
"type": "uint8",
"internalType": "uint8"
}
],
"outputs": [
{
"name": "out",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.AssetEntry[]",
"components": [
{
"name": "originChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "originToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "decimals",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "name",
"type": "string",
"internalType": "string"
},
{
"name": "symbol",
"type": "string",
"internalType": "string"
},
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "priceCadenceMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "maxAgeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "assetsOnChainFor",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "use",
"type": "uint8",
"internalType": "uint8"
}
],
"outputs": [
{
"name": "out",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.AssetChainEntry[]",
"components": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "maxLeveragePct",
"type": "uint16",
"internalType": "uint16"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "chainAt",
"inputs": [
{
"name": "i",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.ChainEntry",
"components": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipNamespace",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipReference",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "settlement",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "accountSpace",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "nativeAsset",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "wrappedNative",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "finalityKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "finalityParam",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "blockTimeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "gasReadBlocks",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "gasHistoryBlocks",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "multicall",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "gasModel",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "l1ChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "gateway",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "vmKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "startHeight",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "role",
"type": "uint8",
"internalType": "uint8"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "chainCount",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "chainLeafHash",
"inputs": [
{
"name": "c",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.ChainEntry",
"components": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipNamespace",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipReference",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "settlement",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "accountSpace",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "nativeAsset",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "wrappedNative",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "finalityKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "finalityParam",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "blockTimeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "gasReadBlocks",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "gasHistoryBlocks",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "multicall",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "gasModel",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "l1ChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "gateway",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "vmKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "startHeight",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "role",
"type": "uint8",
"internalType": "uint8"
}
]
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "chainOf",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.ChainEntry",
"components": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipNamespace",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipReference",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "settlement",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "accountSpace",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "nativeAsset",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "wrappedNative",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "finalityKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "finalityParam",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "blockTimeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "gasReadBlocks",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "gasHistoryBlocks",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "multicall",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "gasModel",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "l1ChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "gateway",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "vmKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "startHeight",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "role",
"type": "uint8",
"internalType": "uint8"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "chainRefFor",
"inputs": [
{
"name": "namespace",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipRef",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "chainRegistryRoot",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "chainTermsOf",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.ChainTermsEntry",
"components": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "defaultLane",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "lanes",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "quoteWindowSeconds",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "floatPremiumBps",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "admissionFloorUsdMicros",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "maxLegsPerIntent",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "configure",
"inputs": [
{
"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": "dexProtocolIdsOn",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "dexProtocolOf",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.DexProtocolEntry",
"components": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "factory",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "quoter",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "router",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "positionManager",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "enabledChainRefs",
"inputs": [],
"outputs": [
{
"name": "out",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "evmChainRef",
"inputs": [
{
"name": "chainId",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "leverageCapPct",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainId",
"type": "uint64",
"internalType": "uint64"
}
],
"outputs": [
{
"name": "",
"type": "uint16",
"internalType": "uint16"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "mutate",
"inputs": [
{
"name": "chainUpdates",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.ChainEntry[]",
"components": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipNamespace",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "caipReference",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "settlement",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "accountSpace",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "nativeAsset",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "wrappedNative",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "finalityKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "finalityParam",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "blockTimeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "gasReadBlocks",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "gasHistoryBlocks",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "multicall",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "gasModel",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "l1ChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "gateway",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "vmKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "startHeight",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "role",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"name": "assetUpdates",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.AssetEntry[]",
"components": [
{
"name": "originChainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "originToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "decimals",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "name",
"type": "string",
"internalType": "string"
},
{
"name": "symbol",
"type": "string",
"internalType": "string"
},
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "priceCadenceMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "maxAgeMs",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
},
{
"name": "assetChainUpdates",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.AssetChainEntry[]",
"components": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "maxLeveragePct",
"type": "uint16",
"internalType": "uint16"
}
]
},
{
"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": "nonce",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "policyAt",
"inputs": [
{
"name": "i",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.PolicyEntry",
"components": [
{
"name": "paramId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "policyCount",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "policyOf",
"inputs": [
{
"name": "paramId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.PolicyEntry",
"components": [
{
"name": "paramId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "priceSourceByKey",
"inputs": [
{
"name": "key",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.PriceSourceEntry",
"components": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "venue",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "symbol",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "quoteAsset",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "weight",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "baseToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "quoteToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "baseIsToken0",
"type": "bool",
"internalType": "bool"
},
{
"name": "twapWindowSeconds",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "twapMinWindowSeconds",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "minLiquidity",
"type": "uint128",
"internalType": "uint128"
},
{
"name": "maxSpotDeviationBps",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "priceSourceKeys",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "priceSourceKeysFor",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "priceSourceOf",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"internalType": "struct FinalAssetRegistry.PriceSourceEntry",
"components": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "venue",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "symbol",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "quoteAsset",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "weight",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "baseToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "quoteToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "baseIsToken0",
"type": "bool",
"internalType": "bool"
},
{
"name": "twapWindowSeconds",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "twapMinWindowSeconds",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "minLiquidity",
"type": "uint128",
"internalType": "uint128"
},
{
"name": "maxSpotDeviationBps",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "publisherRole",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "registry",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract FinalIdentityRegistry"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "registryEpoch",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "registryRootKey",
"inputs": [
{
"name": "which",
"type": "uint8",
"internalType": "uint8"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "pure"
},
{
"type": "function",
"name": "seal",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "seedEpoch",
"inputs": [
{
"name": "epoch_",
"type": "uint64",
"internalType": "uint64"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "setChainTerms",
"inputs": [
{
"name": "updates",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.ChainTermsEntry[]",
"components": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "defaultLane",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "lanes",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "quoteWindowSeconds",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "floatPremiumBps",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "admissionFloorUsdMicros",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "maxLegsPerIntent",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"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": "setPolicy",
"inputs": [
{
"name": "updates",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.PolicyEntry[]",
"components": [
{
"name": "paramId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"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": "setSources",
"inputs": [
{
"name": "protocolUpdates",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.DexProtocolEntry[]",
"components": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "factory",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "quoter",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "router",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "positionManager",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"internalType": "uint64"
}
]
},
{
"name": "sourceUpdates",
"type": "tuple[]",
"internalType": "struct FinalAssetRegistry.PriceSourceEntry[]",
"components": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueKind",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "venue",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "symbol",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "quoteAsset",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "weight",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "baseToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "quoteToken",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "baseIsToken0",
"type": "bool",
"internalType": "bool"
},
{
"name": "twapWindowSeconds",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "twapMinWindowSeconds",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "minLiquidity",
"type": "uint128",
"internalType": "uint128"
},
{
"name": "maxSpotDeviationBps",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "epoch",
"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": "threshold",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "trees",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract FinalStateTrees"
}
],
"stateMutability": "view"
},
{
"type": "event",
"name": "AssetChainSet",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "uses",
"type": "uint8",
"indexed": false,
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "AssetSet",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "uses",
"type": "uint8",
"indexed": false,
"internalType": "uint8"
},
{
"name": "enabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "ChainSet",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "enabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "ChainTermsSet",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "defaultLane",
"type": "uint8",
"indexed": false,
"internalType": "uint8"
},
{
"name": "lanes",
"type": "uint16",
"indexed": false,
"internalType": "uint16"
},
{
"name": "quoteWindowSeconds",
"type": "uint32",
"indexed": false,
"internalType": "uint32"
},
{
"name": "floatPremiumBps",
"type": "uint16",
"indexed": false,
"internalType": "uint16"
},
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "DexProtocolSet",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "enabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "EpochSeeded",
"inputs": [
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "PolicySet",
"inputs": [
{
"name": "paramId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "value",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "enabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "PriceSourceSet",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "venueId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "enabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "RegistryConfigured",
"inputs": [
{
"name": "role",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "threshold",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "RootsPublished",
"inputs": [
{
"name": "chainRoot",
"type": "bytes32",
"indexed": false,
"internalType": "bytes32"
},
{
"name": "assetRoot",
"type": "bytes32",
"indexed": false,
"internalType": "bytes32"
},
{
"name": "epoch",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"type": "event",
"name": "Sealed",
"inputs": [],
"anonymous": false
},
{
"type": "error",
"name": "AlreadySealed",
"inputs": []
},
{
"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": "AssetNotRegistered",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "BadSeal",
"inputs": [
{
"name": "signer",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "BadSignature",
"inputs": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "algorithm",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "ChainNotRegistered",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "ChainRefMismatch",
"inputs": [
{
"name": "claimed",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "derived",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "EmptyBatch",
"inputs": []
},
{
"type": "error",
"name": "IncompleteChainDescription",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "InvalidChainRole",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "role",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "InvalidDexProtocol",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "protocolId",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "InvalidPriceSource",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "LaneNotAvailable",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "defaultLane",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "lanes",
"type": "uint16",
"internalType": "uint16"
}
]
},
{
"type": "error",
"name": "LeverageCapOutOfRange",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "maxLeveragePct",
"type": "uint16",
"internalType": "uint16"
}
]
},
{
"type": "error",
"name": "NotAdmin",
"inputs": [
{
"name": "caller",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "NotConfigured",
"inputs": []
},
{
"type": "error",
"name": "NotFresh",
"inputs": []
},
{
"type": "error",
"name": "PrecompileUnavailable",
"inputs": [
{
"name": "precompile",
"type": "address",
"internalType": "address"
}
]
},
{
"type": "error",
"name": "SettlementRequired",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"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": "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": "live",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "asked",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "UnknownAsset",
"inputs": [
{
"name": "assetId",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "UnknownChain",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "UseBitOutOfScope",
"inputs": [
{
"name": "uses",
"type": "uint8",
"internalType": "uint8"
}
]
},
{
"type": "error",
"name": "VmKindMismatch",
"inputs": [
{
"name": "chainRef",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "vmKind",
"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 · 27,628 bajtů
0x60a0806040526004361015610012575f80fd5b5f905f3560e01c9081630292709e14614de857508063036f9a4314614dc95780630489d03814614d37578063087a5cf414614cd357806308f0ce62146132975780630900767d14614cb95780631591c4a014614686578063178bcc93146146415780631945098e146105815780631a633473146104755780631e000aa51461454b578063200d43ad146138f557806320d4f69614610475578063210de79d146138a95780632b89089c1461388c5780633ec4cc34146138465780633fb27b85146137f057806342cde4e8146137d25780634760fd43146137675780634bcb06a91461374d5780634ec824ff146132975780635483a0b51461370a57806357d70648146136eb5780635b88f50e146132975780635eb320b8146136cd5780635f61b15c1461329757806360c29fac14610581578063622c3f21146136b057806365cea49c1461369157806368cf465e146105815780636c2c44e2146136775780636db817521461365b578063711f3db41461358c578063767c39d2146104755780637b103999146135475780637bb737bc1461352d5780637f49d69f146134f2578063831f33a1146132d75780638513e2d5146132b8578063852a52e81461329c57806388f06eaa146132975780639212a9c91461311d57806392a2e0b3146131015780639377c220146130da5780639493ba1d1461307657806394bc4e9614612d7257806395102c4214612d555780639648eca214612ac257806397b9857b14612a65578063a741113c14612a2a578063aa9239f5146129d3578063ac65bc70146128b0578063affed0e014612889578063b19f48051461284e578063b8da302a14612830578063b9a7f07614612743578063bbbcda4414612724578063bda1019e14612706578063c5dbda66146126e9578063c8cfbc17146108fd578063dc544a09146108db578063de54d429146108bd578063df46c71314610767578063e02e1bfd14610749578063e597e65914610586578063e70475a714610581578063e94a52af14610565578063eafe7a7414610547578063eb7773821461047a578063ee076a5014610475578063ee57e33c146103e8578063f3178f4314610393578063f4e885db146103755763f851a4401461034c575f80fd5b34610372578060031936011261037257546040516001600160a01b039091168152602090f35b80fd5b50346103725780600319360112610372576020600154604051908152f35b50346103725760403660031901126103725761ffff600260406020936103b76152a5565b6004358252600a86526103d56001600160401b038484209216615fa3565b8252855220015460501c16604051908152f35b503461037257806003193601126103725760405180916020601154928381520191601182527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68915b81811061045f5761045b8561044781870382615507565b604051918291602083526020830190615374565b0390f35b8254845260209093019260019283019201610430565b61540e565b5034610372578060031936011261037257600454610497816157ad565b8291835b8181106104eb5750506104ad826157ad565b925b8281106104cc576040516020808252819061045b90820187615374565b806104d96001928461587b565b516104e4828761587b565b52016104af565b806104f76001926157df565b90549060031b1c8652600560205260ff600c60408820015460081c1661051e575b0161049b565b610527816157df565b90549060031b1c61054161053a876159e0565b968661587b565b52610518565b50346103725780600319360112610372576020600754604051908152f35b5034610372578060031936011261037257602060405160058152f35b6153f3565b50346103725760403660031901126103725760243560043560ff82168203610745576007546105b48161609d565b928492855b83811061065257505050506105cd8161609d565b91835b828110610628578385604051918291602083016020845282518091526020604085019301915b818110610604575050500390f35b91935091602060c08261061a600194885161532b565b0194019101918493926105f6565b806106356001928461587b565b51610640828761587b565b5261064b818661587b565b50016105d0565b61065b8161580b565b90549060031b1c808852600860205260ff600560408a20015460481c161561073c578752600a60205260408720828852602052604087206002604051916106a18361547c565b8054835260018101546020840152015460ff811660408301528460ff8260081c161515918260608501526001600160401b038160101c16608085015261ffff8160501c1660a08501528261072d575b5050610702575b506001905b016105b9565b949061072682610714600194916159e0565b9761071f828b61587b565b528861587b565b50906106f7565b60ff925016161515845f6106f0565b506001906106fc565b8280fd5b50346103725780600319360112610372576020600454604051908152f35b503461037257602036600319011261037257610781615443565b6007549061078e8261604e565b918391845b828110610841575050506107a68161604e565b91835b8281106108175783856040519182916020830160208452825180915260408401602060408360051b870101940192905b8282106107e857505050500390f35b919360019193955060206108078192603f198a82030186528851615579565b96019201920185949391926107d9565b806108246001928461587b565b5161082f828761587b565b5261083a818661587b565b50016107a9565b61084a8161580b565b90549060031b1c8652600860205261086460408720615e2e565b6101008101511515806108ab575b610880575b50600101610793565b93906108a482610892600194916159e0565b9661089d828a61587b565b528761587b565b5090610877565b5060ff8360a083015116161515610872565b50346103725780600319360112610372576020600d54604051908152f35b50346103725760206108f56108ef36615315565b90616008565b604051908152f35b50346103725760a0366003190112610372576001600160401b036004351161037257366023600435011215610372576001600160401b036004356004013511610372573660246102a06004356004013502600435010111610372576024356001600160401b038111612393576109779036906004016152e5565b916001600160401b036044351161037257366023604435011215610372576001600160401b0360443560040135116103725736602460c0604435600401350260443501011161037257606435926001600160401b0384168403612393576084356001600160401b038111610745576109f39036906004016152e5565b9460025480156126da576004356004013515806126d2575b806126c4575b6126b557600354926040519760a089016001600160401b03861660208b0152608060408b015260043560040135905260c08901602460043501885b60043560040135811061252e575050601f198a82030160608b01528681526020810160208860051b830101908a928a915b8a83106124225750505060209150601f198b82030160808c01526044356004013581520198602460443501885b6044356004013581106123ab575050610ad381610b759798999a9b03601f198101835282615507565b6020815191012060405160208101915f516020616bac5f395f51905f5283524660408301523060608301527fe10634eb0bf7bd6ad00dc59a6c67fe9b09037b979405fa59137e7e530d60141460808301526001600160401b03871660a083015260c082015260c08152610b4760e082615507565b51902090600154927f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6616100565b506001600160401b03610b89818316615764565b16906001600160401b0319161760035560175491610baf6001600160401b038416615764565b67ffffffffffffffff199093166001600160401b03841617601755610bd9826004803501356159b8565b600460443501356001600160ff1b038116900361239757610c07610c18916044356004013560011b906159b8565b610c10816157ad565b6080526157ad565b90809281935b6004356004013585101561147857610c476004356102a087020160648101359060440135616008565b60246102a087026004350101350361143d57610c6e6102446102a0870260043501016159c5565b806113b0575b61139257600160ff610c916102a46102a0890260043501016159d2565b161161135957610cac6102446102a0870260043501016159c5565b8061133a575b80611326575b611308577f84e75c8576483b53128ed23c26158522388ddb0a5df8516c83887d522196f91f60446102a0870260043501013514806112e6575b6112ad576101e46102a08602600435010135151580611289575b6112695760246102a086026004350101358352600660205260ff604084205416156111e3575b6102a08502600435016024810135808552600560205260408520908155604482013560018201908155606483013560028301556084830135600383015591610d7b9060a4016159d2565b6004828101805460ff191660ff939093169290921790915560c490356102a089020190810135600583015560e48101356006830155610dbd90610104016159d2565b60ff1660ff196007830154161760078201556102a087026004350161012401610de590615f8f565b6007820190610e12919068ffffffffffffffff0082549160081b169068ffffffffffffffff001916179055565b610e276101446004356102a08a0201016159fd565b600782015461ffff60681b610e476101646004356102a08d0201016159ee565b60681b169061ffff60781b610e676101846004356102a08e0201016159ee565b60781b169260481b6cffffffff000000000000000000169067ffffffffffffffff60481b191617171760078201556102a08702600435016101a4013560088201556102a08702600435016101c401610ebe906159d2565b60098201805460ff191660ff929092169190911790556101e46004356102a089020190810135600a830155610204810135600b830155610f0190610224016159d2565b60ff1660ff19600c8301541617600c8201556102a087026004350161024401610f29906159c5565b600c8201805491151560081b61ff001669ffffffffffffffffff00199092169190911760108a901b69ffffffffffffffff000016179055610f756102846004356102a08a020101615f8f565b600c8201805467ffffffffffffffff60501b191660509290921b67ffffffffffffffff60501b16919091179055610fb76102a46004356102a08a0201016159d2565b600c8201805460ff60901b198116609084901b60ff60901b1617909155918690610fec60246004356102a08d020101356158c2565b8660805190610ffa9161587b565b528054945460028201546003830154600484015460058501546006860154600787015460088089015460098a0154600a8b0154600b909b0154604080515f516020616bcc5f395f51905f5260208201529081019d909d5260608d019f909f5260808c019990995260a08b019790975260c08a019590955260ff93841660e08a01526101008901929092526101208801528082166101408801526001600160401b0381841c811661016089015263ffffffff604883901c1661018089015261ffff606883901c81166101a08a015260789290921c9091166101c08801526101e087019390935292831661020086015261022085019390935261024084019690965284811661026084015260ff60901b19909416609093841b60ff60901b16179081901c84161515610280830152601081901c85166102a0830152605081901c9094166102c082015292901c166102e080830191909152815261115d61030082615507565b8051906020012061116e828661587b565b52611178906159e0565b938561118f6102446004356102a0850201016159c5565b6040805191151582526001600160401b039290921660208201526102a083026004350160240135917f523b72ac5fbe8982a57ea63c8ca4daf1602c22eda42d6cc14a884c0310698b8091a260010193610c1e565b600480356102a0870201602401358452600660205260408420805460ff1916600117905554600160401b8110156112555761122981600161124e93016004556004615823565b60246102a089949394026004350101359083549060031b91821b915f19901b19161790565b9055610d31565b634e487b7160e01b84526041600452602484fd5b633f4b99b160e21b8352600480356102a08702016101e401359052602483fd5b506101e46102a086026004350101358352600660205260ff60408420541615610d0b565b60448360ff8760246102a06112cb61022482850260043501016159d2565b92631e9a7d7560e01b86520260043501013560045216602452fd5b50600160ff6113006102246102a0890260043501016159d2565b161415610cf1565b602483816102a088632eb72ead60e11b845202600435010135600452fd5b5060846102a0860260043501013515610cb8565b5060ff6113526102a46102a0880260043501016159d2565b1615610cb2565b60448360ff8760246102a06113776102a482850260043501016159d2565b92631a7ce1dd60e01b86520260043501013560045216602452fd5b602483816102a088633dc2fc1160e01b845202600435010135600452fd5b5063ffffffff6113cb6101446102a0880260043501016159fd565b1615801561141e575b80156113ff575b80610c74575060ff6113f86102246102a0880260043501016159d2565b1615610c74565b5060ff6114176101c46102a0880260043501016159d2565b16156113db565b5060ff6114366101046102a0880260043501016159d2565b16156113d4565b6044838660246102a061145e60043582850201606481013590870135616008565b92631a35ac9960e01b855202600435010135600452602452fd5b919350939483915b85831015611a5e578260051b82013561013e1983360301811215611a5a576114ab9036908401615685565b9283518652600660205260ff60408720541615611a465760a084015160ec8116611a31575063ffffffff60c08501511615611a03575b63ffffffff60e085015116156119d4575b6001600160401b0388166101208501526115128451602086015190615bf0565b808752600960205260ff60408820541615611967575b80875260086020526040872092855184556020860151600185015560ff60408701511660ff60028601911660ff1982541617905560608601518051906001600160401b0382116118825781906115816003880154615d56565b601f8111611909575b50602090601f83116001146118a1578b92611896575b50508160011b915f199060031b1c19161760038501555b60808601518051906001600160401b0382116118825787929189916115df6004890154615d56565b601f8111611819575b506020908c601f8411600114611771579361172b86611725600560019d8860a099611731997fed8946197b4bd5be9ac502b0c187bc46b61770261216a2d4f6b7d01562ef1b2f9e9d9960ff9d92611766575b5050600182901b915f199060031b1c19161760048201555b0198888089830151161689198b5416178a5561168f63ffffffff60c0830151168b9064ffffffff0082549160081b169064ffffffff001916179055565b60e08101518a5468ffffffff0000000000191660289190911b68ffffffff000000000016178a55610100810180518b5461012084015171ffffffffffffffffff0000000000000000001990911691151560481b69ff000000000000000000169190911760509190911b67ffffffffffffffff60501b1617909a556117128c615f5c565b61171e8460805161587b565b528b61656a565b9261587b565b526159e0565b9801519151604080519290931660ff168252151560208201526001600160401b038c1691810191909152606090a20191611480565b015190505f8061163a565b5090600489018d52808d20918d5b601f19851681106117fb57509361172b86611725600560019d8e89611731997fed8946197b4bd5be9ac502b0c187bc46b61770261216a2d4f6b7d01562ef1b2f9e9d9960ff9d60a09d601f198116106117e3575b505050811b016004820155611652565b01515f1960f88460031b161c191690555f80806117d3565b8282015184558c97508d95506001909301926020928301920161177f565b828111156115e857919394509150600487018b5260208b20601f840160051c906020851061187a575b918b93918b9695938e805b83601f860160051c03821061186557505050506115e8565b8383018201558d98508e96506001018f61184d565b8c9150611842565b634e487b7160e01b8a52604160045260248afd5b015190505f806115a0565b600388018c52818c209250601f1984168c5b8181106118f157509084600195949392106118d9575b505050811b0160038501556115b7565b01515f1960f88460031b161c191690555f80806118c9565b929360206001819287860151815501950193016118b3565b8281111561158a57909150600387018b5260208b20601f840160051c60208510611960575b808594939201908d805b82601f860160051c038210611950575050505061158a565b818401558695506001018e611938565b508b61192e565b808752600960205260408720805460ff19166001179055600754600160401b8110156119c0576119b96119a38260018594016007556007615823565b819391549060031b91821b915f19901b19161790565b9055611528565b634e487b7160e01b88526041600452602488fd5b60a0840151600216156119f55763ffffffff610bb85b1660e08501526114f2565b63ffffffff6201d4c06119ea565b60a084015160021615611a245763ffffffff6103e85b1660c08501526114e1565b63ffffffff612710611a19565b6376cbaf6d60e01b875260ff16600452602486fd5b8351633f4b99b160e21b8752600452602486fd5b8580fd5b868585815b60443560040135811015611e965760c081026044350160c06023198236030112611e925760405190611a948261547c565b6024810135825260448101356020830197818952611ab460648401615453565b9260408501938452611ac860848201615548565b9260608601938452611aef60c4611ae160a485016152d1565b936080890194855201615539565b9060a087019182528952600660205260ff60408a20541615611e7e5785518952600960205260ff60408a20541615611e6a57845160f38116611e55575061ffff81511680151580611e40575b611e21575090611c8292916001600160401b038b16825286518a52600a60205260408a208c518b52602052611bf660408b208d60028a519182845551928360018201550160ff808b51161660ff19825416178155611baf89511515829061ff00825491151560081b169061ff001916179055565b8551815469ffffffffffffffff0000191660109190911b69ffffffffffffffff0000161781558451815461ffff60501b191660509190911b61ffff60501b16179055615c10565b611c028460805161587b565b528651918c519160ff8851169161ffff6001600160401b0389511515925116925116926040519460208601965f516020616bcc5f395f51905f528852600260408801526060870152608086015260a085015260c084015260e08301526101008201526101008152611c7561012082615507565b51902061172b828961587b565b83519851835183516040805160ff909316835290151560208301526001600160401b038b1690820152919990917ffdf8afb51a6b8e68fa9d9946922368e4a2e54a69963cf7bcfcdff38235916e7990606090a35115159081611e13575b50611cee575b50600101611a63565b80518452600860205260408420600581019081546103e863ffffffff8260081c1611908115611dfd575b50611d25575b5050611ce5565b815471ffffffffffffffff00ffffffffffffffff001916605088901b67ffffffffffffffff60501b1617660bb8000003e8001782559196600193927fed8946197b4bd5be9ac502b0c187bc46b61770261216a2d4f6b7d01562ef1b2f918891611dbf9190611db590611d978d51615f5c565b611da38460805161587b565b52611daf8d5191615e2e565b9061656a565b61172b828a61587b565b985192546040805160ff808416825260489390931c909216151560208301526001600160401b039290921691810191909152606090a2908680611d1e565b610bb8915063ffffffff9060281c161189611d18565b600491505116151587611cdf565b86518c516368adc18560e01b8c52600491909152602452604452606489fd5b506064811080611b3b57506101f48111611b3b565b6376cbaf6d60e01b8a5260ff16600452602489fd5b85516326ef68cf60e01b8a52600452602489fd5b8a51633f4b99b160e21b8a52600452602489fd5b8380fd5b50608051849052928352917f000000000000000000000000617620cabf90b3ceabe00d8fb83626bdf55e3bd96001600160a01b0316908390823b156123935781611ef7916040518093819263abf1570d60e01b83526080516004840161588f565b038183875af180156121ae5761237e575b50600454611f15816157ad565b8291835b81811061221857505090611f2c91616985565b9182601555600754611f3d816157ad565b8391845b8181106121b957505090611f5491616985565b8060165560609360405191611f698684615507565b60028352601f198601918236602086013760405192611f888885615507565b6002845236602085013760405160208101907fbf8f90cf3ca0101d2844ee0558886a5f257e05327d13dd56af76c4d3c8ead50f825287604082015260408152611fd18982615507565b519020611fdd8561585e565b5260405160208101917f205e4a5b42cbb22c56a771766b971165a627b616bd23460c8e002faad102d0918352876040830152888201526001600160401b03891660808201526080815261203160a082615507565b51902061203d8361585e565b5260405160208101907fbf8f90cf3ca0101d2844ee0558886a5f257e05327d13dd56af76c4d3c8ead50f8252600160408201526040815261207e8882615507565b51902061208a8461586b565b5260405160208101917f205e4a5b42cbb22c56a771766b971165a627b616bd23460c8e002faad102d091835260016040830152878201526001600160401b0388166080820152608081526120df60a082615507565b5190206120eb8261586b565b52823b15611e9257612142926121308580946040519687958694859363abf1570d60e01b85526005600486015260016024860152608060448601526084850190615374565b83810360031901606485015290615374565b03925af180156121ae57612199575b50507ff95ac4707234b4ef5620ff790985a37679e4c7e0b0c603f110ae9bcc65b0cf5c916001600160401b03601554916016546040519384526020840152166040820152a180f35b816121a391615507565b610745578284612151565b6040513d84823e3d90fd5b60019192939495506121ca8161580b565b90549060031b1c895260086020526121e460408a20615e2e565b61010081015115612212576121f890615ec8565b61220461053a876159e0565b525b01908794939291611f41565b50612206565b60019192939450612228816157df565b90549060031b1c8852600560205260408820600c60405191612249836154b3565b80548352848101546020840152600281015460408401526003810154606084015260ff6004820154166080840152600581015460a0840152600681015460c084015261ffff600782015460ff811660e08601526001600160401b038160081c1661010086015263ffffffff8160481c16610120860152818160681c1661014086015260781c16610160840152600881015461018084015260ff6009820154166101a0840152600a8101546101c0840152600b8101546101e0840152015460ff811661020083015260ff808260081c16159182156102208501526001600160401b038160101c166102408501526001600160401b038160501c1661026085015260901c166102808301526123785761235f90615c4a565b61236b61053a876159e0565b525b019086939291611f19565b5061236d565b8161238891615507565b610745578284611f08565b5080fd5b634e487b7160e01b82526011600452602482fd5b909a60c08060019261ffff8f60a081612412923585526020810135602086015260ff6123d960408301615453565b1660408601526123eb60608201615548565b151560608601526001600160401b03612406608083016152d1565b16608086015201615539565b1660a0820152019c019101610aaa565b909192601f1983820301825261013e198d3603018535121561252a576020806001928f8835019081358152828201358382015260ff61246360408401615453565b1660408201526101206001600160401b03612517826124b961249e61248b6060890189615cba565b61014060608a0152610140890191615ceb565b6124ab6080890189615cba565b9088830360808a0152615ceb565b9560ff6124c860a08301615453565b1660a087015263ffffffff6124df60c08301615528565b1660c087015263ffffffff6124f660e08301615528565b1660e08701526125096101008201615548565b1515610100870152016152d1565b1691015296019201930191909392610a7d565b8b80fd5b90916102a0806001928535815260208601356020820152604086013560408201526060860135606082015260ff61256760808801615453565b16608082015260a086013560a082015260c086013560c082015260ff61258f60e08801615453565b1660e08201526001600160401b036125aa61010088016152d1565b1661010082015263ffffffff6125c36101208801615528565b1661012082015261ffff6125da6101408801615539565b1661014082015261ffff6125f16101608801615539565b1661016082015261018086013561018082015260ff6126136101a08801615453565b166101a08201526101c08601356101c08201526101e08601356101e082015260ff6126416102008801615453565b166102008201526126556102208701615548565b15156102208201526001600160401b0361267261024088016152d1565b166102408201526001600160401b0361268e61026088016152d1565b1661026082015260ff6126a46102808801615453565b166102808201520193019101610a4c565b63c2e5347d60e01b8552600485fd5b506044356004013515610a11565b508315610a0b565b63d311bc3960e01b8552600485fd5b503461037257806003193601126103725760206040516101f48152f35b50346103725780600319360112610372576020601554604051908152f35b50346103725760203660031901126103725760206108f5600435615f5c565b503461037257602036600319011261037257600435612760615b66565b50600d5481101561281c577fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50154600382901b1c8152600b602052604090819020905161045b916001600160401b03906002906127bc84615461565b8054845260018101546020850152015460ff81161515604084015260081c1660608201526040519182918291909160606001600160401b038160808401958051855260208101516020860152604081015115156040860152015116910152565b634e487b7160e01b82526032600452602482fd5b503461037257806003193601126103725760206040516201d4c08152f35b503461037257806003193601126103725760206040517f3d07e5a0f372bb3f92469df7a6dcb4a49539b8707a06a699d3727c07dbbb3a4b8152f35b503461037257806003193601126103725760206001600160401b0360035416604051908152f35b5034610372576020366003190112610372576128ca615975565b506004358152600e602052604090206040516128e581615497565b81549182825260010154906020810160ff83168152604082018360081c61ffff168152606083018460181c63ffffffff16815260808401908560381c61ffff16825260a08501928660481c6001600160401b0316845260c08601948760881c61ffff16865260e08701968860981c60ff1615158852610100019760a01c6001600160401b031688526040519889525160ff1660208901525161ffff1660408801525163ffffffff1660608701525161ffff166080860152516001600160401b031660a08501525161ffff1660c084015251151560e0830152516001600160401b031661010082015261012090f35b503461037257602036600319011261037257612a16604061045b926129f6615d0b565b50612a0260043561580b565b90549060031b1c8152600860205220615e2e565b604051918291602083526020830190615579565b503461037257602036600319011261037257600435906001600160401b0382116103725760206108f5612a603660048601615685565b615ec8565b50346103725760203660031901126103725760043590612a83615d0b565b50818152600960205260ff60408220541615612ab05760408161045b93612a169352600860205220615e2e565b60249163082cdf1f60e11b8252600452fd5b50346103725760203660031901126103725760043590612ae0615a0e565b50818152600660205260ff60408220541615612d43578061045b926040925260056020522060ff600c60405192612b16846154b3565b80548452600181015460208501526002810154604085015260038101546060850152826004820154166080850152600581015460a0850152600681015460c085015261ffff600782015484811660e08701526001600160401b038160081c1661010087015263ffffffff8160481c16610120870152818160681c1661014087015260781c166101608501526008810154610180850152826009820154166101a0850152600a8101546101c0850152600b8101546101e08501520154818116610200840152818160081c1615156102208401526001600160401b038160101c166102408401526001600160401b038160501c1661026084015260901c166102808201526040519182918291909161028060ff816102a08401958051855260208101516020860152604081015160408601526060810151606086015282608082015116608086015260a081015160a086015260c081015160c08601528260e08201511660e08601526001600160401b036101008201511661010086015263ffffffff6101208201511661012086015261ffff6101408201511661014086015261ffff61016082015116610160860152610180810151610180860152826101a0820151166101a08601526101c08101516101c08601526101e08101516101e0860152826102008201511661020086015261022081015115156102208601526001600160401b03610240820151166102408601526001600160401b0361026082015116610260860152015116910152565b602491632a80afbd60e21b8252600452fd5b50346103725780600319360112610372576020604051610bb88152f35b503461037257608036600319011261037257600435602435612d926152bb565b6064356001600160401b03811161307257612db19036906004016152e5565b85546001600160a01b03163303612ebc575b50505080612e07575b816040917f40bd951b186ab08230e53414aeaafc99b557bb646ef875822965f0e5a8c32ca5936001558060025582519182526020820152a180f35b60405163342f616360e01b8152600481018390526020816024817f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03165afa908115612eb1578491612e7f575b50818110612e695750612dcc565b633770da3360e11b845260045260245250604490fd5b90506020813d602011612ea9575b81612e9a60209383615507565b81010312611e9257515f612e5b565b3d9150612e8d565b6040513d86823e3d90fd5b60408051602081018781528183018790529181527f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b0316939190612f08606082615507565b519020833b1561306e57908288959493926001600160401b03604051966322f3f44760e11b885260848801927f3d07e5a0f372bb3f92469df7a6dcb4a49539b8707a06a699d3727c07dbbb3a4b60048a01526024890152166044870152608060648701525260a4840160a48360051b86010192828790607e19813603015b838310612fc4575050505050508391838381809403925af180156121ae57612faf575b80612dc3565b81612fb991615507565b61074557825f612fa9565b9193959092949697985060a31989820301865286358281121561306a57830180359160018060a01b038316809303613066576130526020928260019585945260ff613010858401615453565b16848201526130446130396130286040850185615cba565b608060408601526080850191615ceb565b926060810190615cba565b916060818503910152615ceb565b98019601930190918b989796959492612f86565b8d80fd5b8c80fd5b8780fd5b8480fd5b5034610372576020366003190112610372576004358152601460205260408120604051918260208354918281520192825260208220915b8181106130c45761045b8561044781870382615507565b82548452602090930192600192830192016130ad565b503461037257806003193601126103725760206001600160401b0360175416604051908152f35b5034610372578060031936011261037257602060405160048152f35b5034610372576102a0366003190112610372576040519061313d826154b3565b600435825260243560208301526044356040830152606435606083015260843560ff8116810361239357608083015260a43560a083015260c43560c083015260e43560ff811681036123935760e0830152610104356001600160401b0381168103612393576101008301526101243563ffffffff81168103612393576101208301526101443561ffff81168103612393576101408301526101643561ffff8116810361239357610160830152610184356101808301526101a43560ff81168103612393576101a08301526101c4356101c08301526101e4356101e08301526102043560ff811681036123935761020083015261022435801515810361239357610220830152610244356001600160401b038116810361239357610240830152610264356001600160401b038116810361239357610260830152610284359060ff821682036103725760206108f58484610280820152615c4a565b6153a7565b5034610372578060031936011261037257602060405160648152f35b50346103725760203660031901126103725760206108f5600435615fa3565b503461037257602036600319011261037257604061045b916132f7615aa5565b506004358152600f602052206001600160401b03600c6040519261331a846154cf565b80548452600181015460208501526002810154604085015260ff600382015416606085015260048101546080850152600581015460a0850152600681015460c0850152600781015460e085015261ffff6008820154166101008501526009810154610120850152600a81015461014085015260ff600b820154818116151561016087015263ffffffff8160081c1661018087015263ffffffff8160281c166101a08701526001600160801b038160481c166101c087015261ffff8160c81c166101e087015260d81c161515610200850152015416610220820152604051918291829190916102206001600160401b038161024084019580518552602081015160208601526040810151604086015260ff60608201511660608601526080810151608086015260a081015160a086015260c081015160c086015260e081015160e086015261ffff61010082015116610100860152610120810151610120860152610140810151610140860152610160810151151561016086015263ffffffff6101808201511661018086015263ffffffff6101a0820151166101a08601526001600160801b036101c0820151166101c086015261ffff6101e0820151166101e08601526102008101511515610200860152015116910152565b503461037257806003193601126103725760206040517f84e75c8576483b53128ed23c26158522388ddb0a5df8516c83887d522196f91f8152f35b50346103725760206108f561354136615315565b90615c10565b50346103725780600319360112610372576040517f000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa66001600160a01b03168152602090f35b5034610372576020366003190112610372576004356001600160401b0381168091036123935781546001600160a01b03163303613648576017546001600160401b0381161580159061363d575b8015613632575b6136235767ffffffffffffffff191681176017556040519081527f1b0319a7c1b77359b02ef03a86a6e1677b3d55bc74afbe201986efaef1e8b4b590602090a180f35b63dc63d81f60e01b8352600483fd5b5060075415156135e0565b5060045415156135d9565b630bd4212160e11b825233600452602482fd5b5034610372578060031936011261037257602090604051908152f35b50346103725760206108f561368b36615315565b90615bf0565b50346103725760203660031901126103725760206108f5600435615bbd565b503461037257806003193601126103725760206040516127108152f35b50346103725780600319360112610372576020601654604051908152f35b50346103725760203660031901126103725760206108f5600435615b8a565b503461037257602036600319011261037257604061045b9161372a615b66565b506004358152600b602052206001600160401b036002604051926127bc84615461565b50346103725760206108f561376136615429565b91615b27565b503461037257602036600319011261037257602090613784615443565b905060405160ff838201927fbf8f90cf3ca0101d2844ee0558886a5f257e05327d13dd56af76c4d3c8ead50f8452166040820152604081526137c7606082615507565b519020604051908152f35b50346103725780600319360112610372576020600254604051908152f35b503461037257806003193601126103725780546001600160a01b0381163303613648576001600160a01b03191681557f1b2d71eb44f882534bf4e86f940c56ccc869ffb927e2bab86561de93950c22168180a180f35b503461037257604061045b9161386d61385e36615429565b91613867615aa5565b50615b27565b8152600f602052206001600160401b03600c6040519261331a846154cf565b503461037257806003193601126103725760206040516103e88152f35b503461037257602036600319011261037257604061045b916138c9615a0e565b506138d56004356157df565b90549060031b1c815260056020522060ff600c60405192612b16846154b3565b5034610372576080366003190112610372576004356001600160401b038111612393576139269036906004016153c2565b6001600160401b0360243511610745573660236024350112156107455760243560040135916001600160401b038311611e925736602461024085028135010111611e92576139726152bb565b6064356001600160401b038111611a5a576139919036906004016152e5565b9091600254801561453c57851580614534575b61452557600354936001600160401b038516936040518860808201876020840152606060408401525260a0810188908c5b8b81106144975750828103601f190160608401528b8152602001905060248035018c5b8c811061434f57505092613a9b959282613a286001600160401b03999794613a959703601f198101835282615507565b6020815191012060405160208101915f516020616bac5f395f51905f5283524660408301523060608301527f5596fa6720176cbfa2a11ee0d5be50e2016480c70c477d3273492663856b2d0b60808301528a871660a083015260c082015260c08152610b4760e082615507565b50615764565b16906001600160401b0319161760035560175491613ac16001600160401b038416615764565b67ffffffffffffffff199093166001600160401b03841617601755613ae684826159b8565b91613af9613af3846157ad565b936157ad565b908692875b8181106140a35750505085915b8686841015614025575060246102408402813501019283358852600960205260ff6040892054161561401157908791613b438561637b565b613b60602435610240830201606481013590604401358735615b27565b91828452601060205260ff60408520541615613fb2575b828452600f6020526040842086358155602435610240840201604481013560018301556064810135600283015590919060ff90613bb6906084016159d2565b60038401805460ff19169290911691909117905560243561024084020160a4810135600484015560c4810135600584015560e48101356006840155610104810135600784015561ffff90613c0d90610124016159ee565b60088401805461ffff1916929091169190911790556024356102408402016101448101356009840155610164810135600a840155613c6790613c5290610184016159c5565b600b84019060ff801983541691151516179055565b613c9f613c7f6101a4610240860260243501016159fd565b600b84019064ffffffff0082549160081b169064ffffffff001916179055565b613cdf613cb76101c4610240860260243501016159fd565b600b84019068ffffffff000000000082549160281b169068ffffffff00000000001916179055565b6024356102408402016101e401356001600160801b03811690036130725760019382600b613f3f94015461ffff60c81b613d24610204610240890260243501016159ee565b60c81b1660ff60d81b613d426024356102408a0201610224016159c5565b7fffffffff00000000000000000000000000000000000000ffffffffffffffffff9093166024356102408a02016101e4013560481b78ffffffffffffffffffffffffffffffff00000000000000000016179190911791151560d81b1617600b820155600c8101805467ffffffffffffffff19166001600160401b038d1617815591613dcd848c61587b565b52613f325f516020616bcc5f395f51905f52602083549489850154600286015460ff600388015416600488015460058901549060068a01549260078b01549461ffff60088d0154169660806040519e8f908c82019d8e52600460408301526060820152015260a08d015260c08c015260e08b01526101008a01526101208901526101408801526101608701526101608652613e6a61018087615507565b60098501549060ff6001600160401b03600b600a890154980154925416916040519785890194855260408901528181161515606089015263ffffffff8160081c16608089015263ffffffff8160281c1660a08901526001600160801b038160481c1660c089015261ffff8160c81c1660e089015260d81c1615156101008701526101208601526101208552613f0161014086615507565b60405194859383850197518091895e840190838201908d8252519283915e010189815203601f198101835282615507565b51902061172b828861587b565b94613f55610224610240840260243501016159c5565b6040805191151582526001600160401b038a166020830152602435610240850201606481013593604490910135929035917f9738e55ce3503cada06dd3d3338d42623309a94afb5495119306f4a19a92350b91a401929050613b0b565b9091809352601060205260408920600160ff19825416179055601154600160401b811015611882579083613ff46119a38460018e989796016011556011615823565b905585358452601260205261400c8360408620615838565b613b77565b6326ef68cf60e01b88528335600452602488fd5b807f000000000000000000000000617620cabf90b3ceabe00d8fb83626bdf55e3bd96001600160a01b03168685823b1561409e5761407c9284928360405180968195829463abf1570d60e01b84526004840161588f565b03925af180156121ae5761408d5750f35b8161409791615507565b6103725780f35b505050fd5b6140ae818385615964565b9485358a52600660205260ff60408b2054161561433b576020860135158015614306575b6142ea579061426560019287358c52601360205260408c2060208901358d5260205260408c206001600160401b03600782015460081c16156142ca575b8835815560208901358582015560ff61412a60408b016159d2565b1660ff19600283015416176002820155606089013560038201556080890135600482015560a0890135600582015560c0890135600682015561418661417160e08b016159c5565b600783019060ff801983541691151516179055565b60078101805468ffffffffffffffff00191660088d901b68ffffffffffffffff00161790556141ba60208a01358a3561592a565b6141c4838c61587b565b528054906001600160401b03868201549160ff600282015416906003810154600482015460058301549160076006850154940154946040519760208901995f516020616bcc5f395f51905f528b52600560408b015260608a0152608089015260a088015260c087015260e086015261010085015261012084015260ff8116151561014084015260081c166101608201526101608152611c7561018082615507565b9561427260e082016159c5565b7f4b9c1fac36a2aeaf69e62fe75b58c9e69ec27b26ca89e6cde77296d160d452da604051806142c18d6020870135963595839092916001600160401b0360209160408401951515845216910152565b0390a301613afe565b88358d5260146020526142e58d604060208c01359120615838565b61410f565b638940a78560e01b8a528535600452602086013560245260448afd5b5061431360e087016159c5565b80156140d2575060ff614328604088016159d2565b1615806140d257506060860135156140d2565b633f4b99b160e21b8a52853560045260248afd5b909182358152602083013560208201526040830135604082015260ff61437760608501615453565b1660608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e082015261ffff6143b56101008501615539565b166101008201526101208301356101208201526101408301356101408201526143e16101608401615548565b151561016082015263ffffffff6143fb6101808501615528565b1661018082015263ffffffff6144146101a08501615528565b166101a08201526101c0830135906001600160801b03821680920361449357610240816001936101c08394015261ffff6144516101e08801615539565b166101e08201526144656102008701615548565b15156102008201526001600160401b0361448261022088016152d1565b1661022082015201930191016139f8565b8e80fd5b9061012080600192853581526020860135602082015260ff6144bb60408801615453565b166040820152606086013560608201526080860135608082015260a086013560a082015260c086013560c08201526144f560e08701615548565b151560e08201526001600160401b0361451161010088016152d1565b1661010082015201930191019190916139d5565b63c2e5347d60e01b8852600488fd5b5086156139a4565b63d311bc3960e01b8852600488fd5b50346103725760406101209161456036615315565b90614569615975565b5082526013602052828220908252602052206001600160401b036040519161459083615497565b8054928381526001820154916020820192835260ff600282015416604083019081526003820154606084019081526004830154906080850191825260ff60058501549360a08701948552600760068701549660c089019788520154978961010060e08a0199858c1615158b52019960081c1689526040519a8b525160208b0152511660408901525160608801525160808701525160a08601525160c085015251151560e08401525116610100820152f35b50346103725780600319360112610372576040517f000000000000000000000000617620cabf90b3ceabe00d8fb83626bdf55e3bd96001600160a01b03168152602090f35b5034610372576060366003190112610372576004356001600160401b038111612393576146b79036906004016153c2565b6146c29291926152a5565b926044356001600160401b038111611e92576146e29036906004016152e5565b9490600254918215614caa578415614c9b57600395949554926001600160401b038416926040519860208a01898b60408060608301928a865201525260808b019a888a5b8c8110614bd1575050926147c195926001600160401b03979592856147588f97613a959803601f198101835282615507565b51902060405160208101915f516020616bac5f395f51905f5283524660408301523060608301527f7d6f52ebac641da43fc11d5911cdb8aabf4931733ac493d1f5540628174e88d360808301528a871660a083015260c082015260c08152610b4760e082615507565b16906001600160401b03191617600355601754906001600160401b036147e8818416615764565b1680926001600160401b03191617601755614802846157ad565b9061480c856157ad565b9284955b80871061486f5785807f000000000000000000000000617620cabf90b3ceabe00d8fb83626bdf55e3bd96001600160a01b03168688823b1561409e5761407c9284928360405180968195829463abf1570d60e01b84526004840161588f565b61487a878285615964565b9661012088360312614bcd576040519761489389615497565b61491c610100823592838c526148ab60208201615453565b60208d01526148bc60408201615539565b60408d01526148cd60608201615528565b60608d01526148de60808201615539565b60808d01526148ef60a082016152d1565b60a08d015261490060c08201615539565b60c08d015261491160e08201615548565b60e08d0152016152d1565b6101008a01528752600660205260ff60408820541615614bb957604088015160ff60208a01511661ffff6001821b83161615614b9b57505060019088846101008a9b015280518952600e602052614a5760408a20848351918281550160ff806020860151161660ff19825416178155604084015181549066ffffffff000000606087015160181b169068ffff00000000000000608088015160381b169167ffffffffffffffff60481b60a089015160481b1661ffff60881b60c08a015160881b169260e08a0151151560981b946001600160401b0360a01b6101008c015160a01b16966001600160401b0360a01b199460ff60981b199361ffff60881b199262ffff0067ffffffffffffffff60481b199260081b169068ffffffffffffffff0019161716171617161716179060ff60981b1617179055615bbd565b614a61838961587b565b52805160ff60208301511661ffff60408401511663ffffffff60608501511661ffff6080860151166001600160401b0360a08701511661ffff60c0880151169160e08801511515936001600160401b036101008a015116956040519760208901995f516020616bcc5f395f51905f528b52600760408b015260608a0152608089015260a088015260c087015260e08601526101008501526101208401526101408301526101608201526101608152614b1b61018082615507565b519020614b28838a61587b565b527ffb325cc55af6e4500b502905b39f0679ffc3ea5f77a1534709444fdb64a99f0a60a082519260ff6020820151169061ffff6040820151169061ffff608063ffffffff6060840151169201511691604051938452602084015260408301526060820152876080820152a2019650614810565b8951633ecb481360e21b8a5260045260245261ffff16604452606487fd5b8751633f4b99b160e21b8852600452602487fd5b8680fd5b909c610120808f929d9e9d6001600160401b03614c876101008660019735855260ff614bff60208301615453565b16602086015261ffff614c1460408301615539565b16604086015263ffffffff614c2b60608301615528565b16606086015261ffff614c4060808301615539565b16608086015283614c5360a083016152d1565b1660a086015261ffff614c6860c08301615539565b1660c0860152614c7a60e08201615548565b151560e0860152016152d1565b16610100820152019e0191019b9a9b614726565b63c2e5347d60e01b8652600486fd5b63d311bc3960e01b8652600486fd5b50346103725760206108f5614ccd36615315565b9061592a565b5034610372576020366003190112610372576004358152601260205260408120604051918260208354918281520192825260208220915b818110614d215761045b8561044781870382615507565b8254845260209093019260019283019201614d0a565b503461037257604060c091614d4b36615315565b90614d546158fa565b508252600a6020528282209082526020522061ffff600260405192614d788461547c565b8054845260018101546020850152015460ff8116604084015260ff8160081c16151560608401526001600160401b038160101c16608084015260501c1660a0820152614dc7604051809261532b565bf35b50346103725760203660031901126103725760206108f56004356158c2565b9050346151b45760603660031901126151b4576004356001600160401b0381116151b457366023820112156151b4578060040135916001600160401b0383116151b4573660248460071b840101116151b457614e426152a5565b906044356001600160401b0381116151b457614e629036906004016152e5565b929060025490811561529657861561528757600354946001600160401b0386169460208101908960608201888452604080840152526080810160248a015f5b8c811061523a575050926001600160401b0397959282614ed2613a959794614f3b9a9703601f198101835282615507565b51902060405160208101915f516020616bac5f395f51905f5283524660408301523060608301527fe3b31cc24d29add9661dc3a1f58e781f69070e12dc384bcd05cb5622a2f1c57b60808301528a871660a083015260c082015260c08152610b4760e082615507565b16906001600160401b031916176003556017546001600160401b03614f61818316615764565b1680916001600160401b03191617601755614f7b836157ad565b91614f85846157ad565b945f5b858110156151b8578060071b830190608060231983360301126151b457604051614fb181615461565b602483013592838252602082019160448201358352614fe56084614fd760648501615548565b9360408401948552016152d1565b5060608101948886525f52600c6020528a8960ff60405f20541615615120575b917fed4d5e005c9676e1dcabfeb18ab560394b1c30a4409c368d6b8e5b760951353593916151008760019960609685515f52600b60205261509e836117258c615099600260405f208c5193848255516001820155016150738d511515829060ff801983541691151516179055565b8751815468ffffffffffffffff00191660089190911b68ffffffffffffffff0016179055565b615b8a565b528451908951906001600160401b0388511515915116906040519260208401945f516020616bcc5f395f51905f528652600360408601528b850152608084015260a083015260c082015260c081526150f760e082615507565b5190209261587b565b52519351905115156040519182526020820152886040820152a201614f88565b50509081515f52600c60205260405f20600160ff19825416179055815190600d5492600160401b8410156151a0578c615100876001998e60609761518f6119a38b8f7fed4d5e005c9676e1dcabfeb18ab560394b1c30a4409c368d6b8e5b76095135359d01600d55600d615823565b905594965099505050919350615005565b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b507f000000000000000000000000617620cabf90b3ceabe00d8fb83626bdf55e3bd96001600160a01b03168487823b156151b45761520f925f928360405180968195829463abf1570d60e01b84526004840161588f565b03925af1801561522f57615221575080f35b61522d91505f90615507565b005b6040513d5f823e3d90fd5b9091608080600192853581526020860135602082015261525c60408701615548565b151560408201526001600160401b03615277606088016152d1565b1660608201520193019101614ea1565b63c2e5347d60e01b5f5260045ffd5b63d311bc3960e01b5f5260045ffd5b602435906001600160401b03821682036151b457565b604435906001600160401b03821682036151b457565b35906001600160401b03821682036151b457565b9181601f840112156151b4578235916001600160401b0383116151b4576020808501948460051b0101116151b457565b60409060031901126151b4576004359060243590565b61ffff60a08092805185526020810151602086015260ff60408201511660408601526060810151151560608601526001600160401b036080820151166080860152015116910152565b90602080835192838152019201905f5b8181106153915750505090565b8251845260209384019390920191600101615384565b346151b4575f3660031901126151b457602060405160018152f35b9181601f840112156151b4578235916001600160401b0383116151b45760208085019461012085020101116151b457565b346151b4575f3660031901126151b457602060405160028152f35b346151b4575f3660031901126151b457602060405160038152f35b60609060031901126151b457600435906024359060443590565b6004359060ff821682036151b457565b359060ff821682036151b457565b608081019081106001600160401b038211176151a057604052565b60c081019081106001600160401b038211176151a057604052565b61012081019081106001600160401b038211176151a057604052565b6102a081019081106001600160401b038211176151a057604052565b61024081019081106001600160401b038211176151a057604052565b61014081019081106001600160401b038211176151a057604052565b90601f801991011681019081106001600160401b038211176151a057604052565b359063ffffffff821682036151b457565b359061ffff821682036151b457565b359081151582036151b457565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90815181526020820151602082015260ff60408301511660408201526101206001600160401b03816155d16155bf60608701516101406060880152610140870190615555565b60808701518682036080880152615555565b9460ff60a08201511660a086015263ffffffff60c08201511660c086015263ffffffff60e08201511660e0860152610100810151151561010086015201511691015290565b6001600160401b0381116151a057601f01601f191660200190565b92919261563d82615616565b9161564b6040519384615507565b8294818452818301116151b4578281602093845f960137010152565b9080601f830112156151b45781602061568293359101615631565b90565b919091610140818403126151b457604051906156a0826154eb565b819381358352602082013560208401526156bc60408301615453565b604084015260608201356001600160401b0381116151b457816156e0918401615667565b60608401526080820135906001600160401b0382116151b4578261570e610120949261575f94869401615667565b608086015261571f60a08201615453565b60a086015261573060c08201615528565b60c086015261574160e08201615528565b60e08601526157536101008201615548565b610100860152016152d1565b910152565b6001600160401b036001911601906001600160401b03821161578257565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0381116151a05760051b60200190565b906157b782615796565b6157c46040519182615507565b82815280926157d5601f1991615796565b0190602036910137565b6004548110156157f75760045f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b6007548110156157f75760075f5260205f2001905f90565b80548210156157f7575f5260205f2001905f90565b805490600160401b8210156151a057816119a391600161585a94018155615823565b9055565b8051156157f75760200190565b8051600110156157f75760400190565b80518210156157f75760209160051b010190565b90916158b4615682936006845260016020850152608060408501526080840190615374565b916060818403910152615374565b60405160208101915f516020616b8c5f395f51905f5283525f60408301526060820152606081526158f4608082615507565b51902090565b604051906159078261547c565b5f60a0838281528260208201528260408201528260608201528260808201520152565b906040519060208201925f516020616b8c5f395f51905f5284526005604084015260608301526080820152608081526158f460a082615507565b91908110156157f757610120020190565b6040519061598282615497565b5f610100838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201520152565b9190820180921161578257565b3580151581036151b45790565b3560ff811681036151b45790565b5f1981146157825760010190565b3561ffff811681036151b45790565b3563ffffffff811681036151b45790565b60405190615a1b826154b3565b5f610280838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e0820152826102008201528261022082015282610240820152826102608201520152565b60405190615ab2826154cf565b5f610220838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e0820152826102008201520152565b916040519160208301935f516020616b8c5f395f51905f528552600460408501526060840152608083015260a082015260a081526158f460c082615507565b60405190615b7382615461565b5f6060838281528260208201528260408201520152565b60405160208101915f516020616b8c5f395f51905f528352600360408301526060820152606081526158f4608082615507565b60405160208101915f516020616b8c5f395f51905f528352600760408301526060820152606081526158f4608082615507565b9060405190602082019283526040820152604081526158f4606082615507565b906040519060208201925f516020616b8c5f395f51905f5284526002604084015260608301526080820152608081526158f460a082615507565b8051906060810151906001600160401b0361024060ff60808401511692015116906040519260208401947f2d9686edff91aeaad85c9917be9d5ee284127481a229d9857b748073fba5b1c0865260408501526060840152608083015260a082015260a081526158f460c082615507565b9035601e19823603018112156151b45701602081359101916001600160401b0382116151b45781360383136151b457565b908060209392818452848401375f828201840152601f01601f1916010190565b60405190615d18826154eb565b5f61012083828152826020820152826040820152606080820152606060808201528260a08201528260c08201528260e0820152826101008201520152565b90600182811c92168015615d84575b6020831014615d7057565b634e487b7160e01b5f52602260045260245ffd5b91607f1691615d65565b9060405191825f825492615da184615d56565b8084529360018116908115615e0c5750600114615dc8575b50615dc692500383615507565b565b90505f9291925260205f20905f915b818310615df0575050906020615dc6928201015f615db9565b6020919350806001915483858901015201910190918492615dd7565b905060209250615dc694915060ff191682840152151560051b8201015f615db9565b90604051615e3b816154eb565b6101206001600160401b0360058395805485526001810154602086015260ff6002820154166040860152615e7160038201615d8e565b6060860152615e8260048201615d8e565b6080860152015460ff811660a085015263ffffffff8160081c1660c085015263ffffffff8160281c1660e085015260ff8160481c16151561010085015260501c16910152565b80519060208101519060ff604082015116906060810151602081519101206001600160401b0361012060808401516020815191012093015116926040519460208601967f8db2525bc769e56adb6fd2402771f48c08c4503c9dc0bc507a776eaca65d700f885260408701526060860152608085015260a084015260c083015260e082015260e081526158f461010082615507565b60405160208101915f516020616b8c5f395f51905f528352600160408301526060820152606081526158f4608082615507565b356001600160401b03811681036151b45790565b60405160208101917faf7a3b9335082a3b4f22ddf585b52ecc40819755789412bb4253e79b45db1ab383527f84e75c8576483b53128ed23c26158522388ddb0a5df8516c83887d522196f91f60408301526060820152606081526158f4608082615507565b906040519060208201927faf7a3b9335082a3b4f22ddf585b52ecc40819755789412bb4253e79b45db1ab3845260408301526060820152606081526158f4608082615507565b9061605882615796565b6160656040519182615507565b8281528092616076601f1991615796565b01905f5b82811061608657505050565b602090616091615d0b565b8282850101520161607a565b906160a782615796565b6160b46040519182615507565b82815280926160c5601f1991615796565b01905f5b8281106160d557505050565b6020906160e06158fa565b828285010152016160c9565b356001600160a01b03811681036151b45790565b93919594965f95881561636c576001600160401b0316438111616356578043034381116157825761025810616340575060405193602085015260208452616148604085615507565b5f975f965b88881015616316578760051b840135607e19853603018112156151b4578401996161768b6160ec565b6001600160a01b0391821691168110156162ea57506161948a6160ec565b9961619e816160ec565b604051632e4bfa5160e11b81526001600160a01b0391821660048201526024810188905290602090829060449082908d165afa90811561522f575f916162b0575b5015616289576020810190600460ff6161f7846159d2565b16036162565761620888828b6166cf565b1561622257505061621a6001916159e0565b97019661614d565b9061623761623160ff936160ec565b916159d2565b9063bbf82ba360e01b5f5260018060a01b03166004521660245260445ffd5b9061626561623160ff936160ec565b9063587548c360e11b5f5260018060a01b031660045216602452600460445260645ffd5b61629386916160ec565b63ae8bb03960e01b5f5260018060a01b031660045260245260445ffd5b90506020813d82116162e2575b816162ca60209383615507565b810103126151b4575180151581036151b4575f6161df565b3d91506162bd565b6162f38b6160ec565b6311641feb60e21b5f9081526004929092526001600160a01b0316602452604490fd5b9850955095505050505080831061632a5750565b826305bc216760e51b5f5260045260245260445ffd5b630ed38fd160e41b5f526004524360245260445ffd5b637b51505560e01b5f526004524360245260445ffd5b631fc460bf60e11b5f5260045ffd5b60408101359081156164fc5761639461020082016159c5565b156165665761ffff6163a961010083016159ee565b1615801561655a575b6164fc5760608101600260ff6163c7836159d2565b161461653357600160ff6163da836159d2565b161415908161651b575b506164fc57602081013590815f52600660205260ff60405f205416156164e957815f52601360205260405f2060a082013590815f5260205260405f2090159081156164d9575b5061646f576101208101351580156164cc575b61646f57610180810163ffffffff616454826159fd565b16159081156164b0575b8115616488575b5061646f57505050565b638307aca560e01b5f523560045260245260445260645ffd5b905063ffffffff806164a66164a06101a086016159fd565b936159fd565b169116115f616465565b905063ffffffff6164c46101a084016159fd565b16159061645e565b506101408101351561643d565b60ff91506007015416155f61642a565b50633f4b99b160e21b5f5260045260245ffd5b602090638307aca560e01b5f528035600452013560245260445260645ffd5b6003915061652a60ff916159d2565b1614155f6163e4565b50602081013590811580159061654d575b61646f57505050565b5060a08101351515616544565b506080810135156163b2565b5050565b9080519060208101519060ff60408201511660608201516020815191012060808301516020815191012060ff60a0850151169063ffffffff60c0860151169263ffffffff60e087015116946001600160401b03610120610100890151151598015116976040519960208b019b5f516020616bcc5f395f51905f528d52600160408d015260608c015260808b015260a08a015260c089015260e08801526101008701526101208601526101408501526101608401526101808301526101a08201526101a081526158f46101c082615507565b6020818303126151b4578051906001600160401b0382116151b4570181601f820112156151b45780519061666e82615616565b9261667c6040519485615507565b828452602083830101116151b457815f9260208093018386015e8301015290565b903590601e19813603018212156151b457018035906001600160401b0382116151b4576020019181360383136151b457565b9160208201600460ff6166e1836159d2565b16146167905760ff6166f46005926159d2565b1614616701575050505f90565b5f61670b836160ec565b604051639e5adaeb60e01b81526001600160a01b0391821660048201529485916024918391165afa91821561522f57615682935f93616764575b5061675781604061675e93019061669d565b3691615631565b91616901565b61675e919350616788616757913d805f833e6167808183615507565b81019061663b565b939150616745565b505f61679b836160ec565b60405163b7af85d760e01b81526001600160a01b0391821660048201529485916024918391165afa91821561522f57615682935f936167ed575b506167578160406167e793019061669d565b91616811565b6167e7919350616809616757913d805f833e6167808183615507565b9391506167d5565b610a208151148015906168f4575b6168ed5760206168725f948286958160405195869481808701998051918291018b5e8601908282018b8152815193849201905e010190878252805192839101825e0185815203601f198101835282615507565b51906102045afa3d156168e6573d61688981615616565b906168976040519283615507565b81523d5f602083013e5b816168da575b816168b0575090565b90506020815191015190602081106168c9575b50151590565b5f199060200360031b1b165f6168c3565b805160201491506168a7565b60606168a1565b5050505f90565b506112138351141561681f565b6040815114801590616978575b6168ed5760206169615f948286958160405195869481808701998051918291018b5e8601908282018b8152815193849201905e010190878252805192839101825e0185815203601f198101835282615507565b51906102055afa3d156168e6573d61688981615616565b506174608351141561690e565b908015616b855760015b818110616af357505f5b818110616ab257505b600181116169b857506169b49061585e565b5190565b5f905f5b8181106169ca5750506169a2565b6001810180821161578257828114616a94576169e6828661587b565b51905f916169f4828861587b565b511115616a7857616a05838761587b565b519161578257616a15908661587b565b515b604051906020820192600160f81b84526021830152604182015260418152616a40606182615507565b519020616a56616a4f856159e0565b948661587b565b525b60028101809111156169bc57634e487b7160e01b5f52601160045260245ffd5b616a8391508561587b565b51616a8e828661587b565b51616a17565b50616a9f818561587b565b51616aac616a4f856159e0565b52616a58565b80616abf6001928561587b565b5160405160208101915f8352602182015260218152616adf604182615507565b519020616aec828661587b565b5201616999565b92616b008484939461587b565b5192845b8015801580616b67575b15616b4e575f1982019180831161578257616b34616b2c848861587b565b51918761587b565b5215616b0457634e487b7160e01b5f52601160045260245ffd5b5093616b60600193969294958661587b565b520161698f565b505f19820182811161578257616b7e87918761587b565b5111616b0e565b50505f9056febe98de78ab1a89101b9b77b1b3013a49647e63c349ee09cc55e0a33f97e351a9d850f5df47b124511e8e6ec99cf1a0beaf7c6237eff0a31305ce53d85f31267553bfca378ab6d3ea8c58df6b962c50de5515a3d2878713fa6dd8d84f220809ff
Žá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)
| pc | op | operand |
|---|---|---|
| 0000 | PUSH1 | 0xa0 |
| 0002 | DUP1 | |
| 0003 | PUSH1 | 0x40 |
| 0005 | MSTORE | |
| 0006 | PUSH1 | 0x04 |
| 0008 | CALLDATASIZE | |
| 0009 | LT | |
| 000a | ISZERO | |
| 000b | PUSH2 | 0x0012 |
| 000e | JUMPI | |
| 000f | PUSH0 | |
| 0010 | DUP1 | |
| 0011 | REVERT | |
| 0012 | JUMPDEST | |
| 0013 | PUSH0 | |
| 0014 | SWAP1 | |
| 0015 | PUSH0 | |
| 0016 | CALLDATALOAD | |
| 0017 | PUSH1 | 0xe0 |
| 0019 | SHR | |
| 001a | SWAP1 | |
| 001b | DUP2 | |
| 001c | PUSH4 | 0x0292709e |
| 0021 | EQ | |
| 0022 | PUSH2 | 0x4de8 |
| 0025 | JUMPI | |
| 0026 | POP | |
| 0027 | DUP1 | |
| 0028 | PUSH4 | 0x036f9a43 |
| 002d | EQ | |
| 002e | PUSH2 | 0x4dc9 |
| 0031 | JUMPI | |
| 0032 | DUP1 | |
| 0033 | PUSH4 | 0x0489d038 |
| 0038 | EQ | |
| 0039 | PUSH2 | 0x4d37 |
| 003c | JUMPI | |
| 003d | DUP1 | |
| 003e | PUSH4 | 0x087a5cf4 |
| 0043 | EQ | |
| 0044 | PUSH2 | 0x4cd3 |
| 0047 | JUMPI | |
| 0048 | DUP1 | |
| 0049 | PUSH4 | 0x08f0ce62 |
| 004e | EQ | |
| 004f | PUSH2 | 0x3297 |
| 0052 | JUMPI | |
| 0053 | DUP1 | |
| 0054 | PUSH4 | 0x0900767d |
| 0059 | EQ | |
| 005a | PUSH2 | 0x4cb9 |
| 005d | JUMPI | |
| 005e | DUP1 | |
| 005f | PUSH4 | 0x1591c4a0 |
| 0064 | EQ | |
| 0065 | PUSH2 | 0x4686 |
| 0068 | JUMPI | |
| 0069 | DUP1 | |
| 006a | PUSH4 | 0x178bcc93 |
| 006f | EQ | |
| 0070 | PUSH2 | 0x4641 |
| 0073 | JUMPI | |
| 0074 | DUP1 | |
| 0075 | PUSH4 | 0x1945098e |
| 007a | EQ | |
| 007b | PUSH2 | 0x0581 |
| 007e | JUMPI | |
| 007f | DUP1 | |
| 0080 | PUSH4 | 0x1a633473 |
| 0085 | EQ | |
| 0086 | PUSH2 | 0x0475 |
| 0089 | JUMPI | |
| 008a | DUP1 | |
| 008b | PUSH4 | 0x1e000aa5 |
| 0090 | EQ | |
| 0091 | PUSH2 | 0x454b |
| 0094 | JUMPI | |
| 0095 | DUP1 | |
| 0096 | PUSH4 | 0x200d43ad |
| 009b | EQ | |
| 009c | PUSH2 | 0x38f5 |
| 009f | JUMPI | |
| 00a0 | DUP1 | |
| 00a1 | PUSH4 | 0x20d4f696 |
| 00a6 | EQ | |
| 00a7 | PUSH2 | 0x0475 |
| 00aa | JUMPI | |
| 00ab | DUP1 | |
| 00ac | PUSH4 | 0x210de79d |
| 00b1 | EQ | |
| 00b2 | PUSH2 | 0x38a9 |
| 00b5 | JUMPI | |
| 00b6 | DUP1 | |
| 00b7 | PUSH4 | 0x2b89089c |
| 00bc | EQ | |
| 00bd | PUSH2 | 0x388c |
| 00c0 | JUMPI | |
| 00c1 | DUP1 | |
| 00c2 | PUSH4 | 0x3ec4cc34 |
| 00c7 | EQ | |
| 00c8 | PUSH2 | 0x3846 |
| 00cb | JUMPI | |
| 00cc | DUP1 | |
| 00cd | PUSH4 | 0x3fb27b85 |
| 00d2 | EQ | |
| 00d3 | PUSH2 | 0x37f0 |
| 00d6 | JUMPI | |
| 00d7 | DUP1 | |
| 00d8 | PUSH4 | 0x42cde4e8 |
| 00dd | EQ | |
| 00de | PUSH2 | 0x37d2 |
| 00e1 | JUMPI | |
| 00e2 | DUP1 | |
| 00e3 | PUSH4 | 0x4760fd43 |
| 00e8 | EQ | |
| 00e9 | PUSH2 | 0x3767 |
| 00ec | JUMPI | |
| 00ed | DUP1 | |
| 00ee | PUSH4 | 0x4bcb06a9 |
| 00f3 | EQ | |
| 00f4 | PUSH2 | 0x374d |
| 00f7 | JUMPI | |
| 00f8 | DUP1 | |
| 00f9 | PUSH4 | 0x4ec824ff |
| 00fe | EQ | |
| 00ff | PUSH2 | 0x3297 |
| 0102 | JUMPI | |
| 0103 | DUP1 | |
| 0104 | PUSH4 | 0x5483a0b5 |
| 0109 | EQ | |
| 010a | PUSH2 | 0x370a |
| 010d | JUMPI | |
| 010e | DUP1 | |
| 010f | PUSH4 | 0x57d70648 |
| 0114 | EQ | |
| 0115 | PUSH2 | 0x36eb |
| 0118 | JUMPI | |
| 0119 | DUP1 | |
| 011a | PUSH4 | 0x5b88f50e |
| 011f | EQ | |
| 0120 | PUSH2 | 0x3297 |
| 0123 | JUMPI | |
| 0124 | DUP1 | |
| 0125 | PUSH4 | 0x5eb320b8 |
| 012a | EQ | |
| 012b | PUSH2 | 0x36cd |
| 012e | JUMPI | |
| 012f | DUP1 | |
| 0130 | PUSH4 | 0x5f61b15c |
| 0135 | EQ | |
| 0136 | PUSH2 | 0x3297 |
| 0139 | JUMPI | |
| 013a | DUP1 | |
| 013b | PUSH4 | 0x60c29fac |
| 0140 | EQ | |
| 0141 | PUSH2 | 0x0581 |
| 0144 | JUMPI | |
| 0145 | DUP1 | |
| 0146 | PUSH4 | 0x622c3f21 |
| 014b | EQ | |
| 014c | PUSH2 | 0x36b0 |
| 014f | JUMPI | |
| 0150 | DUP1 | |
| 0151 | PUSH4 | 0x65cea49c |
| 0156 | EQ | |
| 0157 | PUSH2 | 0x3691 |
| 015a | JUMPI | |
| 015b | DUP1 | |
| 015c | PUSH4 | 0x68cf465e |
| 0161 | EQ | |
| 0162 | PUSH2 | 0x0581 |
| 0165 | JUMPI | |
| 0166 | DUP1 | |
| 0167 | PUSH4 | 0x6c2c44e2 |
| 016c | EQ | |
| 016d | PUSH2 | 0x3677 |
| 0170 | JUMPI | |
| 0171 | DUP1 | |
| 0172 | PUSH4 | 0x6db81752 |
| 0177 | EQ | |
| 0178 | PUSH2 | 0x365b |
| 017b | JUMPI | |
| 017c | DUP1 | |
| 017d | PUSH4 | 0x711f3db4 |
| 0182 | EQ | |
| 0183 | PUSH2 | 0x358c |
| 0186 | JUMPI | |
| 0187 | DUP1 | |
| 0188 | PUSH4 | 0x767c39d2 |
| 018d | EQ | |
| 018e | PUSH2 | 0x0475 |
| 0191 | JUMPI | |
| 0192 | DUP1 | |
| 0193 | PUSH4 | 0x7b103999 |
| 0198 | EQ | |
| 0199 | PUSH2 | 0x3547 |
| 019c | JUMPI | |
| 019d | DUP1 | |
| 019e | PUSH4 | 0x7bb737bc |
| 01a3 | EQ | |
| 01a4 | PUSH2 | 0x352d |
| 01a7 | JUMPI | |
| 01a8 | DUP1 | |
| 01a9 | PUSH4 | 0x7f49d69f |
| 01ae | EQ | |
| 01af | PUSH2 | 0x34f2 |
| 01b2 | JUMPI | |
| 01b3 | DUP1 | |
| 01b4 | PUSH4 | 0x831f33a1 |
| 01b9 | EQ | |
| 01ba | PUSH2 | 0x32d7 |
| 01bd | JUMPI | |
| 01be | DUP1 | |
| 01bf | PUSH4 | 0x8513e2d5 |
| 01c4 | EQ | |
| 01c5 | PUSH2 | 0x32b8 |
| 01c8 | JUMPI | |
| 01c9 | DUP1 | |
| 01ca | PUSH4 | 0x852a52e8 |
| 01cf | EQ | |
| 01d0 | PUSH2 | 0x329c |
| 01d3 | JUMPI | |
| 01d4 | DUP1 | |
| 01d5 | PUSH4 | 0x88f06eaa |
| 01da | EQ | |
| 01db | PUSH2 | 0x3297 |
| 01de | JUMPI | |
| 01df | DUP1 | |
| 01e0 | PUSH4 | 0x9212a9c9 |
| 01e5 | EQ | |
| 01e6 | PUSH2 | 0x311d |
| 01e9 | JUMPI | |
| 01ea | DUP1 | |
| 01eb | PUSH4 | 0x92a2e0b3 |
| 01f0 | EQ | |
| 01f1 | PUSH2 | 0x3101 |
| 01f4 | JUMPI | |
| 01f5 | DUP1 | |
| 01f6 | PUSH4 | 0x9377c220 |
| 01fb | EQ | |
| 01fc | PUSH2 | 0x30da |
| 01ff | JUMPI | |
| 0200 | DUP1 | |
| 0201 | PUSH4 | 0x9493ba1d |
| 0206 | EQ | |
| 0207 | PUSH2 | 0x3076 |
| 020a | JUMPI | |
| 020b | DUP1 | |
| 020c | PUSH4 | 0x94bc4e96 |
| 0211 | EQ | |
| 0212 | PUSH2 | 0x2d72 |
| 0215 | JUMPI | |
| 0216 | DUP1 | |
| 0217 | PUSH4 | 0x95102c42 |
| 021c | EQ | |
| 021d | PUSH2 | 0x2d55 |
| 0220 | JUMPI | |
| 0221 | DUP1 | |
| 0222 | PUSH4 | 0x9648eca2 |
| 0227 | EQ | |
| 0228 | PUSH2 | 0x2ac2 |
| 022b | JUMPI | |
| 022c | DUP1 | |
| 022d | PUSH4 | 0x97b9857b |
| 0232 | EQ | |
| 0233 | PUSH2 | 0x2a65 |
| 0236 | JUMPI | |
| 0237 | DUP1 | |
| 0238 | PUSH4 | 0xa741113c |
| 023d | EQ | |
| 023e | PUSH2 | 0x2a2a |
| 0241 | JUMPI | |
| 0242 | DUP1 | |
| 0243 | PUSH4 | 0xaa9239f5 |
| 0248 | EQ | |
| 0249 | PUSH2 | 0x29d3 |
| 024c | JUMPI | |
| 024d | DUP1 | |
| 024e | PUSH4 | 0xac65bc70 |
| 0253 | EQ | |
| 0254 | PUSH2 | 0x28b0 |
| 0257 | JUMPI | |
| 0258 | DUP1 | |
| 0259 | PUSH4 | 0xaffed0e0 |
| 025e | EQ | |
| 025f | PUSH2 | 0x2889 |
| 0262 | JUMPI | |
| 0263 | DUP1 | |
| 0264 | PUSH4 | 0xb19f4805 |
| 0269 | EQ | |
| 026a | PUSH2 | 0x284e |
| 026d | JUMPI | |
| 026e | DUP1 | |
| 026f | PUSH4 | 0xb8da302a |
| 0274 | EQ | |
| 0275 | PUSH2 | 0x2830 |
| 0278 | JUMPI | |
| 0279 | DUP1 | |
| 027a | PUSH4 | 0xb9a7f076 |
| 027f | EQ | |
| 0280 | PUSH2 | 0x2743 |
| 0283 | JUMPI | |
| 0284 | DUP1 | |
| 0285 | PUSH4 | 0xbbbcda44 |
| 028a | EQ | |
| 028b | PUSH2 | 0x2724 |
| 028e | JUMPI | |
| 028f | DUP1 | |
| 0290 | PUSH4 | 0xbda1019e |
| 0295 | EQ | |
| 0296 | PUSH2 | 0x2706 |
| 0299 | JUMPI | |
| 029a | DUP1 | |
| 029b | PUSH4 | 0xc5dbda66 |
| 02a0 | EQ | |
| 02a1 | PUSH2 | 0x26e9 |
| 02a4 | JUMPI | |
| 02a5 | DUP1 | |
| 02a6 | PUSH4 | 0xc8cfbc17 |
| 02ab | EQ | |
| 02ac | PUSH2 | 0x08fd |
| 02af | JUMPI | |
| 02b0 | DUP1 | |
| 02b1 | PUSH4 | 0xdc544a09 |
| 02b6 | EQ | |
| 02b7 | PUSH2 | 0x08db |
| 02ba | JUMPI | |
| 02bb | DUP1 | |
| 02bc | PUSH4 | 0xde54d429 |
| 02c1 | EQ | |
| 02c2 | PUSH2 | 0x08bd |
| 02c5 | JUMPI | |
| 02c6 | DUP1 | |
| 02c7 | PUSH4 | 0xdf46c713 |
| 02cc | EQ | |
| 02cd | PUSH2 | 0x0767 |
| 02d0 | JUMPI | |
| 02d1 | DUP1 | |
| 02d2 | PUSH4 | 0xe02e1bfd |
| 02d7 | EQ | |
| 02d8 | PUSH2 | 0x0749 |
| 02db | JUMPI | |
| 02dc | DUP1 | |
| 02dd | PUSH4 | 0xe597e659 |
| 02e2 | EQ | |
| 02e3 | PUSH2 | 0x0586 |
| 02e6 | JUMPI | |
| 02e7 | DUP1 | |
| 02e8 | PUSH4 | 0xe70475a7 |
| 02ed | EQ | |
| 02ee | PUSH2 | 0x0581 |
| 02f1 | JUMPI | |
| 02f2 | DUP1 | |
| 02f3 | PUSH4 | 0xe94a52af |
| 02f8 | EQ | |
| 02f9 | PUSH2 | 0x0565 |
| 02fc | JUMPI | |
| 02fd | DUP1 | |
| 02fe | PUSH4 | 0xeafe7a74 |
| 0303 | EQ | |
| 0304 | PUSH2 | 0x0547 |
| 0307 | JUMPI | |
| 0308 | DUP1 | |
| 0309 | PUSH4 | 0xeb777382 |
| 030e | EQ | |
| 030f | PUSH2 | 0x047a |
| 0312 | JUMPI | |
| 0313 | DUP1 | |
| 0314 | PUSH4 | 0xee076a50 |
| 0319 | EQ | |
| 031a | PUSH2 | 0x0475 |
| 031d | JUMPI | |
| 031e | DUP1 | |
| 031f | PUSH4 | 0xee57e33c |
| 0324 | EQ | |
| 0325 | PUSH2 | 0x03e8 |
| 0328 | JUMPI | |
| 0329 | DUP1 | |
| 032a | PUSH4 | 0xf3178f43 |
| 032f | EQ | |
| 0330 | PUSH2 | 0x0393 |
| 0333 | JUMPI | |
| 0334 | DUP1 | |
| 0335 | PUSH4 | 0xf4e885db |
| 033a | EQ | |
| 033b | PUSH2 | 0x0375 |
| 033e | JUMPI | |
| 033f | PUSH4 | 0xf851a440 |
| 0344 | EQ | |
| 0345 | PUSH2 | 0x034c |
| 0348 | JUMPI | |
| 0349 | PUSH0 | |
| 034a | DUP1 | |
| 034b | REVERT | |
| 034c | JUMPDEST | |
| 034d | CALLVALUE | |
| 034e | PUSH2 | 0x0372 |
| 0351 | JUMPI | |
| 0352 | DUP1 | |
| 0353 | PUSH1 | 0x03 |
| 0355 | NOT | |
| 0356 | CALLDATASIZE | |
| 0357 | ADD | |
| 0358 | SLT | |
| 0359 | PUSH2 | 0x0372 |
| 035c | JUMPI | |
| 035d | SLOAD | |
| 035e | PUSH1 | 0x40 |
| 0360 | MLOAD | |
| 0361 | PUSH1 | 0x01 |
| 0363 | PUSH1 | 0x01 |
| 0365 | PUSH1 | 0xa0 |
| 0367 | SHL | |
| 0368 | SUB | |
| 0369 | SWAP1 | |
| 036a | SWAP2 | |
| 036b | AND | |
| 036c | DUP2 | |
| 036d | MSTORE | |
| 036e | PUSH1 | 0x20 |
| 0370 | SWAP1 | |
| 0371 | RETURN | |
| 0372 | JUMPDEST | |
| 0373 | DUP1 | |
| 0374 | REVERT | |
| 0375 | JUMPDEST | |
| 0376 | POP | |
| 0377 | CALLVALUE | |
| 0378 | PUSH2 | 0x0372 |
| 037b | JUMPI | |
| 037c | DUP1 | |
| 037d | PUSH1 | 0x03 |
| 037f | NOT | |
| 0380 | CALLDATASIZE | |
| 0381 | ADD | |
| 0382 | SLT | |
| 0383 | PUSH2 | 0x0372 |
| 0386 | JUMPI | |
| 0387 | PUSH1 | 0x20 |
| 0389 | PUSH1 | 0x01 |
| 038b | SLOAD | |
| 038c | PUSH1 | 0x40 |
| 038e | MLOAD | |
| 038f | SWAP1 | |
| 0390 | DUP2 | |
| 0391 | MSTORE | |
| 0392 | RETURN | |
| 0393 | JUMPDEST | |
| 0394 | POP | |
| 0395 | CALLVALUE | |
| 0396 | PUSH2 | 0x0372 |
| 0399 | JUMPI | |
| 039a | PUSH1 | 0x40 |
| 039c | CALLDATASIZE | |
| 039d | PUSH1 | 0x03 |
| 039f | NOT | |
| 03a0 | ADD | |
| 03a1 | SLT | |
| 03a2 | PUSH2 | 0x0372 |
| 03a5 | JUMPI | |
| 03a6 | PUSH2 | 0xffff |
| 03a9 | PUSH1 | 0x02 |
| 03ab | PUSH1 | 0x40 |
| 03ad | PUSH1 | 0x20 |
| 03af | SWAP4 | |
| 03b0 | PUSH2 | 0x03b7 |
| 03b3 | PUSH2 | 0x52a5 |
| 03b6 | JUMP | |
| 03b7 | JUMPDEST | |
| 03b8 | PUSH1 | 0x04 |
| 03ba | CALLDATALOAD | |
| 03bb | DUP3 | |
| 03bc | MSTORE | |
| 03bd | PUSH1 | 0x0a |
| 03bf | DUP7 | |
| 03c0 | MSTORE | |
| 03c1 | PUSH2 | 0x03d5 |
| 03c4 | PUSH1 | 0x01 |
| 03c6 | PUSH1 | 0x01 |
| 03c8 | PUSH1 | 0x40 |
| 03ca | SHL | |
| 03cb | SUB | |
| 03cc | DUP5 | |
| 03cd | DUP5 | |
| 03ce | KECCAK256 | |
| 03cf | SWAP3 | |
| 03d0 | AND | |
| 03d1 | PUSH2 | 0x5fa3 |
| 03d4 | JUMP | |
| 03d5 | JUMPDEST | |
| 03d6 | DUP3 | |
| 03d7 | MSTORE | |
| 03d8 | DUP6 | |
| 03d9 | MSTORE | |
| 03da | KECCAK256 | |
| 03db | ADD | |
| 03dc | SLOAD | |
| 03dd | PUSH1 | 0x50 |
| 03df | SHR | |
| 03e0 | AND | |
| 03e1 | PUSH1 | 0x40 |
| 03e3 | MLOAD | |
| 03e4 | SWAP1 | |
| 03e5 | DUP2 | |
| 03e6 | MSTORE | |
| 03e7 | RETURN | |
| 03e8 | JUMPDEST | |
| 03e9 | POP | |
| 03ea | CALLVALUE | |
| 03eb | PUSH2 | 0x0372 |
| 03ee | JUMPI | |
| 03ef | DUP1 | |
| 03f0 | PUSH1 | 0x03 |
| 03f2 | NOT | |
| 03f3 | CALLDATASIZE | |
| 03f4 | ADD | |
| 03f5 | SLT | |
| 03f6 | PUSH2 | 0x0372 |
| 03f9 | JUMPI | |
| 03fa | PUSH1 | 0x40 |
| 03fc | MLOAD | |
| 03fd | DUP1 | |
| 03fe | SWAP2 | |
| 03ff | PUSH1 | 0x20 |
| 0401 | PUSH1 | 0x11 |
| 0403 | SLOAD | |
| 0404 | SWAP3 | |
| 0405 | DUP4 | |
| 0406 | DUP2 | |
| 0407 | MSTORE | |
| 0408 | ADD | |
| 0409 | SWAP2 | |
| 040a | PUSH1 | 0x11 |
| 040c | DUP3 | |
| 040d | MSTORE | |
| 040e | PUSH32 | 0x31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68 |
| 042f | SWAP2 | |
| 0430 | JUMPDEST | |
| 0431 | DUP2 | |
| 0432 | DUP2 | |
| 0433 | LT | |
| 0434 | PUSH2 | 0x045f |
| 0437 | JUMPI | |
| 0438 | PUSH2 | 0x045b |
| 043b | DUP6 | |
| 043c | PUSH2 | 0x0447 |
| 043f | DUP2 | |
| 0440 | DUP8 | |
| 0441 | SUB | |
| 0442 | DUP3 | |
| 0443 | PUSH2 | 0x5507 |
| 0446 | JUMP | |
| 0447 | JUMPDEST | |
| 0448 | PUSH1 | 0x40 |
| 044a | MLOAD | |
| 044b | SWAP2 | |
| 044c | DUP3 | |
| 044d | SWAP2 | |
| 044e | PUSH1 | 0x20 |
| 0450 | DUP4 | |
| 0451 | MSTORE | |
| 0452 | PUSH1 | 0x20 |
| 0454 | DUP4 | |
| 0455 | ADD | |
| 0456 | SWAP1 | |
| 0457 | PUSH2 | 0x5374 |
| 045a | JUMP | |
| 045b | JUMPDEST | |
| 045c | SUB | |
| 045d | SWAP1 | |
| 045e | RETURN | |
| 045f | JUMPDEST | |
| 0460 | DUP3 | |
| 0461 | SLOAD | |
| 0462 | DUP5 | |
| 0463 | MSTORE | |
| 0464 | PUSH1 | 0x20 |
| 0466 | SWAP1 | |
| 0467 | SWAP4 | |
| 0468 | ADD | |
| 0469 | SWAP3 | |
| 046a | PUSH1 | 0x01 |
| 046c | SWAP3 | |
| 046d | DUP4 | |
| 046e | ADD | |
| 046f | SWAP3 | |
| 0470 | ADD | |
| 0471 | PUSH2 | 0x0430 |
| 0474 | JUMP | |
| 0475 | JUMPDEST | |
| 0476 | PUSH2 | 0x540e |
| 0479 | JUMP | |
| 047a | JUMPDEST | |
| 047b | POP | |
| 047c | CALLVALUE | |
| 047d | PUSH2 | 0x0372 |
| 0480 | JUMPI | |
| 0481 | DUP1 | |
| 0482 | PUSH1 | 0x03 |
| 0484 | NOT | |
| 0485 | CALLDATASIZE | |
| 0486 | ADD | |
| 0487 | SLT | |
| 0488 | PUSH2 | 0x0372 |
| 048b | JUMPI | |
| 048c | PUSH1 | 0x04 |
| 048e | SLOAD | |
| 048f | PUSH2 | 0x0497 |
| 0492 | DUP2 | |
| 0493 | PUSH2 | 0x57ad |
| 0496 | JUMP | |
| 0497 | JUMPDEST | |
| 0498 | DUP3 | |
| 0499 | SWAP2 | |
| 049a | DUP4 | |
| 049b | JUMPDEST | |
| 049c | DUP2 | |
| 049d | DUP2 | |
| 049e | LT | |
| 049f | PUSH2 | 0x04eb |
| 04a2 | JUMPI | |
| 04a3 | POP | |
| 04a4 | POP | |
| 04a5 | PUSH2 | 0x04ad |
| 04a8 | DUP3 | |
| 04a9 | PUSH2 | 0x57ad |
| 04ac | JUMP | |
| 04ad | JUMPDEST | |
| 04ae | SWAP3 | |
| 04af | JUMPDEST | |
| 04b0 | DUP3 | |
| 04b1 | DUP2 | |
| 04b2 | LT | |
| 04b3 | PUSH2 | 0x04cc |
| 04b6 | JUMPI | |
| 04b7 | PUSH1 | 0x40 |
| 04b9 | MLOAD | |
| 04ba | PUSH1 | 0x20 |
| 04bc | DUP1 | |
| 04bd | DUP3 | |
| 04be | MSTORE | |
| 04bf | DUP2 | |
| 04c0 | SWAP1 | |
| 04c1 | PUSH2 | 0x045b |
| 04c4 | SWAP1 | |
| 04c5 | DUP3 | |
| 04c6 | ADD | |
| 04c7 | DUP8 | |
| 04c8 | PUSH2 | 0x5374 |
| 04cb | JUMP | |
| 04cc | JUMPDEST | |
| 04cd | DUP1 | |
| 04ce | PUSH2 | 0x04d9 |
| 04d1 | PUSH1 | 0x01 |
| 04d3 | SWAP3 | |
| 04d4 | DUP5 | |
| 04d5 | PUSH2 | 0x587b |
| 04d8 | JUMP | |
| 04d9 | JUMPDEST | |
| 04da | MLOAD | |
| 04db | PUSH2 | 0x04e4 |
| 04de | DUP3 | |
| 04df | DUP8 | |
| 04e0 | PUSH2 | 0x587b |
| 04e3 | JUMP | |
| 04e4 | JUMPDEST | |
| 04e5 | MSTORE | |
| 04e6 | ADD | |
| 04e7 | PUSH2 | 0x04af |
| 04ea | JUMP | |
| 04eb | JUMPDEST | |
| 04ec | DUP1 | |
| 04ed | PUSH2 | 0x04f7 |
| 04f0 | PUSH1 | 0x01 |
| 04f2 | SWAP3 | |
| 04f3 | PUSH2 | 0x57df |
| 04f6 | JUMP | |
| 04f7 | JUMPDEST | |
| 04f8 | SWAP1 | |
| 04f9 | SLOAD | |
| 04fa | SWAP1 | |
| 04fb | PUSH1 | 0x03 |
| 04fd | SHL | |
| 04fe | SHR | |
| 04ff | DUP7 | |
| 0500 | MSTORE | |
| 0501 | PUSH1 | 0x05 |
| 0503 | PUSH1 | 0x20 |
| 0505 | MSTORE | |
| 0506 | PUSH1 | 0xff |
| 0508 | PUSH1 | 0x0c |
| 050a | PUSH1 | 0x40 |
| 050c | DUP9 | |
| 050d | KECCAK256 | |
| 050e | ADD | |
| 050f | SLOAD | |
| 0510 | PUSH1 | 0x08 |
| 0512 | SHR | |
| 0513 | AND | |
| 0514 | PUSH2 | 0x051e |
| 0517 | JUMPI | |
| 0518 | JUMPDEST | |
| 0519 | ADD | |
| 051a | PUSH2 | 0x049b |
| 051d | JUMP | |
| 051e | JUMPDEST | |
| 051f | PUSH2 | 0x0527 |
| 0522 | DUP2 | |
| 0523 | PUSH2 | 0x57df |
| 0526 | JUMP | |
| 0527 | JUMPDEST | |
| 0528 | SWAP1 | |
| 0529 | SLOAD | |
| 052a | SWAP1 | |
| 052b | PUSH1 | 0x03 |
| 052d | SHL | |
| 052e | SHR | |
| 052f | PUSH2 | 0x0541 |
| 0532 | PUSH2 | 0x053a |
| 0535 | DUP8 | |
| 0536 | PUSH2 | 0x59e0 |
| 0539 | JUMP | |
| 053a | JUMPDEST | |
| 053b | SWAP7 | |
| 053c | DUP7 | |
| 053d | PUSH2 | 0x587b |
| 0540 | JUMP | |
| 0541 | JUMPDEST | |
| 0542 | MSTORE | |
| 0543 | PUSH2 | 0x0518 |
| 0546 | JUMP | |
| 0547 | JUMPDEST | |
| 0548 | POP | |
| 0549 | CALLVALUE | |
| 054a | PUSH2 | 0x0372 |
| 054d | JUMPI | |
| 054e | DUP1 | |
| 054f | PUSH1 | 0x03 |
| 0551 | NOT | |
| 0552 | CALLDATASIZE | |
| 0553 | ADD | |
| 0554 | SLT | |
| 0555 | PUSH2 | 0x0372 |
| 0558 | JUMPI | |
| 0559 | PUSH1 | 0x20 |
| 055b | PUSH1 | 0x07 |
| 055d | SLOAD | |
| 055e | PUSH1 | 0x40 |
| 0560 | MLOAD | |
| 0561 | SWAP1 | |
| 0562 | DUP2 | |
| 0563 | MSTORE | |
| 0564 | RETURN | |
| 0565 | JUMPDEST | |
| 0566 | POP | |
| 0567 | CALLVALUE | |
| 0568 | PUSH2 | 0x0372 |
| 056b | JUMPI | |
| 056c | DUP1 | |
| 056d | PUSH1 | 0x03 |
| 056f | NOT | |
| 0570 | CALLDATASIZE | |
| 0571 | ADD | |
| 0572 | SLT | |
| 0573 | PUSH2 | 0x0372 |
| 0576 | JUMPI | |
| 0577 | PUSH1 | 0x20 |
| 0579 | PUSH1 | 0x40 |
| 057b | MLOAD | |
| 057c | PUSH1 | 0x05 |
| 057e | DUP2 | |
| 057f | MSTORE | |
| 0580 | RETURN | |
| 0581 | JUMPDEST | |
| 0582 | PUSH2 | 0x53f3 |
| 0585 | JUMP | |
| 0586 | JUMPDEST | |
| 0587 | POP | |
| 0588 | CALLVALUE | |
| 0589 | PUSH2 | 0x0372 |
| 058c | JUMPI | |
| 058d | PUSH1 | 0x40 |
| 058f | CALLDATASIZE | |
| 0590 | PUSH1 | 0x03 |
| 0592 | NOT | |
| 0593 | ADD | |
| 0594 | SLT | |
| 0595 | PUSH2 | 0x0372 |
| 0598 | JUMPI | |
| 0599 | PUSH1 | 0x24 |
| 059b | CALLDATALOAD | |
| 059c | PUSH1 | 0x04 |
| 059e | CALLDATALOAD | |
| 059f | PUSH1 | 0xff |
| 05a1 | DUP3 | |
| 05a2 | AND | |
| 05a3 | DUP3 | |
| 05a4 | SUB | |
| 05a5 | PUSH2 | 0x0745 |
| 05a8 | JUMPI | |
| 05a9 | PUSH1 | 0x07 |
| 05ab | SLOAD | |
| 05ac | PUSH2 | 0x05b4 |
| 05af | DUP2 | |
| 05b0 | PUSH2 | 0x609d |
| 05b3 | JUMP | |
| 05b4 | JUMPDEST | |
| 05b5 | SWAP3 | |
| 05b6 | DUP5 | |
| 05b7 | SWAP3 | |
| 05b8 | DUP6 | |
| 05b9 | JUMPDEST | |
| 05ba | DUP4 | |
| 05bb | DUP2 | |
| 05bc | LT | |
| 05bd | PUSH2 | 0x0652 |
| 05c0 | JUMPI | |
| 05c1 | POP | |
| 05c2 | POP | |
| 05c3 | POP | |
| 05c4 | POP | |
| 05c5 | PUSH2 | 0x05cd |
| 05c8 | DUP2 | |
| 05c9 | PUSH2 | 0x609d |
| 05cc | JUMP | |
| 05cd | JUMPDEST | |
| 05ce | SWAP2 | |
| 05cf | DUP4 | |
| 05d0 | JUMPDEST | |
| 05d1 | DUP3 | |
| 05d2 | DUP2 | |
| 05d3 | LT | |
| 05d4 | PUSH2 | 0x0628 |
| 05d7 | JUMPI | |
| 05d8 | DUP4 | |
| 05d9 | DUP6 | |
| 05da | PUSH1 | 0x40 |
| 05dc | MLOAD | |
| 05dd | SWAP2 | |
| 05de | DUP3 | |
| 05df | SWAP2 | |
| 05e0 | PUSH1 | 0x20 |
| 05e2 | DUP4 | |
| 05e3 | ADD | |
| 05e4 | PUSH1 | 0x20 |
| 05e6 | DUP5 | |
| 05e7 | MSTORE | |
| 05e8 | DUP3 | |
| 05e9 | MLOAD | |
| 05ea | DUP1 | |
| 05eb | SWAP2 | |
| 05ec | MSTORE | |
| 05ed | PUSH1 | 0x20 |
| 05ef | PUSH1 | 0x40 |
| 05f1 | DUP6 | |
| 05f2 | ADD | |
| 05f3 | SWAP4 | |
| 05f4 | ADD | |
| 05f5 | SWAP2 | |
| 05f6 | JUMPDEST | |
| 05f7 | DUP2 | |
| 05f8 | DUP2 | |
| 05f9 | LT | |
| 05fa | PUSH2 | 0x0604 |
| 05fd | JUMPI | |
| 05fe | POP | |
| 05ff | POP | |
| 0600 | POP | |
| 0601 | SUB | |
| 0602 | SWAP1 | |
| 0603 | RETURN | |
| 0604 | JUMPDEST | |
| 0605 | SWAP2 | |
| 0606 | SWAP4 | |
| 0607 | POP | |
| 0608 | SWAP2 | |
| 0609 | PUSH1 | 0x20 |
| 060b | PUSH1 | 0xc0 |
| 060d | DUP3 | |
| 060e | PUSH2 | 0x061a |
| 0611 | PUSH1 | 0x01 |
| 0613 | SWAP5 | |
| 0614 | DUP9 | |
| 0615 | MLOAD | |
| 0616 | PUSH2 | 0x532b |
| 0619 | JUMP | |
| 061a | JUMPDEST | |
| 061b | ADD | |
| 061c | SWAP5 | |
| 061d | ADD | |
| 061e | SWAP2 | |
| 061f | ADD | |
| 0620 | SWAP2 | |
| 0621 | DUP5 | |
| 0622 | SWAP4 | |
| 0623 | SWAP3 | |
| 0624 | PUSH2 | 0x05f6 |
| 0627 | JUMP | |
| 0628 | JUMPDEST | |
| 0629 | DUP1 | |
| 062a | PUSH2 | 0x0635 |
| 062d | PUSH1 | 0x01 |
| 062f | SWAP3 | |
| 0630 | DUP5 | |
| 0631 | PUSH2 | 0x587b |
| 0634 | JUMP | |
| 0635 | JUMPDEST | |
| 0636 | MLOAD | |
| 0637 | PUSH2 | 0x0640 |
| 063a | DUP3 | |
| 063b | DUP8 | |
| 063c | PUSH2 | 0x587b |
| 063f | JUMP | |
| 0640 | JUMPDEST | |
| 0641 | MSTORE | |
| 0642 | PUSH2 | 0x064b |
| 0645 | DUP2 | |
| 0646 | DUP7 | |
| 0647 | PUSH2 | 0x587b |
| 064a | JUMP | |
| 064b | JUMPDEST | |
| 064c | POP | |
| 064d | ADD | |
| 064e | PUSH2 | 0x05d0 |
| 0651 | JUMP | |
| 0652 | JUMPDEST | |
| 0653 | PUSH2 | 0x065b |
| 0656 | DUP2 | |
| 0657 | PUSH2 | 0x580b |
| 065a | JUMP | |
| 065b | JUMPDEST | |
| 065c | SWAP1 | |
| 065d | SLOAD | |
| 065e | SWAP1 | |
| 065f | PUSH1 | 0x03 |
| 0661 | SHL | |
| 0662 | SHR | |
| 0663 | DUP1 | |
| 0664 | DUP9 | |
| 0665 | MSTORE | |
| 0666 | PUSH1 | 0x08 |
| 0668 | PUSH1 | 0x20 |
| 066a | MSTORE | |
| 066b | PUSH1 | 0xff |
| 066d | PUSH1 | 0x05 |
| 066f | PUSH1 | 0x40 |
| 0671 | DUP11 | |
| 0672 | KECCAK256 | |
| 0673 | ADD | |
| 0674 | SLOAD | |
| 0675 | PUSH1 | 0x48 |
| 0677 | SHR | |
| 0678 | AND | |
| 0679 | ISZERO | |
| 067a | PUSH2 | 0x073c |
| 067d | JUMPI | |
| 067e | DUP8 | |
| 067f | MSTORE | |
| 0680 | PUSH1 | 0x0a |
| 0682 | PUSH1 | 0x20 |
| 0684 | MSTORE | |
| 0685 | PUSH1 | 0x40 |
| 0687 | DUP8 | |
| 0688 | KECCAK256 | |
| 0689 | DUP3 | |
| 068a | DUP9 | |
| 068b | MSTORE | |
| 068c | PUSH1 | 0x20 |
| 068e | MSTORE | |
| 068f | PUSH1 | 0x40 |
| 0691 | DUP8 | |
| 0692 | KECCAK256 | |
| 0693 | PUSH1 | 0x02 |
| 0695 | PUSH1 | 0x40 |
| 0697 | MLOAD | |
| 0698 | SWAP2 | |
| 0699 | PUSH2 | 0x06a1 |
| 069c | DUP4 | |
| 069d | PUSH2 | 0x547c |
| 06a0 | JUMP | |
| 06a1 | JUMPDEST | |
| 06a2 | DUP1 | |
| 06a3 | SLOAD | |
| 06a4 | DUP4 | |
| 06a5 | MSTORE | |
| 06a6 | PUSH1 | 0x01 |
| 06a8 | DUP2 | |
| 06a9 | ADD | |
| 06aa | SLOAD | |
| 06ab | PUSH1 | 0x20 |
| 06ad | DUP5 | |
| 06ae | ADD | |
| 06af | MSTORE | |
| 06b0 | ADD | |
| 06b1 | SLOAD | |
| 06b2 | PUSH1 | 0xff |
| 06b4 | DUP2 | |
| 06b5 | AND | |
| 06b6 | PUSH1 | 0x40 |
| 06b8 | DUP4 | |
| 06b9 | ADD | |
| 06ba | MSTORE | |
| 06bb | DUP5 | |
| 06bc | PUSH1 | 0xff |
| 06be | DUP3 | |
| 06bf | PUSH1 | 0x08 |
| 06c1 | SHR | |
| 06c2 | AND | |
| 06c3 | ISZERO | |
| 06c4 | ISZERO | |
| 06c5 | SWAP2 | |
| 06c6 | DUP3 | |
| 06c7 | PUSH1 | 0x60 |
| 06c9 | DUP6 | |
| 06ca | ADD | |
| 06cb | MSTORE | |
| 06cc | PUSH1 | 0x01 |
| 06ce | PUSH1 | 0x01 |
| 06d0 | PUSH1 | 0x40 |
| 06d2 | SHL | |
| 06d3 | SUB | |
| 06d4 | DUP2 | |
| 06d5 | PUSH1 | 0x10 |
| 06d7 | SHR | |
| 06d8 | AND | |
| 06d9 | PUSH1 | 0x80 |
| 06db | DUP6 | |
| 06dc | ADD | |
| 06dd | MSTORE | |
| 06de | PUSH2 | 0xffff |
| 06e1 | DUP2 | |
| 06e2 | PUSH1 | 0x50 |
| 06e4 | SHR | |
| 06e5 | AND | |
| 06e6 | PUSH1 | 0xa0 |
| 06e8 | DUP6 | |
| 06e9 | ADD | |
| 06ea | MSTORE | |
| 06eb | DUP3 | |
| 06ec | PUSH2 | 0x072d |
| 06ef | JUMPI | |
| 06f0 | JUMPDEST | |
| 06f1 | POP | |
| 06f2 | POP | |
| 06f3 | PUSH2 | 0x0702 |
| 06f6 | JUMPI | |
| 06f7 | JUMPDEST | |
| 06f8 | POP | |
| 06f9 | PUSH1 | 0x01 |
| 06fb | SWAP1 | |
| 06fc | JUMPDEST | |
| 06fd | ADD | |
| 06fe | PUSH2 | 0x05b9 |
| 0701 | JUMP | |
| 0702 | JUMPDEST | |
| 0703 | SWAP5 | |
| 0704 | SWAP1 | |
| 0705 | PUSH2 | 0x0726 |
| 0708 | DUP3 | |
| 0709 | PUSH2 | 0x0714 |
| 070c | PUSH1 | 0x01 |
| 070e | SWAP5 | |
| 070f | SWAP2 | |
| 0710 | PUSH2 | 0x59e0 |
| 0713 | JUMP | |
| 0714 | JUMPDEST | |
| 0715 | SWAP8 | |
| 0716 | PUSH2 | 0x071f |
| 0719 | DUP3 | |
| 071a | DUP12 | |
| 071b | PUSH2 | 0x587b |
| 071e | JUMP | |
| 071f | JUMPDEST | |
| 0720 | MSTORE | |
| 0721 | DUP9 | |
| 0722 | PUSH2 | 0x587b |
| 0725 | JUMP | |
| 0726 | JUMPDEST | |
| 0727 | POP | |
| 0728 | SWAP1 | |
| 0729 | PUSH2 | 0x06f7 |
| 072c | JUMP | |
| 072d | JUMPDEST | |
| 072e | PUSH1 | 0xff |
| 0730 | SWAP3 | |
| 0731 | POP | |
| 0732 | AND | |
| 0733 | AND | |
| 0734 | ISZERO | |
| 0735 | ISZERO | |
| 0736 | DUP5 | |
| 0737 | PUSH0 | |
| 0738 | PUSH2 | 0x06f0 |
| 073b | JUMP | |
| 073c | JUMPDEST | |
| 073d | POP | |
| 073e | PUSH1 | 0x01 |
| 0740 | SWAP1 | |
| 0741 | PUSH2 | 0x06fc |
| 0744 | JUMP | |
| 0745 | JUMPDEST | |
| 0746 | DUP3 | |
| 0747 | DUP1 | |
| 0748 | REVERT | |
| 0749 | JUMPDEST | |
| 074a | POP | |
| 074b | CALLVALUE | |
| 074c | PUSH2 | 0x0372 |
| 074f | JUMPI | |
| 0750 | DUP1 | |
| 0751 | PUSH1 | 0x03 |
| 0753 | NOT | |
| 0754 | CALLDATASIZE | |
| 0755 | ADD | |
| 0756 | SLT | |
| 0757 | PUSH2 | 0x0372 |
| 075a | JUMPI | |
| 075b | PUSH1 | 0x20 |
| 075d | PUSH1 | 0x04 |
| 075f | SLOAD | |
| 0760 | PUSH1 | 0x40 |
| 0762 | MLOAD | |
| 0763 | SWAP1 | |
| 0764 | DUP2 | |
| 0765 | MSTORE | |
| 0766 | RETURN | |
| 0767 | JUMPDEST | |
| 0768 | POP | |
| 0769 | CALLVALUE | |
| 076a | PUSH2 | 0x0372 |
| 076d | JUMPI | |
| 076e | PUSH1 | 0x20 |
| 0770 | CALLDATASIZE | |
| 0771 | PUSH1 | 0x03 |
| 0773 | NOT | |
| 0774 | ADD | |
| 0775 | SLT | |
| 0776 | PUSH2 | 0x0372 |
| 0779 | JUMPI | |
| 077a | PUSH2 | 0x0781 |
| 077d | PUSH2 | 0x5443 |
| 0780 | JUMP | |
| 0781 | JUMPDEST | |
| 0782 | PUSH1 | 0x07 |
| 0784 | SLOAD | |
| 0785 | SWAP1 | |
| 0786 | PUSH2 | 0x078e |
| 0789 | DUP3 | |
| 078a | PUSH2 | 0x604e |
| 078d | JUMP | |
| 078e | JUMPDEST | |
| 078f | SWAP2 | |
| 0790 | DUP4 | |
| 0791 | SWAP2 | |
| 0792 | DUP5 | |
| 0793 | JUMPDEST | |
| 0794 | DUP3 | |
| 0795 | DUP2 | |
| 0796 | LT | |
| 0797 | PUSH2 | 0x0841 |
| 079a | JUMPI | |
| 079b | POP | |
| 079c | POP | |
| 079d | POP | |
| 079e | PUSH2 | 0x07a6 |
| 07a1 | DUP2 | |
| 07a2 | PUSH2 | 0x604e |
| 07a5 | JUMP | |
| 07a6 | JUMPDEST | |
| 07a7 | SWAP2 | |
| 07a8 | DUP4 | |
| 07a9 | JUMPDEST | |
| 07aa | DUP3 | |
| 07ab | DUP2 | |
| 07ac | LT | |
| 07ad | PUSH2 | 0x0817 |
| 07b0 | JUMPI | |
| 07b1 | DUP4 | |
| 07b2 | DUP6 | |
| 07b3 | PUSH1 | 0x40 |
| 07b5 | MLOAD | |
| 07b6 | SWAP2 | |
| 07b7 | DUP3 | |
| 07b8 | SWAP2 | |
| 07b9 | PUSH1 | 0x20 |
| 07bb | DUP4 | |
| 07bc | ADD | |
| 07bd | PUSH1 | 0x20 |
| 07bf | DUP5 | |
| 07c0 | MSTORE | |
| 07c1 | DUP3 | |
| 07c2 | MLOAD | |
| 07c3 | DUP1 | |
| 07c4 | SWAP2 | |
| 07c5 | MSTORE | |
| 07c6 | PUSH1 | 0x40 |
| 07c8 | DUP5 | |
| 07c9 | ADD | |
| 07ca | PUSH1 | 0x20 |
| 07cc | PUSH1 | 0x40 |
| 07ce | DUP4 | |
| 07cf | PUSH1 | 0x05 |
| 07d1 | SHL | |
| 07d2 | DUP8 | |
| 07d3 | ADD | |
| 07d4 | ADD | |
| 07d5 | SWAP5 | |
| 07d6 | ADD | |
| 07d7 | SWAP3 | |
| 07d8 | SWAP1 | |
| 07d9 | JUMPDEST | |
| 07da | DUP3 | |
| 07db | DUP3 | |
| 07dc | LT | |
| 07dd | PUSH2 | 0x07e8 |
| 07e0 | JUMPI | |
| 07e1 | POP | |
| 07e2 | POP | |
| 07e3 | POP | |
| 07e4 | POP | |
| 07e5 | SUB | |
| 07e6 | SWAP1 | |
| 07e7 | RETURN | |
| 07e8 | JUMPDEST | |
| 07e9 | SWAP2 | |
| 07ea | SWAP4 | |
| 07eb | PUSH1 | 0x01 |
| 07ed | SWAP2 | |
| 07ee | SWAP4 | |
| 07ef | SWAP6 | |
| 07f0 | POP | |
| 07f1 | PUSH1 | 0x20 |
| 07f3 | PUSH2 | 0x0807 |
| 07f6 | DUP2 | |
| 07f7 | SWAP3 | |
| 07f8 | PUSH1 | 0x3f |
| 07fa | NOT | |
| 07fb | DUP11 | |
| 07fc | DUP3 | |
| 07fd | SUB | |
| 07fe | ADD | |
| 07ff | DUP7 | |
| 0800 | MSTORE | |
| 0801 | DUP9 | |
| 0802 | MLOAD | |
| 0803 | PUSH2 | 0x5579 |
| 0806 | JUMP | |
| 0807 | JUMPDEST | |
| 0808 | SWAP7 | |
| 0809 | ADD | |
| 080a | SWAP3 | |
| 080b | ADD | |
| 080c | SWAP3 | |
| 080d | ADD | |
| 080e | DUP6 | |
| 080f | SWAP5 | |
| 0810 | SWAP4 | |
| 0811 | SWAP2 | |
| 0812 | SWAP3 | |
| 0813 | PUSH2 | 0x07d9 |
| 0816 | JUMP | |
| 0817 | JUMPDEST | |
| 0818 | DUP1 | |
| 0819 | PUSH2 | 0x0824 |
| 081c | PUSH1 | 0x01 |
| 081e | SWAP3 | |
| 081f | DUP5 | |
| 0820 | PUSH2 | 0x587b |
| 0823 | JUMP | |
| 0824 | JUMPDEST | |
| 0825 | MLOAD | |
| 0826 | PUSH2 | 0x082f |
| 0829 | DUP3 | |
| 082a | DUP8 | |
| 082b | PUSH2 | 0x587b |
| 082e | JUMP | |
| 082f | JUMPDEST | |
| 0830 | MSTORE | |
| 0831 | PUSH2 | 0x083a |
| 0834 | DUP2 | |
| 0835 | DUP7 | |
| 0836 | PUSH2 | 0x587b |
| 0839 | JUMP | |
| 083a | JUMPDEST | |
| 083b | POP | |
| 083c | ADD | |
| 083d | PUSH2 | 0x07a9 |
| 0840 | JUMP | |
| 0841 | JUMPDEST | |
| 0842 | PUSH2 | 0x084a |
| 0845 | DUP2 | |
| 0846 | PUSH2 | 0x580b |
| 0849 | JUMP | |
| 084a | JUMPDEST | |
| 084b | SWAP1 | |
| 084c | SLOAD | |
| 084d | SWAP1 | |
| 084e | PUSH1 | 0x03 |
| 0850 | SHL | |
| 0851 | SHR | |
| 0852 | DUP7 | |
| 0853 | MSTORE | |
| 0854 | PUSH1 | 0x08 |
| 0856 | PUSH1 | 0x20 |
| 0858 | MSTORE | |
| 0859 | PUSH2 | 0x0864 |
| 085c | PUSH1 | 0x40 |
| 085e | DUP8 | |
| 085f | KECCAK256 | |
| 0860 | PUSH2 | 0x5e2e |
| 0863 | JUMP | |
| 0864 | JUMPDEST | |
| 0865 | PUSH2 | 0x0100 |
| 0868 | DUP2 | |
| 0869 | ADD | |
| 086a | MLOAD | |
| 086b | ISZERO | |
| 086c | ISZERO | |
| 086d | DUP1 | |
| 086e | PUSH2 | 0x08ab |
| 0871 | JUMPI | |
| 0872 | JUMPDEST | |
| 0873 | PUSH2 | 0x0880 |
| 0876 | JUMPI | |
| 0877 | JUMPDEST | |
| 0878 | POP | |
| 0879 | PUSH1 | 0x01 |
| 087b | ADD | |
| 087c | PUSH2 | 0x0793 |
| 087f | JUMP | |
| 0880 | JUMPDEST | |
| 0881 | SWAP4 | |
| 0882 | SWAP1 | |
| 0883 | PUSH2 | 0x08a4 |
| 0886 | DUP3 | |
| 0887 | PUSH2 | 0x0892 |
| 088a | PUSH1 | 0x01 |
| 088c | SWAP5 | |
| 088d | SWAP2 | |
| 088e | PUSH2 | 0x59e0 |
| 0891 | JUMP | |
| 0892 | JUMPDEST | |
| 0893 | SWAP7 | |
| 0894 | PUSH2 | 0x089d |
| 0897 | DUP3 | |
| 0898 | DUP11 | |
| 0899 | PUSH2 | 0x587b |
| 089c | JUMP | |
| 089d | JUMPDEST | |
| 089e | MSTORE | |
| 089f | DUP8 | |
| 08a0 | PUSH2 | 0x587b |
| 08a3 | JUMP | |
| 08a4 | JUMPDEST | |
| 08a5 | POP | |
| 08a6 | SWAP1 | |
| 08a7 | PUSH2 | 0x0877 |
| 08aa | JUMP | |
| 08ab | JUMPDEST | |
| 08ac | POP | |
| 08ad | PUSH1 | 0xff |
| 08af | DUP4 | |
| 08b0 | PUSH1 | 0xa0 |
| 08b2 | DUP4 | |
| 08b3 | ADD | |
| 08b4 | MLOAD | |
| 08b5 | AND | |
| 08b6 | AND | |
| 08b7 | ISZERO | |
| 08b8 | ISZERO | |
| 08b9 | PUSH2 | 0x0872 |
| 08bc | JUMP | |
| 08bd | JUMPDEST | |
| 08be | POP | |
| 08bf | CALLVALUE | |
| 08c0 | PUSH2 | 0x0372 |
| 08c3 | JUMPI | |
| 08c4 | DUP1 | |
| 08c5 | PUSH1 | 0x03 |
| 08c7 | NOT | |
| 08c8 | CALLDATASIZE | |
| 08c9 | ADD | |
| 08ca | SLT | |
| 08cb | PUSH2 | 0x0372 |
| 08ce | JUMPI | |
| 08cf | PUSH1 | 0x20 |
| 08d1 | PUSH1 | 0x0d |
| 08d3 | SLOAD | |
| 08d4 | PUSH1 | 0x40 |
| 08d6 | MLOAD | |
| 08d7 | SWAP1 | |
| 08d8 | DUP2 | |
| 08d9 | MSTORE | |
| 08da | RETURN | |
| 08db | JUMPDEST | |
| 08dc | POP | |
| 08dd | CALLVALUE | |
| 08de | PUSH2 | 0x0372 |
| 08e1 | JUMPI | |
| 08e2 | PUSH1 | 0x20 |
| 08e4 | PUSH2 | 0x08f5 |
| 08e7 | PUSH2 | 0x08ef |
| 08ea | CALLDATASIZE | |
| 08eb | PUSH2 | 0x5315 |
| 08ee | JUMP | |
| 08ef | JUMPDEST | |
| 08f0 | SWAP1 | |
| 08f1 | PUSH2 | 0x6008 |
| 08f4 | JUMP | |
| 08f5 | JUMPDEST | |
| 08f6 | PUSH1 | 0x40 |
| 08f8 | MLOAD | |
| 08f9 | SWAP1 | |
| 08fa | DUP2 | |
| 08fb | MSTORE | |
| 08fc | RETURN | |
| 08fd | JUMPDEST | |
| 08fe | POP | |
| 08ff | CALLVALUE | |
| 0900 | PUSH2 | 0x0372 |
| 0903 | JUMPI | |
| 0904 | PUSH1 | 0xa0 |
| 0906 | CALLDATASIZE | |
| 0907 | PUSH1 | 0x03 |
| 0909 | NOT | |
| 090a | ADD | |
| 090b | SLT | |
| 090c | PUSH2 | 0x0372 |
| 090f | JUMPI | |
| 0910 | PUSH1 | 0x01 |
| 0912 | PUSH1 | 0x01 |
| 0914 | PUSH1 | 0x40 |
| 0916 | SHL | |
| 0917 | SUB | |
| 0918 | PUSH1 | 0x04 |
| 091a | CALLDATALOAD | |
| 091b | GT | |
| 091c | PUSH2 | 0x0372 |
| 091f | JUMPI | |
| 0920 | CALLDATASIZE | |
| 0921 | PUSH1 | 0x23 |
| 0923 | PUSH1 | 0x04 |
| 0925 | CALLDATALOAD | |
| 0926 | ADD | |
| 0927 | SLT | |
| 0928 | ISZERO | |
| 0929 | PUSH2 | 0x0372 |
| 092c | JUMPI | |
| 092d | PUSH1 | 0x01 |
| 092f | PUSH1 | 0x01 |
| 0931 | PUSH1 | 0x40 |
| 0933 | SHL | |
| 0934 | SUB | |
| 0935 | PUSH1 | 0x04 |
| 0937 | CALLDATALOAD | |
| 0938 | PUSH1 | 0x04 |
| 093a | ADD | |
| 093b | CALLDATALOAD | |
| 093c | GT | |
| 093d | PUSH2 | 0x0372 |
| 0940 | JUMPI | |
| 0941 | CALLDATASIZE | |
| 0942 | PUSH1 | 0x24 |
| 0944 | PUSH2 | 0x02a0 |
| 0947 | PUSH1 | 0x04 |
| 0949 | CALLDATALOAD | |
| 094a | PUSH1 | 0x04 |
| 094c | ADD | |
| 094d | CALLDATALOAD | |
| 094e | MUL | |
| 094f | PUSH1 | 0x04 |
| 0951 | CALLDATALOAD | |
| 0952 | ADD | |
| 0953 | ADD | |
| 0954 | GT | |
| 0955 | PUSH2 | 0x0372 |
| 0958 | JUMPI | |
| 0959 | PUSH1 | 0x24 |
| 095b | CALLDATALOAD | |
| 095c | PUSH1 | 0x01 |
| 095e | PUSH1 | 0x01 |
| 0960 | PUSH1 | 0x40 |
| 0962 | SHL | |
| 0963 | SUB | |
| 0964 | DUP2 | |
| 0965 | GT | |
| 0966 | PUSH2 | 0x2393 |
| 0969 | JUMPI | |
| 096a | PUSH2 | 0x0977 |
| 096d | SWAP1 | |
| 096e | CALLDATASIZE | |
| 096f | SWAP1 | |
| 0970 | PUSH1 | 0x04 |
| 0972 | ADD | |
| 0973 | PUSH2 | 0x52e5 |
| 0976 | JUMP | |
| 0977 | JUMPDEST | |
| 0978 | SWAP2 | |
| 0979 | PUSH1 | 0x01 |
| 097b | PUSH1 | 0x01 |
| 097d | PUSH1 | 0x40 |
| 097f | SHL | |
| 0980 | SUB | |
| 0981 | PUSH1 | 0x44 |
| 0983 | CALLDATALOAD | |
| 0984 | GT | |
| 0985 | PUSH2 | 0x0372 |
| 0988 | JUMPI | |
| 0989 | CALLDATASIZE | |
| 098a | PUSH1 | 0x23 |
| 098c | PUSH1 | 0x44 |
| 098e | CALLDATALOAD | |
| 098f | ADD | |
| 0990 | SLT | |
| 0991 | ISZERO | |
| 0992 | PUSH2 | 0x0372 |
| 0995 | JUMPI | |
| 0996 | PUSH1 | 0x01 |
| 0998 | PUSH1 | 0x01 |
| 099a | PUSH1 | 0x40 |
| 099c | SHL | |
| 099d | SUB | |
| 099e | PUSH1 | 0x44 |
| 09a0 | CALLDATALOAD | |
| 09a1 | PUSH1 | 0x04 |
| 09a3 | ADD | |
| 09a4 | CALLDATALOAD | |
| 09a5 | GT | |
| 09a6 | PUSH2 | 0x0372 |
| 09a9 | JUMPI | |
| 09aa | CALLDATASIZE | |
| 09ab | PUSH1 | 0x24 |
| 09ad | PUSH1 | 0xc0 |
| 09af | PUSH1 | 0x44 |
| 09b1 | CALLDATALOAD | |
| 09b2 | PUSH1 | 0x04 |
| 09b4 | ADD | |
| 09b5 | CALLDATALOAD | |
| 09b6 | MUL | |
| 09b7 | PUSH1 | 0x44 |
| 09b9 | CALLDATALOAD | |
| 09ba | ADD | |
| 09bb | ADD | |
| 09bc | GT | |
| 09bd | PUSH2 | 0x0372 |
| 09c0 | JUMPI | |
| 09c1 | PUSH1 | 0x64 |
| 09c3 | CALLDATALOAD | |
| 09c4 | SWAP3 | |
| 09c5 | PUSH1 | 0x01 |
| 09c7 | PUSH1 | 0x01 |
| 09c9 | PUSH1 | 0x40 |
| 09cb | SHL | |
| 09cc | SUB | |
| 09cd | DUP5 | |
| 09ce | AND | |
| 09cf | DUP5 | |
| 09d0 | SUB | |
| 09d1 | PUSH2 | 0x2393 |
| 09d4 | JUMPI | |
| 09d5 | PUSH1 | 0x84 |
| 09d7 | CALLDATALOAD | |
| 09d8 | PUSH1 | 0x01 |
| 09da | PUSH1 | 0x01 |
| 09dc | PUSH1 | 0x40 |
| 09de | SHL | |
| 09df | SUB | |
| 09e0 | DUP2 | |
| 09e1 | GT | |
| 09e2 | PUSH2 | 0x0745 |
| 09e5 | JUMPI | |
| 09e6 | PUSH2 | 0x09f3 |
| 09e9 | SWAP1 | |
| 09ea | CALLDATASIZE | |
| 09eb | SWAP1 | |
| 09ec | PUSH1 | 0x04 |
| 09ee | ADD | |
| 09ef | PUSH2 | 0x52e5 |
| 09f2 | JUMP | |
| 09f3 | JUMPDEST | |
| 09f4 | SWAP5 | |
| 09f5 | PUSH1 | 0x02 |
| 09f7 | SLOAD | |
| 09f8 | DUP1 | |
| 09f9 | ISZERO | |
| 09fa | PUSH2 | 0x26da |
| 09fd | JUMPI | |
| 09fe | PUSH1 | 0x04 |
| 0a00 | CALLDATALOAD | |
| 0a01 | PUSH1 | 0x04 |
| 0a03 | ADD | |
| 0a04 | CALLDATALOAD | |
| 0a05 | ISZERO | |
| 0a06 | DUP1 | |
| 0a07 | PUSH2 | 0x26d2 |
| 0a0a | JUMPI | |
| 0a0b | JUMPDEST | |
| 0a0c | DUP1 | |
| 0a0d | PUSH2 | 0x26c4 |
| 0a10 | JUMPI | |
| 0a11 | JUMPDEST | |
| 0a12 | PUSH2 | 0x26b5 |
| 0a15 | JUMPI | |
| 0a16 | PUSH1 | 0x03 |
| 0a18 | SLOAD | |
| 0a19 | SWAP3 | |
| 0a1a | PUSH1 | 0x40 |
| 0a1c | MLOAD | |
| 0a1d | SWAP8 | |
| 0a1e | PUSH1 | 0xa0 |
| 0a20 | DUP10 | |
| 0a21 | ADD | |
| 0a22 | PUSH1 | 0x01 |
| 0a24 | PUSH1 | 0x01 |
| 0a26 | PUSH1 | 0x40 |
| 0a28 | SHL | |
| 0a29 | SUB | |
| 0a2a | DUP7 | |
| 0a2b | AND | |
| 0a2c | PUSH1 | 0x20 |
| 0a2e | DUP12 | |
| 0a2f | ADD | |
| 0a30 | MSTORE | |
| 0a31 | PUSH1 | 0x80 |
| 0a33 | PUSH1 | 0x40 |
| 0a35 | DUP12 | |
| 0a36 | ADD | |
| 0a37 | MSTORE | |
| 0a38 | PUSH1 | 0x04 |
| 0a3a | CALLDATALOAD | |
| 0a3b | PUSH1 | 0x04 |
| 0a3d | ADD | |
| 0a3e | CALLDATALOAD | |
| 0a3f | SWAP1 | |
| 0a40 | MSTORE | |
| 0a41 | PUSH1 | 0xc0 |
| 0a43 | DUP10 | |
| 0a44 | ADD | |
| 0a45 | PUSH1 | 0x24 |
| 0a47 | PUSH1 | 0x04 |
| 0a49 | CALLDATALOAD | |
| 0a4a | ADD | |
| 0a4b | DUP9 | |
| 0a4c | JUMPDEST | |
| 0a4d | PUSH1 | 0x04 |
| 0a4f | CALLDATALOAD | |
| 0a50 | PUSH1 | 0x04 |
| 0a52 | ADD | |
| 0a53 | CALLDATALOAD | |
| 0a54 | DUP2 | |
| 0a55 | LT | |
| 0a56 | PUSH2 | 0x252e |
| 0a59 | JUMPI | |
| 0a5a | POP | |
| 0a5b | POP | |
| 0a5c | PUSH1 | 0x1f |
| 0a5e | NOT | |
| 0a5f | DUP11 | |
| 0a60 | DUP3 | |
| 0a61 | SUB | |
| 0a62 | ADD | |
| 0a63 | PUSH1 | 0x60 |
| 0a65 | DUP12 | |
| 0a66 | ADD | |
| 0a67 | MSTORE | |
| 0a68 | DUP7 | |
| 0a69 | DUP2 | |
| 0a6a | MSTORE | |
| 0a6b | PUSH1 | 0x20 |
| 0a6d | DUP2 | |
| 0a6e | ADD | |
| 0a6f | PUSH1 | 0x20 |
| 0a71 | DUP9 | |
| 0a72 | PUSH1 | 0x05 |
| 0a74 | SHL | |
| 0a75 | DUP4 | |
| 0a76 | ADD | |
| 0a77 | ADD | |
| 0a78 | SWAP1 | |
| 0a79 | DUP11 | |
| 0a7a | SWAP3 | |
| 0a7b | DUP11 | |
| 0a7c | SWAP2 | |
| 0a7d | JUMPDEST | |
| 0a7e | DUP11 | |
| 0a7f | DUP4 | |
| 0a80 | LT | |
| 0a81 | PUSH2 | 0x2422 |
| 0a84 | JUMPI | |
| 0a85 | POP | |
| 0a86 | POP | |
| 0a87 | POP | |
| 0a88 | PUSH1 | 0x20 |
| 0a8a | SWAP2 | |
| 0a8b | POP | |
| 0a8c | PUSH1 | 0x1f |
| 0a8e | NOT | |
| 0a8f | DUP12 | |
| 0a90 | DUP3 | |
| 0a91 | SUB | |
| 0a92 | ADD | |
| 0a93 | PUSH1 | 0x80 |
| 0a95 | DUP13 | |
| 0a96 | ADD | |
| 0a97 | MSTORE | |
| 0a98 | PUSH1 | 0x44 |
| 0a9a | CALLDATALOAD | |
| 0a9b | PUSH1 | 0x04 |
| 0a9d | ADD | |
| 0a9e | CALLDATALOAD | |
| 0a9f | DUP2 | |
| 0aa0 | MSTORE | |
| 0aa1 | ADD | |
| 0aa2 | SWAP9 | |
| 0aa3 | PUSH1 | 0x24 |
| 0aa5 | PUSH1 | 0x44 |
| 0aa7 | CALLDATALOAD | |
| 0aa8 | ADD | |
| 0aa9 | DUP9 | |
| 0aaa | JUMPDEST | |
| 0aab | PUSH1 | 0x44 |
| 0aad | CALLDATALOAD | |
| 0aae | PUSH1 | 0x04 |
| 0ab0 | ADD | |
| 0ab1 | CALLDATALOAD | |
| 0ab2 | DUP2 | |
| 0ab3 | LT | |
| 0ab4 | PUSH2 | 0x23ab |
| 0ab7 | JUMPI | |
| 0ab8 | POP | |
| 0ab9 | POP | |
| 0aba | PUSH2 | 0x0ad3 |
| 0abd | DUP2 | |
| 0abe | PUSH2 | 0x0b75 |
| 0ac1 | SWAP8 | |
| 0ac2 | SWAP9 | |
| 0ac3 | SWAP10 | |
| 0ac4 | SWAP11 | |
| 0ac5 | SWAP12 | |
| 0ac6 | SUB | |
| 0ac7 | PUSH1 | 0x1f |
| 0ac9 | NOT | |
| 0aca | DUP2 | |
| 0acb | ADD | |
| 0acc | DUP4 | |
| 0acd | MSTORE | |
| 0ace | DUP3 | |
| 0acf | PUSH2 | 0x5507 |
| 0ad2 | JUMP | |
| 0ad3 | JUMPDEST | |
| 0ad4 | PUSH1 | 0x20 |
| 0ad6 | DUP2 | |
| 0ad7 | MLOAD | |
| 0ad8 | SWAP2 | |
| 0ad9 | ADD | |
| 0ada | KECCAK256 | |
| 0adb | PUSH1 | 0x40 |
| 0add | MLOAD | |
| 0ade | PUSH1 | 0x20 |
| 0ae0 | DUP2 | |
| 0ae1 | ADD | |
| 0ae2 | SWAP2 | |
| 0ae3 | PUSH0 | |
| 0ae4 | MLOAD | |
| 0ae5 | PUSH1 | 0x20 |
| 0ae7 | PUSH2 | 0x6bac |
| 0aea | PUSH0 | |
| 0aeb | CODECOPY | |
| 0aec | PUSH0 | |
| 0aed | MLOAD | |
| 0aee | SWAP1 | |
| 0aef | PUSH0 | |
| 0af0 | MSTORE | |
| 0af1 | DUP4 | |
| 0af2 | MSTORE | |
| 0af3 | CHAINID | |
| 0af4 | PUSH1 | 0x40 |
| 0af6 | DUP4 | |
| 0af7 | ADD | |
| 0af8 | MSTORE | |
| 0af9 | ADDRESS | |
| 0afa | PUSH1 | 0x60 |
| 0afc | DUP4 | |
| 0afd | ADD | |
| 0afe | MSTORE | |
| 0aff | PUSH32 | 0xe10634eb0bf7bd6ad00dc59a6c67fe9b09037b979405fa59137e7e530d601414 |
| 0b20 | PUSH1 | 0x80 |
| 0b22 | DUP4 | |
| 0b23 | ADD | |
| 0b24 | MSTORE | |
| 0b25 | PUSH1 | 0x01 |
| 0b27 | PUSH1 | 0x01 |
| 0b29 | PUSH1 | 0x40 |
| 0b2b | SHL | |
| 0b2c | SUB | |
| 0b2d | DUP8 | |
| 0b2e | AND | |
| 0b2f | PUSH1 | 0xa0 |
| 0b31 | DUP4 | |
| 0b32 | ADD | |
| 0b33 | MSTORE | |
| 0b34 | PUSH1 | 0xc0 |
| 0b36 | DUP3 | |
| 0b37 | ADD | |
| 0b38 | MSTORE | |
| 0b39 | PUSH1 | 0xc0 |
| 0b3b | DUP2 | |
| 0b3c | MSTORE | |
| 0b3d | PUSH2 | 0x0b47 |
| 0b40 | PUSH1 | 0xe0 |
| 0b42 | DUP3 | |
| 0b43 | PUSH2 | 0x5507 |
| 0b46 | JUMP | |
| 0b47 | JUMPDEST | |
| 0b48 | MLOAD | |
| 0b49 | SWAP1 | |
| 0b4a | KECCAK256 | |
| 0b4b | SWAP1 | |
| 0b4c | PUSH1 | 0x01 |
| 0b4e | SLOAD | |
| 0b4f | SWAP3 | |
| 0b50 | PUSH32 | 0x000000000000000000000000a2e71fc2fb02d1ce93aa958e56cab83d26f3bfa6 |
| 0b71 | PUSH2 | 0x6100 |
| 0b74 | JUMP | |
| 0b75 | JUMPDEST | |
| 0b76 | POP | |
| 0b77 | PUSH1 | 0x01 |
| 0b79 | PUSH1 | 0x01 |
| 0b7b | PUSH1 | 0x40 |
| 0b7d | SHL | |
| 0b7e | SUB | |
| 0b7f | PUSH2 | 0x0b89 |
| 0b82 | DUP2 | |
| 0b83 | DUP4 | |
| 0b84 | AND | |
| 0b85 | PUSH2 | 0x5764 |
| 0b88 | JUMP | |
| 0b89 | JUMPDEST | |
| 0b8a | AND | |
| 0b8b | SWAP1 | |
| 0b8c | PUSH1 | 0x01 |
| 0b8e | PUSH1 | 0x01 |
| 0b90 | PUSH1 | 0x40 |
| 0b92 | SHL | |
| 0b93 | SUB | |
| 0b94 | NOT | |
| 0b95 | AND | |
| 0b96 | OR | |
| 0b97 | PUSH1 | 0x03 |
| 0b99 | SSTORE | |
| 0b9a | PUSH1 | 0x17 |
| 0b9c | SLOAD | |
| 0b9d | SWAP2 | |
| 0b9e | PUSH2 | 0x0baf |
| 0ba1 | PUSH1 | 0x01 |
| 0ba3 | PUSH1 | 0x01 |
| 0ba5 | PUSH1 | 0x40 |
| 0ba7 | SHL | |
| 0ba8 | SUB | |
| 0ba9 | DUP5 | |
| 0baa | AND | |
| 0bab | PUSH2 | 0x5764 |
| 0bae | JUMP | |
| 0baf | JUMPDEST | |
| 0bb0 | PUSH8 | 0xffffffffffffffff |
| 0bb9 | NOT | |
| 0bba | SWAP1 | |
| 0bbb | SWAP4 | |
| 0bbc | AND | |
| 0bbd | PUSH1 | 0x01 |
| 0bbf | PUSH1 | 0x01 |
| 0bc1 | PUSH1 | 0x40 |
| 0bc3 | SHL | |
| 0bc4 | SUB | |
| 0bc5 | DUP5 | |
| 0bc6 | AND | |
| 0bc7 | OR | |
| 0bc8 | PUSH1 | 0x17 |
| 0bca | SSTORE | |
| 0bcb | PUSH2 | 0x0bd9 |
| 0bce | DUP3 | |
| 0bcf | PUSH1 | 0x04 |
| 0bd1 | DUP1 | |
| 0bd2 | CALLDATALOAD | |
| 0bd3 | ADD | |
| 0bd4 | CALLDATALOAD | |
| 0bd5 | PUSH2 | 0x59b8 |
| 0bd8 | JUMP | |
| 0bd9 | JUMPDEST | |
| 0bda | PUSH1 | 0x04 |
| 0bdc | PUSH1 | 0x44 |
| 0bde | CALLDATALOAD | |
| 0bdf | ADD | |
| 0be0 | CALLDATALOAD | |
| 0be1 | PUSH1 | 0x01 |
| 0be3 | PUSH1 | 0x01 |
| 0be5 | PUSH1 | 0xff |
| 0be7 | SHL | |
| 0be8 | SUB | |
| 0be9 | DUP2 | |
| 0bea | AND | |
| 0beb | SWAP1 | |
| 0bec | SUB | |
| 0bed | PUSH2 | 0x2397 |
| 0bf0 | JUMPI | |
| 0bf1 | PUSH2 | 0x0c07 |
| 0bf4 | PUSH2 | 0x0c18 |
| 0bf7 | SWAP2 | |
| 0bf8 | PUSH1 | 0x44 |
| 0bfa | CALLDATALOAD | |
| 0bfb | PUSH1 | 0x04 |
| 0bfd | ADD | |
| 0bfe | CALLDATALOAD | |
| 0bff | PUSH1 | 0x01 |
| 0c01 | SHL | |
| 0c02 | SWAP1 | |
| 0c03 | PUSH2 | 0x59b8 |
| 0c06 | JUMP | |
| 0c07 | JUMPDEST | |
| 0c08 | PUSH2 | 0x0c10 |
| 0c0b | DUP2 | |
| 0c0c | PUSH2 | 0x57ad |
| 0c0f | JUMP | |
| 0c10 | JUMPDEST | |
| 0c11 | PUSH1 | 0x80 |
| 0c13 | MSTORE | |
| 0c14 | PUSH2 | 0x57ad |
| 0c17 | JUMP | |
| 0c18 | JUMPDEST | |
| 0c19 | SWAP1 | |
| 0c1a | DUP1 | |
| 0c1b | SWAP3 | |
| 0c1c | DUP2 | |
| 0c1d | SWAP4 | |
| 0c1e | JUMPDEST | |
| 0c1f | PUSH1 | 0x04 |
| 0c21 | CALLDATALOAD | |
| 0c22 | PUSH1 | 0x04 |
| 0c24 | ADD | |
| 0c25 | CALLDATALOAD | |
| 0c26 | DUP6 | |
| 0c27 | LT | |
| 0c28 | ISZERO | |
| 0c29 | PUSH2 | 0x1478 |
| 0c2c | JUMPI | |
| 0c2d | PUSH2 | 0x0c47 |
| 0c30 | PUSH1 | 0x04 |
| 0c32 | CALLDATALOAD | |
| 0c33 | PUSH2 | 0x02a0 |
| 0c36 | DUP8 | |
| 0c37 | MUL | |
| 0c38 | ADD | |
| 0c39 | PUSH1 | 0x64 |
| 0c3b | DUP2 | |
| 0c3c | ADD | |
| 0c3d | CALLDATALOAD | |
| 0c3e | SWAP1 | |
| 0c3f | PUSH1 | 0x44 |
| 0c41 | ADD | |
| 0c42 | CALLDATALOAD | |
| 0c43 | PUSH2 | 0x6008 |
| 0c46 | JUMP | |
| 0c47 | JUMPDEST | |
| 0c48 | PUSH1 | 0x24 |
| 0c4a | PUSH2 | 0x02a0 |
| 0c4d | DUP8 | |
| 0c4e | MUL | |
| 0c4f | PUSH1 | 0x04 |
| 0c51 | CALLDATALOAD | |
| 0c52 | ADD | |
| 0c53 | ADD | |
| 0c54 | CALLDATALOAD | |
| 0c55 | SUB | |
| 0c56 | PUSH2 | 0x143d |
| 0c59 | JUMPI | |
| 0c5a | PUSH2 | 0x0c6e |
| 0c5d | PUSH2 | 0x0244 |
| 0c60 | PUSH2 | 0x02a0 |
| 0c63 | DUP8 | |
| 0c64 | MUL | |
| 0c65 | PUSH1 | 0x04 |
| 0c67 | CALLDATALOAD | |
| 0c68 | ADD | |
| 0c69 | ADD | |
| 0c6a | PUSH2 | 0x59c5 |
| 0c6d | JUMP | |
| 0c6e | JUMPDEST | |
| 0c6f | DUP1 | |
| 0c70 | PUSH2 | 0x13b0 |
| 0c73 | JUMPI | |
| 0c74 | JUMPDEST | |
| 0c75 | PUSH2 | 0x1392 |
| 0c78 | JUMPI | |
| 0c79 | PUSH1 | 0x01 |
| 0c7b | PUSH1 | 0xff |
| 0c7d | PUSH2 | 0x0c91 |
| 0c80 | PUSH2 | 0x02a4 |
| 0c83 | PUSH2 | 0x02a0 |
| 0c86 | DUP10 | |
| 0c87 | MUL | |
| 0c88 | PUSH1 | 0x04 |
| 0c8a | CALLDATALOAD | |
| 0c8b | ADD | |
| 0c8c | ADD | |
| 0c8d | PUSH2 | 0x59d2 |
| 0c90 | JUMP | |
| 0c91 | JUMPDEST | |
| 0c92 | AND | |
| 0c93 | GT | |
| 0c94 | PUSH2 | 0x1359 |
| 0c97 | JUMPI | |
| 0c98 | PUSH2 | 0x0cac |
| 0c9b | PUSH2 | 0x0244 |
| 0c9e | PUSH2 | 0x02a0 |
| 0ca1 | DUP8 | |
| 0ca2 | MUL | |
| 0ca3 | PUSH1 | 0x04 |
| 0ca5 | CALLDATALOAD | |
| 0ca6 | ADD | |
| 0ca7 | ADD | |
| 0ca8 | PUSH2 | 0x59c5 |
| 0cab | JUMP | |
| 0cac | JUMPDEST | |
| 0cad | DUP1 | |
| 0cae | PUSH2 | 0x133a |
| 0cb1 | JUMPI | |
| 0cb2 | JUMPDEST | |
| 0cb3 | DUP1 | |
| 0cb4 | PUSH2 | 0x1326 |
| 0cb7 | JUMPI | |
| 0cb8 | JUMPDEST | |
| 0cb9 | PUSH2 | 0x1308 |
| 0cbc | JUMPI | |
| 0cbd | PUSH32 | 0x84e75c8576483b53128ed23c26158522388ddb0a5df8516c83887d522196f91f |
| 0cde | PUSH1 | 0x44 |
| 0ce0 | PUSH2 | 0x02a0 |
| 0ce3 | DUP8 | |
| 0ce4 | MUL | |
| 0ce5 | PUSH1 | 0x04 |
| 0ce7 | CALLDATALOAD | |
| 0ce8 | ADD | |
| 0ce9 | ADD | |
| 0cea | CALLDATALOAD | |
| 0ceb | EQ | |
| 0cec | DUP1 | |
| 0ced | PUSH2 | 0x12e6 |
| 0cf0 | JUMPI | |
| 0cf1 | JUMPDEST | |
| 0cf2 | PUSH2 | 0x12ad |
| 0cf5 | JUMPI | |
| 0cf6 | PUSH2 | 0x01e4 |
| 0cf9 | PUSH2 | 0x02a0 |
| 0cfc | DUP7 | |
| 0cfd | MUL | |
| 0cfe | PUSH1 | 0x04 |
| 0d00 | CALLDATALOAD | |
| 0d01 | ADD | |
| 0d02 | ADD | |
| 0d03 | CALLDATALOAD | |
| 0d04 | ISZERO | |
| 0d05 | ISZERO | |
| 0d06 | DUP1 | |
| 0d07 | PUSH2 | 0x1289 |
| 0d0a | JUMPI | |
| 0d0b | JUMPDEST | |
| 0d0c | PUSH2 | 0x1269 |
| 0d0f | JUMPI | |
| 0d10 | PUSH1 | 0x24 |
| 0d12 | PUSH2 | 0x02a0 |
| 0d15 | DUP7 | |
| 0d16 | MUL | |
| 0d17 | PUSH1 | 0x04 |
| 0d19 | CALLDATALOAD | |
| 0d1a | ADD | |
| 0d1b | ADD | |
| 0d1c | CALLDATALOAD | |
| 0d1d | DUP4 | |
| 0d1e | MSTORE | |
| 0d1f | PUSH1 | 0x06 |
| 0d21 | PUSH1 | 0x20 |
| 0d23 | MSTORE | |
| 0d24 | PUSH1 | 0xff |
| 0d26 | PUSH1 | 0x40 |
| 0d28 | DUP5 | |
| 0d29 | KECCAK256 | |
| 0d2a | SLOAD | |
| 0d2b | AND | |
| 0d2c | ISZERO | |
| 0d2d | PUSH2 | 0x11e3 |
| 0d30 | JUMPI | |
| 0d31 | JUMPDEST | |
| 0d32 | PUSH2 | 0x02a0 |
| 0d35 | DUP6 | |
| 0d36 | MUL | |
| 0d37 | PUSH1 | 0x04 |
| 0d39 | CALLDATALOAD | |
| 0d3a | ADD | |
| 0d3b | PUSH1 | 0x24 |
| 0d3d | DUP2 | |
| 0d3e | ADD | |
| 0d3f | CALLDATALOAD | |
| 0d40 | DUP1 | |
| 0d41 | DUP6 | |
| 0d42 | MSTORE | |
| 0d43 | PUSH1 | 0x05 |
| 0d45 | PUSH1 | 0x20 |
| 0d47 | MSTORE | |
| 0d48 | PUSH1 | 0x40 |
| 0d4a | DUP6 | |
| 0d4b | KECCAK256 | |
| 0d4c | SWAP1 | |
| 0d4d | DUP2 | |
| 0d4e | SSTORE | |
| 0d4f | PUSH1 | 0x44 |
| 0d51 | DUP3 | |
| 0d52 | ADD | |
| 0d53 | CALLDATALOAD | |
| 0d54 | PUSH1 | 0x01 |
| 0d56 | DUP3 | |
| 0d57 | ADD | |
| 0d58 | SWAP1 | |
| 0d59 | DUP2 | |
| 0d5a | SSTORE | |
| 0d5b | PUSH1 | 0x64 |
| 0d5d | DUP4 | |
| 0d5e | ADD | |
| 0d5f | CALLDATALOAD | |
| 0d60 | PUSH1 | 0x02 |
| 0d62 | DUP4 | |
| 0d63 | ADD | |
| 0d64 | SSTORE | |
| 0d65 | PUSH1 | 0x84 |
| 0d67 | DUP4 | |
| 0d68 | ADD | |
| 0d69 | CALLDATALOAD | |
| 0d6a | PUSH1 | 0x03 |
| 0d6c | DUP4 | |
| 0d6d | ADD | |
| 0d6e | SSTORE | |
| 0d6f | SWAP2 | |
| 0d70 | PUSH2 | 0x0d7b |
| 0d73 | SWAP1 | |
| 0d74 | PUSH1 | 0xa4 |
| 0d76 | ADD | |
| 0d77 | PUSH2 | 0x59d2 |
| 0d7a | JUMP | |
| 0d7b | JUMPDEST | |
| 0d7c | PUSH1 | 0x04 |
| 0d7e | DUP3 | |
| 0d7f | DUP2 | |
| 0d80 | ADD | |
| 0d81 | DUP1 | |
| 0d82 | SLOAD | |
| 0d83 | PUSH1 | 0xff |
| 0d85 | NOT | |
| 0d86 | AND | |
| 0d87 | PUSH1 | 0xff |
| 0d89 | SWAP4 | |
| 0d8a | SWAP1 | |
| 0d8b | SWAP4 | |
| 0d8c | AND | |
| 0d8d | SWAP3 | |
| 0d8e | SWAP1 | |
| 0d8f | SWAP3 | |
| 0d90 | OR | |
| 0d91 | SWAP1 | |
| 0d92 | SWAP2 | |
| 0d93 | SSTORE | |
| 0d94 | PUSH1 | 0xc4 |
| 0d96 | SWAP1 | |
| 0d97 | CALLDATALOAD | |
| 0d98 | PUSH2 | 0x02a0 |
| 0d9b | DUP10 | |
| 0d9c | MUL | |
| 0d9d | ADD | |
| 0d9e | SWAP1 | |
| 0d9f | DUP2 | |
| 0da0 | ADD | |
| 0da1 | CALLDATALOAD | |
| 0da2 | PUSH1 | 0x05 |
| 0da4 | DUP4 | |
| 0da5 | ADD | |
| 0da6 | SSTORE | |
| 0da7 | PUSH1 | 0xe4 |
| 0da9 | DUP2 | |
| 0daa | ADD | |
| 0dab | CALLDATALOAD | |
| 0dac | PUSH1 | 0x06 |
| 0dae | DUP4 | |
| 0daf | ADD | |
| 0db0 | SSTORE | |
| 0db1 | PUSH2 | 0x0dbd |
| 0db4 | SWAP1 | |
| 0db5 | PUSH2 | 0x0104 |
| 0db8 | ADD | |
| 0db9 | PUSH2 | 0x59d2 |
| 0dbc | JUMP | |
| 0dbd | JUMPDEST | |
| 0dbe | PUSH1 | 0xff |
| 0dc0 | AND | |
| 0dc1 | PUSH1 | 0xff |
| 0dc3 | NOT | |
| 0dc4 | PUSH1 | 0x07 |
| 0dc6 | DUP4 | |
| 0dc7 | ADD | |
| 0dc8 | SLOAD | |
| 0dc9 | AND | |
| 0dca | OR | |
| 0dcb | PUSH1 | 0x07 |
| 0dcd | DUP3 | |
| 0dce | ADD | |
| 0dcf | SSTORE | |
| 0dd0 | PUSH2 | 0x02a0 |
| 0dd3 | DUP8 | |
| 0dd4 | MUL | |
| 0dd5 | PUSH1 | 0x04 |
| 0dd7 | CALLDATALOAD | |
| 0dd8 | ADD | |
| 0dd9 | PUSH2 | 0x0124 |
| 0ddc | ADD | |
| 0ddd | PUSH2 | 0x0de5 |
| 0de0 | SWAP1 | |
| 0de1 | PUSH2 | 0x5f8f |
| 0de4 | JUMP | |
| 0de5 | JUMPDEST | |
| 0de6 | PUSH1 | 0x07 |
| 0de8 | DUP3 | |
| 0de9 | ADD | |
| 0dea | SWAP1 | |
| 0deb | PUSH2 | 0x0e12 |
| 0dee | SWAP2 | |
| 0def | SWAP1 | |
| 0df0 | PUSH9 | 0xffffffffffffffff00 |
| 0dfa | DUP3 | |
| 0dfb | SLOAD | |
| 0dfc | SWAP2 | |
| 0dfd | PUSH1 | 0x08 |
| 0dff | SHL | |
| 0e00 | AND | |
| 0e01 | SWAP1 | |
| 0e02 | PUSH9 | 0xffffffffffffffff00 |
| 0e0c | NOT | |
| 0e0d | AND | |
| 0e0e | OR | |
| 0e0f | SWAP1 | |
| 0e10 | SSTORE | |
| 0e11 | JUMP | |
| 0e12 | JUMPDEST | |
| 0e13 | PUSH2 | 0x0e27 |
| 0e16 | PUSH2 | 0x0144 |
| 0e19 | PUSH1 | 0x04 |
| 0e1b | CALLDATALOAD | |
| 0e1c | PUSH2 | 0x02a0 |
| 0e1f | DUP11 | |
| 0e20 | MUL | |
| 0e21 | ADD | |
| 0e22 | ADD | |
| 0e23 | PUSH2 | 0x59fd |
| 0e26 | JUMP | |
| 0e27 | JUMPDEST | |
| 0e28 | PUSH1 | 0x07 |
| 0e2a | DUP3 | |
| 0e2b | ADD | |
| 0e2c | SLOAD | |
| 0e2d | PUSH2 | 0xffff |
| 0e30 | PUSH1 | 0x68 |
| 0e32 | SHL | |
| 0e33 | PUSH2 | 0x0e47 |
| 0e36 | PUSH2 | 0x0164 |
| 0e39 | PUSH1 | 0x04 |
| 0e3b | CALLDATALOAD | |
| 0e3c | PUSH2 | 0x02a0 |
| 0e3f | DUP14 | |
| 0e40 | MUL | |
| 0e41 | ADD | |
| 0e42 | ADD | |
| 0e43 | PUSH2 | 0x59ee |
| 0e46 | JUMP | |
| 0e47 | JUMPDEST | |
| 0e48 | PUSH1 | 0x68 |
| 0e4a | SHL | |
| 0e4b | AND | |
| 0e4c | SWAP1 | |
| 0e4d | PUSH2 | 0xffff |
| 0e50 | PUSH1 | 0x78 |
| 0e52 | SHL | |
| 0e53 | PUSH2 | 0x0e67 |
| 0e56 | PUSH2 | 0x0184 |
| 0e59 | PUSH1 | 0x04 |
| 0e5b | CALLDATALOAD | |
| 0e5c | PUSH2 | 0x02a0 |
| 0e5f | DUP15 | |
| 0e60 | MUL | |
| 0e61 | ADD | |
| 0e62 | ADD | |
| 0e63 | PUSH2 | 0x59ee |
| 0e66 | JUMP | |
| 0e67 | JUMPDEST | |
| 0e68 | PUSH1 | 0x78 |
| 0e6a | SHL | |
| 0e6b | AND | |
| 0e6c | SWAP3 | |
| 0e6d | PUSH1 | 0x48 |
| 0e6f | SHL | |
| 0e70 | PUSH13 | 0xffffffff000000000000000000 |
| 0e7e | AND | |
| 0e7f | SWAP1 | |
| 0e80 | PUSH8 | 0xffffffffffffffff |
| 0e89 | PUSH1 | 0x48 |
| 0e8b | SHL | |
| 0e8c | NOT | |
| 0e8d | AND | |
| 0e8e | OR | |
| 0e8f | OR | |
| 0e90 | OR | |
| 0e91 | PUSH1 | 0x07 |
| 0e93 | DUP3 | |
| 0e94 | ADD | |
| 0e95 | SSTORE | |
| 0e96 | PUSH2 | 0x02a0 |
| 0e99 | DUP8 | |
| 0e9a | MUL | |
| 0e9b | PUSH1 | 0x04 |
| 0e9d | CALLDATALOAD | |
| 0e9e | ADD | |
| 0e9f | PUSH2 | 0x01a4 |
| 0ea2 | ADD | |
| 0ea3 | CALLDATALOAD | |
| 0ea4 | PUSH1 | 0x08 |
| 0ea6 | DUP3 | |
| 0ea7 | ADD | |
| 0ea8 | SSTORE | |
| 0ea9 | PUSH2 | 0x02a0 |
| 0eac | DUP8 | |
| 0ead | MUL | |
| 0eae | PUSH1 | 0x04 |
| 0eb0 | CALLDATALOAD | |
| 0eb1 | ADD | |
| 0eb2 | PUSH2 | 0x01c4 |
| 0eb5 | ADD | |
| 0eb6 | PUSH2 | 0x0ebe |
| 0eb9 | SWAP1 | |
| 0eba | PUSH2 | 0x59d2 |
| 0ebd | JUMP | |
| 0ebe | JUMPDEST | |
| 0ebf | PUSH1 | 0x09 |
| 0ec1 | DUP3 | |
| 0ec2 | ADD | |
| 0ec3 | DUP1 | |
| 0ec4 | SLOAD | |
| 0ec5 | PUSH1 | 0xff |
| 0ec7 | NOT | |
| 0ec8 | AND | |
| 0ec9 | PUSH1 | 0xff |
| 0ecb | SWAP3 | |
| 0ecc | SWAP1 | |
| 0ecd | SWAP3 | |
| 0ece | AND | |
| 0ecf | SWAP2 | |
| 0ed0 | SWAP1 | |
| 0ed1 | SWAP2 | |
| 0ed2 | OR | |
| 0ed3 | SWAP1 | |
| 0ed4 | SSTORE | |
| 0ed5 | PUSH2 | 0x01e4 |
| 0ed8 | PUSH1 | 0x04 |
| 0eda | CALLDATALOAD | |
| 0edb | PUSH2 | 0x02a0 |
| 0ede | DUP10 | |
| 0edf | MUL | |
| 0ee0 | ADD | |
| 0ee1 | SWAP1 | |
| 0ee2 | DUP2 | |
| 0ee3 | ADD | |
| 0ee4 | CALLDATALOAD | |
| 0ee5 | PUSH1 | 0x0a |
| 0ee7 | DUP4 | |
| 0ee8 | ADD | |
| 0ee9 | SSTORE | |
| 0eea | PUSH2 | 0x0204 |
| 0eed | DUP2 | |
| 0eee | ADD | |
| 0eef | CALLDATALOAD | |
| 0ef0 | PUSH1 | 0x0b |
| 0ef2 | DUP4 | |
| 0ef3 | ADD | |
| 0ef4 | SSTORE | |
| 0ef5 | PUSH2 | 0x0f01 |
| 0ef8 | SWAP1 | |
| 0ef9 | PUSH2 | 0x0224 |
| 0efc | ADD | |
| 0efd | PUSH2 | 0x59d2 |
| 0f00 | JUMP | |
| 0f01 | JUMPDEST | |
| 0f02 | PUSH1 | 0xff |
| 0f04 | AND | |
| 0f05 | PUSH1 | 0xff |
| 0f07 | NOT | |
| 0f08 | PUSH1 | 0x0c |
| 0f0a | DUP4 | |
| 0f0b | ADD | |
| 0f0c | SLOAD | |
| 0f0d | AND | |
| 0f0e | OR | |
| 0f0f | PUSH1 | 0x0c |
| 0f11 | DUP3 | |
| 0f12 | ADD | |
| 0f13 | SSTORE | |
| 0f14 | PUSH2 | 0x02a0 |
| 0f17 | DUP8 | |
| 0f18 | MUL | |
| 0f19 | PUSH1 | 0x04 |
| 0f1b | CALLDATALOAD | |
| 0f1c | ADD | |
| 0f1d | PUSH2 | 0x0244 |
| 0f20 | ADD | |
| 0f21 | PUSH2 | 0x0f29 |
| 0f24 | SWAP1 | |
| 0f25 | PUSH2 | 0x59c5 |
| 0f28 | JUMP | |
| 0f29 | JUMPDEST | |
| 0f2a | PUSH1 | 0x0c |
| 0f2c | DUP3 | |
| 0f2d | ADD | |
| 0f2e | DUP1 | |
| 0f2f | SLOAD | |
| 0f30 | SWAP2 | |
| 0f31 | ISZERO | |
| 0f32 | ISZERO | |
| 0f33 | PUSH1 | 0x08 |
| 0f35 | SHL | |
| 0f36 | PUSH2 | 0xff00 |
| 0f39 | AND | |
| 0f3a | PUSH10 | 0xffffffffffffffffff00 |
| 0f45 | NOT | |
| 0f46 | SWAP1 | |
| 0f47 | SWAP3 | |
| 0f48 | AND | |
| 0f49 | SWAP2 | |
| 0f4a | SWAP1 | |
| 0f4b | SWAP2 | |
| 0f4c | OR | |
| 0f4d | PUSH1 | 0x10 |
| 0f4f | DUP11 | |
| 0f50 | SWAP1 | |
| 0f51 | SHL | |
| 0f52 | PUSH10 | 0xffffffffffffffff0000 |
| 0f5d | AND | |
| 0f5e | OR | |
| 0f5f | SWAP1 | |
| 0f60 | SSTORE | |
| 0f61 | PUSH2 | 0x0f75 |
| 0f64 | PUSH2 | 0x0284 |
| 0f67 | PUSH1 | 0x04 |
| 0f69 | CALLDATALOAD | |
| 0f6a | PUSH2 | 0x02a0 |
| 0f6d | DUP11 | |
| 0f6e | MUL | |
| 0f6f | ADD | |
| 0f70 | ADD | |
| 0f71 | PUSH2 | 0x5f8f |
| 0f74 | JUMP | |
| 0f75 | JUMPDEST | |
| 0f76 | PUSH1 | 0x0c |
| 0f78 | DUP3 | |
| 0f79 | ADD | |
| 0f7a | DUP1 | |
| 0f7b | SLOAD | |
| 0f7c | PUSH8 | 0xffffffffffffffff |
| 0f85 | PUSH1 | 0x50 |
| 0f87 | SHL | |
| 0f88 | NOT | |
| 0f89 | AND | |
| 0f8a | PUSH1 | 0x50 |
| 0f8c | SWAP3 | |
| 0f8d | SWAP1 | |
| 0f8e | SWAP3 | |
| 0f8f | SHL | |
| 0f90 | PUSH8 | 0xffffffffffffffff |
| 0f99 | PUSH1 | 0x50 |
| 0f9b | SHL | |
| 0f9c | AND | |
| 0f9d | SWAP2 | |
| 0f9e | SWAP1 | |
| 0f9f | SWAP2 | |
| 0fa0 | OR | |
| 0fa1 | SWAP1 | |
| 0fa2 | SSTORE | |
| 0fa3 | PUSH2 | 0x0fb7 |
| 0fa6 | PUSH2 | 0x02a4 |
| 0fa9 | PUSH1 | 0x04 |
| 0fab | CALLDATALOAD | |
| 0fac | PUSH2 | 0x02a0 |
| 0faf | DUP11 | |
| 0fb0 | MUL | |
| 0fb1 | ADD | |
| 0fb2 | ADD | |
| 0fb3 | PUSH2 | 0x59d2 |
| 0fb6 | JUMP | |
| 0fb7 | JUMPDEST | |
| 0fb8 | PUSH1 | 0x0c |
| 0fba | DUP3 | |
| 0fbb | ADD | |
| 0fbc | DUP1 | |
| 0fbd | SLOAD | |
| 0fbe | PUSH1 | 0xff |
| 0fc0 | PUSH1 | 0x90 |
| 0fc2 | SHL | |
| 0fc3 | NOT | |
| 0fc4 | DUP2 | |
| 0fc5 | AND | |
| 0fc6 | PUSH1 | 0x90 |
| 0fc8 | DUP5 | |
| 0fc9 | SWAP1 | |
| 0fca | SHL | |
| 0fcb | PUSH1 | 0xff |
| 0fcd | PUSH1 | 0x90 |
| 0fcf | SHL | |
| 0fd0 | AND | |
| 0fd1 | OR | |
| 0fd2 | SWAP1 | |
| 0fd3 | SWAP2 | |
| 0fd4 | SSTORE | |
| 0fd5 | SWAP2 | |
| 0fd6 | DUP7 | |
| 0fd7 | SWAP1 | |
| 0fd8 | PUSH2 | 0x0fec |
| 0fdb | PUSH1 | 0x24 |
| 0fdd | PUSH1 | 0x04 |
| 0fdf | CALLDATALOAD | |
| 0fe0 | PUSH2 | 0x02a0 |
| 0fe3 | DUP14 | |
| 0fe4 | MUL | |
| 0fe5 | ADD | |
| 0fe6 | ADD | |
| 0fe7 | CALLDATALOAD | |
| 0fe8 | PUSH2 | 0x58c2 |
| 0feb | JUMP | |
| 0fec | JUMPDEST | |
| 0fed | DUP7 | |
| 0fee | PUSH1 | 0x80 |
| 0ff0 | MLOAD | |
| 0ff1 | SWAP1 | |
| 0ff2 | PUSH2 | 0x0ffa |
| 0ff5 | SWAP2 | |
| 0ff6 | PUSH2 | 0x587b |
| 0ff9 | JUMP | |
| 0ffa | JUMPDEST | |
| 0ffb | MSTORE | |
| 0ffc | DUP1 | |
| 0ffd | SLOAD | |
| 0ffe | SWAP5 | |
| 0fff | SLOAD | |
| 1000 | PUSH1 | 0x02 |
| 1002 | DUP3 | |
| 1003 | ADD | |
| 1004 | SLOAD | |
| 1005 | PUSH1 | 0x03 |
| 1007 | DUP4 | |
| 1008 | ADD | |
| 1009 | SLOAD | |
| 100a | PUSH1 | 0x04 |
| 100c | DUP5 | |
| 100d | ADD | |
| 100e | SLOAD | |
| 100f | PUSH1 | 0x05 |
| 1011 | DUP6 | |
| 1012 | ADD | |
| 1013 | SLOAD | |
| 1014 | PUSH1 | 0x06 |
| 1016 | DUP7 | |
| 1017 | ADD | |
| 1018 | SLOAD | |
| 1019 | PUSH1 | 0x07 |
| 101b | DUP8 | |
| 101c | ADD | |
| 101d | SLOAD | |
| 101e | PUSH1 | 0x08 |
| 1020 | DUP1 | |
| 1021 | DUP10 | |
| 1022 | ADD | |
| 1023 | SLOAD | |
| 1024 | PUSH1 | 0x09 |
| 1026 | DUP11 | |
| 1027 | ADD | |
| 1028 | SLOAD | |
| 1029 | PUSH1 | 0x0a |
| 102b | DUP12 | |
| 102c | ADD | |
| 102d | SLOAD | |
| 102e | PUSH1 | 0x0b |
| 1030 | SWAP1 | |
| 1031 | SWAP12 | |
| 1032 | ADD | |
| 1033 | SLOAD | |
| 1034 | PUSH1 | 0x40 |
| 1036 | DUP1 | |
| 1037 | MLOAD | |
| 1038 | PUSH0 | |
| 1039 | MLOAD | |
| 103a | PUSH1 | 0x20 |
| 103c | PUSH2 | 0x6bcc |
| 103f | PUSH0 | |
| 1040 | CODECOPY | |
| 1041 | PUSH0 | |
| 1042 | MLOAD | |
| 1043 | SWAP1 | |
| 1044 | PUSH0 | |
| 1045 | MSTORE | |
| 1046 | PUSH1 | 0x20 |
| 1048 | DUP3 | |
| 1049 | ADD | |
| 104a | MSTORE | |
| 104b | SWAP1 | |
| 104c | DUP2 | |
| 104d | ADD | |
| 104e | SWAP14 | |
| 104f | SWAP1 | |
| 1050 | SWAP14 | |
| 1051 | MSTORE | |
| 1052 | PUSH1 | 0x60 |
| 1054 | DUP14 | |
| 1055 | ADD | |
| 1056 | SWAP16 | |
| 1057 | SWAP1 | |
| 1058 | SWAP16 | |
| 1059 | MSTORE | |
| 105a | PUSH1 | 0x80 |
| 105c | DUP13 | |
| 105d | ADD | |
| 105e | SWAP10 | |
| 105f | SWAP1 | |
| 1060 | SWAP10 | |
| 1061 | MSTORE | |
| 1062 | PUSH1 | 0xa0 |
| 1064 | DUP12 | |
| 1065 | ADD | |
| 1066 | SWAP8 | |
| 1067 | SWAP1 | |
| 1068 | SWAP8 | |
| 1069 | MSTORE | |
| 106a | PUSH1 | 0xc0 |
| 106c | DUP11 | |
| 106d | ADD | |
| 106e | SWAP6 | |
| 106f | SWAP1 | |
| 1070 | SWAP6 | |
| 1071 | MSTORE | |
| 1072 | PUSH1 | 0xff |
| 1074 | SWAP4 | |
| 1075 | DUP5 | |
| 1076 | AND | |
| 1077 | PUSH1 | 0xe0 |
| 1079 | DUP11 | |
| 107a | ADD | |
| 107b | MSTORE | |
| 107c | PUSH2 | 0x0100 |
| 107f | DUP10 | |
| 1080 | ADD | |
| 1081 | SWAP3 | |
| 1082 | SWAP1 | |
| 1083 | SWAP3 | |
| 1084 | MSTORE | |
| 1085 | PUSH2 | 0x0120 |
| 1088 | DUP9 | |
| 1089 | ADD | |
| 108a | MSTORE | |
| 108b | DUP1 | |
| 108c | DUP3 | |
| 108d | AND | |
| 108e | PUSH2 | 0x0140 |
| 1091 | DUP9 | |
| 1092 | ADD | |
| 1093 | MSTORE | |
| 1094 | PUSH1 | 0x01 |
| 1096 | PUSH1 | 0x01 |
| 1098 | PUSH1 | 0x40 |
| 109a | SHL | |
| 109b | SUB | |
| 109c | DUP2 | |
| 109d | DUP5 | |
| 109e | SHR | |
| 109f | DUP2 | |
| 10a0 | AND | |
| 10a1 | PUSH2 | 0x0160 |
| 10a4 | DUP10 | |
| 10a5 | ADD | |
| 10a6 | MSTORE | |
| 10a7 | PUSH4 | 0xffffffff |
| 10ac | PUSH1 | 0x48 |
| 10ae | DUP4 | |
| 10af | SWAP1 | |
| 10b0 | SHR | |
| 10b1 | AND | |
| 10b2 | PUSH2 | 0x0180 |
| 10b5 | DUP10 | |
| 10b6 | ADD | |
| 10b7 | MSTORE | |
| 10b8 | PUSH2 | 0xffff |
| 10bb | PUSH1 | 0x68 |
| 10bd | DUP4 | |
| 10be | SWAP1 | |
| 10bf | SHR | |
| 10c0 | DUP2 | |
| 10c1 | AND | |
| 10c2 | PUSH2 | 0x01a0 |
| 10c5 | DUP11 | |
| 10c6 | ADD | |
| 10c7 | MSTORE | |
| 10c8 | PUSH1 | 0x78 |
| 10ca | SWAP3 | |
| 10cb | SWAP1 | |
| 10cc | SWAP3 | |
| 10cd | SHR | |
| 10ce | SWAP1 | |
| 10cf | SWAP2 | |
| 10d0 | AND | |
| 10d1 | PUSH2 | 0x01c0 |
| 10d4 | DUP9 | |
| 10d5 | ADD | |
| 10d6 | MSTORE | |
| 10d7 | PUSH2 | 0x01e0 |
| 10da | DUP8 | |
| 10db | ADD | |
| 10dc | SWAP4 | |
| 10dd | SWAP1 | |
| 10de | SWAP4 | |
| 10df | MSTORE | |
| 10e0 | SWAP3 | |
| 10e1 | DUP4 | |
| 10e2 | AND | |
| 10e3 | PUSH2 | 0x0200 |
| 10e6 | DUP7 | |
| 10e7 | ADD | |
| 10e8 | MSTORE | |
| 10e9 | PUSH2 | 0x0220 |
| 10ec | DUP6 | |
| 10ed | ADD | |
| 10ee | SWAP4 | |
| 10ef | SWAP1 | |
| 10f0 | SWAP4 | |
| 10f1 | MSTORE | |
| 10f2 | PUSH2 | 0x0240 |
| 10f5 | DUP5 | |
| 10f6 | ADD | |
| 10f7 | SWAP7 | |
| 10f8 | SWAP1 | |
| 10f9 | SWAP7 | |
| 10fa | MSTORE | |
| 10fb | DUP5 | |
| 10fc | DUP2 | |
| 10fd | AND | |
| 10fe | PUSH2 | 0x0260 |
| 1101 | DUP5 | |
| 1102 | ADD | |
| 1103 | MSTORE | |
| 1104 | PUSH1 | 0xff |
| 1106 | PUSH1 | 0x90 |
| 1108 | SHL | |
| 1109 | NOT | |
| 110a | SWAP1 | |
| 110b | SWAP5 | |
| 110c | AND | |
| 110d | PUSH1 | 0x90 |
| 110f | SWAP4 | |
| 1110 | DUP5 | |
| 1111 | SHL | |
| 1112 | PUSH1 | 0xff |
| 1114 | PUSH1 | 0x90 |
| 1116 | SHL | |
| 1117 | AND | |
| 1118 | OR | |
| 1119 | SWAP1 | |
| 111a | DUP2 | |
| 111b | SWAP1 | |
| 111c | SHR | |
| 111d | DUP5 | |
| 111e | AND | |
| 111f | ISZERO | |
| 1120 | ISZERO | |
| 1121 | PUSH2 | 0x0280 |
| 1124 | DUP4 | |
| 1125 | ADD | |
| 1126 | MSTORE | |
| 1127 | PUSH1 | 0x10 |
| 1129 | DUP2 | |
| 112a | SWAP1 | |
| 112b | SHR | |
| 112c | DUP6 | |
| 112d | AND | |
| 112e | PUSH2 | 0x02a0 |
| 1131 | DUP4 | |
| 1132 | ADD | |
| 1133 | MSTORE | |
| 1134 | PUSH1 | 0x50 |
| 1136 | DUP2 | |
| 1137 | SWAP1 | |
| 1138 | SHR | |
| 1139 | SWAP1 | |
| 113a | SWAP5 | |
| 113b | AND | |
| 113c | PUSH2 | 0x02c0 |
| 113f | DUP3 | |
| 1140 | ADD | |
| 1141 | MSTORE | |
| 1142 | SWAP3 | |
| 1143 | SWAP1 | |
| 1144 | SHR | |
| 1145 | AND | |
| 1146 | PUSH2 | 0x02e0 |
| 1149 | DUP1 | |
| 114a | DUP4 | |
| 114b | ADD | |
| 114c | SWAP2 | |
| 114d | SWAP1 | |
| 114e | SWAP2 | |
| 114f | MSTORE | |
| 1150 | DUP2 | |
| 1151 | MSTORE | |
| 1152 | PUSH2 | 0x115d |
| 1155 | PUSH2 | 0x0300 |
| 1158 | DUP3 | |
| 1159 | PUSH2 | 0x5507 |
| 115c | JUMP | |
| 115d | JUMPDEST | |
| 115e | DUP1 | |
| 115f | MLOAD | |
| 1160 | SWAP1 | |
| 1161 | PUSH1 | 0x20 |
| 1163 | ADD | |
| 1164 | KECCAK256 | |
| 1165 | PUSH2 | 0x116e |
| 1168 | DUP3 | |
| 1169 | DUP7 | |
| 116a | PUSH2 | 0x587b |
| 116d | JUMP | |
| 116e | JUMPDEST | |
| 116f | MSTORE | |
| 1170 | PUSH2 | 0x1178 |
| 1173 | SWAP1 | |
| 1174 | PUSH2 | 0x59e0 |
| 1177 | JUMP | |
| 1178 | JUMPDEST | |
| 1179 | SWAP4 | |
| 117a | DUP6 | |
| 117b | PUSH2 | 0x118f |
| 117e | PUSH2 | 0x0244 |
| 1181 | PUSH1 | 0x04 |
| 1183 | CALLDATALOAD | |
| 1184 | PUSH2 | 0x02a0 |
| 1187 | DUP6 | |
| 1188 | MUL | |
| 1189 | ADD | |
| 118a | ADD | |
| 118b | PUSH2 | 0x59c5 |
| 118e | JUMP | |
| 118f | JUMPDEST | |
| 1190 | PUSH1 | 0x40 |
| 1192 | DUP1 | |
| 1193 | MLOAD | |
| 1194 | SWAP2 | |
| 1195 | ISZERO | |
| 1196 | ISZERO | |
| 1197 | DUP3 | |
| 1198 | MSTORE | |
| 1199 | PUSH1 | 0x01 |
| 119b | PUSH1 | 0x01 |
| 119d | PUSH1 | 0x40 |
| 119f | SHL | |
| 11a0 | SUB | |
| 11a1 | SWAP3 | |
| 11a2 | SWAP1 | |
| 11a3 | SWAP3 | |
| 11a4 | AND | |
| 11a5 | PUSH1 | 0x20 |
| 11a7 | DUP3 | |
| 11a8 | ADD | |
| 11a9 | MSTORE | |
| 11aa | PUSH2 | 0x02a0 |
| 11ad | DUP4 | |
| 11ae | MUL | |
| 11af | PUSH1 | 0x04 |
| 11b1 | CALLDATALOAD | |
| 11b2 | ADD | |
| 11b3 | PUSH1 | 0x24 |
| 11b5 | ADD | |
| 11b6 | CALLDATALOAD | |
| 11b7 | SWAP2 | |
| 11b8 | PUSH32 | 0x523b72ac5fbe8982a57ea63c8ca4daf1602c22eda42d6cc14a884c0310698b80 |
| 11d9 | SWAP2 | |
| 11da | LOG2 | |
| 11db | PUSH1 | 0x01 |
| 11dd | ADD | |
| 11de | SWAP4 | |
| 11df | PUSH2 | 0x0c1e |
| 11e2 | JUMP | |
| 11e3 | JUMPDEST | |
| 11e4 | PUSH1 | 0x04 |
| 11e6 | DUP1 | |
| 11e7 | CALLDATALOAD | |
| 11e8 | PUSH2 | 0x02a0 |
| 11eb | DUP8 | |
| 11ec | MUL | |
| 11ed | ADD | |
| 11ee | PUSH1 | 0x24 |
| 11f0 | ADD | |
| 11f1 | CALLDATALOAD | |
| 11f2 | DUP5 | |
| 11f3 | MSTORE | |
| 11f4 | PUSH1 | 0x06 |
| 11f6 | PUSH1 | 0x20 |
| 11f8 | MSTORE | |
| 11f9 | PUSH1 | 0x40 |
| 11fb | DUP5 | |
| 11fc | KECCAK256 | |
| 11fd | DUP1 | |
| 11fe | SLOAD | |
| 11ff | PUSH1 | 0xff |
| 1201 | NOT | |
| 1202 | AND | |
| 1203 | PUSH1 | 0x01 |
| 1205 | OR | |
| 1206 | SWAP1 | |
| 1207 | SSTORE | |
| 1208 | SLOAD | |
| 1209 | PUSH1 | 0x01 |
| 120b | PUSH1 | 0x40 |
| 120d | SHL | |
| 120e | DUP2 | |
| 120f | LT | |
| 1210 | ISZERO | |
| 1211 | PUSH2 | 0x1255 |
| 1214 | JUMPI | |
| 1215 | PUSH2 | 0x1229 |
| 1218 | DUP2 | |
| 1219 | PUSH1 | 0x01 |
| 121b | PUSH2 | 0x124e |
| 121e | SWAP4 | |
| 121f | ADD | |
| 1220 | PUSH1 | 0x04 |
| 1222 | SSTORE | |
| 1223 | PUSH1 | 0x04 |
| 1225 | PUSH2 | 0x5823 |
| 1228 | JUMP | |
| 1229 | JUMPDEST | |
| 122a | PUSH1 | 0x24 |
| 122c | PUSH2 | 0x02a0 |
| 122f | DUP10 | |
| 1230 | SWAP5 | |
| 1231 | SWAP4 | |
| 1232 | SWAP5 | |
| 1233 | MUL | |
| 1234 | PUSH1 | 0x04 |
| 1236 | CALLDATALOAD | |
| 1237 | ADD | |
| 1238 | ADD | |
| 1239 | CALLDATALOAD | |
| 123a | SWAP1 | |
| 123b | DUP4 | |
| 123c | SLOAD | |
| 123d | SWAP1 | |
| 123e | PUSH1 | 0x03 |
| 1240 | SHL | |
| 1241 | SWAP2 | |
| 1242 | DUP3 | |
| 1243 | SHL | |
| 1244 | SWAP2 | |
| 1245 | PUSH0 | |
| 1246 | NOT | |
| 1247 | SWAP1 | |
| 1248 | SHL | |
| 1249 | NOT | |
| 124a | AND | |
| 124b | OR | |
| 124c | SWAP1 | |
| 124d | JUMP | |
| 124e | JUMPDEST | |
| 124f | SWAP1 | |
| 1250 | SSTORE | |
| 1251 | PUSH2 | 0x0d31 |
| 1254 | JUMP | |
| 1255 | JUMPDEST | |
| 1256 | PUSH4 | 0x4e487b71 |
| 125b | PUSH1 | 0xe0 |
| 125d | SHL | |
| 125e | DUP5 | |
| 125f | MSTORE | |
| 1260 | PUSH1 | 0x41 |
| 1262 | PUSH1 | 0x04 |
| 1264 | MSTORE | |
| 1265 | PUSH1 | 0x24 |
| 1267 | DUP5 | |
| 1268 | REVERT | |
| 1269 | JUMPDEST | |
| 126a | PUSH4 | 0x3f4b99b1 |
| 126f | PUSH1 | 0xe2 |
| 1271 | SHL | |
| 1272 | DUP4 | |
| 1273 | MSTORE | |
| 1274 | PUSH1 | 0x04 |
| 1276 | DUP1 | |
| 1277 | CALLDATALOAD | |
| 1278 | PUSH2 | 0x02a0 |
| 127b | DUP8 | |
| 127c | MUL | |
| 127d | ADD | |
| 127e | PUSH2 | 0x01e4 |
| 1281 | ADD | |
| 1282 | CALLDATALOAD | |
| 1283 | SWAP1 | |
| 1284 | MSTORE | |
| 1285 | PUSH1 | 0x24 |
| 1287 | DUP4 | |
| 1288 | REVERT | |
| 1289 | JUMPDEST | |
| 128a | POP | |
| 128b | PUSH2 | 0x01e4 |
| 128e | PUSH2 | 0x02a0 |
| 1291 | DUP7 | |
| 1292 | MUL | |
| 1293 | PUSH1 | 0x04 |
| 1295 | CALLDATALOAD | |
| 1296 | ADD | |
| 1297 | ADD | |
| 1298 | CALLDATALOAD | |
| 1299 | DUP4 | |
| 129a | MSTORE | |
| 129b | PUSH1 | 0x06 |
| 129d | PUSH1 | 0x20 |
| 129f | MSTORE | |
| 12a0 | PUSH1 | 0xff |
| 12a2 | PUSH1 | 0x40 |
| 12a4 | DUP5 | |
| 12a5 | KECCAK256 | |
| 12a6 | SLOAD | |
| 12a7 | AND | |
| 12a8 | ISZERO | |
| 12a9 | PUSH2 | 0x0d0b |
| 12ac | JUMP | |
| 12ad | JUMPDEST | |
| 12ae | PUSH1 | 0x44 |
| 12b0 | DUP4 | |
| 12b1 | PUSH1 | 0xff |
| 12b3 | DUP8 | |
| 12b4 | PUSH1 | 0x24 |
| 12b6 | PUSH2 | 0x02a0 |
| 12b9 | PUSH2 | 0x12cb |
| 12bc | PUSH2 | 0x0224 |
| 12bf | DUP3 | |
| 12c0 | DUP6 | |
| 12c1 | MUL | |
| 12c2 | PUSH1 | 0x04 |
| 12c4 | CALLDATALOAD | |
| 12c5 | ADD | |
| 12c6 | ADD | |
| 12c7 | PUSH2 | 0x59d2 |
| 12ca | JUMP | |
| 12cb | JUMPDEST | |
| 12cc | SWAP3 | |
| 12cd | PUSH4 | 0x1e9a7d75 |
| 12d2 | PUSH1 | 0xe0 |
| 12d4 | SHL | |
| 12d5 | DUP7 | |
| 12d6 | MSTORE | |
| 12d7 | MUL | |
| 12d8 | PUSH1 | 0x04 |
| 12da | CALLDATALOAD | |
| 12db | ADD | |
| 12dc | ADD | |
| 12dd | CALLDATALOAD | |
| 12de | PUSH1 | 0x04 |
| 12e0 | MSTORE | |
| 12e1 | AND | |
| 12e2 | PUSH1 | 0x24 |
| 12e4 | MSTORE | |
| 12e5 | REVERT | |
| 12e6 | JUMPDEST | |
| 12e7 | POP | |
| 12e8 | PUSH1 | 0x01 |
| 12ea | PUSH1 | 0xff |
| 12ec | PUSH2 | 0x1300 |
| 12ef | PUSH2 | 0x0224 |
| 12f2 | PUSH2 | 0x02a0 |
| 12f5 | DUP10 | |
| 12f6 | MUL | |
| 12f7 | PUSH1 | 0x04 |
| 12f9 | CALLDATALOAD | |
| 12fa | ADD | |
| 12fb | ADD | |
| 12fc | PUSH2 | 0x59d2 |
| 12ff | JUMP | |
| 1300 | JUMPDEST | |
| 1301 | AND | |
| 1302 | EQ | |
| 1303 | ISZERO | |
| 1304 | PUSH2 | 0x0cf1 |
| 1307 | JUMP | |
| 1308 | JUMPDEST | |
| 1309 | PUSH1 | 0x24 |
| 130b | DUP4 | |
| 130c | DUP2 | |
| 130d | PUSH2 | 0x02a0 |
| 1310 | DUP9 | |
| 1311 | PUSH4 | 0x2eb72ead |
| 1316 | PUSH1 | 0xe1 |
| 1318 | SHL | |
| 1319 | DUP5 | |
| 131a | MSTORE | |
| 131b | MUL | |
| 131c | PUSH1 | 0x04 |
| 131e | CALLDATALOAD | |
| 131f | ADD | |
| 1320 | ADD | |
| 1321 | CALLDATALOAD | |
| 1322 | PUSH1 | 0x04 |
| 1324 | MSTORE | |
| 1325 | REVERT | |
| 1326 | JUMPDEST | |
| 1327 | POP | |
| 1328 | PUSH1 | 0x84 |
| 132a | PUSH2 | 0x02a0 |
| 132d | DUP7 | |
| 132e | MUL | |
| 132f | PUSH1 | 0x04 |
| 1331 | CALLDATALOAD | |
| 1332 | ADD | |
| 1333 | ADD | |
| 1334 | CALLDATALOAD | |
| 1335 | ISZERO | |
| 1336 | PUSH2 | 0x0cb8 |
| 1339 | JUMP | |
| 133a | JUMPDEST | |
| 133b | POP | |
| 133c | PUSH1 | 0xff |
| 133e | PUSH2 | 0x1352 |
| 1341 | PUSH2 | 0x02a4 |
| 1344 | PUSH2 | 0x02a0 |
| 1347 | DUP9 | |
| 1348 | MUL | |
| 1349 | PUSH1 | 0x04 |
| 134b | CALLDATALOAD | |
| 134c | ADD | |
| 134d | ADD | |
| 134e | PUSH2 | 0x59d2 |
| 1351 | JUMP | |
| 1352 | JUMPDEST | |
| 1353 | AND | |
| 1354 | ISZERO | |
| 1355 | PUSH2 | 0x0cb2 |
| 1358 | JUMP | |
| 1359 | JUMPDEST | |
| 135a | PUSH1 | 0x44 |
| 135c | DUP4 | |
| 135d | PUSH1 | 0xff |
| 135f | DUP8 | |
| 1360 | PUSH1 | 0x24 |
| 1362 | PUSH2 | 0x02a0 |
| 1365 | PUSH2 | 0x1377 |
| 1368 | PUSH2 | 0x02a4 |
| 136b | DUP3 | |
| 136c | DUP6 | |
| 136d | MUL | |
| 136e | PUSH1 | 0x04 |
| 1370 | CALLDATALOAD | |
| 1371 | ADD | |
| 1372 | ADD | |
| 1373 | PUSH2 | 0x59d2 |
| 1376 | JUMP | |
| 1377 | JUMPDEST | |
| 1378 | SWAP3 | |
| 1379 | PUSH4 | 0x1a7ce1dd |
| 137e | PUSH1 | 0xe0 |
| 1380 | SHL | |
| 1381 | DUP7 | |
| 1382 | MSTORE | |
| 1383 | MUL | |
| 1384 | PUSH1 | 0x04 |
| 1386 | CALLDATALOAD | |
| 1387 | ADD | |
| 1388 | ADD | |
| 1389 | CALLDATALOAD | |
| 138a | PUSH1 | 0x04 |
| 138c | MSTORE | |
| 138d | AND | |
| 138e | PUSH1 | 0x24 |
| 1390 | MSTORE | |
| 1391 | REVERT | |
| 1392 | JUMPDEST | |
| 1393 | PUSH1 | 0x24 |
| 1395 | DUP4 | |
| 1396 | DUP2 | |
| 1397 | PUSH2 | 0x02a0 |
| 139a | DUP9 | |
| 139b | PUSH4 | 0x3dc2fc11 |
| 13a0 | PUSH1 | 0xe0 |
| 13a2 | SHL | |
| 13a3 | DUP5 | |
| 13a4 | MSTORE | |
| 13a5 | MUL | |
| 13a6 | PUSH1 | 0x04 |
| 13a8 | CALLDATALOAD | |
| 13a9 | ADD | |
| 13aa | ADD | |
| 13ab | CALLDATALOAD | |
| 13ac | PUSH1 | 0x04 |
| 13ae | MSTORE | |
| 13af | REVERT | |
| 13b0 | JUMPDEST | |
| 13b1 | POP | |
| 13b2 | PUSH4 | 0xffffffff |
| 13b7 | PUSH2 | 0x13cb |
| 13ba | PUSH2 | 0x0144 |
| 13bd | PUSH2 | 0x02a0 |
| 13c0 | DUP9 | |
| 13c1 | MUL | |
| 13c2 | PUSH1 | 0x04 |
| 13c4 | CALLDATALOAD | |
| 13c5 | ADD | |
| 13c6 | ADD | |
| 13c7 | PUSH2 | 0x59fd |
| 13ca | JUMP | |
| 13cb | JUMPDEST | |
| 13cc | AND | |
| 13cd | ISZERO | |
| 13ce | DUP1 | |
| 13cf | ISZERO | |
| 13d0 | PUSH2 | 0x141e |
| 13d3 | JUMPI | |
| 13d4 | JUMPDEST | |
| 13d5 | DUP1 | |
| 13d6 | ISZERO | |
| 13d7 | PUSH2 | 0x13ff |
| 13da | JUMPI | |
| 13db | JUMPDEST | |
| 13dc | DUP1 | |
| 13dd | PUSH2 | 0x0c74 |
| 13e0 | JUMPI | |
| 13e1 | POP | |
| 13e2 | PUSH1 | 0xff |
| 13e4 | PUSH2 | 0x13f8 |
| 13e7 | PUSH2 | 0x0224 |
| 13ea | PUSH2 | 0x02a0 |
| 13ed | DUP9 | |
| 13ee | MUL | |
| 13ef | PUSH1 | 0x04 |
| 13f1 | CALLDATALOAD | |
| 13f2 | ADD | |
| 13f3 | ADD | |
| 13f4 | PUSH2 | 0x59d2 |
| 13f7 | JUMP | |
| 13f8 | JUMPDEST | |
| 13f9 | AND | |
| 13fa | ISZERO | |
| 13fb | PUSH2 | 0x0c74 |
| 13fe | JUMP | |
| 13ff | JUMPDEST | |
| 1400 | POP | |
| 1401 | PUSH1 | 0xff |
| 1403 | PUSH2 | 0x1417 |
| 1406 | PUSH2 | 0x01c4 |
| 1409 | PUSH2 | 0x02a0 |
| 140c | DUP9 | |
| 140d | MUL | |
| 140e | PUSH1 | 0x04 |
| 1410 | CALLDATALOAD | |
| 1411 | ADD | |
| 1412 | ADD | |
| 1413 | PUSH2 | 0x59d2 |
| 1416 | JUMP | |
| 1417 | JUMPDEST | |
| 1418 | AND | |
| 1419 | ISZERO | |
| 141a | PUSH2 | 0x13db |
| 141d | JUMP | |
| 141e | JUMPDEST | |
| 141f | POP | |
| 1420 | PUSH1 | 0xff |
| 1422 | PUSH2 | 0x1436 |
| 1425 | PUSH2 | 0x0104 |
| 1428 | PUSH2 | 0x02a0 |
| 142b | DUP9 | |
| 142c | MUL | |
| 142d | PUSH1 | 0x04 |
| 142f | CALLDATALOAD | |
| 1430 | ADD | |
| 1431 | ADD | |
| 1432 | PUSH2 | 0x59d2 |
| 1435 | JUMP | |
| 1436 | JUMPDEST | |
| 1437 | AND | |
| 1438 | ISZERO | |
| 1439 | PUSH2 | 0x13d4 |
| 143c | JUMP | |
| 143d | JUMPDEST | |
| 143e | PUSH1 | 0x44 |
| 1440 | DUP4 | |
| 1441 | DUP7 | |
| 1442 | PUSH1 | 0x24 |
| 1444 | PUSH2 | 0x02a0 |
| 1447 | PUSH2 | 0x145e |
| 144a | PUSH1 | 0x04 |
| 144c | CALLDATALOAD | |
| 144d | DUP3 | |
| 144e | DUP6 | |
| 144f | MUL | |
| 1450 | ADD | |
| 1451 | PUSH1 | 0x64 |
| 1453 | DUP2 | |
| 1454 | ADD | |
| 1455 | CALLDATALOAD | |
| 1456 | SWAP1 | |
| 1457 | DUP8 | |
| 1458 | ADD | |
| 1459 | CALLDATALOAD | |
| 145a | PUSH2 | 0x6008 |
| 145d | JUMP | |
| 145e | JUMPDEST | |
| 145f | SWAP3 | |
| 1460 | PUSH4 | 0x1a35ac99 |
| 1465 | PUSH1 | 0xe0 |
| 1467 | SHL | |
| 1468 | DUP6 | |
| 1469 | MSTORE | |
| 146a | MUL | |
| 146b | PUSH1 | 0x04 |
| 146d | CALLDATALOAD | |
| 146e | ADD | |
| 146f | ADD | |
| 1470 | CALLDATALOAD | |
| 1471 | PUSH1 | 0x04 |
| 1473 | MSTORE | |
| 1474 | PUSH1 | 0x24 |
| 1476 | MSTORE | |
| 1477 | REVERT | |
| 1478 | JUMPDEST | |
| 1479 | SWAP2 | |
| 147a | SWAP4 | |
| 147b | POP | |
| 147c | SWAP4 | |
| 147d | SWAP5 | |
| 147e | DUP4 | |
| 147f | SWAP2 | |
| 1480 | JUMPDEST | |
| 1481 | DUP6 | |
| 1482 | DUP4 | |
| 1483 | LT | |
| 1484 | ISZERO | |
| 1485 | PUSH2 | 0x1a5e |
| 1488 | JUMPI | |
| 1489 | DUP3 | |
| 148a | PUSH1 | 0x05 |
| 148c | SHL | |
| 148d | DUP3 | |
| 148e | ADD | |
| 148f | CALLDATALOAD | |
| 1490 | PUSH2 | 0x013e |
| 1493 | NOT | |
| 1494 | DUP4 | |
| 1495 | CALLDATASIZE | |
| 1496 | SUB | |
| 1497 | ADD | |
| 1498 | DUP2 | |
| 1499 | SLT | |
| 149a | ISZERO | |
| 149b | PUSH2 | 0x1a5a |
| 149e | JUMPI | |
| 149f | PUSH2 | 0x14ab |
| 14a2 | SWAP1 | |
| 14a3 | CALLDATASIZE | |
| 14a4 | SWAP1 | |
| 14a5 | DUP5 | |
| 14a6 | ADD | |
| 14a7 | PUSH2 | 0x5685 |
| 14aa | JUMP | |
| 14ab | JUMPDEST | |
| 14ac | SWAP3 | |
| 14ad | DUP4 | |
| 14ae | MLOAD | |
| 14af | DUP7 | |
| 14b0 | MSTORE | |
| 14b1 | PUSH1 | 0x06 |
| 14b3 | PUSH1 | 0x20 |
| 14b5 | MSTORE | |
| 14b6 | PUSH1 | 0xff |
| 14b8 | PUSH1 | 0x40 |
| 14ba | DUP8 | |
| 14bb | KECCAK256 | |
| 14bc | SLOAD | |
| 14bd | AND | |
| 14be | ISZERO | |
| 14bf | PUSH2 | 0x1a46 |
| 14c2 | JUMPI | |
| 14c3 | PUSH1 | 0xa0 |
| 14c5 | DUP5 | |
| 14c6 | ADD | |
| 14c7 | MLOAD | |
| 14c8 | PUSH1 | 0xec |
| 14ca | DUP2 | |
| 14cb | AND | |
| 14cc | PUSH2 | 0x1a31 |
| 14cf | JUMPI | |
| 14d0 | POP | |
| 14d1 | PUSH4 | 0xffffffff |
| 14d6 | PUSH1 | 0xc0 |
| 14d8 | DUP6 | |
| 14d9 | ADD | |
| 14da | MLOAD | |
| 14db | AND | |
| 14dc | ISZERO | |
| 14dd | PUSH2 | 0x1a03 |
| 14e0 | JUMPI | |
| 14e1 | JUMPDEST | |
| 14e2 | PUSH4 | 0xffffffff |
| 14e7 | PUSH1 | 0xe0 |
| 14e9 | DUP6 | |
| 14ea | ADD | |
| 14eb | MLOAD | |
| 14ec | AND | |
| 14ed | ISZERO | |
| 14ee | PUSH2 | 0x19d4 |
| 14f1 | JUMPI | |
| 14f2 | JUMPDEST | |
| 14f3 | PUSH1 | 0x01 |
| 14f5 | PUSH1 | 0x01 |
| 14f7 | PUSH1 | 0x40 |
| 14f9 | SHL | |
| 14fa | SUB | |
| 14fb | DUP9 | |
| 14fc | AND | |
| 14fd | PUSH2 | 0x0120 |
| 1500 | DUP6 | |
| 1501 | ADD | |
| 1502 | MSTORE | |
| 1503 | PUSH2 | 0x1512 |
| 1506 | DUP5 | |
| 1507 | MLOAD | |
| 1508 | PUSH1 | 0x20 |
| 150a | DUP7 | |
| 150b | ADD | |
| 150c | MLOAD | |
| 150d | SWAP1 | |
| 150e | PUSH2 | 0x5bf0 |
| 1511 | JUMP | |
| 1512 | JUMPDEST | |
| 1513 | DUP1 | |
| 1514 | DUP8 | |
| 1515 | MSTORE | |
| 1516 | PUSH1 | 0x09 |
| 1518 | PUSH1 | 0x20 |
| 151a | MSTORE | |
| 151b | PUSH1 | 0xff |
| 151d | PUSH1 | 0x40 |
| 151f | DUP9 | |
| 1520 | KECCAK256 | |
| 1521 | SLOAD | |
| 1522 | AND | |
| 1523 | ISZERO | |
| 1524 | PUSH2 | 0x1967 |
| 1527 | JUMPI | |
| 1528 | JUMPDEST | |
| 1529 | DUP1 | |
| 152a | DUP8 | |
| 152b | MSTORE | |
| 152c | PUSH1 | 0x08 |
| 152e | PUSH1 | 0x20 |
| 1530 | MSTORE | |
| 1531 | PUSH1 | 0x40 |
| 1533 | DUP8 | |
| 1534 | KECCAK256 | |
| 1535 | SWAP3 | |
| 1536 | DUP6 | |
| 1537 | MLOAD | |
| 1538 | DUP5 | |
| 1539 | SSTORE | |
| 153a | PUSH1 | 0x20 |
| 153c | DUP7 | |
| 153d | ADD | |
| 153e | MLOAD | |
| 153f | PUSH1 | 0x01 |
| 1541 | DUP6 | |
| 1542 | ADD | |
| 1543 | SSTORE | |
| 1544 | PUSH1 | 0xff |
| 1546 | PUSH1 | 0x40 |
| 1548 | DUP8 | |
| 1549 | ADD | |
| 154a | MLOAD | |
| 154b | AND | |
| 154c | PUSH1 | 0xff |
| 154e | PUSH1 | 0x02 |
| 1550 | DUP7 | |
| 1551 | ADD | |
| 1552 | SWAP2 | |
| 1553 | AND | |
| 1554 | PUSH1 | 0xff |
| 1556 | NOT | |
| 1557 | DUP3 | |
| 1558 | SLOAD | |
| 1559 | AND | |
| 155a | OR | |
| 155b | SWAP1 | |
| 155c | SSTORE | |
| 155d | PUSH1 | 0x60 |
| 155f | DUP7 | |
| 1560 | ADD | |
| 1561 | MLOAD | |
| 1562 | DUP1 | |
| 1563 | MLOAD | |
| 1564 | SWAP1 | |
| 1565 | PUSH1 | 0x01 |
| 1567 | PUSH1 | 0x01 |
| 1569 | PUSH1 | 0x40 |
| 156b | SHL | |
| 156c | SUB | |
| 156d | DUP3 | |
| 156e | GT | |
| 156f | PUSH2 | 0x1882 |
| 1572 | JUMPI | |
| 1573 | DUP2 | |
| 1574 | SWAP1 | |
| 1575 | PUSH2 | 0x1581 |
| 1578 | PUSH1 | 0x03 |
| 157a | DUP9 | |
| 157b | ADD | |
| 157c | SLOAD | |
| 157d | PUSH2 | 0x5d56 |
| 1580 | JUMP | |
| 1581 | JUMPDEST | |
| 1582 | PUSH1 | 0x1f |
| 1584 | DUP2 | |
| 1585 | GT | |
| 1586 | PUSH2 | 0x1909 |
| 1589 | JUMPI | |
| 158a | JUMPDEST | |
| 158b | POP | |
| 158c | PUSH1 | 0x20 |
| 158e | SWAP1 | |
| 158f | PUSH1 | 0x1f |
| 1591 | DUP4 | |
| 1592 | GT | |
| 1593 | PUSH1 | 0x01 |
| 1595 | EQ | |
| 1596 | PUSH2 | 0x18a1 |
| 1599 | JUMPI | |
| 159a | DUP12 | |
| 159b | SWAP3 | |
| 159c | PUSH2 | 0x1896 |
| 159f | JUMPI | |
| 15a0 | JUMPDEST | |
| 15a1 | POP | |
| 15a2 | POP | |
| 15a3 | DUP2 | |
| 15a4 | PUSH1 | 0x01 |
| 15a6 | SHL | |
| 15a7 | SWAP2 | |
| 15a8 | PUSH0 | |
| 15a9 | NOT | |
| 15aa | SWAP1 | |
| 15ab | PUSH1 | 0x03 |
| 15ad | SHL | |
| 15ae | SHR | |
| 15af | NOT | |
| 15b0 | AND | |
| 15b1 | OR | |
| 15b2 | PUSH1 | 0x03 |
| 15b4 | DUP6 | |
| 15b5 | ADD | |
| 15b6 | SSTORE | |
| 15b7 | JUMPDEST | |
| 15b8 | PUSH1 | 0x80 |
| 15ba | DUP7 | |
| 15bb | ADD | |
| 15bc | MLOAD | |
| 15bd | DUP1 | |
| 15be | MLOAD | |
| 15bf | SWAP1 | |
| 15c0 | PUSH1 | 0x01 |
| 15c2 | PUSH1 | 0x01 |
| 15c4 | PUSH1 | 0x40 |
| 15c6 | SHL | |
| 15c7 | SUB | |
| 15c8 | DUP3 | |
| 15c9 | GT | |
| 15ca | PUSH2 | 0x1882 |
| 15cd | JUMPI | |
| 15ce | DUP8 | |
| 15cf | SWAP3 | |
| 15d0 | SWAP2 | |
| 15d1 | DUP10 | |
| 15d2 | SWAP2 | |
| 15d3 | PUSH2 | 0x15df |
| 15d6 | PUSH1 | 0x04 |
| 15d8 | DUP10 | |
| 15d9 | ADD | |
| 15da | SLOAD | |
| 15db | PUSH2 | 0x5d56 |
| 15de | JUMP | |
| 15df | JUMPDEST | |
| 15e0 | PUSH1 | 0x1f |
| 15e2 | DUP2 | |
| 15e3 | GT | |
| 15e4 | PUSH2 | 0x1819 |
| 15e7 | JUMPI | |
| 15e8 | JUMPDEST | |
| 15e9 | POP | |
| 15ea | PUSH1 | 0x20 |
| 15ec | SWAP1 | |
| 15ed | DUP13 | |
| 15ee | PUSH1 | 0x1f |
| 15f0 | DUP5 | |
| 15f1 | GT | |
| 15f2 | PUSH1 | 0x01 |
| 15f4 | EQ | |
| 15f5 | PUSH2 | 0x1771 |
| 15f8 | JUMPI | |
| 15f9 | SWAP4 | |
| 15fa | PUSH2 | 0x172b |
| 15fd | DUP7 | |
| 15fe | PUSH2 | 0x1725 |
| 1601 | PUSH1 | 0x05 |
| 1603 | PUSH1 | 0x01 |
| 1605 | SWAP14 | |
| 1606 | DUP9 | |
| 1607 | PUSH1 | 0xa0 |
| 1609 | SWAP10 | |
| 160a | PUSH2 | 0x1731 |
| 160d | SWAP10 | |
| 160e | PUSH32 | 0xed8946197b4bd5be9ac502b0c187bc46b61770261216a2d4f6b7d01562ef1b2f |
| 162f | SWAP15 | |
| 1630 | SWAP14 | |
| 1631 | SWAP10 | |
| 1632 | PUSH1 | 0xff |
| 1634 | SWAP14 | |
| 1635 | SWAP3 | |
| 1636 | PUSH2 | 0x1766 |
| 1639 | JUMPI | |
| 163a | JUMPDEST | |
| 163b | POP | |
| 163c | POP | |
| 163d | PUSH1 | 0x01 |
| 163f | DUP3 | |
| 1640 | SWAP1 | |
| 1641 | SHL | |
| 1642 | SWAP2 | |
| 1643 | PUSH0 | |
| 1644 | NOT | |
| 1645 | SWAP1 | |
| 1646 | PUSH1 | 0x03 |
| 1648 | SHL | |
| 1649 | SHR | |
| 164a | NOT | |
| 164b | AND | |
| 164c | OR | |
| 164d | PUSH1 | 0x04 |
| 164f | DUP3 | |
| 1650 | ADD | |
| 1651 | SSTORE | |
| 1652 | JUMPDEST | |
| 1653 | ADD | |
| 1654 | SWAP9 | |
| 1655 | DUP9 | |
| 1656 | DUP1 | |
| 1657 | DUP10 | |
| 1658 | DUP4 | |
| 1659 | ADD | |
| 165a | MLOAD | |
| 165b | AND | |
| 165c | AND | |
| 165d | DUP10 | |
| 165e | NOT | |
| 165f | DUP12 | |
| 1660 | SLOAD | |
| 1661 | AND | |
| 1662 | OR | |
| 1663 | DUP11 | |
| 1664 | SSTORE | |
| 1665 | PUSH2 | 0x168f |
| 1668 | PUSH4 | 0xffffffff |
| 166d | PUSH1 | 0xc0 |
| 166f | DUP4 | |
| 1670 | ADD | |
| 1671 | MLOAD | |
| 1672 | AND | |
| 1673 | DUP12 | |
| 1674 | SWAP1 | |
| 1675 | PUSH5 | 0xffffffff00 |
| 167b | DUP3 | |
| 167c | SLOAD | |
| 167d | SWAP2 | |
| 167e | PUSH1 | 0x08 |
| 1680 | SHL | |
| 1681 | AND | |
| 1682 | SWAP1 | |
| 1683 | PUSH5 | 0xffffffff00 |
| 1689 | NOT | |
| 168a | AND | |
| 168b | OR | |
| 168c | SWAP1 | |
| 168d | SSTORE | |
| 168e | JUMP | |
| 168f | JUMPDEST | |
| 1690 | PUSH1 | 0xe0 |
| 1692 | DUP2 | |
| 1693 | ADD | |
| 1694 | MLOAD | |
| 1695 | DUP11 | |
| 1696 | SLOAD | |
| 1697 | PUSH9 | 0xffffffff0000000000 |
| 16a1 | NOT | |
| 16a2 | AND | |
| 16a3 | PUSH1 | 0x28 |
| 16a5 | SWAP2 | |
| 16a6 | SWAP1 | |
| 16a7 | SWAP2 | |
| 16a8 | SHL | |
| 16a9 | PUSH9 | 0xffffffff0000000000 |
| 16b3 | AND | |
| 16b4 | OR | |
| 16b5 | DUP11 | |
| 16b6 | SSTORE | |
| 16b7 | PUSH2 | 0x0100 |
| 16ba | DUP2 | |
| 16bb | ADD | |
| 16bc | DUP1 | |
| 16bd | MLOAD | |
| 16be | DUP12 | |
| 16bf | SLOAD | |
| 16c0 | PUSH2 | 0x0120 |
| 16c3 | DUP5 | |
| 16c4 | ADD | |
| 16c5 | MLOAD | |
| 16c6 | PUSH18 | 0xffffffffffffffffff000000000000000000 |
| 16d9 | NOT | |
| 16da | SWAP1 | |
| 16db | SWAP2 | |
| 16dc | AND | |
| 16dd | SWAP2 | |
| 16de | ISZERO | |
| 16df | ISZERO | |
| 16e0 | PUSH1 | 0x48 |
| 16e2 | SHL | |
| 16e3 | PUSH10 | 0xff000000000000000000 |
| 16ee | AND | |
| 16ef | SWAP2 | |
| 16f0 | SWAP1 | |
| 16f1 | SWAP2 | |
| 16f2 | OR | |
| 16f3 | PUSH1 | 0x50 |
| 16f5 | SWAP2 | |
| 16f6 | SWAP1 | |
| 16f7 | SWAP2 | |
| 16f8 | SHL | |
| 16f9 | PUSH8 | 0xffffffffffffffff |
| 1702 | PUSH1 | 0x50 |
| 1704 | SHL | |
| 1705 | AND | |
| 1706 | OR | |
| 1707 | SWAP1 | |
| 1708 | SWAP11 | |
| 1709 | SSTORE | |
| 170a | PUSH2 | 0x1712 |
| 170d | DUP13 | |
| 170e | PUSH2 | 0x5f5c |
| 1711 | JUMP | |
| 1712 | JUMPDEST | |
| 1713 | PUSH2 | 0x171e |
| 1716 | DUP5 | |
| 1717 | PUSH1 | 0x80 |
| 1719 | MLOAD | |
| 171a | PUSH2 | 0x587b |
| 171d | JUMP | |
| 171e | JUMPDEST | |
| 171f | MSTORE | |
| 1720 | DUP12 | |
| 1721 | PUSH2 | 0x656a |
| 1724 | JUMP | |
| 1725 | JUMPDEST | |
| 1726 | SWAP3 | |
| 1727 | PUSH2 | 0x587b |
| 172a | JUMP | |
| 172b | JUMPDEST | |
| 172c | MSTORE | |
| 172d | PUSH2 | 0x59e0 |
| 1730 | JUMP | |
| 1731 | JUMPDEST | |
| 1732 | SWAP9 | |
| 1733 | ADD | |
| 1734 | MLOAD | |
| 1735 | SWAP2 | |
| 1736 | MLOAD | |
| 1737 | PUSH1 | 0x40 |
| 1739 | DUP1 | |
| 173a | MLOAD | |
| 173b | SWAP3 | |
| 173c | SWAP1 | |
| 173d | SWAP4 | |
| 173e | AND | |
| 173f | PUSH1 | 0xff |
| 1741 | AND | |
| 1742 | DUP3 | |
| 1743 | MSTORE | |
| 1744 | ISZERO | |
| 1745 | ISZERO | |
| 1746 | PUSH1 | 0x20 |
| 1748 | DUP3 | |
| 1749 | ADD | |
| 174a | MSTORE | |
| 174b | PUSH1 | 0x01 |
| 174d | PUSH1 | 0x01 |
| 174f | PUSH1 | 0x40 |
| 1751 | SHL | |
| 1752 | SUB | |
| 1753 | DUP13 | |
| 1754 | AND | |
| 1755 | SWAP2 | |
| 1756 | DUP2 | |
| 1757 | ADD | |
| 1758 | SWAP2 | |
| 1759 | SWAP1 | |
| 175a | SWAP2 | |
| 175b | MSTORE | |
| 175c | PUSH1 | 0x60 |
| 175e | SWAP1 | |
| 175f | LOG2 | |
| 1760 | ADD | |
| 1761 | SWAP2 | |
| 1762 | PUSH2 | 0x1480 |
| 1765 | JUMP | |
| 1766 | JUMPDEST | |
| 1767 | ADD | |
| 1768 | MLOAD | |
| 1769 | SWAP1 | |
| 176a | POP | |
| 176b | PUSH0 | |
| 176c | DUP1 | |
| 176d | PUSH2 | 0x163a |
| 1770 | JUMP | |
| 1771 | JUMPDEST | |
| 1772 | POP | |
| 1773 | SWAP1 | |
| 1774 | PUSH1 | 0x04 |
| 1776 | DUP10 | |
| 1777 | ADD | |
| 1778 | DUP14 | |
| 1779 | MSTORE | |
| 177a | DUP1 | |
| 177b | DUP14 | |
| 177c | KECCAK256 | |
| 177d | SWAP2 | |
| 177e | DUP14 | |
| 177f | JUMPDEST | |
| 1780 | PUSH1 | 0x1f |
| 1782 | NOT | |
| 1783 | DUP6 | |
| 1784 | AND | |
| 1785 | DUP2 | |
| 1786 | LT | |
| 1787 | PUSH2 | 0x17fb |
| 178a | JUMPI | |
| 178b | POP | |
| 178c | SWAP4 | |
| 178d | PUSH2 | 0x172b |
| 1790 | DUP7 | |
| 1791 | PUSH2 | 0x1725 |
| 1794 | PUSH1 | 0x05 |
| 1796 | PUSH1 | 0x01 |
| 1798 | SWAP14 | |
| 1799 | DUP15 | |
| 179a | DUP10 | |
| 179b | PUSH2 | 0x1731 |
| 179e | SWAP10 | |
| 179f | PUSH32 | 0xed8946197b4bd5be9ac502b0c187bc46b61770261216a2d4f6b7d01562ef1b2f |
| 17c0 | SWAP15 | |
| 17c1 | SWAP14 | |
| 17c2 | SWAP10 | |
| 17c3 | PUSH1 | 0xff |
| 17c5 | SWAP14 | |
| 17c6 | PUSH1 | 0xa0 |
| 17c8 | SWAP14 | |
| 17c9 | PUSH1 | 0x1f |
| 17cb | NOT | |
| 17cc | DUP2 | |
| 17cd | AND | |
| 17ce | LT | |
| 17cf | PUSH2 | 0x17e3 |
| 17d2 | JUMPI | |
| 17d3 | JUMPDEST | |
| 17d4 | POP | |
| 17d5 | POP | |
| 17d6 | POP | |
| 17d7 | DUP2 | |
| 17d8 | SHL | |
| 17d9 | ADD | |
| 17da | PUSH1 | 0x04 |
| 17dc | DUP3 | |
| 17dd | ADD | |
| 17de | SSTORE | |
| 17df | PUSH2 | 0x1652 |
| 17e2 | JUMP | |
| 17e3 | JUMPDEST | |
| 17e4 | ADD | |
| 17e5 | MLOAD | |
| 17e6 | PUSH0 | |
| 17e7 | NOT | |
| 17e8 | PUSH1 | 0xf8 |
| 17ea | DUP5 | |
| 17eb | PUSH1 | 0x03 |
| 17ed | SHL | |
| 17ee | AND | |
| 17ef | SHR | |
| 17f0 | NOT | |
| 17f1 | AND | |
| 17f2 | SWAP1 | |
| 17f3 | SSTORE | |
| 17f4 | PUSH0 | |
| 17f5 | DUP1 | |
| 17f6 | DUP1 | |
| 17f7 | PUSH2 | 0x17d3 |
| 17fa | JUMP | |
| 17fb | JUMPDEST | |
| 17fc | DUP3 | |
| 17fd | DUP3 | |
| 17fe | ADD | |
| 17ff | MLOAD | |
| 1800 | DUP5 | |
| 1801 | SSTORE | |
| 1802 | DUP13 | |
| 1803 | SWAP8 | |
| 1804 | POP | |
| 1805 | DUP14 | |
| 1806 | SWAP6 | |
| 1807 | POP | |
| 1808 | PUSH1 | 0x01 |
| 180a | SWAP1 | |
| 180b | SWAP4 | |
| 180c | ADD | |
| 180d | SWAP3 | |
| 180e | PUSH1 | 0x20 |
| 1810 | SWAP3 | |
| 1811 | DUP4 | |
| 1812 | ADD | |
| 1813 | SWAP3 | |
| 1814 | ADD | |
| 1815 | PUSH2 | 0x177f |
| 1818 | JUMP | |
| 1819 | JUMPDEST | |
| 181a | DUP3 | |
| 181b | DUP2 | |
| 181c | GT | |
| 181d | ISZERO | |
| 181e | PUSH2 | 0x15e8 |
| 1821 | JUMPI | |
| 1822 | SWAP2 | |
| 1823 | SWAP4 | |
| 1824 | SWAP5 | |
| 1825 | POP | |
| 1826 | SWAP2 | |
| 1827 | POP | |
| 1828 | PUSH1 | 0x04 |
| 182a | DUP8 | |
| 182b | ADD | |
| 182c | DUP12 | |
| 182d | MSTORE | |
| 182e | PUSH1 | 0x20 |
| 1830 | DUP12 | |
| 1831 | KECCAK256 | |
| 1832 | PUSH1 | 0x1f |
| 1834 | DUP5 | |
| 1835 | ADD | |
| 1836 | PUSH1 | 0x05 |
| 1838 | SHR | |
| 1839 | SWAP1 | |
| 183a | PUSH1 | 0x20 |
| 183c | DUP6 | |
| 183d | LT | |
| 183e | PUSH2 | 0x187a |
| 1841 | JUMPI | |
| 1842 | JUMPDEST | |
| 1843 | SWAP2 | |
| 1844 | DUP12 | |
| 1845 | SWAP4 | |
| 1846 | SWAP2 | |
| 1847 | DUP12 | |
| 1848 | SWAP7 | |
| 1849 | SWAP6 | |
| 184a | SWAP4 | |
| 184b | DUP15 | |
| 184c | DUP1 | |
| 184d | JUMPDEST | |
| 184e | DUP4 | |
| 184f | PUSH1 | 0x1f |
| 1851 | DUP7 | |
| 1852 | ADD | |
| 1853 | PUSH1 | 0x05 |
| 1855 | SHR | |
| 1856 | SUB | |
| 1857 | DUP3 | |
| 1858 | LT | |
| 1859 | PUSH2 | 0x1865 |
| 185c | JUMPI |