Skip to content

feat(sui-indexer): add Ika coordinator event polling for signature completion tracking#301

Closed
Rcc999 wants to merge 14 commits intomasterfrom
rayane/ika-event-in-gql-query
Closed

feat(sui-indexer): add Ika coordinator event polling for signature completion tracking#301
Rcc999 wants to merge 14 commits intomasterfrom
rayane/ika-event-in-gql-query

Conversation

@Rcc999
Copy link
Contributor

@Rcc999 Rcc999 commented Jan 15, 2026

Closes: #297

During deployment :

UPDATE setups SET coordinator_pkg = '0x6573a6c13daf26a64eb8a37d3c7a4391b353031e223072ca45b1ff9366f59293' WHERE sui_network = 'testnet';
UPDATE setups SET coordinator_pkg = '0x23b5bd96051923f800c3a2150aacdcdd8d39e1df2dce4dac69a00d2d8c7f7e77' WHERE sui_network = 'mainnet';

Summary by Sourcery

Generalized GraphQL event fetching to support per-module queries (not hardcoded to nbtc)
Added separate pollIkaEvents() that polls coordinator_inner::CompletedSignEvent and RejectedSignEvent
Event handlers log outcomes; actual recordIkaSig calls are deferred to a follow-up PR
Coordinator package ID resolved from @ika.xyz/sdk (mainnet/testnet only)
DB migration at deploy time:

UPDATE setups SET coordinator_pkg = '...' WHERE sui_network = 'testnet';
UPDATE setups SET coordinator_pkg = '...' WHERE sui_network = 'mainnet';

…mpletion tracking

Signed-off-by: Rayane Charif <rayane.charif@gonative.cc>
Signed-off-by: Rayane Charif <rayane.charif@gonative.cc>
@Rcc999 Rcc999 requested a review from a team as a code owner January 15, 2026 11:52
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 15, 2026

Reviewer's Guide

Extends the Sui indexer to poll and process both NBTC and Ika coordinator events by introducing coordinator package support, module-aware GraphQL event fetching, new Ika event handlers, and schema/config changes to track coordinator package IDs.

ER diagram for setups table with coordinator_pkg

erDiagram
  setups {
    integer id PK
    text sui_network
    text nbtc_pkg
    text coordinator_pkg
    integer is_active
  }
Loading

Updated class diagram for Sui indexer polling and Ika event handling

classDiagram
  class Processor {
    - netCfg NetworkConfig
    - storage IndexerStorage
    - eventFetcher EventFetcher
    + constructor(netCfg NetworkConfig, storage IndexerStorage, eventFetcher EventFetcher)
    + pollAllEvents(nbtcPkgs PkgCfg[]) Promise~void~
  }

  class SuiEventHandler {
    - storage IndexerStorage
    - setupId number
    + constructor(storage IndexerStorage, setupId number)
    + handleEvents(events SuiEventNode[]) Promise~void~
    + handleIkaEvents(events SuiEventNode[]) Promise~void~
    - handleCompletedSign(e SuiEventNode) Promise~void~
    - handleRejectedSign(e SuiEventNode) Promise~void~
  }

  class IndexerStorage {
    - db any
    + getActiveNbtcPkgs(networkName string) Promise~PkgCfg[]~
    + getMultipleSuiGqlCursors(ids number[]) Promise~Record~number,string~~
  }

  class EventFetcher {
    <<interface>>
    + fetchEvents(packages FetchPackage[]) Promise~Record~string,EventsBatch~~
  }

  class SuiGraphQLClient {
    - client GraphQLClient
    + fetchEvents(packages FetchPackage[]) Promise~Record~string,EventsBatch~~
  }

  class FetchPackage {
    + id string
    + module string
    + cursor string | null
  }

  class EventsBatch {
    + events SuiEventNode[]
    + endCursor string
    + hasNextPage boolean
  }

  class PkgCfg {
    + id number
    + nbtc_pkg string
    + coordinator_pkg string
  }

  class IkaCompletedSignEventRaw {
    + sign_id string
    + signature number[]
    + is_future_sign boolean
  }

  class IkaRejectedSignEventRaw {
    + sign_id string
    + is_future_sign boolean
  }

  class SuiEventNode {
    + type string
    + json any
    + txDigest string
  }

  class NetworkConfig {
    + name string
  }

  Processor --> IndexerStorage : uses
  Processor --> EventFetcher : uses
  SuiGraphQLClient ..|> EventFetcher
  Processor --> SuiEventHandler : creates
  SuiEventHandler --> IndexerStorage : uses
  IndexerStorage --> PkgCfg : returns
  SuiEventHandler --> IkaCompletedSignEventRaw : parses
  SuiEventHandler --> IkaRejectedSignEventRaw : parses
  EventFetcher --> EventsBatch : returns
  EventsBatch --> SuiEventNode : contains
Loading

File-Level Changes

Change Details Files
Generalize event polling to support both nBTC and Ika coordinator events, with separate cursors and module-aware fetching.
  • Rename pollAllNbtcEvents to pollAllEvents and extend it to build a combined package list including optional coordinator packages per setup
  • Introduce an allPackages structure with module and isCoordinator flags, assigning negative cursor IDs for coordinator streams to keep cursors independent from nBTC cursors
  • Update cursor retrieval and persistence to use cursorId (including negative IDs) and to loop over allPackages instead of nbtcPkgs
  • Pass module information into eventFetcher.fetchEvents and use composite packageId::module keys when reading results
  • Route fetched events to either handleEvents or the new handleIkaEvents based on isCoordinator, with error logging around per‑package processing
  • Adjust error logging metadata in pollAllEvents to use the new method name and per‑package fields
packages/sui-indexer/src/processor.ts
packages/sui-indexer/src/graphql-client.ts
packages/sui-indexer/src/index.ts
Add handling for Ika coordinator CompletedSign and RejectedSign events, with structured logging and error reporting.
  • Extend models with IkaCompletedSignEventRaw and IkaRejectedSignEventRaw interfaces describing sign_id, signature, and is_future_sign fields
  • Add handleIkaEvents to SuiEventHandler to dispatch coordinator_inner CompletedSignEvent and RejectedSignEvent types to dedicated handlers with robust error logging
  • Implement handleCompletedSign to parse the event payload and log an info-level message summarizing the completed signature, leaving TODO for redeem_solver integration
  • Implement handleRejectedSign to parse the event payload and log a warn-level message for rejected signatures, also with a TODO for redeem_solver integration
  • Import logError into handler.ts and use it in the new Ika event handling code
packages/sui-indexer/src/handler.ts
packages/sui-indexer/src/models.ts
Wire coordinator package IDs into storage and configuration, including a DB migration and temporary config-based fallback.
  • Extend PkgCfg to include an optional coordinator_pkg field representing the Ika coordinator package ID
  • Update getActiveNbtcPkgs query to select coordinator_pkg from the setups table in addition to id and nbtc_pkg
  • Introduce IKA_COORDINATOR_PACKAGES config mapping SuiNet to default coordinator package IDs for testnet and mainnet, marked as temporary
  • Enhance getActiveNbtcPkgs to fall back to IKA_COORDINATOR_PACKAGES when coordinator_pkg is null in the database
  • Add a SQL migration that adds a coordinator_pkg TEXT column to the setups table with a comment describing its purpose
packages/sui-indexer/src/storage.ts
packages/sui-indexer/src/config.ts
packages/btcindexer/db/migrations/0002_add_coordinator_pkg.sql

Assessment against linked issues

Issue Objective Addressed Explanation
#297 Extend the sui-indexer to poll Ika coordinator events associated with each active nBTC setup (for signature completion/failure).
#297 Handle Ika coordinator signature completion and rejection events in the sui-indexer (at least decoding and logging, enabling tracking of completion/failure).

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • Using cursorId: -pkg.id and then persisting it as setupId: p.cursorId changes the meaning of setupId for cursors and may conflict with DB constraints/foreign keys; consider explicitly separating cursor namespaces (e.g., an extra module/kind column or composite key) instead of overloading setupId with negative values.
  • The event type checks in handleIkaEvents rely on e.type.includes("::coordinator_inner::CompletedSignEvent")/RejectedSignEvent; if the type format is known, it would be safer to compare against the full expected type string to avoid accidentally matching unrelated events.
  • The string literal "coordinator_inner" is used in multiple places (GraphQL filters and handler type checks); consider extracting it (and the full event type suffixes) into shared constants to keep the filtering and handling logic in sync and reduce the risk of typos.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Using `cursorId: -pkg.id` and then persisting it as `setupId: p.cursorId` changes the meaning of `setupId` for cursors and may conflict with DB constraints/foreign keys; consider explicitly separating cursor namespaces (e.g., an extra `module`/`kind` column or composite key) instead of overloading `setupId` with negative values.
- The event type checks in `handleIkaEvents` rely on `e.type.includes("::coordinator_inner::CompletedSignEvent")`/`RejectedSignEvent`; if the type format is known, it would be safer to compare against the full expected type string to avoid accidentally matching unrelated events.
- The string literal `"coordinator_inner"` is used in multiple places (GraphQL filters and handler type checks); consider extracting it (and the full event type suffixes) into shared constants to keep the filtering and handling logic in sync and reduce the risk of typos.

## Individual Comments

### Comment 1
<location> `packages/sui-indexer/src/processor.ts:67-76` </location>
<code_context>
+				for (const p of allPackages) {
</code_context>

<issue_to_address>
**issue (bug_risk):** Avoid advancing the cursor when event handling fails to prevent data loss.

Because the try/catch now wraps `handleEvents` / `handleIkaEvents`, a thrown error still leads to advancing the cursor:

```ts
try {
  const handler = new SuiEventHandler(this.storage, p.setupId);
  if (p.isCoordinator) {
    await handler.handleIkaEvents(result.events);
  } else {
    await handler.handleEvents(result.events);
  }
} catch (error) {
  logError(...);
}

if (result.endCursor && result.endCursor !== cursors[p.cursorId]) {
  cursorsToSave.push({ setupId: p.cursorId, cursor: result.endCursor });
  cursors[p.cursorId] = result.endCursor;
}
```

If the handler throws, those events are effectively dropped and never reprocessed. Please ensure the cursor is only advanced on successful processing (e.g., move the cursor update into the `try` after the `await`s, or otherwise skip cursor updates when a package fails).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Signed-off-by: Rayane Charif <rayane.charif@gonative.cc>
Copy link
Contributor

@sczembor sczembor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets improve the namings a little bit. If we are looping through all packages, lets just call it packages rather than allPackages it doesnt tell us anything extra

Copy link
Contributor

@sczembor sczembor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets exctract the ika events logic to a separte function, the pollEvetns method is too complex

setupId: this.setupId,
});

// TODO: Call redeem_solver service binding to record signature
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets add it here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setupId: this.setupId,
});

// TODO: Call redeem_solver service binding to mark signature as rejected
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Get coordinator package from Ika SDK
const network = this.netCfg.name;
const coordinatorPkg =
network === "mainnet" || network === "testnet"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets add a note why we only handle two networks here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


if (coordinatorPkg) {
packages.push({
cursorId: -1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you explain the -1? i am a little bit lost

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use -1 to avoid collisions with the nBTC cursor IDs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you give an example of how it works

});
}

if (coordinatorPkg) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets extract the ika events to a separate function and just call it from here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@sczembor
Copy link
Contributor

I think the description is outdated

Signed-off-by: Rayane Charif <rayane.charif@gonative.cc>
Signed-off-by: Rayane Charif <rayane.charif@gonative.cc>
Signed-off-by: Rayane Charif <rayane.charif@gonative.cc>
@Rcc999
Copy link
Contributor Author

Rcc999 commented Jan 23, 2026

based on a PP with Stan, this pr was false and decided to start from scratch : #322

@Rcc999 Rcc999 closed this Jan 23, 2026
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.

nBTC Redeem: Add Ika event listening to sui-indexer

3 participants