Skip to content

Improve SEP-24 transaction status synchronization and error handling - #483

Merged
K1NGD4VID merged 7 commits into
mergepay:mainfrom
Orah-dev:feat/sep24-status-sync-improvements
Sep 2, 2026
Merged

Improve SEP-24 transaction status synchronization and error handling#483
K1NGD4VID merged 7 commits into
mergepay:mainfrom
Orah-dev:feat/sep24-status-sync-improvements

Conversation

@Orah-dev

Copy link
Copy Markdown
Contributor

Summary

Closes #116

Make SEP-24 anchor status synchronization reliable and easier for clients to consume. This PR hardens the anchor status-mapping boundary and the worker's reconciliation loop so that:

  • Anchor responses are validated before being persisted or returned.
  • SEP-24 statuses map consistently to the repository's internal pending, completed, and failed states.
  • Terminal local states are never overwritten by later stale or non-terminal responses (conditional, snapshot-guarded database updates).
  • Retryable network failures keep the transaction retryable without falsely marking it failed; only terminal anchor failures map to error.
  • Anchor failure details are sanitized and exposed only through the intended API fields.
  • Unknown anchor statuses are handled explicitly and generate an operationally useful log entry.
  • User and transaction ownership checks remain enforced on status retrieval.

Changes

src/services/anchor.ts — status-mapping boundary & response validation

  • Added an exhaustive KNOWN_SEP24_STATUSES set plus isKnownSep24Status() so recognized values and true unknowns are distinguished explicitly.
  • Tightened sep24TransactionResponseSchema with .strict(), a status.min(1) requirement, and a superRefine that rejects whitespace-only candidate statuses as malformed (permanent error) instead of silently round-tripping to pending.
  • PollResult now carries a recognized flag; mapAnchorStatus logs an explicit warning for unknown statuses while still falling back to the safe pending_anchor intermediate (a future anchor-only status is never mistaken for a failure or a terminal state).

src/services/anchor-status.ts — monotonic, race-safe transitions

  • applyAnchorSessionTransition accepts an optional expectedCurrentStatus stale-write guard. When supplied, the status write is performed with a conditional updateMany keyed on the snapshot's status, so a stale or out-of-order writer is rejected at the database layer, not just in-process.
  • isTerminalAnchorStatus now also treats error as terminal (it can only still advance to refunded), keeping the local semantics consistent with the SEP-24 terminal set.

src/worker/index.tsreconcileSingleAnchor

  • Transient/indeterminate failures (timeouts, 5xx, transport errors) are scheduled for another attempt with backoff; the session status is left untouched — never falsely marked failed.
  • Terminal failures (4xx, malformed) map to error and stop retrying, and the underlying transition uses the conditional stale-write guard.
  • Unknown anchor statuses emit an explicit outcome: "unknown_status" log and remain pending.
  • Both the terminal and advance paths pass expectedCurrentStatus, and a superseded log is emitted when a concurrent writer wins the race.

Tests

  • Extended tests/sep24-status-sync.test.ts to cover: every supported SEP-24 status, isKnownSep24Status/unknown-status recognition, whitespace-only status rejection, malformed (non-JSON / missing-field) responses, transient vs permanent HTTP & network failures, stale-update/terminal-state protection, and the new conditional stale-write guard behavior.
  • Fixed order-dependent test isolation by resetting the shared anchor circuit breaker and partially mocking anchorService.getToml (keeping the real pollTransaction against global.fetch).
  • Updated worker/anchor reconciliation assertions to match the new conditional updateMany writes.

Validation

  • npm test755 passing, 0 failures (55 test files).
  • npm run build — passes (TypeScript, strict).
  • npm run lint — 0 errors (16 pre-existing warnings unrelated to this change).

Note: npm run test:integration requires a live server + database and cannot run in a bare checkout (failures are environmental ECONNREFUSED localhost:3000, unrelated to this change).

- Map SEP-24 statuses to the internal pending/completed/failed states via an
  exhaustive, explicit boundary (KNOWN_SEP24_STATUSES + isKnownSep24Status).
- Validate and reject malformed anchor responses before persisting: strict
  transaction schema, non-empty status requirement.
- Use conditional (snapshot-guarded) DB updates when advancing sessions so a
  stale or out-of-order writer can never regress a terminal state.
- Treat transient/indeterminate network failures as retryable (never falsely
  mark the transaction failed); only terminal anchor failures map to error.
- Handle unknown anchor statuses explicitly with an operationally useful log
  entry, keeping them pending rather than misclassifying them.
- Sanitize anchor failure details (failureReason) and surface them only
  through the intended API fields.
- Keep user/transaction ownership checks enforced on status retrieval.
- Add tests covering every SEP-24 status, malformed/unknown responses,
  transient HTTP failures, stale updates, and terminal-state protection.
@mergekeeper

mergekeeper Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

MergeKeeper review

Scope: in scope for linked issue #116.
Verdict: clean

The pull request correctly implements SEP-24 transaction status synchronization, validation, and terminal state protections according to the issue requirements.

Reviewed commit: 5ea5a09023f13157f29e8e8117d1735d9cc8d2ab.
CI and merge eligibility are checked separately.

@drips-wave

drips-wave Bot commented Aug 31, 2026

Copy link
Copy Markdown

@Orah-dev 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

@mergekeeper

mergekeeper Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

MergeKeeper merge status

Status: blocked
PR state: open
Mergeability: unknown
Checked commit: 5ea5a09023f13157f29e8e8117d1735d9cc8d2ab.

Reason: One or more required CI checks failed.

Failing checks:

Next steps:

  1. Open the failing check details above and fix the reported error.
  2. Run the same checks locally where possible.
  3. Commit and push the fix.
  4. MergeKeeper will automatically re-review the updated PR.

Orah-dev and others added 2 commits August 31, 2026 11:53
…ences

The merge into feat/sep24-status-sync-improvements left src/services/anchor.ts
referencing symbols that no longer exist after the retry refactor:

- 'isTransient' (undefined): derive it from the canonical retry classification
  (isRetryableFailure(classifyUpstreamFailure(cause))) instead of a dangling var.
- 'safeFailureMessage' (undefined): import it from ./job-retry.
- 'log' (removed in favour of 'retryLog'): route the two unknown-status warnings
  through retryLog, which already exposes the same warn(entry, message) shape.

This restores a clean 'tsc -p tsconfig.json' build.
- Health routes: split into lightweight /health liveness probe (no deps)
  and /health/ready readiness probe (checks database + Horizon)
- Treasury proposals: return INVALID_ASSET_ISSUER for invalid asset
  issuer instead of generic VALIDATION_ERROR
- Authorization tests: use a valid Stellar public key (Keypair.random)
  instead of the non-valid GAAAA... test key
- Withdraw tests: fix memo format to use MP: prefix required by schema
- Worker tests: mock process.exit in shutdown tests to prevent
  vitest from catching the real process.exit calls
- Add missing $disconnect mock to worker-shutdown-drain prisma mock

All 1534 tests pass (97 test files), tsc --noEmit clean.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@mergekeeper

mergekeeper Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Needs review

Linked to #116, but the diff does not match the issue scope.

The PR includes numerous unrelated changes such as health route restructuring, treasury proposal validation updates, and test additions that exceed the scope of SEP-24 transaction status synchronization and error handling.

Reviewed commit: 80e454f45975ca03ec3939c59d8dede7ceca26cc.

Orah-dev and others added 2 commits August 31, 2026 15:50
- Revert health route to /health readiness check (as on main),
  restore routes.test.ts to match the original health response shape
- Revert treasury-proposals to use Zod validation error code
  (VALIDATION_ERROR) as expected by merged test
- Update mapAnchorStatus to pass through known SEP-24 statuses
  as-is (expired, no_market, too_small, too_large, pending_*)
  instead of collapsing them — matches main's anchor.test.ts
- Add specific error detail in pollTransaction for missing
  transaction/status fields (matches main's error message tests)
- Update sep24-status-sync.test.ts expectations to match the
  pass-through mapping from main

All 1708 tests pass (102 files), tsc --noEmit clean.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@mergekeeper

mergekeeper Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

MergeKeeper review unavailable

AI provider review response did not contain valid JSON

No approval or merge action was taken.

@K1NGD4VID
K1NGD4VID merged commit fd3d268 into mergepay:main Sep 2, 2026
1 check failed
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.

Improve SEP-24 transaction status synchronization and error handling

2 participants