Skip to content

feat: index corporate actions, checkpoints, ballots and relayer subsidies; merge external-agent entities and retire TransferManager - #356

Draft
prashantasdeveloper wants to merge 7 commits into
fix/redesign-post-resync-defectsfrom
redesign/08-coverage
Draft

prashantasdeveloper wants to merge 7 commits into
fix/redesign-post-resync-defectsfrom
redesign/08-coverage

Conversation

@prashantasdeveloper

Copy link
Copy Markdown
Contributor

What's in each commit

  1. feat: index corporate actionsCorporateAction + CorporateActionDefaultConfig
    entities. CAInitiated, CARemoved, RecordDateChanged, CALinkedToDoc,
    DefaultTargetIdentitiesChanged, DefaultWithholdingTaxChanged, DidWithholdingTaxChanged.
  2. feat: index checkpoints and schedulesCheckpoint + CheckpointSchedule entities.
    CheckpointCreated, ScheduleCreated, ScheduleRemoved. The one real version change in this
    whole domain: ScheduleCreated/ScheduleRemoved went 3→4 args at chain v6.0.0 (ScheduleId
    inserted, payload type changed) — handled as a two-entry decode shape, with a regression test
    asserting both arities decode and a mismatch throws.
  3. feat: index corporate ballotsCorporateBallot + CorporateBallotVote entities.
    Created, MetaChanged, RangeChanged, RCVChanged, Removed, VoteCast — shape-identical
    across every checked spec version, no version work.
  4. feat: index the Subsidy/Relayer pallet — new Subsidy entity. The relayer pallet had
    zero indexer coverage despite full SDK support. Renamed paying-key → subsidy at chain v8.0.0;
    both eras are handled, since pre-v8 is the bulk of relayer history on any live chain.
  5. feat!: merge the TickerExternalAgent entities (breaking) — TickerExternalAgent
    AssetAgent, TickerExternalAgentHistoryAssetAgentHistory (these two duplicated their
    write path, which is what actually merges); TickerExternalAgentActionAssetAgentAction
    (straight rename, unchanged shape — it answers a different question and is driven from an
    unrelated 20-pallet event table). AgentGroup gains the asset relation it was missing
    entirely; AgentGroupMembership.member is now an Identity relation instead of a bare string.
  6. feat!: retire the TransferManager dual model (breaking) — removes the TransferManager
    entity, its Asset.transferManagers derived field, and its writer. It was documented deprecated
    in its own schema comment yet still written unconditionally; neither consumer queries it.

Commits 1–4 are purely additive — new entities and events that didn't exist before, no existing
query changes. Commits 5–6 are breaking schema changes.

Consumer impact

Commits 1–4: none. New entities, new coverage, nothing existing changes shape.

Commit 5 needs a coordinated SDK/portal release. Both consumers query all three renamed
entities:

Old query New query Other changes
tickerExternalAgents assetAgents callerId filters/selections → identityId
tickerExternalAgentHistories assetAgentHistories type is now the AgentHistoryType enum (was a plain string)
tickerExternalAgentActions assetAgentActions no field changes

permissions on AssetAgent/AssetAgentHistory stays a JSON-encoded string, not a typed
jsonField — see the docstring on AssetAgent in schema.graphql for why (the existing
PermissionsJson shape was built for a different on-chain permission model and doesn't fit an
AgentGroup's ExtrinsicPermissions without a lossy flattening).

Commit 6: no consumer query changes. Confirmed neither the SDK nor the portal queries
transferManagers today. It's a schema removal, hence the BREAKING CHANGE footer, but nothing
downstream needs to change.

Verification

yarn codegen && yarn typecheck && yarn lint && yarn test:unit all pass (571 unit tests, 53
suites), plus yarn build (validates every project.ts handler name resolves) and the
10-index-per-entity cap check across the whole schema. A fixture test exists for every new
handler. Full genesis resync was not run for this PR (out of scope for the bar here, matching how
earlier phases were verified) — worth doing before merge given how central corporate actions and
checkpoints are to the securities domain.

Handles CAInitiated, CARemoved, RecordDateChanged, CALinkedToDoc,
DefaultTargetIdentitiesChanged, DefaultWithholdingTaxChanged and
DidWithholdingTaxChanged. New CorporateAction and
CorporateActionDefaultConfig entities. CAInitiated and the
CorporateAction struct are shape-identical v4.1.3-v8.0.0
(docs/reference/event-shape-verification.md); only Ticker->AssetId at
7.x, already handled by getAssetId/getCaIdValue.

MaxDetailsLengthChanged stays unregistered (chain config, no entity),
as does the removed CAATransferred event (superseded by external
agents pre-6.0, per docs/implementation/06-corporate-actions.md).

src/decode/shapes/corporateActions.ts also registers the checkpoint
and corporateBallot shapes landing in the next two commits — one
cohesive shape-registration file for the domain rather than an
artificial split.
New Checkpoint and CheckpointSchedule entities. Handles
CheckpointCreated, ScheduleCreated and ScheduleRemoved.

ScheduleCreated/ScheduleRemoved are the one real version change in
this domain: arity 3->4 at v6.0.0 (ScheduleId inserted at index 2,
payload StoredSchedule -> ScheduleCheckpoints). Registered as a
two-entry shape in src/decode/shapes/corporateActions.ts; parseSchedule
decodes either era, with a regression test asserting both arities
decode correctly and a mismatched arity throws.

MaximumSchedulesComplexityChanged stays unregistered (chain config, no
entity).
New CorporateBallot and CorporateBallotVote entities. Handles Created,
MetaChanged, RangeChanged, RCVChanged, Removed and VoteCast — all six
events are shape-identical across every checked spec version
(docs/reference/event-shape-verification.md), so no version branching
here.
New Subsidy entity. The relayer pallet was renamed paying-key ->
subsidy at a clean v8.0.0 boundary (verified against
pallets/relayer/src/lib.rs at v6.3.0/v7.0.0/v7.4.0/v8.0.0); pre-v8
events carry a leading EventDid that v8 events drop. Both eras are
handled — pre-v8 is the bulk of relayer history on mainnet — via
AuthorizedPayingKey/AcceptedPayingKey/RemovedPayingKey/UpdatedPolyxLimit
and ApprovedSubsidy/AcceptedSubsidy/RemovedSubsidy/RemovedPendingSubsidy/
SubsidyDebited/UpdatedPolyxLimit, each pair sharing one handler.
UpdatedPolyxLimit is the one arity change (5 args pre-v8, 4 at v8+),
covered by a decode regression test.
TickerExternalAgent -> AssetAgent (caller -> identity), and
TickerExternalAgentHistory -> AssetAgentHistory (type: String! ->
AgentHistoryType! enum) — these two duplicated their write path (both
fire on the same externalagents events), which is what actually
merges. TickerExternalAgentAction -> AssetAgentAction is a straight
rename with an unchanged shape: it is driven from a 20-pallet lookup
table on every chain event, not from externalagents, and answers a
different question (what an agent did, not who is an agent), so it
stays its own entity — caller stays caller there, deliberately
asymmetric with AssetAgent.identity.

AgentGroup gains the asset relation it was missing entirely (defect
G). AgentGroupMembership.member is now an Identity relation instead of
a bare String.

permissions stays a JSON-in-a-string String, not the PermissionsJson
jsonField this was originally going to become: that type's shape was
built for the secondary-key permission model, and an AgentGroup's
ExtrinsicPermissions is a different, richer on-chain structure that
would need a lossy flattening to fit it. See the schema docstrings.

AssetAgent.group/.permissions are populated by AgentAdded but not kept
current by GroupChanged yet (only AssetAgentHistory is) — noted in
mapExternalAgent.ts.

BREAKING CHANGE: TickerExternalAgent renamed to AssetAgent (caller
field renamed to identity); TickerExternalAgentHistory renamed to
AssetAgentHistory (type field is now the AgentHistoryType enum, was
String); TickerExternalAgentAction renamed to AssetAgentAction (no
field changes); AgentGroupMembership.member is now an Identity
relation instead of a String. SDK queries tickerExternalAgents,
tickerExternalAgentHistories and tickerExternalAgentActions all need
updating to the renamed root fields, and callerId filters/selections
on the first need to become identityId. Needs a coordinated SDK/portal
release — see the PR description.
TransferManager was documented deprecated in its own schema comment in
favour of TransferCompliance, yet both were written unconditionally
for the same pre-v5 statistics events. Neither is queried by either
consumer (the SDK reads transfer restrictions from chain) — confirmed
against docs/reference/consumer-queries.md.

Removes the TransferManager entity, its Asset.transferManagers derived
field, and mapTransferManager.ts. TransferRestrictionTypeEnum is kept:
handleStatisticTransferManagerAdded and the surviving exemption
handlers in mapStatistics.ts still use it for the pre-v5
percentage/count StatType and TransferComplianceExemption model.

BREAKING CHANGE: the TransferManager entity and the transferManagers
query (including Asset.transferManagers) are removed. Confirmed
unused by both the SDK and the portal, so no consumer query changes,
but it is a schema removal.
@prashantasdeveloper
prashantasdeveloper requested a review from a team as a code owner September 14, 2026 10:49
…d assetAgentActions.test.ts

registerShape('relayer', X, [{ from: V8, fields: [...] }]) repeated the same
two field lists across five event names; adds introducedAt/registerShapes to
registry.ts (siblings of stable/discontinuedAt) and uses them instead.
@sonarqubecloud

Copy link
Copy Markdown

@prashantasdeveloper
prashantasdeveloper added this pull request to stack #358 September 15, 2026 08:23
@prashantasdeveloper
prashantasdeveloper marked this pull request as draft September 15, 2026 10:29
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.

1 participant