Description
processRewardClaim in src/modules/rewards/reward.service.ts:67-78 marks rewardClaimed: true (line 67-70), then separately increments credits (line 72-77) with two independent DB calls (no transaction wrapping).
If the process crashes or the second update fails after the first succeeds:
rewardClaimed is true but credits never added → user permanently loses the reward (retry sees rewardClaimed === true on line 37 and skips)
- If on-chain claim succeeded but DB update fails → retry attempts second on-chain claim → double-spend
Impact
Inconsistent state between on-chain and DB with no recovery path.
File
src/modules/rewards/reward.service.ts:67-78
Suggested Fix
Wrap the rewardClaimed update and credit increment in a single DB transaction using db.transaction().
Description
processRewardClaiminsrc/modules/rewards/reward.service.ts:67-78marksrewardClaimed: true(line 67-70), then separately increments credits (line 72-77) with two independent DB calls (no transaction wrapping).If the process crashes or the second update fails after the first succeeds:
rewardClaimedistruebut credits never added → user permanently loses the reward (retry seesrewardClaimed === trueon line 37 and skips)Impact
Inconsistent state between on-chain and DB with no recovery path.
File
src/modules/rewards/reward.service.ts:67-78Suggested Fix
Wrap the
rewardClaimedupdate and credit increment in a single DB transaction usingdb.transaction().