@pafi-dev/issuer 0.5.37 → 0.5.39

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.cjs CHANGED
@@ -43,6 +43,8 @@ __export(index_exports, {
43
43
  PTRedeemHandler: () => PTRedeemHandler,
44
44
  PafiBackendClient: () => PafiBackendClient,
45
45
  PafiBackendError: () => PafiBackendError,
46
+ PafiSdkError: () => PafiSdkError,
47
+ PendingUserOpForbiddenError: () => PendingUserOpForbiddenError,
46
48
  PendingUserOpNotFoundError: () => PendingUserOpNotFoundError,
47
49
  PerpDepositError: () => PerpDepositError,
48
50
  PerpDepositHandler: () => PerpDepositHandler,
@@ -56,6 +58,7 @@ __export(index_exports, {
56
58
  authenticateRequest: () => authenticateRequest,
57
59
  createIssuerService: () => createIssuerService,
58
60
  createNativePtQuoter: () => createNativePtQuoter,
61
+ createSdkErrorMapper: () => createSdkErrorMapper,
59
62
  createSubgraphNativeUsdtQuoter: () => createSubgraphNativeUsdtQuoter,
60
63
  createSubgraphPoolsProvider: () => createSubgraphPoolsProvider,
61
64
  handleClaimStatus: () => handleClaimStatus,
@@ -73,6 +76,22 @@ __export(index_exports, {
73
76
  });
74
77
  module.exports = __toCommonJS(index_exports);
75
78
 
79
+ // src/errors.ts
80
+ var PafiSdkError = class extends Error {
81
+ /**
82
+ * `true` when the FE should consider a retry safe — typically because
83
+ * the failure is transient (`MINT_CAP_EXCEEDED` may free up,
84
+ * `RELAY_FEE_EXCEEDS_AMOUNT` may drop on next quote). Defaults to
85
+ * `false` — subclasses opt in per-code.
86
+ */
87
+ safeToRetry = false;
88
+ details;
89
+ constructor(message) {
90
+ super(message);
91
+ this.name = new.target.name;
92
+ }
93
+ };
94
+
76
95
  // src/policy/defaultPolicy.ts
77
96
  var DefaultPolicyEngine = class {
78
97
  ledger;
@@ -1374,18 +1393,19 @@ var import_viem7 = require("viem");
1374
1393
  var import_core4 = require("@pafi-dev/core");
1375
1394
  var DEFAULT_REDEEM_LOCK_MS = 15 * 60 * 1e3;
1376
1395
  var DEFAULT_SIG_DEADLINE_SEC = 15 * 60;
1377
- var PTRedeemError = class extends Error {
1396
+ var PTRedeemError = class extends PafiSdkError {
1397
+ httpStatus = "unprocessable";
1398
+ code;
1378
1399
  constructor(code, message) {
1379
1400
  super(message);
1380
1401
  this.code = code;
1381
- this.name = "PTRedeemError";
1382
1402
  }
1383
- code;
1384
1403
  };
1385
1404
  var PTRedeemHandler = class {
1386
1405
  ledger;
1387
1406
  relayService;
1388
1407
  provider;
1408
+ feeService;
1389
1409
  pointTokenAddress;
1390
1410
  batchExecutorAddress;
1391
1411
  chainId;
@@ -1424,6 +1444,7 @@ var PTRedeemHandler = class {
1424
1444
  this.ledger = config.ledger;
1425
1445
  this.relayService = config.relayService;
1426
1446
  this.provider = config.provider;
1447
+ this.feeService = config.feeService;
1427
1448
  this.pointTokenAddress = (0, import_viem7.getAddress)(config.pointTokenAddress);
1428
1449
  this.batchExecutorAddress = (0, import_viem7.getAddress)(config.batchExecutorAddress);
1429
1450
  this.chainId = config.chainId;
@@ -1481,7 +1502,15 @@ var PTRedeemHandler = class {
1481
1502
  }
1482
1503
  }
1483
1504
  async _handleAfterNonceLock(request, burnNonce) {
1484
- const fee = request.feeAmount && request.feeAmount > 0n ? request.feeAmount : 0n;
1505
+ let fee;
1506
+ if (request.feeAmount !== void 0) {
1507
+ fee = request.feeAmount > 0n ? request.feeAmount : 0n;
1508
+ } else if (this.feeService) {
1509
+ fee = await this.feeService.estimateGasFee();
1510
+ } else {
1511
+ fee = 0n;
1512
+ }
1513
+ const feeRecipient = request.feeRecipient ?? (request.chainId !== void 0 ? (0, import_core4.getContractAddresses)(request.chainId).pafiFeeRecipient : (0, import_core4.getContractAddresses)(this.chainId).pafiFeeRecipient);
1485
1514
  if (fee > 0n && fee >= request.amount) {
1486
1515
  throw new PTRedeemError(
1487
1516
  "INVALID_AMOUNT",
@@ -1538,7 +1567,7 @@ var PTRedeemHandler = class {
1538
1567
  burnRequest: sponsoredBurnRequest,
1539
1568
  burnerSignature: sponsoredSig,
1540
1569
  feeAmount: fee,
1541
- feeRecipient: request.feeRecipient
1570
+ feeRecipient
1542
1571
  });
1543
1572
  let fallback = void 0;
1544
1573
  if (fee > 0n) {
@@ -1587,6 +1616,7 @@ var PTRedeemHandler = class {
1587
1616
  lockId: sponsoredLockId,
1588
1617
  userOp: sponsoredUserOp,
1589
1618
  netCreditAmount: sponsoredBurnAmount,
1619
+ feeAmount: fee,
1590
1620
  fallback,
1591
1621
  expiresInSeconds: Math.floor(this.redeemLockDurationMs / 1e3),
1592
1622
  signatureDeadline: deadline
@@ -1659,11 +1689,11 @@ var TopUpRedemptionHandler = class {
1659
1689
  };
1660
1690
 
1661
1691
  // src/api/statusHandlers.ts
1662
- var LockNotFoundError = class extends Error {
1692
+ var LockNotFoundError = class extends PafiSdkError {
1663
1693
  code = "LOCK_NOT_FOUND";
1694
+ httpStatus = "not_found";
1664
1695
  constructor() {
1665
1696
  super("Lock not found or does not belong to authenticated user");
1666
- this.name = "LockNotFoundError";
1667
1697
  }
1668
1698
  };
1669
1699
  async function handleClaimStatus(params) {
@@ -1759,6 +1789,7 @@ async function handleRedeemStatus(params) {
1759
1789
  }
1760
1790
 
1761
1791
  // src/api/mobileHandlers.ts
1792
+ var import_viem9 = require("viem");
1762
1793
  var import_core8 = require("@pafi-dev/core");
1763
1794
 
1764
1795
  // src/userop-store/serialize.ts
@@ -1895,21 +1926,21 @@ async function prepareMobileUserOp(params) {
1895
1926
  }
1896
1927
 
1897
1928
  // src/pafi-backend/helpers.ts
1898
- var BundlerNotConfiguredError = class extends Error {
1929
+ var BundlerNotConfiguredError = class extends PafiSdkError {
1899
1930
  code = "BUNDLER_NOT_CONFIGURED";
1931
+ httpStatus = "service_unavailable";
1900
1932
  constructor() {
1901
1933
  super(
1902
1934
  "PAFI backend client not configured \u2014 set PAFI_BACKEND_URL, PAFI_ISSUER_ID, PAFI_API_KEY to enable mobile submit."
1903
1935
  );
1904
- this.name = "BundlerNotConfiguredError";
1905
1936
  }
1906
1937
  };
1907
- var BundlerRejectedError = class extends Error {
1938
+ var BundlerRejectedError = class extends PafiSdkError {
1908
1939
  code = "BUNDLER_REJECTED";
1940
+ httpStatus = "unprocessable";
1909
1941
  cause;
1910
1942
  constructor(message, cause) {
1911
1943
  super(message);
1912
- this.name = "BundlerRejectedError";
1913
1944
  this.cause = cause;
1914
1945
  }
1915
1946
  };
@@ -1965,13 +1996,22 @@ async function relayUserOp(params) {
1965
1996
  }
1966
1997
 
1967
1998
  // src/api/mobileHandlers.ts
1968
- var PendingUserOpNotFoundError = class extends Error {
1999
+ var PendingUserOpNotFoundError = class extends PafiSdkError {
1969
2000
  code = "PENDING_USEROP_NOT_FOUND";
2001
+ httpStatus = "not_found";
1970
2002
  constructor(lockId) {
1971
2003
  super(
1972
2004
  `No pending UserOp found for lockId ${lockId} \u2014 it may have expired or already been submitted.`
1973
2005
  );
1974
- this.name = "PendingUserOpNotFoundError";
2006
+ }
2007
+ };
2008
+ var PendingUserOpForbiddenError = class extends PafiSdkError {
2009
+ code = "PENDING_USEROP_FORBIDDEN";
2010
+ httpStatus = "forbidden";
2011
+ constructor(lockId) {
2012
+ super(
2013
+ `Pending UserOp ${lockId} does not belong to the authenticated user.`
2014
+ );
1975
2015
  }
1976
2016
  };
1977
2017
  async function handleMobilePrepare(params) {
@@ -2013,6 +2053,9 @@ async function handleMobileSubmit(params) {
2013
2053
  if (!entry) {
2014
2054
  throw new PendingUserOpNotFoundError(params.lockId);
2015
2055
  }
2056
+ if ((0, import_viem9.getAddress)(entry.sender) !== (0, import_viem9.getAddress)(params.authenticatedAddress)) {
2057
+ throw new PendingUserOpForbiddenError(params.lockId);
2058
+ }
2016
2059
  const variant = params.variant ?? "sponsored";
2017
2060
  const userOpJson = serializeEntryToJsonRpc(entry, params.signature, variant);
2018
2061
  const result = await relayUserOp({
@@ -2026,31 +2069,33 @@ async function handleMobileSubmit(params) {
2026
2069
  }
2027
2070
 
2028
2071
  // src/api/handlers/ptClaimHandler.ts
2029
- var import_viem9 = require("viem");
2072
+ var import_viem10 = require("viem");
2030
2073
  var import_core9 = require("@pafi-dev/core");
2031
2074
 
2032
2075
  // src/issuer-state/types.ts
2033
- var IssuerStateError = class extends Error {
2076
+ var IssuerStateError = class extends PafiSdkError {
2077
+ httpStatus = "unprocessable";
2078
+ code;
2079
+ details;
2080
+ safeToRetry;
2034
2081
  constructor(code, message, details) {
2035
2082
  super(message);
2036
2083
  this.code = code;
2037
2084
  this.details = details;
2038
- this.name = "IssuerStateError";
2085
+ this.safeToRetry = code === "MINT_CAP_EXCEEDED";
2039
2086
  }
2040
- code;
2041
- details;
2042
2087
  };
2043
2088
 
2044
2089
  // src/api/handlers/ptClaimHandler.ts
2045
- var PTClaimError = class extends Error {
2090
+ var PTClaimError = class extends PafiSdkError {
2091
+ httpStatus = "unprocessable";
2092
+ code;
2093
+ details;
2046
2094
  constructor(code, message, details) {
2047
2095
  super(message);
2048
2096
  this.code = code;
2049
2097
  this.details = details;
2050
- this.name = "PTClaimError";
2051
2098
  }
2052
- code;
2053
- details;
2054
2099
  };
2055
2100
  var DEFAULT_LOCK_MS = 15 * 60 * 1e3;
2056
2101
  var DEFAULT_SIG_DEADLINE_SEC2 = 15 * 60;
@@ -2065,7 +2110,7 @@ var PTClaimHandler = class {
2065
2110
  };
2066
2111
  }
2067
2112
  async handle(request) {
2068
- if ((0, import_viem9.getAddress)(request.authenticatedAddress) !== (0, import_viem9.getAddress)(request.userAddress)) {
2113
+ if ((0, import_viem10.getAddress)(request.authenticatedAddress) !== (0, import_viem10.getAddress)(request.userAddress)) {
2069
2114
  throw new PTClaimError(
2070
2115
  "VALIDATION_FAILED",
2071
2116
  `userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
@@ -2165,13 +2210,13 @@ var PTClaimHandler = class {
2165
2210
 
2166
2211
  // src/api/handlers/swapHandler.ts
2167
2212
  var import_core10 = require("@pafi-dev/core");
2168
- var SwapError = class extends Error {
2213
+ var SwapError = class extends PafiSdkError {
2214
+ httpStatus = "unprocessable";
2215
+ code;
2169
2216
  constructor(code, message) {
2170
2217
  super(message);
2171
2218
  this.code = code;
2172
- this.name = "SwapError";
2173
2219
  }
2174
- code;
2175
2220
  };
2176
2221
  var DEFAULT_SLIPPAGE_BPS = 50;
2177
2222
  var DEFAULT_SWAP_DEADLINE_SEC = 5 * 60;
@@ -2297,13 +2342,15 @@ var SwapHandler = class {
2297
2342
 
2298
2343
  // src/api/handlers/perpDepositHandler.ts
2299
2344
  var import_core11 = require("@pafi-dev/core");
2300
- var PerpDepositError = class extends Error {
2345
+ var PerpDepositError = class extends PafiSdkError {
2346
+ httpStatus = "unprocessable";
2347
+ code;
2348
+ safeToRetry;
2301
2349
  constructor(code, message) {
2302
2350
  super(message);
2303
2351
  this.code = code;
2304
- this.name = "PerpDepositError";
2352
+ this.safeToRetry = code === "RELAY_FEE_EXCEEDS_AMOUNT";
2305
2353
  }
2306
- code;
2307
2354
  };
2308
2355
  var DEFAULT_MAX_FEE_PREMIUM_BPS = 5e3;
2309
2356
  var PerpDepositHandler = class {
@@ -2546,8 +2593,33 @@ async function quotePointTokenToUsdt(params) {
2546
2593
  };
2547
2594
  }
2548
2595
 
2596
+ // src/api/errorMapper.ts
2597
+ function createSdkErrorMapper(factories) {
2598
+ return (err) => {
2599
+ if (!(err instanceof PafiSdkError)) {
2600
+ throw err;
2601
+ }
2602
+ const body = {
2603
+ code: err.code,
2604
+ message: err.message,
2605
+ details: err.details,
2606
+ safeToRetry: err.safeToRetry
2607
+ };
2608
+ switch (err.httpStatus) {
2609
+ case "not_found":
2610
+ throw factories.notFound(body);
2611
+ case "forbidden":
2612
+ throw factories.forbidden(body);
2613
+ case "unprocessable":
2614
+ throw factories.unprocessable(body);
2615
+ case "service_unavailable":
2616
+ throw factories.serviceUnavailable(body);
2617
+ }
2618
+ };
2619
+ }
2620
+
2549
2621
  // src/pools/subgraphPoolsProvider.ts
2550
- var import_viem10 = require("viem");
2622
+ var import_viem11 = require("viem");
2551
2623
  var import_core14 = require("@pafi-dev/core");
2552
2624
  var DEFAULT_CACHE_TTL_MS = 3e4;
2553
2625
  var POOL_QUERY = `
@@ -2648,7 +2720,7 @@ async function fetchPoolsFromSubgraph(fetchImpl, subgraphUrl, pointTokenAddress)
2648
2720
  return [];
2649
2721
  }
2650
2722
  const { pool } = token;
2651
- if (!(0, import_viem10.isAddress)(pool.hooks)) {
2723
+ if (!(0, import_viem11.isAddress)(pool.hooks)) {
2652
2724
  console.error(
2653
2725
  "[PAFI] SubgraphPoolsProvider: invalid hooks address in response:",
2654
2726
  pool.hooks,
@@ -2656,7 +2728,7 @@ async function fetchPoolsFromSubgraph(fetchImpl, subgraphUrl, pointTokenAddress)
2656
2728
  );
2657
2729
  return [];
2658
2730
  }
2659
- if (!(0, import_viem10.isAddress)(pool.token0.id) || !(0, import_viem10.isAddress)(pool.token1.id)) {
2731
+ if (!(0, import_viem11.isAddress)(pool.token0.id) || !(0, import_viem11.isAddress)(pool.token1.id)) {
2660
2732
  console.error(
2661
2733
  "[PAFI] SubgraphPoolsProvider: invalid token address in response \u2014 skipping pool"
2662
2734
  );
@@ -2806,8 +2878,8 @@ function toUsdtPerNative(priceFloat, usdtDecimals) {
2806
2878
  }
2807
2879
 
2808
2880
  // src/pools/nativePtQuoter.ts
2809
- var import_viem11 = require("viem");
2810
- var CHAINLINK_ABI = (0, import_viem11.parseAbi)([
2881
+ var import_viem12 = require("viem");
2882
+ var CHAINLINK_ABI = (0, import_viem12.parseAbi)([
2811
2883
  "function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)"
2812
2884
  ]);
2813
2885
  var CHAINLINK_MAX_AGE_S = 3600n;
@@ -3164,7 +3236,7 @@ var PafiBackendClient = class {
3164
3236
  };
3165
3237
 
3166
3238
  // src/config.ts
3167
- var import_viem12 = require("viem");
3239
+ var import_viem13 = require("viem");
3168
3240
  var import_core16 = require("@pafi-dev/core");
3169
3241
  function createIssuerService(config) {
3170
3242
  if (!config.provider) {
@@ -3185,7 +3257,7 @@ function createIssuerService(config) {
3185
3257
  "createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
3186
3258
  );
3187
3259
  }
3188
- const tokenAddresses = rawAddresses.map((a) => (0, import_viem12.getAddress)(a));
3260
+ const tokenAddresses = rawAddresses.map((a) => (0, import_viem13.getAddress)(a));
3189
3261
  const ledger = config.ledger;
3190
3262
  const sessionStore = config.sessionStore ?? new MemorySessionStore();
3191
3263
  const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
@@ -3283,7 +3355,7 @@ function createIssuerService(config) {
3283
3355
  }
3284
3356
 
3285
3357
  // src/issuer-state/validator.ts
3286
- var import_viem13 = require("viem");
3358
+ var import_viem14 = require("viem");
3287
3359
  var import_core17 = require("@pafi-dev/core");
3288
3360
  var ISSUER_RECORD_TTL_MS = 3e4;
3289
3361
  var IssuerStateValidator = class _IssuerStateValidator {
@@ -3310,7 +3382,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
3310
3382
  */
3311
3383
  invalidate(pointToken) {
3312
3384
  if (pointToken) {
3313
- const key = (0, import_viem13.getAddress)(pointToken);
3385
+ const key = (0, import_viem14.getAddress)(pointToken);
3314
3386
  this.pointTokenIssuerCache.delete(key);
3315
3387
  this.stateCache.delete(key);
3316
3388
  this.inflight.delete(key);
@@ -3325,7 +3397,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
3325
3397
  * The issuer field is set at `initialize()` and never changes.
3326
3398
  */
3327
3399
  async getIssuerAddressForPointToken(pointToken) {
3328
- const key = (0, import_viem13.getAddress)(pointToken);
3400
+ const key = (0, import_viem14.getAddress)(pointToken);
3329
3401
  const cached = this.pointTokenIssuerCache.get(key);
3330
3402
  if (cached) return cached;
3331
3403
  const issuer = await this.provider.readContract({
@@ -3333,15 +3405,15 @@ var IssuerStateValidator = class _IssuerStateValidator {
3333
3405
  abi: import_core17.POINT_TOKEN_V2_ABI,
3334
3406
  functionName: "issuer"
3335
3407
  });
3336
- this.pointTokenIssuerCache.set(key, (0, import_viem13.getAddress)(issuer));
3337
- return (0, import_viem13.getAddress)(issuer);
3408
+ this.pointTokenIssuerCache.set(key, (0, import_viem14.getAddress)(issuer));
3409
+ return (0, import_viem14.getAddress)(issuer);
3338
3410
  }
3339
3411
  /**
3340
3412
  * Read registry record + totalSupply, with 30s cache and in-flight
3341
3413
  * deduplication. Does NOT throw on inactive/missing — returns raw state.
3342
3414
  */
3343
3415
  async getIssuerState(pointToken) {
3344
- const tokenAddr = (0, import_viem13.getAddress)(pointToken);
3416
+ const tokenAddr = (0, import_viem14.getAddress)(pointToken);
3345
3417
  const now = Date.now();
3346
3418
  const cached = this.stateCache.get(tokenAddr);
3347
3419
  if (cached && cached.expiresAt > now) return cached.value;
@@ -3465,6 +3537,8 @@ var PAFI_ISSUER_SDK_VERSION = "0.4.0";
3465
3537
  PTRedeemHandler,
3466
3538
  PafiBackendClient,
3467
3539
  PafiBackendError,
3540
+ PafiSdkError,
3541
+ PendingUserOpForbiddenError,
3468
3542
  PendingUserOpNotFoundError,
3469
3543
  PerpDepositError,
3470
3544
  PerpDepositHandler,
@@ -3478,6 +3552,7 @@ var PAFI_ISSUER_SDK_VERSION = "0.4.0";
3478
3552
  authenticateRequest,
3479
3553
  createIssuerService,
3480
3554
  createNativePtQuoter,
3555
+ createSdkErrorMapper,
3481
3556
  createSubgraphNativeUsdtQuoter,
3482
3557
  createSubgraphPoolsProvider,
3483
3558
  handleClaimStatus,