Task 2-1: Idempotency via txHash trong Queue Job
Phase: 2 Priority: High Module:
marketplaceDepends on: Không có Reference: docs/bountyhunter-blockchain-p2/details/feature-marketplace/SPEC.md
Background
Pattern idempotency thông qua data.txHash tồn tại trong handleSendAdminSellNft: nếu txHash đã có trong job data, processor kiểm tra receipt thay vì gửi lại transaction. Đây là pattern tốt ngăn duplicate transaction khi job bị retry. Tuy nhiên, chưa có bằng chứng pattern này được áp dụng nhất quán trên tất cả 10+ handler. Cần audit và đồng bộ hóa.
Tasks
Note:
job.update(data)của Bull cập nhật job data trong Redis — cần gọi sau khi setdata.txHashđể đảm bảo khi job retry, txHash vẫn còn trong data. ImportJobtừbull. Pattern: lấy data từjob.data, set txHash, gọijob.update({ ...job.data, txHash }), sau đó tiếp tục xử lý.
- [ ] Audit toàn bộ 10+
@Process()handler — liệt kê các handler chưa có checkdata.txHash:// Pattern cần có trong mỗi handler: const { txHash: existingTxHash } = job.data; if (existingTxHash) { this.logger.log(`[MARKETPLACE] jobId=${job.id} txHash already exists, checking receipt`); const receipt = await this.checkTransactionReceipt(existingTxHash, chainId); if (receipt?.status === 1) { await postWithRetry(webhookUrl, { status: 'SUCCESS', txHash: existingTxHash }); return; } } - [ ] Thêm
job.update()ngay sau khi có txHash từ on-chain tx:const txHash = await this.executeTransaction(data); await job.update({ ...job.data, txHash }); // Persist txHash vào Redis this.logger.log(`[MARKETPLACE] jobId=${job.id} txHash=${txHash} saved to job`); - [ ] Áp dụng cho tất cả handler xử lý on-chain transaction:
handleSendAdminSellNft— verify đã có, chuẩn hóajob.updatecallhandleUserBuyNfthandleListingNfthandleCancelListingNfthandleBurnTicket- Và các handler còn lại
- [ ] Handler thuần webhook (không có on-chain tx) không cần txHash check — document rõ trong comment
- [ ] Tạo helper
checkTransactionReceipt(txHash, chainId)nếu chưa có — hỗ trợ cả EVM và Solana
Verification / Acceptance Criteria
- [ ] Tất cả handler xử lý on-chain tx đều có check
data.txHashở đầu - [ ] Sau khi tx được broadcast →
job.update({ ...job.data, txHash })được gọi trước khi confirm - [ ] Simulate job retry với txHash đã có → không gửi lại tx, chỉ check receipt
- [ ] Simulate job retry không có txHash → gửi tx bình thường
- [ ] Không có duplicate transaction khi job retry sau khi tx đã broadcast nhưng chưa confirm
- [ ] TypeScript compile không có lỗi
Files to Modify
src/marketplace/marketplace.processor.ts