Severity: Medium
Type: Bug
Scope: Campaigns, Donations
Labels: bug, help wanted, Official Campaign
Description
CampaignsService.recalculateCampaignStats (src/campaigns/campaigns.service.ts, lines ~270–286) sets raisedAmount = sum(donations where status = 'CONFIRMED'). It is invoked from createDonation after each successful confirmation.
The Donation.status enum supports REFUNDED (prisma/schema.prisma); when a refund flow lands, the underlying row flips from CONFIRMED to REFUNDED and is excluded from the aggregation. However, no caller ever re-runs recalculateCampaignStats on refund, so Campaign.raisedAmount continues to include the refunded value. Sequential refunds will monotonically grow the discrepancy between on-chain balance and recorded total.
Recommendation
- After any state transition that flips a donation out of
CONFIRMED (REFUNDED, FAILED), re-call recalculateCampaignStats from the same code path.
- Add a Prisma
$transaction that updates the status and the campaign aggregate atomically.
- Add a regression test that issues a refund and asserts
Campaign.raisedAmount decreases by exactly the refunded amount.
Severity: Medium
Type: Bug
Scope: Campaigns, Donations
Labels:
bug,help wanted,Official CampaignDescription
CampaignsService.recalculateCampaignStats(src/campaigns/campaigns.service.ts, lines ~270–286) setsraisedAmount = sum(donations where status = 'CONFIRMED'). It is invoked fromcreateDonationafter each successful confirmation.The
Donation.statusenum supportsREFUNDED(prisma/schema.prisma); when a refund flow lands, the underlying row flips fromCONFIRMEDtoREFUNDEDand is excluded from the aggregation. However, no caller ever re-runsrecalculateCampaignStatson refund, soCampaign.raisedAmountcontinues to include the refunded value. Sequential refunds will monotonically grow the discrepancy between on-chain balance and recorded total.Recommendation
CONFIRMED(REFUNDED,FAILED), re-callrecalculateCampaignStatsfrom the same code path.$transactionthat updates the status and the campaign aggregate atomically.Campaign.raisedAmountdecreases by exactly the refunded amount.