Indexing fixture requirements¶
Fixtures should prove decoder and reducer behavior with raw logs, expected state transitions, fallback reads, and explicit block identity.
Fixture envelope¶
Every generated group uses this envelope:
{
"schemaVersion": "kuru-indexer-fixture/v1",
"abiSchema": "S1",
"id": "spot-lifecycle",
"addresses": {},
"scenarios": [
{
"id": "spot-placement",
"covers": ["spot.placement"],
"initialState": {},
"reducer": {"operations": [], "expectedFinalState": {}},
"receipt": {"logs": [], "blockPinnedCalls": []},
"fallbackReads": []
}
]
}
Each log retains emitter, topics/data, decoded values, raw packed bytes, and record order. Each scenario retains transaction input and block identity plus deterministic pre-state, ordered projection operations, final state, fallbacks, and block-pinned calls. Addresses are explicit documentation aliases. Do not substitute them for deployment configuration.
Required scenario coverage¶
The fixture suite should cover the scenarios below with exact assertions and expected reducer state.
Discovery, ownership, and versions¶
| Scenario | Required assertions |
|---|---|
| Deploy Spot market | Proxy initialization logs precede Engine/AccountCore/Router discovery; numeric config recovered from calldata/getters. |
| Deploy active Perp market | Engine registration precedes proxy and AccountCore route; no Router route-replacement event. |
| Deploy paused Perp market | MarketStateUpdated occurs before AccountCore registration and Router discovery. |
| Add Perp Engine | AccountCore PerpEngineAdded precedes Router event. |
| Replace/revoke Perp book | AccountCore route event precedes Router route event; old/new intervals close/open exactly. |
| Upgrade Router/Engine/book | Proxy Upgraded is decoder boundary; Router confirmation follows target migration logs. |
| Concurrent ownership requests | Two candidates for one contract coexist under distinct (chainId,contract,pendingOwner) keys; request expiry is 48 hours for schema S1. |
| Cancel ownership request | Delete only the caller/candidate row; another candidate survives; canceling a missing request is an idempotent event. |
| Direct ownership transfer | Owner changes while every pending handover survives; calldata distinguishes it from completion. |
| Complete ownership handover | Completion at timestamp == expiresAt succeeds; only the calldata-named candidate is consumed and another candidate survives. |
| Change authority/roles | Each child pointer and ProtocolAuthority role changes independently. |
Accounts, custody, fees, and referrals¶
| Scenario | Required assertions |
|---|---|
| First deposit | Auto-registration, reserve snapshot, then Deposit activity; no double credit. |
| Withdrawal | Reserve snapshot then Withdrawal; exact free balance. |
| Internal transfer | No snapshots; apply from/to deltas once. |
| Signer authorize/revoke/expiry | Permission snapshot/delete, auth epoch increment, silent expiry evaluation. |
| Subaccount creation | AccountRegistered before SubaccountCreated; root edge and epoch. |
| Builder approve/revoke/claim | Approval replacement, accrual deltas, claim reset/activity. |
| Spot/Perp fee and referral tiers | Root scoping, activation/expiry, independent Spot and Perp policies. |
| Spot protocol fee | Fee-collector snapshot with no dedicated Spot fee event. |
| Per-book Spot reserve | Active-slot reserve sum equals makerLockedReserves; aggregate AccountCore reserve remains separate. |
| Perp fee | Maker/taker attribution, aggregate protocol accrual, collector snapshot, optional builder accrual. |
| Eventless controls | Successful pause and fee-collector calldata plus getter reconciliation. |
Spot active book¶
| Scenario | Required assertions |
|---|---|
| Place bid/ask | 36-byte live records; reserve snapshots precede packed update. |
| Same-price replacement | New order ID and FIFO tail; old delete cannot remove replacement. |
| Multi-slot packed replace | Record order follows operation order; zero-length payload is a no-op if supported by the current Spot entrypoint. |
| Explicit cancel/cancel-all | Non-live records and exact reserve release. |
| Partial/full fill | Trade updatedSize; full delete has no double subtraction. |
| Last-trade observation | Final packed price converted to X8 and containing block timestamp; no-fill action leaves it unchanged. |
| Stale partial/dust | Spot reports trade updatedSize=0 and may append a no-op delete describing the discarded visible residual; Perp can report a nonzero trade residual followed by its real delete. Hidden stale excess is not emitted. |
minSizeAfterBlock |
Executable size changes only at threshold+1 without a log; Spot getter discrepancy documented. |
| Swap active-only | Account snapshots, SpotSwap, TradesPacked, BookUpdatesPacked ordering. |
| Self-trade behavior | Revert/no logs or documented self-cancel path, depending on entrypoint. |
| Maker/queue priming | Audit events do not change economic L2/L3. |
Spot passive and hooks¶
| Scenario | Required assertions |
|---|---|
| Mint one-/two-sided band | Mint event and band snapshot; shares/principal/checkpoints. |
| Batch mint clipped leg | Zero-result leg produces no false position; accepted legs ordered. |
| Passive ask fill | Account snapshots, band update, synthetic trade flags/slot/order ID, fee-growth quote. |
| Passive bid fill | Conservative base conversion and base fee growth. |
| Mixed active/passive swap | Match-order packed records and aggregate swap result. |
| Partial/full burn | Band update precedes burn summary; position decrement/delete. |
| Fee-only claim | Claim event with no band snapshot; checkpoint advances. |
| Hook set/config | Hook pointer, gas limit, min notional reducers. |
| Hook replenish/requote | Synchronous generated orders in same packed event, including same-taker consumption. |
| Hook failure | Fail-open result: no hook state event and normal trade remains. |
| Example hook price push | Orderbook TradesPacked/BookUpdatesPacked, then SlotSync, PriceAccepted/Requoted. |
Perp normal flow¶
| Scenario | Required assertions |
|---|---|
| Cross/isolated activation | Mode and slot/snapshot creation before exposure/fill. |
| Place/cancel/replace | 40-byte records, maker fee PPS, exposure and reserve snapshots, final book events. |
| Partial/full fill | Maker then taker PerpFillSettled; exact position/PnL/OI replay; packed records last. |
| Last price and cross order count | Final fill updates Engine/orderbook last-price projections; cross insert/delete count matches getter. |
| Reduce-only fill | Maker/taker flags, capacity exposure, no side-cross. |
| Position side flip | Entry-quote rounding and long/short OI transition. |
| Funding update/settlement | Signed accumulator, receiver floor/payer ceil, checkpoint/margin. |
| Implicit funding on fill | Margin mutation and checkpoint without standalone funding-settled event. |
| Margin deposit/withdraw/move | Audit delta plus snapshot, AccountCore free snapshot order. |
| Isolated-to-cross migration | Slot, clear, both margins, migration summary; OI unchanged. |
| Cross-to-isolated migration | Slot release, both margins, full isolated snapshot; OI unchanged. |
| Risk/status/price config | Engine mutation before AccountCore request; getter fallback for omitted fields. |
| Health and thresholds | Cross/isolated threshold structures and transfer requirements reconcile at one pinned state boundary. |
Perp liquidation¶
| Scenario | Required assertions |
|---|---|
| Cross start, unrecovered | Per-market book deletes before Engine exposure, final lock event. |
| Cross start, recovered | recovered=true means no stored lock and no separate clear event. |
| Isolated start | PerpIsolatedPositionUpdated sets the liquidating flag before forced BookUpdatesPacked; cancellation snapshots follow, and recovery clears the flag before PerpLiquidationStarted. |
| Partial reduction, still unhealthy | Packed trades before Engine settlement; lock remains; AccountCore summary last. |
| Partial reduction, recovered | Engine clear before AccountCore liquidation summary. |
| Permissionless clear | Clear event only after health check; no fresh-price event assumption. |
| Cross takeover | Per-market mode/slot transfer, margin transfer, namespace summary; OI unchanged. |
| Isolated takeover | Target clear and destination full snapshot before margin/summary. |
| Hard-paused forced execution | Expected revert/no logs for current implementation; forced cancellation remains testable. |
Intents and example integration¶
| Scenario | Required assertions |
|---|---|
| Immediate replace/batch | Orderbook records precede IntentExecuted; monotonic signer nonce. |
| Trigger create/cancel | Full lifecycle and unordered nonce consumption from calldata/getter. |
| Trigger fire | Orderbook records precede TriggerFired; report hash retained. |
| Trigger expiry-on-execute | Only TriggerExpired; no orderbook mutation. |
| Failed trigger execution | Entire status/order mutation reverts; no canonical logs. |
| Submitter/skew config | Snapshot replacement. |
Executable negative and replay-safety vectors¶
Replay-safety tests must define accepted controls and expected rejection or final state for every boundary below:
- emitter registration selects Spot versus Perp before the shared
BookUpdatesPackedtopic is decoded; - empty or non-divisible 64/36/40-byte payloads, nonzero reserved fields, unsupported flags, and
active maker slots
>= 62are rejected; - trade IDs are strictly increasing per book history;
- a stale delete cannot remove a newer order ID and duplicate raw identities apply once;
- a schema outside its exact implementation interval and an undiscovered emitter are rejected;
- Spot and Perp dust apply their distinct trade/book-update sequences without double deletion;
- the saved Perp
placementMode, rather than the later Engine mode, controls cross-order-count deletion; - isolated-to-cross migration and cross takeover preserve every position-content field; and
- an orphaned block's packed mutation, implementation upgrade, and getter evidence roll back in one journal before a same-height replacement branch is applied.
Validator requirements¶
The fixture validator must verify:
- ABI schema identity is consistent across every fixture in a run;
- every raw log matches its selected ABI and re-encodes to the declared topics/data;
- every transaction/getter signature and return shape matches its target reference ABI and its calldata/output re-encodes with the same ABI rules used by the indexer;
- packed widths, round-trip decoding, allowed flags, and user-slot range;
- ordered reducer operations produce the declared final state, including price/health arithmetic reconciliation for getter-only boundaries, including nested quote, margin, PnL, funding, cross-sum, and transfer-floor outputs;
- every required flow/projection has at least one scenario and selected critical event-order relationships hold; and
- eventless/log-incomplete examples declare calldata or block-pinned getter evidence.
Replay validation must additionally cover rejection, idempotence, inverse journals, getter invalidation, placement mode, product-specific dust, and position transfers.