Skip to content

Escrow funding event, resumable SDK batches, endpoint health, and worker donation dry-run - #1024

Merged
DevMuhdishaq merged 2 commits into
Dfunder:mainfrom
ykargeee-bit:feature/escrow-funding-event-sdk-batch-health-worker-dryrun
Oct 18, 2026
Merged

DevMuhdishaq merged 2 commits into
Dfunder:mainfrom
ykargeee-bit:feature/escrow-funding-event-sdk-batch-health-worker-dryrun

Conversation

@ykargeee-bit

@ykargeee-bit ykargeee-bit commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Four open issues, one branch: an additive escrow funding event, a resumable
batch checkpoint for the SDK, actionable endpoint health + error
classification, and a worker donation dry-run. All changes are additive where
they touch existing APIs.

#978 — Emit an escrow funding event (and audit the transitions)

An artist had no way to learn when an escrow was funded short of polling. The
existing escrow/created event only carries (commission_id, amount), and its
payload is indexed downstream, so it is left untouched.

  • create_escrow also emits the additive topic escrow/funded with
    (commission_id, funder, amount, asset, ledger) — enough to notify without a
    follow-up read.
  • finalize_atomic_commit emits escrow/settled, so a client watching the
    four-step atomic-commit flow is not forced to poll get_atomic_commit.
  • Every escrow transition is audited and the events documented in
    docs/EVENTS.md (new state-transition audit table) and
    docs/event_schemas.md (EscrowFunded, EscrowSettled).
  • Covered by contracts/escrow/src/funding_event_tests.rs.

#979 — Resumable batch submission

BatchBuilder::take_calls() is one-shot: a process that dies between two
submissions loses the rest of the batch.

  • New sdk::batch_checkpoint: BatchCheckpoint (serde-ready), a
    caller-supplied BatchCheckpointStore trait (MemoryCheckpointStore for
    tests, FileCheckpointStore with atomic temp-write + rename), and
    ResumableBatch::submit_remaining, which persists after every call.
  • Additive BatchBuilder::checkpoint / from_checkpoint; no existing method
    changed.
  • The guarantee is stated plainly: submission is at-least-once, a crash
    loses at most the one in-flight call, and callers must make calls idempotent
    (deterministic per-call marker) to dedupe. This is complementary to
    retry_async, not a replacement.

#980 — Endpoint health + actionable error taxonomy

  • New StellarAidError variants: EndpointUnreachable, RequestTimeout,
    BadRequest, TransactionRejected, with is_retryable() and
    from_reqwest/from_http_status. Adding variants breaks exhaustive matches;
    acceptable for an unreleased SDK and called out here.
  • retry_async_classified (predicate) and retry_stellar_async — the fix that
    lets retrying stop on a non-retryable error instead of retrying it fully.
  • Typed SorobanRpcClient::health() and HorizonClient::health(), bounded by a
    short HEALTH_PROBE_TIMEOUT, for setup/diagnostics only.
  • Config::validate() verifies the URLs and that the passphrase matches the
    named network, so the most common integration mistake fails immediately with
    a clear message.

#981 — Worker donation dry-run

  • New POST /api/donations/validate builds the same XDR as submit and simulates
    it through the real SorobanRpcClient::simulate_transaction RPC — not the
    placeholder TransactionSimulator, which returns success unconditionally
    (Fix SDK transaction_simulator.rs returning placeholder results #795). It returns valid, gas_consumed, the contract's own error, and an
    extracted error_code.
  • Cheap local validation first (donor, positive amount, non-zero campaign id,
    memo length) so obvious failures cost no round trip. The route has its own
    tighter rate-limit budget; /api/donations/submit is unchanged. A stale-since-
    simulation note is included in every response.
  • The success / contract-error / RPC-failure interpretation is a pure function
    (interpret_simulation), so those paths are unit-tested without a network
    round trip.
  • Unit tests: valid request, invalid campaign id, zero amount, empty donor,
    over-long memo, error-code extraction, and the three simulation outcomes.

Enabling fixes (called out deliberately)

These are outside the four issues but required for the SDK/worker to build; the
issue bodies name them as prerequisites.

  • stellar-strkey 0.0.2 → 0.0.8 (workspace). The SDK is written against the
    0.0.8 API (stellar_strkey::ed25519::*, stellar_strkey::Contract); 0.0.2
    exposes neither. 0.0.8 is also what stellar-xdr 21 already pulls in, so the
    duplicate 0.0.2 copy goes away. transaction_builder.rs moves from the
    nonexistent Strkey::ContractId to Strkey::Contract.
  • ed25519-dalek declared in sdk (already a workspace dependency) for the
    key derivation in utils/keypair.rs and setup/token_setup.rs.
  • hmac, sha2, hex declared in worker, already used by
    webhooks.rs::verify_signature.

Notes / limitations

Closes #978
Closes #979
Closes #980
Closes #981

@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@ykargeee-bit 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

…ealth, donation dry-run

Additive escrow funding notification, resumable SDK batch submission,
actionable endpoint health/error classification, and a worker donation
dry-run. One branch for Dfunder#978/Dfunder#979/Dfunder#980/Dfunder#981.

Dfunder#978 — Escrow funding event + transition audit
- `create_escrow` now also emits the additive topic `escrow`/`funded`
  carrying `(commission_id, funder, amount, asset, ledger)`. The existing
  `escrow`/`created` topic and its `(commission_id, amount)` payload are
  unchanged, so downstream indexers keep working.
- `finalize_atomic_commit` now emits `escrow`/`settled`, so a client
  watching the multi-step atomic-commit flow no longer has to poll
  `get_atomic_commit` after each of the four steps.
- Audited every escrow transition (create/fund, partial, release, auto
  release, refund, dispute open, dispute resolve, cancel, expire) and
  documented the events in `docs/EVENTS.md` (with a state-transition audit
  table) and `docs/event_schemas.md` (`EscrowFunded`, `EscrowSettled`).
- New `contracts/escrow/src/funding_event_tests.rs` covers the funded
  event values, the unchanged `created` payload, and the finalize event.

Dfunder#979 — Resumable batch checkpoint
- New `sdk::batch_checkpoint`: `BatchCheckpoint` (serde-serialisable calls +
  per-call `CallState` + cursor), a caller-supplied `BatchCheckpointStore`
  trait (with in-memory and atomic file implementations), and
  `ResumableBatch::submit_remaining`, which persists after every call.
- Additive `BatchBuilder::checkpoint` / `from_checkpoint`; no existing
  `BatchBuilder` method changed.
- Documented the guarantee explicitly: submission is at-least-once, a crash
  loses at most the one in-flight call, and callers must make calls
  idempotent (deterministic per-call marker) to dedupe. Complements
  `retry_async` rather than replacing it.

Dfunder#980 — Endpoint health + actionable error taxonomy
- New `StellarAidError` variants `EndpointUnreachable`, `RequestTimeout`,
  `BadRequest`, `TransactionRejected`, plus `is_retryable()` and
  `from_reqwest`/`from_http_status` classifiers. (Adding variants is a
  breaking change for exhaustive matches; acceptable for an unreleased SDK.)
- `retry_async_classified` (predicate) and `retry_stellar_async`, so
  retrying can stop on a non-retryable error instead of retrying it fully.
- Typed `SorobanRpcClient::health()` and `HorizonClient::health()`, bounded
  by a short `HEALTH_PROBE_TIMEOUT`; intentionally not a per-operation
  precondition.
- `Config::validate()` checks the URL shapes and that the passphrase
  matches the named network — the most common integration mistake now fails
  immediately with a clear message.

Dfunder#981 — Worker donation dry-run
- New `POST /api/donations/validate` builds the same XDR as submit and
  simulates it via the real `SorobanRpcClient::simulate_transaction` RPC
  (not the placeholder `TransactionSimulator`, which returns success
  unconditionally). It reports `valid`, `gas_consumed`, the contract's own
  error, and an extracted `error_code`.
- Cheap local validation first (donor, positive amount, non-zero campaign
  id, memo length) so obvious failures cost no round trip; the route has its
  own tighter rate-limit budget. `/api/donations/submit` is unchanged.
- The success / contract-error / RPC-failure interpretation is a pure function
  (`interpret_simulation`), so it is covered without a network round trip.
- Unit tests for a valid request, an invalid campaign id, a zero amount,
  an empty donor, an over-long memo, error-code extraction, and the three
  simulation outcomes.

Enabling fixes needed for the SDK/worker to build:
- Bump the workspace `stellar-strkey` pin from 0.0.2 to 0.0.8 — the SDK is
  written against the 0.0.8 API (`stellar_strkey::ed25519::*`,
  `stellar_strkey::Contract`) and 0.0.2 has neither; 0.0.8 is also what
  `stellar-xdr` 21 already pulls in. `transaction_builder.rs` updated to the
  0.0.8 `Strkey::Contract` variant.
- Declare `ed25519-dalek` in `sdk` (already a workspace dep) for key
  derivation.
- Declare `hmac`, `sha2` and `hex` in `worker` for the webhook signature
  verification already present in `webhooks.rs`.

Note: `worker` is still not a workspace member, so its integration cannot be
verified in CI; that remains a separate prerequisite (as the issue states).

Closes Dfunder#978
Closes Dfunder#979
Closes Dfunder#980
Closes Dfunder#981
@ykargeee-bit
ykargeee-bit force-pushed the feature/escrow-funding-event-sdk-batch-health-worker-dryrun branch 2 times, most recently from a4673c9 to 129d874 Compare October 17, 2026 06:49
@DevMuhdishaq
DevMuhdishaq merged commit 9da4069 into Dfunder:main Oct 18, 2026
1 of 10 checks passed
@DevMuhdishaq

Copy link
Copy Markdown
Contributor

Merged after resolving merge conflicts against main.

Conflict resolutions (merge commit df9bf86):

  • Cargo.toml: both sides already pinned stellar-strkey = "0.0.8"; kept the explanatory comment added by this PR.
  • sdk/src/transaction_builder.rs: this branch did the Strkey::ContractId -> Strkey::Contract rename but retained the old Hash(Uint256(c.0)) wrapping, while main (via fix(sdk): secure key generation and live transaction simulation  #1020) had already renamed and dropped the Uint256 wrapper (Hash(h.0)). Kept main's form, since it is the superset and matches the current stellar_xdr API.

Everything else (escrow funding events, SDK batch paths, worker dry-run, health worker) merged cleanly. Thanks!

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