Skip to content

[Backend] handleTokensWithdrawn increments withdrawnAmount before the dedup guard - replay/reset double-counts and corrupts claimable #802

Description

@grantfox-oss

Telegram (ask questions / claim the issue here first): https://t.me/+DOylgFv1jyJlNzM0

Why this matters

soroban-event-worker.ts:631-641 computes newWithdrawnAmount = BigInt(stream.withdrawnAmount) + BigInt(amount) and applies tx.stream.update UNCONDITIONALLY; the dedup check at line 643 only guards the StreamEvent row insert, not the financial field. The admin POST /v1/admin/indexer/replay resets the cursor and re-polls the same ledgers, so every replay re-adds amount to withdrawnAmount even though the StreamEvent row is skipped. withdrawnAmount inflates -> claimable shrinks -> recipient under-paid. The replay swagger doc even advertises idempotent.

Acceptance criteria

  • Make withdrawnAmount update idempotent (only mutate the stream when the StreamEvent row was newly created, inside the same transaction)
  • Add a test that processes the same tokens_withdrawn event twice and asserts withdrawnAmount changes only once
  • Audit handleStreamCancelled/handleStreamCompleted to confirm they remain idempotent

Files to touch

  • backend/src/workers/soroban-event-worker.ts
  • backend/src/services/indexerService.ts
  • backend/src/routes/v1/admin.routes.ts

Out of scope

  • Changing the on-chain event schema

Metadata

Metadata

Assignees

Labels

Stellar WaveIssues in the Stellar wave programbackendBackend related tasksbugSomething isn't workingcriticaldatabasePostgreSQL / Prisma

Type

No type

Fields

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