Conversation
Map each SvmSpoke event name to its generated event data type and type EventWithData as a name-discriminated union, so checking event.name statically narrows event.data. queryEvents() and queryDerivedAddressEvents() return EventWithData<T> for the requested name, and SVMEventNames members are interchangeable with their string literals. The decoder table constructs name/data pairs per event type, proving the correlation at compile time. Mock event construction is consequently checked against the generated types, which surfaced a missing repaymentChainId field in mocked FilledRelay events. Supersedes the interim runtime shape-check in findDeposit(); the types now carry that proof.
|
@codex review |
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
The
The narrowing itself is a genuine improvement — the previous |
Non-mechanical resolution in decodeEvent(): composed svm3's typed SvmSpoke dispatch (correlated DecodedEvent constructor table) with svm2's CCTP codama decoder registry and generic Anchor fallback, in that order. The two sides' event types cannot share one shape, so types.ts gains RawDecodedEvent/RawEventWithData (shared envelope extracted); queryAllEvents() and processEventFromTx() now carry raw events, and the typed queryEvents() narrows raw to EventWithData<T> via its existing predicate, which is sound because every typed event is structurally a raw event. decodeEvent() returns DecodedEvent | RawDecodedEvent accordingly.
…ddress query Typed SvmSpoke event names don't apply here: downstream consumers (the relayer's CCTP flows) query TokenMessengerMinter and MessageTransmitter events through this method with plain string event names, so constraining it to EventName would break them. Return raw (untyped) events instead, and document that results are scoped to transactions referencing the derived address, so callers must associate events themselves. Adds a regression test in the relayer's call pattern.
| ): Promise<EventWithData<T>[]> { | ||
| const events = await this.queryAllEvents(fromSlot, toSlot, options); | ||
| return events.filter((event) => event.name === eventName); | ||
| return events.filter((event): event is EventWithData<T> => event.name === eventName); |
There was a problem hiding this comment.
Blocking: queryAllEvents() returns raw events because createFor(...) can bind this client to an arbitrary IDL, but this predicate treats name equality alone as proof of the SvmSpoke payload layout. An unrelated program can emit an event named FundsDeposited with different data and TypeScript will expose it as SvmSpokeClient.FundsDeposited. I reproduced this with raw data { fromAnotherProgram: true }: TypeScript accepted data.inputAmount as bigint, while it was undefined at runtime. Could queryEvents() require the exact bundled SvmSpokeIdl before returning typed events, or live on a Spoke-specific client instead?
Sent from Reinis Martinsons's Codex CLI Agent using gpt-5.6-sol 🤖
Map each SvmSpoke event name to its generated event data type and type
EventWithData as a name-discriminated union, so checking event.name statically
narrows event.data. queryEvents() returns EventWithData for the requested
name, and SVMEventNames members are interchangeable with their string literals.
The decoder table's type annotation is tightened so each entry's name/data
correlation is proven at compile time. queryDerivedAddressEvents() deliberately
stays untyped (eventName: string, raw events): it is the generic query for
non-SvmSpoke programs, and the relayer's CCTP flows call it with plain string
event names a regression test pins that call pattern. findDeposit()'s
depositId cast is replaced by typed access, and mock event construction is
checked against the generated types, which surfaced a missing repaymentChainId
field in mocked FilledRelay events.