# @zkp2p/indexer-schema

Consumer-facing GraphQL domain schema and TypeScript types for the ZKP2P indexer. The package is generated from `schema/domain.graphql`; raw audit-event entities are available in the live indexer but are not exported here.

## Install

```bash
pnpm add @zkp2p/indexer-schema
```

## Source vs Published Package

This repository currently declares `0.22.0`. Publishing is an autonomous OIDC workflow after the focused version PR is merged and tagged; merging a schema change alone does not update npm.

Before depending on a newly merged entity or field, compare both versions:

```bash
jq -r .version packages/indexer-schema/package.json
npm view @zkp2p/indexer-schema version
```

Version `0.22.0` stabilizes the schema published as `0.22.0-rc.4`. Do not assume the stable version or its reviewed documentation exists in the installed package until the trusted publish workflow completes.

## Usage

```ts
import { schemaSDL } from "@zkp2p/indexer-schema";
import type {
  DisputeProtectionIntent,
  Deposit,
  Intent,
  QuoteCandidate,
} from "@zkp2p/indexer-schema/types";
```

The raw SDL is also exported at `@zkp2p/indexer-schema/schema.graphql`.

## Exported types

- Orders and quotes: `Deposit`, `DepositPaymentMethod`, `MethodCurrency`, `Intent`, `QuoteCandidate`, `OrderbookEntry`, `WhitelistEntry`, `ReferralFeeDistribution`
- Statistics and activity: taker, maker, manager, platform, global, deposit, price, profit, referral, and daily cursor entities
- Delegated rates: `RateManager`, `RateManagerRate`, `ManagerStats`, `ManagerAggregateStats`, `ManagerDailySnapshot`
- Stake custody: `TakerStakeState`, `StakeAccountState`, `ClaimAccountState`, `TakerStakeAuthorization`, `StakeLock`, `StakeActivity`, `StakeVaultConfig`
- V3 disputes: `DisputeProtectionIntent`, `DepositDisputeProtectionConfig`, `DisputeProtectionRiskWindow`, `DisputeProtectionPolicyState`, `IntentLifecycleHookState`
- V3 registry state: `OrchestratorRegistrationState`
- Payment binding: `PaymentIntentBinding`
- Whitelist policy: `AddressGroup`, `AddressGroupMember`, `DepositWhitelistPolicy`, `DepositWhitelistedAddress`, `DepositAllowedGroup`

## Transaction attribution

Version `0.20.0-rc.1` exposed canonical ERC-8021 attribution on deposit creation as well as every newly signaled V2, V2.1, V2.2, and V3 intent:

- `Deposit.attributionCodes` and `Deposit.attributionSource` identify the client that created the seller's deposit.
- `Intent.attributionCodes` preserves all decoded attribution codes in calldata order.
- `Intent.attributionSource` is the first code other than ZKP2P's Base builder code (`bc_nbn6qkni`), or `null` when no client/referrer code is present.
- Version `0.21.0-rc.0` retains at most five ordered codes and decodes either direct calldata or the address-matched ERC-4337 user operation's ERC-7579 execution payload.
- Missing, malformed, ambiguous, or unsupported suffixes fail closed to an empty code list and `null` source.

These fields replace calldata parsing in analytics consumers. They do not change deposit or intent identity, lifecycle, amounts, payment method, or fulfillment semantics.

## Intent guardian

Version `0.21.0-rc.1` adds `Deposit.intentGuardian` and `QuoteCandidate.intentGuardian` for the guardian selected when a deposit was created:

- Both fields are lowercase-normalized.
- Legacy V2 deposits use the zero address as the non-null guardian sentinel.
- `QuoteCandidate.intentGuardian` is indexed for exact-equality and `_in` filtering with lowercase address values.

## Orderbook gating service

Version `0.20.0` stabilizes the schema published and production-exercised as `0.20.0-rc.2`, including `OrderbookEntry.intentGatingService`, projected from `QuoteCandidate.intentGatingService`:

- It is non-null. The zero address is the ungated sentinel, so "no gating service" never collapses with "value absent".
- It is lowercase-normalized, unlike its `QuoteCandidate` source, so consumers can compare addresses with exact equality.
- A non-zero value means the deposit's intents must be authorized by that gating service, so consumers that cannot produce its signature should exclude the row.

## Method-scoped dispute protection and liquidity aggregates

Version `0.22.0` stabilizes the schema published as `0.22.0-rc.4`, synchronizing the consumer projection with the method-scoped dispute stack and removing the retired Peer Curator accounting surface:

- `QuoteCandidate.disputeProtectionOptedIn` and `OrderbookEntry.disputeProtectionOptedIn` are hard-renamed to `disputeProtectionOptedOut`.
- Dispute protection is default-on for payment methods with a nonzero active-policy risk window. Only an explicit active-policy `enabled: false` row is opted out; missing config and `enabled: true` are not opted out.
- Projection fan-out is gated to the configured active policy. Passive or predecessor policy events retain raw/config rows without changing quote or orderbook admission.
- Staging and production now bind the dedicated-vault method-scoped dispute stack from contracts #293-#295 (fresh `StakeVault`), using `@zkp2p/contracts-v2@0.4.1-rc.9`.
- `CurrencyLiquidity` and its deduplicated `DepositCurrencyLiquidity` contributions aggregate immediately available token liquidity by fiat currency.
- `TokenLiquidity` and its deduplicated `DepositTokenLiquidity` contributions aggregate immediately available liquidity by deposited token.
- `CurrencyPlatformLiquidity` and its deduplicated `DepositCurrencyPlatformLiquidity` contributions aggregate immediately available token liquidity by fiat currency and payment platform.
- `MakerPeerPayStats` is removed.
- `MakerPlatformStats.totalAmountTakenPreEarnCutover` is removed.
- Generic `AddressGroup` and method-scoped whitelist-policy entities remain supported for the Peer Pay merchant group.

Version `0.22.0-rc.2` scopes whitelist-group admission and dispute-protection opt-in to a deposit/payment-method tuple:

- `DepositWhitelistPolicy`, `DepositAllowedGroup`, and `DepositDisputeProtectionConfig` IDs now include `paymentMethodHash`.
- `DepositWhitelistPolicy.paymentMethodHash`, `DepositAllowedGroup.paymentMethodHash`, and `DepositDisputeProtectionConfig.paymentMethodHash` are required.
- Direct `DepositWhitelistedAddress` rows remain deposit-wide and no longer expose the invalid tuple-policy `policyEntityId`; `whitelistedAddressCount` is the deposit-wide count repeated on each method policy.
- Only the matching payment-method rows in `QuoteCandidate` and `OrderbookEntry` are refreshed by policy events.

Version `0.22.0-rc.1` made the dispute-admission projection a hard cut and added stable staking/dispute pagination fields. Its explicit opt-in semantics are superseded by `0.22.0-rc.3`:

- The rc.1 fields represented explicit opt-in; rc.3 replaces them with the default-on `disputeProtectionOptedOut` projection described above.
- `QuoteCandidate.disputeProtectionRequiresStake` and `OrderbookEntry.disputeProtectionRequiresStake` are true only when the tuple is not opted out and has a nonzero active-policy risk window for the row's payment method.
- Staging and production bind only their configured active policy and vault; retired deployments are not indexed.
- `DisputeProtectionIntent.disputedAtBlockNumber` and `disputedAtLogIndex` record the exact `DisputeResolved` event cursor and remain null before a dispute.
- Stake and finalized-dispute records expose indexed fields for stable keyset pagination; page validation and query execution remain consumer responsibilities.

Missing `DepositDisputeProtectionConfig` rows are not opted out. An explicit active-policy `enabled: false` config records opt-out; `enabled: true` undoes it. Missing or zero payment-method risk windows do not require stake. The contracts remain authoritative for admission, collateral, maturity, attestation verification, and dispute settlement.

### Merchant stake and dispute pagination

- `StakeLock` pages require exact `chainId`, `vaultAddress`, and `stakeOwner` filters plus optional status. Advance the opaque keyset with `id > afterId` and `id` ascending. `maturesAt` remains a surfaced UI value, but it is mutable and therefore not the cursor.
- `StakeActivity` pages require exact `chainId` and `vaultAddress`, merchant relevance `(stakeOwner == merchant OR taker == merchant)`, and optional kind. Order `(blockNumber, logIndex, id)` ascending. After cursor `(b, l, i)`, continue with `blockNumber > b OR (blockNumber = b AND logIndex > l) OR (blockNumber = b AND logIndex = l AND id > i)`. StakeActivity cursor stability comes from immutable append-only event positions.
- Finalized dispute pages require exact `chainId`, `status = DISPUTED`, and the active `policyAddress`, with no merchant-address predicate. Order `(disputedAtBlockNumber, disputedAtLogIndex, id)` ascending. After cursor `(b, l, i)`, continue with `disputedAtBlockNumber > b OR (disputedAtBlockNumber = b AND disputedAtLogIndex > l) OR (disputedAtBlockNumber = b AND disputedAtLogIndex = l AND id > i)`. The two event cursor fields are non-null for rows admitted by the `DISPUTED` filter even though they remain nullable before dispute. Finalized DISPUTED cursor stability comes from terminal rows and immutable dispute event positions.

These keysets guarantee strict forward progress; use no offset pagination. Page sizes `1..100` are validated by the clients SDK; the indexer schema does not enforce a page-size limit.

## V3 dispute boundary

Version `0.19.0-rc.0` was the contracts PR #219 hard cut:

- `RiskManager` entities, enums, intent-extension projections, deferred settlement state, and risk-exposure aggregates are removed.
- `DisputeProtectionIntent` records the `PENDING -> CANCELLED | SETTLED -> RELEASED | DISPUTED` lifecycle.
- `DisputeProtectionRiskWindow` records future-admission minimum holds per policy and payment method.
- `DisputeProtectionPolicyState` is the current pause flag plus the normalized, sorted set of authorized lifecycle hooks for one Policy deployment.
- `OrchestratorRegistrationState` is current Orchestrator membership in one registry deployment; removal overwrites the same row with `registered: false`.
- `IntentLifecycleHookState` records the hook snapshotted by `OrchestratorV3`.
- `PaymentIntentBinding` remains the canonical `(chainId, registryAddress, nullifier) -> intentHash` relationship.

`TakerStakeState.selectedStakeOwner` preserves the emitted preference. `selectionAuthorized` and
`stakeOwner` are effective state: authorization makes the selection active, revocation immediately
falls back to the taker's own stake, and unrelated authorization updates do not rewrite the row.

These current-state, split dispute-projection, and cursor fields require a clean staging and production
reindex. Do not deploy a consumer that treats a missing field as opt-out against an older database.

`EscrowIntentPeriodState` remains part of the V2.2 Escrow surface and is not a DisputeProtectionPolicy or RiskManager entity.

See the bundled `docs/schema-field-reference.md` and `llms.txt` for identity and field semantics.

## Breaking-change detection

The indexer CI runs `check-schema-breaking.cjs` on every PR that touches `schema/`. It fails if any fields, types, or enum values are removed or have incompatible type changes unless the package advances to a later RC on the same unreleased line, or to the next minor after a stable release.

When making intentional breaking changes:
1. Advance the RC while the release line is unstable; after stable, bump the minor version
2. Update downstream consumers before merging
3. Confirm the CI check reports the intentional version bump

## Codegen Example

```yaml
# codegen.yml
schema: node_modules/@zkp2p/indexer-schema/dist/schema.graphql
generates:
  src/__generated__/types.ts:
    plugins:
      - typescript
      - typescript-operations
```

## Development and release

From the repository root:

```bash
pnpm install --frozen-lockfile
pnpm build:schema
pnpm schema-package:build
pnpm --dir packages/indexer-schema verify:release
cd packages/indexer-schema && npm pack --dry-run
```

Publishing is performed only by the protected GitHub Actions trusted-publishing workflow. It uses npm OIDC without a long-lived npm token and adds provenance whenever the source repository is public. See [the release runbook](../../NPM_RELEASE.md).
