Skip to content

Type indexed coinType columns as CoinType end-to-end (drop bigint round-trips) #2293

Description

@shrugs

Problem

CoinType (enssdk) is a branded number — the invariant is that every real coinType fits in Number.MAX_SAFE_INTEGER (bigintToCoinType does Number(v) and throws above 2^53).

But the indexed schema stores coinType as t.bigint(), which Ponder persists as numeric(78) and hydrates as a JS bigint. The result is bigint ⇄ number conversions sprinkled across every read/write boundary:

  • Write: BigInt(coinType)StandaloneReverseRegistrar handler, resolver-db-helpers, resolution operations.
  • Read: bigintToCoinType(row.coinType)resolver-records, make-records-response, execute-operations, and Account.nameReferences's resolver (find-name-references-resolver).

These conversions are pure friction and an easy thing to get wrong — coinType should be CoinType the whole way down, including the indexed schema.

Fix

There's a precedent in the same schema file: chainId is the same kind of value (a number that overflows Postgres int4 but fits MAX_SAFE_INTEGER) and is typed correctly:

chainId: t.int8({ mode: "number" }).notNull().$type<ChainId>(),

Type coinType identically:

// instead of: coinType: t.bigint().notNull(),
coinType: t.int8({ mode: "number" }).notNull().$type<CoinType>(),
  • int4 genuinely overflows (DEFAULT_EVM_COIN_TYPE = 0x80000000 is int4_max + 1 — presumably why bigint was chosen). int8 holds up to 2^63, comfortably above the 2^53 CoinType invariant. numeric(78) is for uint256 amounts — overkill for a coinType.
  • Once the columns hydrate as typed number, every BigInt(coinType) / bigintToCoinType(...) at these boundaries can be dropped.

Scope

  • Columns (packages/ensdb-sdk/src/ensindexer-abstract/):
    • protocol-acceleration.schema.tsreverseNameRecord.coinType (L35), resolverAddressRecord.coinType (L204)
    • subgraph.schema.ts (L523) — legacy Subgraph path; this one serializes BigInt→string on the wire, so either include a wire-compat check or deliberately leave it on t.bigint().
  • Write sites: apps/ensindexer/src/plugins/protocol-acceleration/handlers/StandaloneReverseRegistrar.ts, apps/ensindexer/src/lib/protocol-acceleration/resolver-db-helpers.ts (drop BigInt(coinType)).
  • Read sites: apps/ensapi/src/omnigraph-api/schema/resolver-records.ts, apps/ensapi/src/omnigraph-api/lib/find-name-references/find-name-references-resolver.ts, apps/ensapi/src/lib/resolution/make-records-response.ts, apps/ensapi/src/lib/resolution/execute-operations.ts (drop bigintToCoinType).
  • Storage-type change (numericint8) ⇒ a re-index / migration.

Semantic nuance

bigintToCoinType throws on values > 2^53 (fail-loud on corrupt data); t.int8({ mode: "number" }) hydrates via Number() (lossless ≤ 2^53, silent truncation only on an impossible/corrupt > 2^53 row). Given the CoinType invariant and int8's 2^63 DB ceiling, real data is always safe — the only change is losing a loud failure on data that cannot legitimately exist.

Context

Surfaced while implementing Account.nameReferences (#1962 / #2292). That PR follows the existing bigint-boundary convention; this issue tracks doing the systemic cleanup so new code (and old) can use CoinType directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions