Skip to content

w3sper: Write-call ergonomics and event delivery gaps #4080

Description

@ichbindas

Summary

Four gaps in the generic contract-call/event layer: no help constructing signature-bearing call arguments, write calls that return nothing about their own effect, events with no correlating metadata, and no bulk-subscribe/resume for event streams.

1. No helper for building signed, structured contract-call arguments

Problem: Some contract calls require structured arguments containing a domain signature - e.g. a call needing a signature over a specific constructed message. w3sper's call-building/encoding layer offers no support for this: constructing the message and producing the signature has to be done entirely by hand, reimplementing key-derivation and signing logic in JS, even though the SDK already ships key-derivation/signing primitives elsewhere for its own transaction signing.

Example:

// what we'd want: the SDK helping construct + sign a schema-typed argument
// const signedArg = await profile.signStructuredArg(schema, fields);

// what we do: hand-roll the entire message construction + signing path
// ourselves, independently of anything the SDK's own tx-signing code does
const message = buildSignatureMessage(/* ...201 bytes, by hand... */);
const signature = signWithBls(profile, message); // our own implementation

Current workaround / suggested fix: We reimplemented the relevant signing logic from scratch, duplicating cryptographic code the SDK already has internally for its own purposes. Exposing that same signing capability as a general-purpose helper - "sign this byte message with this profile's key" - would let consumers building signature-bearing arguments reuse the SDK's implementation instead of independently re-deriving it (and independently maintaining its correctness).

2. Contract write calls return no effect data about what they actually did

Problem: Calls that mutate contract state don't return the resulting on-chain effect (e.g. an amount released) from the submission call itself. To learn what a write actually did, callers must separately pre-subscribe to a correlated contract event before submitting, race it against a timeout, and handle the "nothing arrived in time" case - essentially building an event-correlation layer just to observe your own transaction's outcome. This is distinct from the return-type inconsistency covered in #4072 - here nothing meaningful comes back at all, consistently.

Example:

// submission returns only confirmation that it landed, not its effect:
await executeContractTx(...); // succeeded - but released how much?

// so we pre-arm a correlated event listener before submitting, and race it:
const resultPromise = awaitContractEvent("liquidate", provisionerId, { timeoutBlocks: 10 });
await executeContractTx(...);
const result = await resultPromise; // now we know the amount

Current workaround / suggested fix: We built a dedicated await-and-correlate module for this, including a deliberate no-op .catch() to avoid an unhandled-rejection crash if the event never arrives. Having write calls resolve with a structured result describing their actual on-chain effect - the same data the corresponding event carries - would remove the need to build a parallel event-correlation path just to observe a transaction's own outcome.

3. Contract events carry no correlating block/tx metadata

Problem: Event payloads delivered via contract.events.<name>.on(...) include only the decoded event data - no block height, no transaction hash. Any consumer that needs to know when or in which transaction an event happened has to build its own correlation layer.

Example:

contractInstance.events.liquidate.on((event) => {
  // event has the decoded payload only - no blockHeight, no txHash
  // we maintain a separate block-tx cache + fallback GraphQL lookup
  // purely to attach that metadata ourselves
});

Current workaround / suggested fix: We built a two-tier fallback (first check our own executor-row correlation, then a block-tx cache backed by a GraphQL lookup) just to attach block height and tx hash to each event. Including a basic correlating envelope - block height and tx hash - alongside every emitted event would remove an entire correlation subsystem that every event-driven consumer likely needs to build independently otherwise.

4. No bulk-subscribe and no resume-from-height for contract events

Problem: Subscribing to a contract's events means registering each event type individually (events.deposit.on(...), events.donate.on(...), events.reward.on(...), etc. - one call per event name). On every disconnect, all of these have to be torn down and individually re-registered one at a time. There's no "subscribe to everything on this contract" call, and no cursor/height-based resubscribe - anything emitted during a disconnected window is permanently lost from the live stream unless the consumer has its own out-of-band reconciliation.

Example:

// one call per event type, repeated on every reconnect:
contractInstance.events.deposit.on(handleDeposit);
contractInstance.events.donate.on(handleDonate);
contractInstance.events.reward.on(handleReward);
// ...N more, each separately awaited, each needing its own re-registration logic

Current workaround / suggested fix: We rely on a periodic backfill query against chain history to catch anything missed during a disconnected window, as a reconciliation bandaid rather than a real fix - it papers over gaps after the fact rather than preventing them. A bulk-subscribe API (contract.events.on("*", handler), or a way to subscribe to a named group) and a resume-from-block-height option on reconnect would remove both the per-event-type registration boilerplate and the permanent-loss window.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions