Skip to content

Task 1-2: Granular Error Codes cho Rental Failures

Phase: 1 Priority: Medium Module: rental Depends on: Không có Reference: docs/bountyhunter-blockchain-p2/details/feature-nft-rental/SPEC.md

Background

Hiện tại tất cả lỗi trong RentalService được map vào các error code rất chung: CREATE_RENTAL_FAILED, RETURN_RENTAL_FAILED, RENT_FAILED. Điều này làm cho Backend (và người dùng) không thể phân biệt lý do thất bại cụ thể — số dư không đủ, NFT không thuộc sở hữu, hay contract bị revert do điều kiện nghiệp vụ. Cần thêm subcodes chi tiết bằng cách parse revert reason từ blockchain error.

Tasks

Note: Blockchain revert reason thường nằm trong error.reason, error.data, hoặc error.message. Với ethers.js v5: error.reason hoặc error.error?.reason. Với Solana: parse từ error.logs array. Parse cần xảy ra trong catch block trước khi throw hoặc return error response.

  • [ ] Định nghĩa granular error codes trong rental.constants.ts hoặc errors.constants.ts:
    export const RENTAL_ERROR_CODES = {
      // Generic (giữ backward compatible)
      CREATE_RENTAL_FAILED: 'CREATE_RENTAL_FAILED',
      RETURN_RENTAL_FAILED: 'RETURN_RENTAL_FAILED',
      RENT_FAILED: 'RENT_FAILED',
      CANCEL_RENTAL_FAILED: 'CANCEL_RENTAL_FAILED',
    
      // Granular subcodes
      RENTAL_INSUFFICIENT_BALANCE: 'RENTAL_INSUFFICIENT_BALANCE',
      RENTAL_NFT_NOT_OWNED: 'RENTAL_NFT_NOT_OWNED',
      RENTAL_CONTRACT_REVERTED: 'RENTAL_CONTRACT_REVERTED',
      RENTAL_NFT_ALREADY_RENTED: 'RENTAL_NFT_ALREADY_RENTED',
      RENTAL_PERIOD_EXPIRED: 'RENTAL_PERIOD_EXPIRED',
      RENTAL_UNSUPPORTED_CHAIN: 'RENTAL_UNSUPPORTED_CHAIN',
      RENTAL_RPC_TIMEOUT: 'RENTAL_RPC_TIMEOUT',
    } as const;
    
  • [ ] Tạo helper parseRentalError(error: unknown): string:
    function parseRentalError(error: unknown): string {
      const message = error instanceof Error ? error.message : String(error);
      const reason = (error as any)?.reason ?? '';
    
      if (message.includes('insufficient funds') || reason.includes('insufficient')) {
        return RENTAL_ERROR_CODES.RENTAL_INSUFFICIENT_BALANCE;
      }
      if (reason.includes('not the owner') || message.includes('not owner')) {
        return RENTAL_ERROR_CODES.RENTAL_NFT_NOT_OWNED;
      }
      if (reason.includes('already rented') || reason.includes('isRented')) {
        return RENTAL_ERROR_CODES.RENTAL_NFT_ALREADY_RENTED;
      }
      if (message.includes('timeout') || message.includes('TIMEOUT')) {
        return RENTAL_ERROR_CODES.RENTAL_RPC_TIMEOUT;
      }
      if (message.includes('reverted')) {
        return RENTAL_ERROR_CODES.RENTAL_CONTRACT_REVERTED;
      }
      return RENTAL_ERROR_CODES.CREATE_RENTAL_FAILED; // fallback
    }
    
  • [ ] Cập nhật catch blocks trong RentalService (và RentalProcessor sau task-1-1):
    catch (error) {
      const errorCode = parseRentalError(error);
      this.logger.error(`[RENTAL] action=CREATE_RENT errorCode=${errorCode} message=${error.message}`);
      throw new RpcException({ code: errorCode, message: error.message });
    }
    
  • [ ] Trả về errorCode trong HTTP error response và webhook failure payload

Verification / Acceptance Criteria

  • [ ] Simulate insufficient funds error từ EVM → errorCode = 'RENTAL_INSUFFICIENT_BALANCE'
  • [ ] Simulate not owner revert → errorCode = 'RENTAL_NFT_NOT_OWNED'
  • [ ] Simulate already rented revert → errorCode = 'RENTAL_NFT_ALREADY_RENTED'
  • [ ] Lỗi không xác định → fallback về generic code (không throw unhandled exception)
  • [ ] Error response HTTP có trường code với giá trị từ RENTAL_ERROR_CODES
  • [ ] TypeScript compile không có lỗi

Files to Modify

  • src/rental/rental.service.ts
  • src/rental/constants/rental.constants.ts (tạo mới hoặc cập nhật)