Skip to content

fix: JWT wallet passthrough, duration-aware premium, Unix-second timestamps, pagination clamping#143

Open
CelestinaBeing wants to merge 1 commit into
Parashield-Protocol:mainfrom
CelestinaBeing:feat/issues-111-112-113-114-policy-claims-fixes
Open

fix: JWT wallet passthrough, duration-aware premium, Unix-second timestamps, pagination clamping#143
CelestinaBeing wants to merge 1 commit into
Parashield-Protocol:mainfrom
CelestinaBeing:feat/issues-111-112-113-114-policy-claims-fixes

Conversation

@CelestinaBeing

Copy link
Copy Markdown

Summary

Four backend bug fixes across policy.controller.ts, policy.service.ts, and claims.service.ts:

#112confirmPolicy does not pass wallet from JWT to service

Root cause: When req.user?.walletAddress and req.wallet were both absent, authedWallet was undefined and no UnauthorizedException was thrown. The service received only dto and never saw the JWT-verified wallet.

Fix:

  • confirmPolicy now explicitly throws UnauthorizedException('Not authenticated') when authedWallet is falsy.
  • authedWallet is passed to confirmAndCreatePolicy(dto, authedWallet) as a typed second argument.
  • The service validates tx.source === authenticatedWallet (JWT wallet) first, then tx.source === dto.walletAddress, so neither can be spoofed independently.

#111calculatePremium does not account for duration

Root cause: calculatePremium(coverageXlm, premiumRate) ignored duration, so a 30-day and a 365-day policy quoted the same premium.

Fix:

  • calculatePremium signature extended to (coverageXlm, premiumRate, durationDays).
  • Formula: Math.ceil(coverage * rate * duration / (10000 * 30)) — pro-rated against a 30-day base period, matching the PolicyEngine contract.
  • Both the quote in buyPolicy (controller) and the stored premiumPaid in createPolicy (service) now pass dto.duration.

#113createPolicy uses JavaScript Date milliseconds vs Soroban Unix seconds

Root cause: new Date() includes milliseconds; Soroban timestamps are Unix seconds. A policy stored with endTime = new Date(ms) where ms % 1000 ≠ 0 could expire 0–999 ms before the on-chain contract expected.

Fix:

const nowSeconds = Math.floor(Date.now() / 1000);
const endTimeSeconds = nowSeconds + dto.duration * 24 * 3600;
const now = new Date(nowSeconds * 1000);      // ms component = 0
const endTime = new Date(endTimeSeconds * 1000); // ms component = 0

#114getClaimsByWallet pagination can skip rows or return unbounded results

Root cause: Raw page and limit values were passed straight to Prisma. page=0 produced skip = -limit (Prisma error or negative offset), limit=0 produced take=0 (empty result forever), and limit=100000 could return the entire table.

Fix:

const safePage  = Math.max(1, page);
const safeLimit = Math.min(Math.max(1, limit), 100);
skip: (safePage - 1) * safeLimit,
take: safeLimit,

Page numbers are 1-based; limit is clamped to 1–100.

Files changed

File Issues
src/policy/policy.controller.ts #111, #112
src/policy/policy.service.ts #111, #112, #113
src/claims/claims.service.ts #114

Closes #111, Closes #112, Closes #113, Closes #114

…um, fix timestamps, clamp pagination

Closes Parashield-Protocol#111, Closes Parashield-Protocol#112, Closes Parashield-Protocol#113, Closes Parashield-Protocol#114

- fix(Parashield-Protocol#112): confirmPolicy now throws UnauthorizedException when no wallet
  in JWT/header (instead of a misleading ForbiddenException), and passes
  the verified authenticatedWallet to confirmAndCreatePolicy as a separate
  argument. The service validates the XDR source against the JWT wallet
  first, then against dto.walletAddress, so neither can be spoofed
  independently.

- fix(Parashield-Protocol#111): calculatePremium now accepts durationDays and pro-rates the
  premium against a 30-day base period (coverage * rate * duration /
  (10000 * 30)), matching the PolicyEngine contract formula. Both the
  buyPolicy quote in the controller and the createPolicy call in the
  service now pass dto.duration so short/long policies price correctly.

- fix(Parashield-Protocol#113): createPolicy now derives startTime and endTime from Unix
  seconds (Math.floor(Date.now() / 1000)) before constructing Date
  objects, eliminating the sub-second millisecond component that caused
  policies to expire 0-999 ms early compared to Soroban contract
  timestamps.

- fix(Parashield-Protocol#114): getClaimsByWallet clamps page to min 1 and limit to 1-100
  before calculating skip = (safePage - 1) * safeLimit, preventing
  negative skip values (page=0), zero take values (limit=0), and
  unbounded result sets (limit=10000) that caused row skips or repeats.
@drips-wave

drips-wave Bot commented Jun 27, 2026

Copy link
Copy Markdown

@CelestinaBeing Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment