Centrifuge protocol indexer: Ponder ingests EVM logs/blocks → src/handlers/ → src/services/ → PostgreSQL (Drizzle); GraphQL while running. Setup/ops: README.md. Extra Cursor rules: .cursor/rules/*.mdc (always-on summary: cfg-api-v3-core.mdc).
- Handlers — Only under
src/handlers/. Subscribe withmultiMapper(...)orponder.on(...). Thin: decodeevent.args, orchestrate; no heavy domain logic here. - No DB in handlers — No
context.db.*, Drizzle builders, or raw SQL. Use services. Exception: callsnapshotterfor snapshot inserts (do not duplicate that pattern). - No ad-hoc Ponder handler DB — Persistence lives in entity services on
Service.ts; do not inlinecontext.dbin handlers. - Batching — Prefer
insertMany/saveMany; query once, update in memory, batch-save when it fits. generated/— Never edit. Regenerate viapnpm update-registry,pnpm codegen, or build/start tooling; fix registry/upstream instead.
- Shape: one service per main schema entity (or tight group).
export class EntityService extends Service<typeof Table> { static readonly entityTable = Table; static readonly entityName = "EntityName"; }— seeAdapterService.ts. Statics (onService):insert,insertMany,saveMany,get,getOrInit,upsert,query,count; instancesave/delete. Add methods for shared business rules. - Logging —
serviceLog/serviceErrorfromlogger.ts;expandInlineObjectfor record-shaped logs. Every meaningful public/static method (DB, side effects, non-trivial branches) logs at least once; trivial pure accessors (e.g. shallowread()) need not.serviceLogis a no-op underponder start;serviceErrorstill logs — seelogger.ts. - Handlers never call
context.db.sql/context.db.find; they callFooService.get(...)etc. Base layer uses Drizzle internally. - Batch upserts — Extending
saveMany: followService.tscomment; usesql.raw(\excluded."column"`)for Ponder’s PG proxy, not brokenexcluded` expansions.
Batch choice: insertMany — many rows, conflict-do-nothing style. saveMany — same upsert semantics as instance save. Examples: gatewayHandlers.ts, CrosschainMessageService.ts.
Versioned keys in ponder.config.ts (HubV3, HubV3_1, …). Register once with unversioned name, e.g. multiMapper("Hub:NotifyPool", handler) → all ABI-compatible versions. :setup is lifecycle, not an ABI event. Overloaded events: follow string rules in multiMapper.ts.
chains.ts — RPC, blocks, deduped chains. contracts.ts — decorateDeploymentContracts. ponder.config.ts — merged contractsV3 / contractsV3_1, ordering: "omnichain". Optional migration blocks: config.ts (V3_1_MIGRATION_BLOCKS).
snapshotter.ts — copy live entity rows into snapshot tables with block/time/trigger/tx/chain (onConflictDoNothing). Period: timekeeper.ts + blockHandlers.ts (${chainName}:NewPeriod). Event-driven: handlers call snapshotter(context, event, "<ContractVersion>:EventName", entities, SnapshotTable) after updates — ripgrep snapshotter( under src/handlers/.
tsconfig.json: strict, noUncheckedIndexedAccess. Types from ponder:registry / ponder:schema; no any. Narrow types, early returns, small functions; match local naming/import/JSDoc style.
- Typecheck — Full
pnpm typecheckexpects mainnet registry ingenerated/(fresh, not testnet) After non-trivial TS edits, also runpnpm lint(repo root); fix issues unless excluded by task. ponder.schema.tsor Ponder config changes:pnpm codegen.- New handler/service work: copy the closest existing file (same contract family / table); mirror
multiMapper, services, batching, snapshots. - New entity services: export from
services/index.tswhen shared. - Focused diffs; no drive-by refactors.
- Find events: ripgrep
src/handlers/,generated/formultiMapper("Contract:Eventand ABI names. Logs:scripts/evgrep.sh. - PR/commit: note registry version, chain id, or migration-block assumptions so later changes do not “fix” intentional behavior.
src/helpers/ — utilities (multiMapper, timekeeper, snapshotter, IPFS, logger). Not a substitute for entity services for CRUD/domain. Handlers may use logEvent where appropriate.
| Area | Path |
|---|---|
| Cursor rules | .cursor/rules/*.mdc |
| Generated | generated/ |
| Ponder config | ponder.config.ts |
| Schema | ponder.schema.ts |
| Base service | src/services/Service.ts |
| Service exports | src/services/index.ts |
| Logger | src/helpers/logger.ts |
| multiMapper | src/helpers/multiMapper.ts |
| snapshotter | src/helpers/snapshotter.ts |
| Period snapshots | src/handlers/blockHandlers.ts, src/helpers/timekeeper.ts |
| Chains | src/chains.ts |
| Contracts | src/contracts.ts |