Skip to content

Task 2-1: Idempotency via txHash trong Queue Job

Phase: 2 Priority: High Module: marketplace Depends 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 set data.txHash để đảm bảo khi job retry, txHash vẫn còn trong data. Import Job từ bull. Pattern: lấy data từ job.data, set txHash, gọi job.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ó check data.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óa job.update call
  • handleUserBuyNft
  • handleListingNft
  • handleCancelListingNft
  • handleBurnTicket
  • 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