Skip to content

fix: 🐛 read key records through the augmented types - #351

Closed
F-OBrien wants to merge 1 commit into
redesign/04-polyx-ledgerfrom
fix/key-record-typing-and-block-cache
Closed

F-OBrien wants to merge 1 commit into
redesign/04-polyx-ledgerfrom
fix/key-record-typing-and-block-cache

Conversation

@F-OBrien

@F-OBrien F-OBrien commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Found while reviewing #349. Draft — #349 is still under review.

The bug

identity.keyRecords was cast to a bare Codec and read via .toJSON(), which hid that KeyRecord has three variants. MultiSigSignerKey carries the multisig's account, not a DID, so a signer key produced:

  • an Identity row keyed by an SS58 address
  • an Account pointing at that fabricated identity
  • a portfolio 0 for it

A signer key has no identity and no permissions, so it now resolves to no Account at all. ledgerAccount still creates a bare one if the address ever holds POLYX, which is the same row it creates for a pallet or system address. No DID is resolved for a signer on purpose: the signer→multisig and multisig→identity links rebind independently, and multiSig.adminDid is the admin, not the identity the multisig is joined to.

MultiSigSigner rows are left to the multisig event handlers and the genesis/seed scan — neither status nor createdBlock is derivable from a key record.

The cache

BlockContext.accounts held the resolved Account, so a cache hit skipped the store read as well as the chain read. The chain read cannot change within a block (api is api.at(blockHash) of the block being indexed, so it serves end-of-block state), but the row can — a handler links or unlinks a key partway through. So a cached negative shadowed a row another handler had just written, and ledgerAccount then overwrote it, dropping its identityId.

It now caches only the key record. Account is read from the store on every lookup. Two regression tests cover both staleness modes; they fail if entity caching is put back.

Casts

Removed where they were discarding real augmented types: both system.account reads and scripts/reconcile-polyx.ts, narrowed to the balance fields — the only part that must stay spec-agnostic, since frozen is miscFrozen/feeFrozen on older runtimes.

Kept where they are load-bearing: every event.event.data[i] / extrinsic.args[0] site. Those pass tsc and fail subql build — two physical copies of @polkadot/types-codec (cjs/types/codec vs types/codec) that the two toolchains resolve differently. They mask nothing: event data is Codec[], so there are no augmented types on that path.

scripts/reconcile-polyx.ts now loads the chain-type augmentation itself. Only tsconfig.test.json includes scripts/**/*, so an editor was type-checking it against a different, unaugmented type than CI — worth closing regardless of this PR. The other two chain-reading scripts still have that gap.

Verified

tsc --noEmit -p tsconfig.test.json clean, eslint src scripts tests clean, subql build exits 0, 409/409 unit tests pass.

Not included

Pre-existing rows in any already-synced database still have the fabricated identities; that needs a reindex or migration.

`identity.keyRecords` was cast to a bare `Codec` and read as JSON, which hid
that `KeyRecord` has three variants. `MultiSigSignerKey` names the multisig
account, not a DID, so the JSON read created an `Identity` keyed by an SS58
address, an `Account` pointing at it, and a portfolio 0 for it. A signer key
has no identity and no permissions, so it now resolves to no `Account` at all;
`ledgerAccount` still creates a bare one if the address holds POLYX.

The same cast is gone from the two `system.account` reads and the
reconciliation script, narrowed to the balance fields - the only part that must
stay spec-agnostic, since `frozen` is `miscFrozen`/`feeFrozen` on older
runtimes. The script loads the chain-type augmentation itself now: only
`tsconfig.test.json` includes `scripts/**/*`, so an editor was type-checking it
against a different, unaugmented type than CI.

The event-data casts are load-bearing and stay: two physical copies of
`@polkadot/types-codec`, which `subql build` resolves differently from `tsc`.

Separately, the per-block account cache held the resolved `Account`, so a hit
skipped the store read as well as the chain read. The chain read cannot change
within a block - `api` serves the block's end-of-block state - but the row can,
when a handler links or unlinks a key. A cached negative therefore shadowed a
row another handler had just written, and `ledgerAccount` went on to overwrite
it. The cache now holds only the key record; `Account` is read from the store
on every lookup.
@F-OBrien
F-OBrien force-pushed the fix/key-record-typing-and-block-cache branch from 6734e4f to 924d6ec Compare September 10, 2026 09:05
@F-OBrien
F-OBrien marked this pull request as ready for review September 10, 2026 09:05
@F-OBrien
F-OBrien requested a review from a team as a code owner September 10, 2026 09:05
@sonarqubecloud

Copy link
Copy Markdown

@prashantasdeveloper

Copy link
Copy Markdown
Contributor

I have added this commit in #353 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants