Skip to content

[CRITICAL] — Idempotent createDonation path returns a stale FAILED / PENDING row without re-verifying on-chain status, permanently masking recovered donations #2

Description

@Alqku

Severity: Critical
Type: Bug
Scope: Donations, Stellar
Labels: bug, help wanted, Official Campaign

Description

DonationsService.createDonation (src/donations/donations.service.ts, lines ~46–72) short-circuits when prisma.donation.findUnique({ where: { txHash } }) returns a row, returning the previously stored DonationResponseDto verbatim. There is no live re-check against Horizon or the Soroban RPC, even when the stored status is PENDING or FAILED.

Concrete impact:

  • If verifyDonationOnChain polled during a Stellar RPC outage and stored FAILED, but the transaction actually succeeded on-chain, every subsequent POST with the same txHash returns the stale FAILED row. The user's UI shows the donation was rejected even though it is in the ledger.
  • If a future flow promotes the row to REFUNDED and the user retries, the idempotent path returns the pre-refund CONFIRMED payload.
  • Clients cannot distinguish "idempotent replay" from "actual recovery", so the only remediation today is a manual database fix.

Recommendation

  • On the idempotent branch, if donation.status is PENDING or FAILED, re-run stellarTxs.verifyDonationTransaction and update the row before returning.
  • Add an idempotency window (e.g. stop re-polling after 30 seconds) to absorb legitimate retry storms from the wallet client instead of triggering one Horizon call per retry.
  • Include a recovered: boolean flag in the response so consumers can tell a fresh confirmation apart from a cached one.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignAudit finding under the Official CampaignbugSomething isn't workinghelp wantedExtra attention is needed

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions