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
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
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
Files to touch
backend/src/workers/soroban-event-worker.tsbackend/src/services/indexerService.tsbackend/src/routes/v1/admin.routes.tsOut of scope