Skip to content

feat(stellar): add cancellation and timeouts to the remaining Horizon helpers - #255

Merged
truthixify merged 4 commits into
wraith-protocol:developfrom
aratass:feat/235-stellar-helper-cancellation
Oct 16, 2026
Merged

truthixify merged 4 commits into
wraith-protocol:developfrom
aratass:feat/235-stellar-helper-cancellation

Conversation

@aratass

@aratass aratass commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Closes #235.

Summary

findStrictReceivePath(), buildMultisigStealthWithdraw() and submitOfflineStellarTransaction() could not be cancelled, and the account lookup and the submission had no deadline, so a stalled Horizon request could hang them. This PR brings all three in line with the Horizon and RPC clients from #202:

  1. Optional signal and timeouts on each helper. Path finding and the multisig account lookup take them in their options object; submission takes a new optional third argument, submitOfflineStellarTransaction(xdr, network, { signal, timeouts }).

  2. Every failure maps onto the error taxonomy.

    Failure Error
    The caller aborts, before or during the request New RPCAbortedError (WRAITH/NETWORK/RPC_ABORTED), the signal's reason on cause
    A timeout fires RPCTimeoutError, attempt: 1
    Horizon answers with a non-2xx status RPCRequestError, the Horizon body on context.responseText
    A success response that is not JSON, or an account record without its sequence, thresholds or signers HorizonResponseError from fix(stellar): read and validate Horizon strict-receive responses #251, with the failing part on field
  3. Fake-timer tests for every path of all three helpers.

Design

  • Built on fix(stellar): read and validate Horizon strict-receive responses #251. Path finding keeps its request format, record validation and capped body read; this PR only links the caller's signal to its AttemptDeadline and lets timeouts override the Horizon client defaults.

  • The account lookup and the submission go through one internal helper, src/chains/stellar/request.ts (requestHorizonJson), which sends a single attempt through the same AttemptDeadline. The fetch and the body read race the deadline, so a fetch that ignores the signal still times out, and timers are cleared once the request settles.

  • AttemptDeadline gains abort(error). A caller abort cancels the in-flight fetch and rejects with RPCAbortedError instead of a bare AbortError. An already aborted signal rejects before anything is sent.

  • No retries. These helpers made one request before and still do; a submission must not be repeated blindly.

  • Defaults:

    Helper Default timeouts
    findStrictReceivePath, buildMultisigStealthWithdraw 10 s to the headers, 30 s in total, as in the clients
    submitOfflineStellarTransaction No headers timeout, 60 s in total

    Horizon answers POST /transactions only once the transaction is in a ledger or its own wait expires, so a short headers timeout would cut off normal submissions. The JSDoc and docs warn that a timed-out or aborted submission may still be applied, so callers should look up the hash before resubmitting.

  • BuildMultisigStealthWithdrawOptions already has timeout, the transaction's validity window in seconds. Its doc now says so, to keep it apart from the new timeouts.

Behaviour changes (under Changed in the CHANGELOG)

  • buildMultisigStealthWithdraw(): a non-2xx status from the account lookup is now an RPCRequestError instead of a plain Error, and an incomplete account record is a HorizonResponseError.
  • submitOfflineStellarTransaction(): reports the full /transactions URL on RPCRequestError (it was the base URL), throws HorizonResponseError for a success body that is not a JSON object, and gives up after 60 s by default. { timeouts: { requestMs: 0 } } restores waiting indefinitely.

Semver

Additive: new optional signal and timeouts options, a new optional third argument on submitOfflineStellarTransaction(), and the new RPCAbortedError export. The error-type changes and the 60 s submission default above are behaviour changes, so the CHANGELOG entries sit in the Upcoming: 2.0.0 section (Added and Changed).

Bundle size

After #251, develop measures 30.22 kB of the 30.5 kB Stellar ESM budget; this PR brings it to 30.68 kB. As BUNDLE_SIZE.md asks, the budget change is in the same PR, in its own chore(size) commit, with a note in the table (new budget 31 kB). Happy to drop that commit and trim instead if you'd rather keep 30.5 kB.

Tests

  • test/chains/stellar/helper-timeouts.test.ts (35 tests). Each helper runs the same nine fake-timer cases: the signal reaches fetch and no timers are left; an already aborted signal (no fetch); an abort in flight; the default timeout fires at exactly its value, not 1 ms before; custom timeouts; a fetch that ignores the abort; a body that stalls after the headers; an HTTP error with its body; a success body that is not JSON. Plus account record and submission response checks.
  • test/errors.test.ts: instanceof chain, code, docs link and non-serialised cause for RPCAbortedError.

Checks run locally: pnpm format:check, pnpm build, pnpm test (1,527 SDK tests and the Svelte package), pnpm test:types, pnpm test:exports, pnpm size, pnpm api:check (reports updated in etc/) and pnpm pack:check.

Note

docs/offline-signing.md imports the offline helpers from @wraith-protocol/sdk/chains/stellar, but src/chains/stellar/index.ts does not export them. I left the exports unchanged to keep this PR to the issue. Happy to export them here or in a follow-up.

… helpers

findStrictReceivePath(), buildMultisigStealthWithdraw() and
submitOfflineStellarTransaction() accept an AbortSignal and the same
timeouts as the Horizon and RPC clients. Failures map onto the error
taxonomy: the new RPCAbortedError, RPCTimeoutError, RPCRequestError and
HorizonResponseError.

Refs wraith-protocol#235
The cancellation and timeout support in the one-shot Horizon helpers
brings the Stellar ESM entry to 30.68 KB; develop measured 30.22 KB of
the 30.5 KB budget.

Refs wraith-protocol#235
@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

@aratass Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@truthixify truthixify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cancellation and deadlines are consistently threaded through the remaining Horizon helpers. The focused timeout/error suite passes 75/75 locally and all required CI checks are green.

@truthixify
truthixify merged commit 2fc17e8 into wraith-protocol:develop Oct 16, 2026
22 checks passed
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.

[Wave 10] Add cancellation and timeouts to the remaining Stellar network helpers

3 participants