@pafi-dev/issuer 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1889,17 +1889,6 @@ interface PTRedeemHandlerConfig {
1889
1889
  * Every `handle()` call validates the request's `pointTokenAddress`
1890
1890
  * against this set BEFORE any chain read, signer call, or pending
1891
1891
  * credit reservation.
1892
- *
1893
- * Audit PACI5-18 (redeem twin of the claim-side finding) — the read
1894
- * surface (`IssuerApiHandlers.handleUser` / `handleRedemptionEvaluate`)
1895
- * enforces this allowlist, but the write surface previously skipped
1896
- * it. The asymmetry meant an issuer burner signer whitelisted on
1897
- * multiple PointTokens could be coerced into signing a valid
1898
- * `BurnRequest` for an off-allowlist token where no BurnIndexer is
1899
- * running — the on-chain burn would succeed but the off-chain
1900
- * pending credit would stay PENDING forever (or worse, resolve via
1901
- * the bundler-receipt fallback against an unbacked credit balance).
1902
- *
1903
1892
  * Pass the SAME set used to construct `IssuerApiHandlers` and
1904
1893
  * `PTClaimHandler` so the read, claim, and redeem paths agree on
1905
1894
  * what this issuer is willing to sign for.
@@ -2295,6 +2284,23 @@ interface PendingUserOpEntry {
2295
2284
  verificationGasLimit: string;
2296
2285
  preVerificationGas: string;
2297
2286
  userOpHash: Hex;
2287
+ /**
2288
+ * Lock id (PendingCredit on redeem, LockedMint on claim) reserved
2289
+ * for the FALLBACK path — distinct from the outer entry's
2290
+ * sponsored lock because the on-chain amount the user burns/mints
2291
+ * differs between variants.
2292
+ *
2293
+ * Audit PACI5-21 — pre-fix `handleMobileSubmit.bindUserOpHash`
2294
+ * always bound the submitted userOpHash to the SPONSORED lockId,
2295
+ * even when the user submitted the fallback variant. The
2296
+ * bundler-receipt fallback in `handleRedeemStatus` then resolved
2297
+ * the sponsored credit (`amount - fee`) against the on-chain
2298
+ * burn of the FULL `amount` — every fee-bearing fallback redeem
2299
+ * permanently under-credited the user by exactly the fee. Surface
2300
+ * the fallback lockId here so submit can route the bind to the
2301
+ * correct ledger row.
2302
+ */
2303
+ lockId?: string;
2298
2304
  };
2299
2305
  /**
2300
2306
  * EIP-7702 authorization tuple — present only on the `delegate`
@@ -2515,6 +2521,21 @@ interface PrepareMobileUserOpParams {
2515
2521
  maxFeePerGas?: bigint;
2516
2522
  maxPriorityFeePerGas?: bigint;
2517
2523
  };
2524
+ /**
2525
+ * Optional separate lock id for the FALLBACK path — required when
2526
+ * the upstream handler reserves a DIFFERENT ledger row for the
2527
+ * fallback variant (PTRedeemHandler reserves
2528
+ * `amount` for fallback vs `amount - fee` for sponsored).
2529
+ *
2530
+ * Audit PACI5-21 — when omitted, `handleMobileSubmit` binds the
2531
+ * fallback userOpHash to the outer sponsored `lockId`, which made
2532
+ * `handleRedeemStatus`'s bundler-receipt fallback resolve the
2533
+ * SMALLER sponsored credit against the on-chain burn of the FULL
2534
+ * amount. Every fee-bearing fallback redeem permanently
2535
+ * under-credited the user by exactly the fee. Pass the fallback
2536
+ * lockId here so submit can route the bind to the correct row.
2537
+ */
2538
+ lockIdFallback?: string;
2518
2539
  /** Paymaster sponsorship response, or `undefined` if PAFI declined. */
2519
2540
  paymasterFields?: {
2520
2541
  paymaster: Address;
@@ -2628,6 +2649,13 @@ interface HandleMobilePrepareParams {
2628
2649
  partialUserOp: PartialUserOperation;
2629
2650
  /** Optional fee-stripped fallback variant. */
2630
2651
  partialUserOpFallback?: PartialUserOperation;
2652
+ /**
2653
+ * Optional separate lock id for the fallback path. Required when the
2654
+ * upstream handler (e.g. PTRedeemHandler) reserves a DIFFERENT
2655
+ * ledger row for the fallback variant. See audit PACI5-21 +
2656
+ * `prepareMobileUserOp` for the full rationale.
2657
+ */
2658
+ lockIdFallback?: string;
2631
2659
  /**
2632
2660
  * Scenario tag — passed to `requestSponsorship` so the relayer can
2633
2661
  * apply per-scenario L1 enforcement (`mint`, `burn`, etc.).
@@ -2781,8 +2809,6 @@ interface PTClaimHandlerConfig {
2781
2809
  * Every `handle()` call validates the request's `pointTokenAddress`
2782
2810
  * against this set BEFORE any chain read or signer call.
2783
2811
  *
2784
- * Audit PACI5-18 — the read surface (`IssuerApiHandlers.handleUser`,
2785
- * `handleRedemptionEvaluate`) enforces this allowlist, but the
2786
2812
  * write surface (claim/redeem) previously skipped it. The asymmetry
2787
2813
  * meant an issuer signer whitelisted as minter on PointTokens
2788
2814
  * outside the configured indexer set could be coerced into signing
@@ -3324,6 +3350,18 @@ interface MobilePrepareDto {
3324
3350
  interface RedeemPrepareDto extends MobilePrepareDto {
3325
3351
  netCreditAmount: string;
3326
3352
  netCreditAmountFallback?: string;
3353
+ /**
3354
+ * Lock id reserved for the FALLBACK redeem path (= full `amount`).
3355
+ * Mobile FE polls `/redeem/status/:lockIdFallback` when it submits
3356
+ * the fallback variant (`variant: 'fallback'` on `/redeem/submit`).
3357
+ *
3358
+ * Audit PACI5-21 — pre-fix the adapter exposed only the sponsored
3359
+ * `lockId` (= `amount - fee`), so the bundler-receipt fallback in
3360
+ * `handleRedeemStatus` resolved the smaller credit against the
3361
+ * on-chain burn of the full amount → user under-credited by exactly
3362
+ * the fee on every fallback redeem.
3363
+ */
3364
+ lockIdFallback?: string;
3327
3365
  }
3328
3366
  interface MobileSubmitDto {
3329
3367
  userOpHash: Hex;
package/dist/index.d.ts CHANGED
@@ -1889,17 +1889,6 @@ interface PTRedeemHandlerConfig {
1889
1889
  * Every `handle()` call validates the request's `pointTokenAddress`
1890
1890
  * against this set BEFORE any chain read, signer call, or pending
1891
1891
  * credit reservation.
1892
- *
1893
- * Audit PACI5-18 (redeem twin of the claim-side finding) — the read
1894
- * surface (`IssuerApiHandlers.handleUser` / `handleRedemptionEvaluate`)
1895
- * enforces this allowlist, but the write surface previously skipped
1896
- * it. The asymmetry meant an issuer burner signer whitelisted on
1897
- * multiple PointTokens could be coerced into signing a valid
1898
- * `BurnRequest` for an off-allowlist token where no BurnIndexer is
1899
- * running — the on-chain burn would succeed but the off-chain
1900
- * pending credit would stay PENDING forever (or worse, resolve via
1901
- * the bundler-receipt fallback against an unbacked credit balance).
1902
- *
1903
1892
  * Pass the SAME set used to construct `IssuerApiHandlers` and
1904
1893
  * `PTClaimHandler` so the read, claim, and redeem paths agree on
1905
1894
  * what this issuer is willing to sign for.
@@ -2295,6 +2284,23 @@ interface PendingUserOpEntry {
2295
2284
  verificationGasLimit: string;
2296
2285
  preVerificationGas: string;
2297
2286
  userOpHash: Hex;
2287
+ /**
2288
+ * Lock id (PendingCredit on redeem, LockedMint on claim) reserved
2289
+ * for the FALLBACK path — distinct from the outer entry's
2290
+ * sponsored lock because the on-chain amount the user burns/mints
2291
+ * differs between variants.
2292
+ *
2293
+ * Audit PACI5-21 — pre-fix `handleMobileSubmit.bindUserOpHash`
2294
+ * always bound the submitted userOpHash to the SPONSORED lockId,
2295
+ * even when the user submitted the fallback variant. The
2296
+ * bundler-receipt fallback in `handleRedeemStatus` then resolved
2297
+ * the sponsored credit (`amount - fee`) against the on-chain
2298
+ * burn of the FULL `amount` — every fee-bearing fallback redeem
2299
+ * permanently under-credited the user by exactly the fee. Surface
2300
+ * the fallback lockId here so submit can route the bind to the
2301
+ * correct ledger row.
2302
+ */
2303
+ lockId?: string;
2298
2304
  };
2299
2305
  /**
2300
2306
  * EIP-7702 authorization tuple — present only on the `delegate`
@@ -2515,6 +2521,21 @@ interface PrepareMobileUserOpParams {
2515
2521
  maxFeePerGas?: bigint;
2516
2522
  maxPriorityFeePerGas?: bigint;
2517
2523
  };
2524
+ /**
2525
+ * Optional separate lock id for the FALLBACK path — required when
2526
+ * the upstream handler reserves a DIFFERENT ledger row for the
2527
+ * fallback variant (PTRedeemHandler reserves
2528
+ * `amount` for fallback vs `amount - fee` for sponsored).
2529
+ *
2530
+ * Audit PACI5-21 — when omitted, `handleMobileSubmit` binds the
2531
+ * fallback userOpHash to the outer sponsored `lockId`, which made
2532
+ * `handleRedeemStatus`'s bundler-receipt fallback resolve the
2533
+ * SMALLER sponsored credit against the on-chain burn of the FULL
2534
+ * amount. Every fee-bearing fallback redeem permanently
2535
+ * under-credited the user by exactly the fee. Pass the fallback
2536
+ * lockId here so submit can route the bind to the correct row.
2537
+ */
2538
+ lockIdFallback?: string;
2518
2539
  /** Paymaster sponsorship response, or `undefined` if PAFI declined. */
2519
2540
  paymasterFields?: {
2520
2541
  paymaster: Address;
@@ -2628,6 +2649,13 @@ interface HandleMobilePrepareParams {
2628
2649
  partialUserOp: PartialUserOperation;
2629
2650
  /** Optional fee-stripped fallback variant. */
2630
2651
  partialUserOpFallback?: PartialUserOperation;
2652
+ /**
2653
+ * Optional separate lock id for the fallback path. Required when the
2654
+ * upstream handler (e.g. PTRedeemHandler) reserves a DIFFERENT
2655
+ * ledger row for the fallback variant. See audit PACI5-21 +
2656
+ * `prepareMobileUserOp` for the full rationale.
2657
+ */
2658
+ lockIdFallback?: string;
2631
2659
  /**
2632
2660
  * Scenario tag — passed to `requestSponsorship` so the relayer can
2633
2661
  * apply per-scenario L1 enforcement (`mint`, `burn`, etc.).
@@ -2781,8 +2809,6 @@ interface PTClaimHandlerConfig {
2781
2809
  * Every `handle()` call validates the request's `pointTokenAddress`
2782
2810
  * against this set BEFORE any chain read or signer call.
2783
2811
  *
2784
- * Audit PACI5-18 — the read surface (`IssuerApiHandlers.handleUser`,
2785
- * `handleRedemptionEvaluate`) enforces this allowlist, but the
2786
2812
  * write surface (claim/redeem) previously skipped it. The asymmetry
2787
2813
  * meant an issuer signer whitelisted as minter on PointTokens
2788
2814
  * outside the configured indexer set could be coerced into signing
@@ -3324,6 +3350,18 @@ interface MobilePrepareDto {
3324
3350
  interface RedeemPrepareDto extends MobilePrepareDto {
3325
3351
  netCreditAmount: string;
3326
3352
  netCreditAmountFallback?: string;
3353
+ /**
3354
+ * Lock id reserved for the FALLBACK redeem path (= full `amount`).
3355
+ * Mobile FE polls `/redeem/status/:lockIdFallback` when it submits
3356
+ * the fallback variant (`variant: 'fallback'` on `/redeem/submit`).
3357
+ *
3358
+ * Audit PACI5-21 — pre-fix the adapter exposed only the sponsored
3359
+ * `lockId` (= `amount - fee`), so the bundler-receipt fallback in
3360
+ * `handleRedeemStatus` resolved the smaller credit against the
3361
+ * on-chain burn of the full amount → user under-credited by exactly
3362
+ * the fee on every fallback redeem.
3363
+ */
3364
+ lockIdFallback?: string;
3327
3365
  }
3328
3366
  interface MobileSubmitDto {
3329
3367
  userOpHash: Hex;
package/dist/index.js CHANGED
@@ -2685,7 +2685,10 @@ async function prepareMobileUserOp(params) {
2685
2685
  callGasLimit: fallback.userOp.callGasLimit.toString(),
2686
2686
  verificationGasLimit: fallback.userOp.verificationGasLimit.toString(),
2687
2687
  preVerificationGas: fallback.userOp.preVerificationGas.toString(),
2688
- userOpHash: fallback.userOpHash
2688
+ userOpHash: fallback.userOpHash,
2689
+ // Audit PACI5-21 — carry the fallback-specific lockId so submit
2690
+ // can bind the fallback userOpHash to the correct ledger row.
2691
+ lockId: params.lockIdFallback
2689
2692
  };
2690
2693
  }
2691
2694
  const entry = {
@@ -2878,6 +2881,7 @@ async function handleMobilePrepare(params) {
2878
2881
  lockId: params.lockId,
2879
2882
  partialUserOp: sponsoredOp,
2880
2883
  partialUserOpFallback: params.partialUserOpFallback,
2884
+ lockIdFallback: params.lockIdFallback,
2881
2885
  paymasterFields,
2882
2886
  chainId: params.chainId,
2883
2887
  store: params.store,
@@ -2906,7 +2910,8 @@ async function handleMobileSubmit(params) {
2906
2910
  entryPoint: params.entryPoint ?? ENTRY_POINT_V08,
2907
2911
  eip7702Auth: entry.eip7702Auth
2908
2912
  });
2909
- await params.bindUserOpHash(params.lockId, result.userOpHash);
2913
+ const targetLockId = variant === "fallback" && entry.fallback?.lockId ? entry.fallback.lockId : params.lockId;
2914
+ await params.bindUserOpHash(targetLockId, result.userOpHash);
2910
2915
  await params.store.delete(params.lockId);
2911
2916
  return { userOpHash: result.userOpHash };
2912
2917
  }
@@ -3676,10 +3681,16 @@ var IssuerApiAdapter = class {
3676
3681
  "burn",
3677
3682
  pointTokenAddress,
3678
3683
  redeemResponse.expiresInSeconds,
3679
- input.eip7702Auth
3684
+ input.eip7702Auth,
3685
+ // Audit PACI5-21 — fallback path reserves a separate
3686
+ // PendingCredit row for the full `amount`. Surface its lockId so
3687
+ // mobile FE can poll the correct row + `handleMobileSubmit`
3688
+ // routes the userOpHash bind to it on fallback submit.
3689
+ redeemResponse.fallback?.lockId
3680
3690
  );
3681
3691
  return {
3682
3692
  lockId: redeemResponse.lockId,
3693
+ lockIdFallback: redeemResponse.fallback?.lockId,
3683
3694
  userOpHash: prepared.sponsored.userOpHash,
3684
3695
  typedData: prepared.sponsored.typedData,
3685
3696
  userOpHashFallback: prepared.fallback?.userOpHash,
@@ -3810,13 +3821,14 @@ var IssuerApiAdapter = class {
3810
3821
  issuerSignerWallet: this.cfg.issuerSignerWallet
3811
3822
  });
3812
3823
  }
3813
- async runMobilePrepare(authenticatedAddress, chainId, lockId, partialUserOp, partialUserOpFallback, scenario, pointTokenAddress, ttlSeconds, eip7702Auth) {
3824
+ async runMobilePrepare(authenticatedAddress, chainId, lockId, partialUserOp, partialUserOpFallback, scenario, pointTokenAddress, ttlSeconds, eip7702Auth, lockIdFallback) {
3814
3825
  return await handleMobilePrepare({
3815
3826
  userAddress: authenticatedAddress,
3816
3827
  chainId,
3817
3828
  lockId,
3818
3829
  partialUserOp,
3819
3830
  partialUserOpFallback,
3831
+ lockIdFallback,
3820
3832
  scenario,
3821
3833
  pointTokenAddress,
3822
3834
  ttlSeconds,
@@ -5087,7 +5099,7 @@ var MemoryRedemptionHistoryStore = class {
5087
5099
  };
5088
5100
 
5089
5101
  // src/index.ts
5090
- var PAFI_ISSUER_SDK_VERSION = true ? "0.30.0" : "dev";
5102
+ var PAFI_ISSUER_SDK_VERSION = true ? "0.31.0" : "dev";
5091
5103
  export {
5092
5104
  AdapterMisconfiguredError,
5093
5105
  AuthError,