@pafi-dev/issuer 0.6.1 → 0.7.1

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
@@ -20,12 +20,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ AdapterMisconfiguredError: () => AdapterMisconfiguredError,
23
24
  AuthError: () => AuthError,
24
25
  AuthService: () => AuthService,
25
26
  BalanceAggregator: () => BalanceAggregator,
26
27
  BundlerNotConfiguredError: () => BundlerNotConfiguredError,
27
28
  BundlerRejectedError: () => BundlerRejectedError,
28
29
  BurnIndexer: () => BurnIndexer,
30
+ ConfigurationError: () => ConfigurationError,
29
31
  DefaultPolicyEngine: () => DefaultPolicyEngine,
30
32
  FeeManager: () => FeeManager,
31
33
  InMemoryCursorStore: () => InMemoryCursorStore,
@@ -53,6 +55,7 @@ __export(index_exports, {
53
55
  PointIndexer: () => PointIndexer,
54
56
  RelayError: () => RelayError,
55
57
  RelayService: () => RelayService,
58
+ ValidationError: () => ValidationError,
56
59
  authenticateRequest: () => authenticateRequest,
57
60
  createIssuerService: () => createIssuerService,
58
61
  createNativePtQuoter: () => createNativePtQuoter,
@@ -88,6 +91,26 @@ var PafiSdkError = class extends Error {
88
91
  this.name = new.target.name;
89
92
  }
90
93
  };
94
+ var ValidationError = class extends PafiSdkError {
95
+ httpStatus = "unprocessable";
96
+ code;
97
+ details;
98
+ constructor(code, message, details) {
99
+ super(message);
100
+ this.code = code;
101
+ this.details = details;
102
+ }
103
+ };
104
+ var ConfigurationError = class extends PafiSdkError {
105
+ httpStatus = "service_unavailable";
106
+ code;
107
+ details;
108
+ constructor(code, message, details) {
109
+ super(message);
110
+ this.code = code;
111
+ this.details = details;
112
+ }
113
+ };
91
114
 
92
115
  // src/policy/defaultPolicy.ts
93
116
  var DefaultPolicyEngine = class {
@@ -258,12 +281,32 @@ var import_viem2 = require("viem");
258
281
  var import_core = require("@pafi-dev/core");
259
282
 
260
283
  // src/auth/errors.ts
261
- var AuthError = class extends Error {
284
+ function statusForCode(code) {
285
+ switch (code) {
286
+ case "INVALID_MESSAGE":
287
+ case "DOMAIN_MISMATCH":
288
+ case "CHAIN_MISMATCH":
289
+ case "MESSAGE_EXPIRED":
290
+ case "MESSAGE_NOT_YET_VALID":
291
+ case "MALFORMED_TOKEN":
292
+ return "unprocessable";
293
+ case "NONCE_INVALID":
294
+ case "SIGNATURE_INVALID":
295
+ case "MISSING_TOKEN":
296
+ case "TOKEN_INVALID":
297
+ case "TOKEN_EXPIRED":
298
+ return "forbidden";
299
+ case "SESSION_REVOKED":
300
+ return "not_found";
301
+ }
302
+ }
303
+ var AuthError = class extends PafiSdkError {
262
304
  code;
305
+ httpStatus;
263
306
  constructor(code, message) {
264
307
  super(message);
265
- this.name = "AuthError";
266
308
  this.code = code;
309
+ this.httpStatus = statusForCode(code);
267
310
  }
268
311
  };
269
312
 
@@ -460,14 +503,15 @@ async function authenticateRequest(authHeader, authService) {
460
503
  }
461
504
 
462
505
  // src/relay/types.ts
463
- var RelayError = class extends Error {
506
+ var RelayError = class extends PafiSdkError {
507
+ httpStatus = "unprocessable";
464
508
  code;
465
- cause;
466
509
  constructor(code, message, cause) {
467
510
  super(message);
468
- this.name = "RelayError";
469
511
  this.code = code;
470
- if (cause !== void 0) this.cause = cause;
512
+ if (cause !== void 0) {
513
+ this.cause = cause;
514
+ }
471
515
  }
472
516
  };
473
517
 
@@ -1168,13 +1212,16 @@ var IssuerApiHandlers = class {
1168
1212
  /** `POST /auth/login` */
1169
1213
  async handleLogin(body) {
1170
1214
  if (!body || typeof body.message !== "string" || body.message.length === 0 || typeof body.signature !== "string" || body.signature.length <= 2) {
1171
- throw new Error("handleLogin: message and signature are required");
1215
+ throw new ValidationError(
1216
+ "INVALID_LOGIN_BODY",
1217
+ "handleLogin: message and signature are required"
1218
+ );
1172
1219
  }
1173
1220
  if (body.message.length > 4096) {
1174
- throw new Error("message too long");
1221
+ throw new ValidationError("MESSAGE_TOO_LONG", "message too long");
1175
1222
  }
1176
1223
  if (body.signature.length > 260) {
1177
- throw new Error("signature too long");
1224
+ throw new ValidationError("SIGNATURE_TOO_LONG", "signature too long");
1178
1225
  }
1179
1226
  const result = await this.authService.login(body.message, body.signature);
1180
1227
  return {
@@ -1191,11 +1238,15 @@ var IssuerApiHandlers = class {
1191
1238
  */
1192
1239
  async handleConfig(chainId) {
1193
1240
  if (!Number.isInteger(chainId) || chainId <= 0) {
1194
- throw new Error("invalid chainId");
1241
+ throw new ValidationError("INVALID_CHAIN_ID", "invalid chainId", {
1242
+ chainId
1243
+ });
1195
1244
  }
1196
1245
  if (chainId !== this.chainId) {
1197
- throw new Error(
1198
- `handleConfig: unsupported chainId ${chainId}`
1246
+ throw new ValidationError(
1247
+ "UNSUPPORTED_CHAIN_ID",
1248
+ `handleConfig: unsupported chainId ${chainId}`,
1249
+ { requested: chainId, supported: this.chainId }
1199
1250
  );
1200
1251
  }
1201
1252
  const contracts = {
@@ -1212,7 +1263,8 @@ var IssuerApiHandlers = class {
1212
1263
  /** `GET /gas-fee` — quoted in USDT (6-decimal base units). */
1213
1264
  async handleGasFee() {
1214
1265
  if (!this.feeManager) {
1215
- throw new Error(
1266
+ throw new ConfigurationError(
1267
+ "FEE_MANAGER_NOT_CONFIGURED",
1216
1268
  "handleGasFee: feeManager is not configured on this issuer"
1217
1269
  );
1218
1270
  }
@@ -1234,13 +1286,16 @@ var IssuerApiHandlers = class {
1234
1286
  */
1235
1287
  async handlePools(_userAddress, request) {
1236
1288
  if (!this.poolsProvider) {
1237
- throw new Error(
1289
+ throw new ConfigurationError(
1290
+ "POOLS_PROVIDER_NOT_CONFIGURED",
1238
1291
  "handlePools: poolsProvider is not configured on this issuer"
1239
1292
  );
1240
1293
  }
1241
1294
  if (request.chainId !== this.chainId) {
1242
- throw new Error(
1243
- `handlePools: unsupported chainId ${request.chainId}`
1295
+ throw new ValidationError(
1296
+ "UNSUPPORTED_CHAIN_ID",
1297
+ `handlePools: unsupported chainId ${request.chainId}`,
1298
+ { requested: request.chainId, supported: this.chainId }
1244
1299
  );
1245
1300
  }
1246
1301
  return this.poolsProvider(request);
@@ -1254,21 +1309,27 @@ var IssuerApiHandlers = class {
1254
1309
  */
1255
1310
  async handleUser(userAddress, request) {
1256
1311
  if (request.chainId !== this.chainId) {
1257
- throw new Error(
1258
- `handleUser: unsupported chainId ${request.chainId}`
1312
+ throw new ValidationError(
1313
+ "UNSUPPORTED_CHAIN_ID",
1314
+ `handleUser: unsupported chainId ${request.chainId}`,
1315
+ { requested: request.chainId, supported: this.chainId }
1259
1316
  );
1260
1317
  }
1261
1318
  const normalizedAuthed = (0, import_viem6.getAddress)(userAddress);
1262
1319
  const normalizedRequest = (0, import_viem6.getAddress)(request.userAddress);
1263
1320
  if (normalizedAuthed !== normalizedRequest) {
1264
- throw new Error(
1265
- "handleUser: request userAddress must match authenticated user"
1321
+ throw new ValidationError(
1322
+ "USER_ADDRESS_MISMATCH",
1323
+ "handleUser: request userAddress must match authenticated user",
1324
+ { authenticated: normalizedAuthed, requested: normalizedRequest }
1266
1325
  );
1267
1326
  }
1268
1327
  const pointToken = (0, import_viem6.getAddress)(request.pointTokenAddress);
1269
1328
  if (!this.supportedTokens.has(pointToken)) {
1270
- throw new Error(
1271
- `handleUser: unsupported pointToken ${pointToken}`
1329
+ throw new ValidationError(
1330
+ "UNSUPPORTED_POINT_TOKEN",
1331
+ `handleUser: unsupported pointToken ${pointToken}`,
1332
+ { requested: pointToken }
1272
1333
  );
1273
1334
  }
1274
1335
  const [mintRequestNonce, receiverConsentNonce, offChainBalance, onChainBalance, minter] = await Promise.all([
@@ -1464,69 +1525,82 @@ var PTRedeemHandler = class {
1464
1525
  this.redeemLockDurationMs,
1465
1526
  this.pointTokenAddress
1466
1527
  );
1467
- const sponsoredUserOp = await this.relayService.prepareBurn({
1468
- mode: "burnWithSig",
1469
- userAddress: request.userAddress,
1470
- aaNonce: request.aaNonce,
1471
- pointTokenAddress: this.pointTokenAddress,
1472
- batchExecutorAddress: this.batchExecutorAddress,
1473
- burnRequest: sponsoredBurnRequest,
1474
- burnerSignature: sponsoredSig,
1475
- feeAmount: fee,
1476
- feeRecipient
1477
- });
1478
- let fallback = void 0;
1479
- if (fee > 0n) {
1480
- const fallbackBurnRequest = {
1481
- from: request.userAddress,
1482
- amount: request.amount,
1483
- nonce: burnNonce,
1484
- deadline
1485
- };
1486
- let fallbackSig;
1487
- try {
1488
- fallbackSig = (await (0, import_core4.signBurnRequest)(this.burnerSignerWallet, domain, fallbackBurnRequest)).serialized;
1489
- } catch (err) {
1490
- throw new PTRedeemError(
1491
- "SIGNING_FAILED",
1492
- `failed to sign fallback BurnRequest: ${err instanceof Error ? err.message : String(err)}`
1493
- );
1494
- }
1495
- const fallbackLockId = await this.ledger.reservePendingCredit(
1496
- request.userAddress,
1497
- request.amount,
1498
- this.redeemLockDurationMs,
1499
- this.pointTokenAddress
1500
- );
1501
- const fallbackUserOp = await this.relayService.prepareBurn({
1528
+ try {
1529
+ const sponsoredUserOp = await this.relayService.prepareBurn({
1502
1530
  mode: "burnWithSig",
1503
1531
  userAddress: request.userAddress,
1504
1532
  aaNonce: request.aaNonce,
1505
1533
  pointTokenAddress: this.pointTokenAddress,
1506
1534
  batchExecutorAddress: this.batchExecutorAddress,
1507
- burnRequest: fallbackBurnRequest,
1508
- burnerSignature: fallbackSig,
1509
- // Explicit 0n — fallback is fee-free regardless of how
1510
- // RelayService is configured. Without this, an
1511
- // auto-quoting RelayService would try to quote a fee here
1512
- // and re-add the PT.transfer we're trying to strip.
1513
- feeAmount: 0n
1535
+ burnRequest: sponsoredBurnRequest,
1536
+ burnerSignature: sponsoredSig,
1537
+ feeAmount: fee,
1538
+ feeRecipient
1514
1539
  });
1515
- fallback = {
1516
- lockId: fallbackLockId,
1517
- userOp: fallbackUserOp,
1518
- netCreditAmount: request.amount
1540
+ let fallback = void 0;
1541
+ if (fee > 0n) {
1542
+ const fallbackBurnRequest = {
1543
+ from: request.userAddress,
1544
+ amount: request.amount,
1545
+ nonce: burnNonce,
1546
+ deadline
1547
+ };
1548
+ let fallbackSig;
1549
+ try {
1550
+ fallbackSig = (await (0, import_core4.signBurnRequest)(this.burnerSignerWallet, domain, fallbackBurnRequest)).serialized;
1551
+ } catch (err) {
1552
+ throw new PTRedeemError(
1553
+ "SIGNING_FAILED",
1554
+ `failed to sign fallback BurnRequest: ${err instanceof Error ? err.message : String(err)}`
1555
+ );
1556
+ }
1557
+ const fallbackLockId = await this.ledger.reservePendingCredit(
1558
+ request.userAddress,
1559
+ request.amount,
1560
+ this.redeemLockDurationMs,
1561
+ this.pointTokenAddress
1562
+ );
1563
+ let fallbackUserOp;
1564
+ try {
1565
+ fallbackUserOp = await this.relayService.prepareBurn({
1566
+ mode: "burnWithSig",
1567
+ userAddress: request.userAddress,
1568
+ aaNonce: request.aaNonce,
1569
+ pointTokenAddress: this.pointTokenAddress,
1570
+ batchExecutorAddress: this.batchExecutorAddress,
1571
+ burnRequest: fallbackBurnRequest,
1572
+ burnerSignature: fallbackSig,
1573
+ // Explicit 0n — fallback is fee-free regardless of how
1574
+ // RelayService is configured. Without this, an
1575
+ // auto-quoting RelayService would try to quote a fee here
1576
+ // and re-add the PT.transfer we're trying to strip.
1577
+ feeAmount: 0n
1578
+ });
1579
+ } catch (err) {
1580
+ await this.ledger.releaseLock(fallbackLockId).catch(() => {
1581
+ });
1582
+ throw err;
1583
+ }
1584
+ fallback = {
1585
+ lockId: fallbackLockId,
1586
+ userOp: fallbackUserOp,
1587
+ netCreditAmount: request.amount
1588
+ };
1589
+ }
1590
+ return {
1591
+ lockId: sponsoredLockId,
1592
+ userOp: sponsoredUserOp,
1593
+ netCreditAmount: sponsoredBurnAmount,
1594
+ feeAmount: fee,
1595
+ fallback,
1596
+ expiresInSeconds: Math.floor(this.redeemLockDurationMs / 1e3),
1597
+ signatureDeadline: deadline
1519
1598
  };
1599
+ } catch (err) {
1600
+ await this.ledger.releaseLock(sponsoredLockId).catch(() => {
1601
+ });
1602
+ throw err;
1520
1603
  }
1521
- return {
1522
- lockId: sponsoredLockId,
1523
- userOp: sponsoredUserOp,
1524
- netCreditAmount: sponsoredBurnAmount,
1525
- feeAmount: fee,
1526
- fallback,
1527
- expiresInSeconds: Math.floor(this.redeemLockDurationMs / 1e3),
1528
- signatureDeadline: deadline
1529
- };
1530
1604
  }
1531
1605
  };
1532
1606
 
@@ -1794,6 +1868,41 @@ async function prepareMobileUserOp(params) {
1794
1868
  };
1795
1869
  }
1796
1870
 
1871
+ // src/pafi-backend/types.ts
1872
+ var PafiBackendError = class extends Error {
1873
+ constructor(code, message, httpStatus, details, opts) {
1874
+ super(message);
1875
+ this.code = code;
1876
+ this.httpStatus = httpStatus;
1877
+ this.details = details;
1878
+ this.name = "PafiBackendError";
1879
+ if (opts?.retryAfter !== void 0) this.retryAfter = opts.retryAfter;
1880
+ if (opts?.safeToRetry !== void 0) this.serverSafeToRetry = opts.safeToRetry;
1881
+ }
1882
+ code;
1883
+ httpStatus;
1884
+ details;
1885
+ retryAfter;
1886
+ serverSafeToRetry;
1887
+ get safeToRetry() {
1888
+ if (this.serverSafeToRetry !== void 0) return this.serverSafeToRetry;
1889
+ switch (this.code) {
1890
+ case "RATE_LIMITER_UNAVAILABLE":
1891
+ case "INTERNAL_ERROR":
1892
+ case "TIMEOUT":
1893
+ case "NETWORK_ERROR":
1894
+ return true;
1895
+ case "RATE_LIMIT_EXCEEDED":
1896
+ case "RATE_LIMIT_EXCEEDED_DAILY":
1897
+ case "RATE_LIMIT_EXCEEDED_PER_USER":
1898
+ case "ISSUER_BUDGET_EXCEEDED":
1899
+ return true;
1900
+ default:
1901
+ return false;
1902
+ }
1903
+ }
1904
+ };
1905
+
1797
1906
  // src/pafi-backend/helpers.ts
1798
1907
  var BundlerNotConfiguredError = class extends PafiSdkError {
1799
1908
  code = "BUNDLER_NOT_CONFIGURED";
@@ -1829,8 +1938,23 @@ async function requestPaymaster(params) {
1829
1938
  });
1830
1939
  } catch (err) {
1831
1940
  const msg = err instanceof Error ? err.message : String(err);
1832
- params.onWarning?.(`Paymaster sponsorship declined: ${msg}`);
1833
- return void 0;
1941
+ if (err instanceof PafiBackendError && isTransientPaymasterError(err.code)) {
1942
+ params.onWarning?.(`Paymaster sponsorship declined (transient): ${msg}`);
1943
+ return void 0;
1944
+ }
1945
+ throw err;
1946
+ }
1947
+ }
1948
+ function isTransientPaymasterError(code) {
1949
+ switch (code) {
1950
+ case "NETWORK_ERROR":
1951
+ case "TIMEOUT":
1952
+ case "PAYMASTER_UNAVAILABLE":
1953
+ case "RATE_LIMITER_UNAVAILABLE":
1954
+ case "INTERNAL_ERROR":
1955
+ return true;
1956
+ default:
1957
+ return false;
1834
1958
  }
1835
1959
  }
1836
1960
  function defaultFunctionForScenario(scenario) {
@@ -2011,39 +2135,19 @@ var PTClaimHandler = class {
2011
2135
  this.cfg.lockDurationMs,
2012
2136
  request.pointTokenAddress
2013
2137
  );
2014
- const signatureDeadline = BigInt(
2015
- Math.floor(this.cfg.now() / 1e3) + this.cfg.signatureDeadlineSeconds
2016
- );
2017
- const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee() : 0n;
2018
- const domain = {
2019
- name: this.cfg.pointTokenDomainName,
2020
- chainId: request.chainId,
2021
- verifyingContract: request.pointTokenAddress
2022
- };
2023
- let userOp;
2024
2138
  try {
2025
- userOp = await this.cfg.relayService.prepareMint({
2026
- userAddress: request.userAddress,
2027
- aaNonce: request.aaNonce,
2028
- batchExecutorAddress,
2029
- pointTokenAddress: request.pointTokenAddress,
2030
- amount: request.amount,
2031
- issuerSignerWallet: this.cfg.issuerSignerWallet,
2032
- domain,
2033
- mintRequestNonce: request.mintRequestNonce,
2034
- deadline: signatureDeadline
2035
- // No feeAmount/feeRecipient — RelayService auto-resolves.
2036
- });
2037
- } catch (err) {
2038
- throw new PTClaimError(
2039
- "BUILD_FAILED",
2040
- `prepareMint failed: ${err instanceof Error ? err.message : String(err)}`
2139
+ const signatureDeadline = BigInt(
2140
+ Math.floor(this.cfg.now() / 1e3) + this.cfg.signatureDeadlineSeconds
2041
2141
  );
2042
- }
2043
- let fallback;
2044
- if (feeAmount > 0n) {
2142
+ const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee() : 0n;
2143
+ const domain = {
2144
+ name: this.cfg.pointTokenDomainName,
2145
+ chainId: request.chainId,
2146
+ verifyingContract: request.pointTokenAddress
2147
+ };
2148
+ let userOp;
2045
2149
  try {
2046
- fallback = await this.cfg.relayService.prepareMint({
2150
+ userOp = await this.cfg.relayService.prepareMint({
2047
2151
  userAddress: request.userAddress,
2048
2152
  aaNonce: request.aaNonce,
2049
2153
  batchExecutorAddress,
@@ -2052,28 +2156,54 @@ var PTClaimHandler = class {
2052
2156
  issuerSignerWallet: this.cfg.issuerSignerWallet,
2053
2157
  domain,
2054
2158
  mintRequestNonce: request.mintRequestNonce,
2055
- deadline: signatureDeadline,
2056
- feeAmount: 0n
2159
+ deadline: signatureDeadline
2160
+ // No feeAmount/feeRecipient — RelayService auto-resolves.
2057
2161
  });
2058
2162
  } catch (err) {
2059
2163
  throw new PTClaimError(
2060
2164
  "BUILD_FAILED",
2061
- `prepareMint (fallback) failed: ${err instanceof Error ? err.message : String(err)}`
2165
+ `prepareMint failed: ${err instanceof Error ? err.message : String(err)}`
2062
2166
  );
2063
2167
  }
2168
+ let fallback;
2169
+ if (feeAmount > 0n) {
2170
+ try {
2171
+ fallback = await this.cfg.relayService.prepareMint({
2172
+ userAddress: request.userAddress,
2173
+ aaNonce: request.aaNonce,
2174
+ batchExecutorAddress,
2175
+ pointTokenAddress: request.pointTokenAddress,
2176
+ amount: request.amount,
2177
+ issuerSignerWallet: this.cfg.issuerSignerWallet,
2178
+ domain,
2179
+ mintRequestNonce: request.mintRequestNonce,
2180
+ deadline: signatureDeadline,
2181
+ feeAmount: 0n
2182
+ });
2183
+ } catch (err) {
2184
+ throw new PTClaimError(
2185
+ "BUILD_FAILED",
2186
+ `prepareMint (fallback) failed: ${err instanceof Error ? err.message : String(err)}`
2187
+ );
2188
+ }
2189
+ }
2190
+ const calls = (0, import_core8.decodeBatchExecuteCalls)(userOp.callData);
2191
+ const callsFallback = fallback ? (0, import_core8.decodeBatchExecuteCalls)(fallback.callData) : void 0;
2192
+ return {
2193
+ userOp,
2194
+ fallback,
2195
+ lockId,
2196
+ feeAmount,
2197
+ signatureDeadline,
2198
+ expiresInSeconds: Math.floor(this.cfg.lockDurationMs / 1e3),
2199
+ calls,
2200
+ callsFallback
2201
+ };
2202
+ } catch (err) {
2203
+ await this.cfg.ledger.releaseLock(lockId).catch(() => {
2204
+ });
2205
+ throw err;
2064
2206
  }
2065
- const calls = (0, import_core8.decodeBatchExecuteCalls)(userOp.callData);
2066
- const callsFallback = fallback ? (0, import_core8.decodeBatchExecuteCalls)(fallback.callData) : void 0;
2067
- return {
2068
- userOp,
2069
- fallback,
2070
- lockId,
2071
- feeAmount,
2072
- signatureDeadline,
2073
- expiresInSeconds: Math.floor(this.cfg.lockDurationMs / 1e3),
2074
- calls,
2075
- callsFallback
2076
- };
2077
2207
  }
2078
2208
  };
2079
2209
 
@@ -2140,14 +2270,19 @@ var PerpDepositHandler = class {
2140
2270
  totalAmount: request.amount,
2141
2271
  maxFee: 0n
2142
2272
  };
2143
- const [relayTokenFee, ptGasFee] = await Promise.all([
2273
+ const [relayTokenFee, usdcGasFee] = await Promise.all([
2144
2274
  this.cfg.provider.readContract({
2145
2275
  address: relayAddress,
2146
2276
  abi: import_core9.ORDERLY_RELAY_ABI,
2147
2277
  functionName: "quoteTokenFee",
2148
2278
  args: [requestForQuote]
2149
2279
  }),
2150
- this.cfg.feeService ? this.cfg.feeService.estimateGasFee() : Promise.resolve(0n)
2280
+ (0, import_core9.quoteOperatorFeeUsdt)({
2281
+ provider: this.cfg.provider,
2282
+ chainId: request.chainId,
2283
+ gasUnits: this.cfg.gasUnits,
2284
+ premiumBps: this.cfg.gasPremiumBps
2285
+ })
2151
2286
  ]);
2152
2287
  if (relayTokenFee >= request.amount) {
2153
2288
  throw new PerpDepositError(
@@ -2155,6 +2290,12 @@ var PerpDepositHandler = class {
2155
2290
  `Relay quoted fee ${relayTokenFee} >= deposit amount ${request.amount}`
2156
2291
  );
2157
2292
  }
2293
+ if (usdcGasFee > 0n && usdcGasFee >= request.amount) {
2294
+ throw new PerpDepositError(
2295
+ "FEE_EXCEEDS_AMOUNT",
2296
+ `USDC gas fee ${usdcGasFee} >= deposit amount ${request.amount}`
2297
+ );
2298
+ }
2158
2299
  const maxFee = relayTokenFee * BigInt(1e4 + this.cfg.maxFeePremiumBps) / 10000n;
2159
2300
  const depositReq = {
2160
2301
  token: usdcAddress,
@@ -2168,11 +2309,10 @@ var PerpDepositHandler = class {
2168
2309
  aaNonce: request.aaNonce,
2169
2310
  relayAddress,
2170
2311
  request: depositReq,
2171
- pointTokenAddress: this.cfg.pointTokenAddress,
2172
- gasFeePt: ptGasFee,
2173
- gasFeePtRecipient: pafiFeeRecipient
2312
+ gasFeeUsdc: usdcGasFee,
2313
+ gasFeeUsdcRecipient: pafiFeeRecipient
2174
2314
  });
2175
- const fallbackOp = ptGasFee > 0n ? (0, import_core9.buildPerpDepositViaRelay)({
2315
+ const fallbackOp = usdcGasFee > 0n ? (0, import_core9.buildPerpDepositViaRelay)({
2176
2316
  userAddress: request.userAddress,
2177
2317
  aaNonce: request.aaNonce,
2178
2318
  relayAddress,
@@ -2181,7 +2321,7 @@ var PerpDepositHandler = class {
2181
2321
  return {
2182
2322
  userOp: sponsoredOp,
2183
2323
  fallback: fallbackOp,
2184
- feeAmount: ptGasFee,
2324
+ feeAmount: usdcGasFee,
2185
2325
  relayTokenFee,
2186
2326
  maxFee,
2187
2327
  netDeposit: request.amount - relayTokenFee,
@@ -2293,9 +2433,45 @@ function createSdkErrorMapper(factories) {
2293
2433
  // src/api/issuerApiAdapter.ts
2294
2434
  var import_viem10 = require("viem");
2295
2435
  var import_core11 = require("@pafi-dev/core");
2436
+ var AdapterMisconfiguredError = class extends Error {
2437
+ code = "ADAPTER_MISCONFIGURED";
2438
+ constructor(message) {
2439
+ super(message);
2440
+ this.name = "AdapterMisconfiguredError";
2441
+ }
2442
+ };
2296
2443
  var IssuerApiAdapter = class {
2297
2444
  cfg;
2298
2445
  constructor(config) {
2446
+ if (config.ptClaimHandler) {
2447
+ if (typeof config.ledger.bindMintUserOpHash !== "function") {
2448
+ throw new AdapterMisconfiguredError(
2449
+ "ledger.bindMintUserOpHash is required when ptClaimHandler is wired (mobile claim flow). Implement it on your IPointLedger or omit ptClaimHandler from IssuerApiAdapter config."
2450
+ );
2451
+ }
2452
+ if (typeof config.ledger.getMintLock !== "function") {
2453
+ throw new AdapterMisconfiguredError(
2454
+ "ledger.getMintLock is required when ptClaimHandler is wired \u2014 claimStatus uses it to look up the lock."
2455
+ );
2456
+ }
2457
+ }
2458
+ if (config.ptRedeemHandler) {
2459
+ if (typeof config.ledger.reservePendingCredit !== "function") {
2460
+ throw new AdapterMisconfiguredError(
2461
+ "ledger.reservePendingCredit is required when ptRedeemHandler is wired (burn/redeem reverse flow). PTRedeemHandler also enforces this at construction; see ledger/types.ts comments."
2462
+ );
2463
+ }
2464
+ if (typeof config.ledger.bindCreditUserOpHash !== "function") {
2465
+ throw new AdapterMisconfiguredError(
2466
+ "ledger.bindCreditUserOpHash is required when ptRedeemHandler is wired (mobile redeem flow)."
2467
+ );
2468
+ }
2469
+ if (typeof config.ledger.getPendingCredit !== "function") {
2470
+ throw new AdapterMisconfiguredError(
2471
+ "ledger.getPendingCredit is required when ptRedeemHandler is wired \u2014 redeemStatus uses it to look up the credit."
2472
+ );
2473
+ }
2474
+ }
2299
2475
  this.cfg = config;
2300
2476
  }
2301
2477
  // ------------------------------ Read endpoints ---------------------------
@@ -2596,6 +2772,11 @@ var IssuerApiAdapter = class {
2596
2772
  /**
2597
2773
  * Build + sign a SponsorAuth payload. Returns `undefined` when no
2598
2774
  * issuer id is configured, so the controller can skip the field.
2775
+ *
2776
+ * v0.7.1 — `scenario` typed as `SponsorshipScenario` (was `string`).
2777
+ * Previously a typo (`"perp_deposit"` vs `"perp-deposit"`) compiled
2778
+ * fine but rejected at L1 by sponsor-relayer's IntentValidator. See
2779
+ * SDK_ISSUER_AUDIT.md N5.
2599
2780
  */
2600
2781
  async buildSponsorAuth(authenticatedAddress, callData, chainId, scenario) {
2601
2782
  if (!this.cfg.pafiIssuerId) return void 0;
@@ -3056,41 +3237,6 @@ var BalanceAggregator = class {
3056
3237
  }
3057
3238
  };
3058
3239
 
3059
- // src/pafi-backend/types.ts
3060
- var PafiBackendError = class extends Error {
3061
- constructor(code, message, httpStatus, details, opts) {
3062
- super(message);
3063
- this.code = code;
3064
- this.httpStatus = httpStatus;
3065
- this.details = details;
3066
- this.name = "PafiBackendError";
3067
- if (opts?.retryAfter !== void 0) this.retryAfter = opts.retryAfter;
3068
- if (opts?.safeToRetry !== void 0) this.serverSafeToRetry = opts.safeToRetry;
3069
- }
3070
- code;
3071
- httpStatus;
3072
- details;
3073
- retryAfter;
3074
- serverSafeToRetry;
3075
- get safeToRetry() {
3076
- if (this.serverSafeToRetry !== void 0) return this.serverSafeToRetry;
3077
- switch (this.code) {
3078
- case "RATE_LIMITER_UNAVAILABLE":
3079
- case "INTERNAL_ERROR":
3080
- case "TIMEOUT":
3081
- case "NETWORK_ERROR":
3082
- return true;
3083
- case "RATE_LIMIT_EXCEEDED":
3084
- case "RATE_LIMIT_EXCEEDED_DAILY":
3085
- case "RATE_LIMIT_EXCEEDED_PER_USER":
3086
- case "ISSUER_BUDGET_EXCEEDED":
3087
- return true;
3088
- default:
3089
- return false;
3090
- }
3091
- }
3092
- };
3093
-
3094
3240
  // src/pafi-backend/client.ts
3095
3241
  function serializeBigInt(_key, value) {
3096
3242
  return typeof value === "bigint" ? value.toString(10) : value;
@@ -3531,15 +3677,17 @@ var IssuerStateValidator = class _IssuerStateValidator {
3531
3677
  };
3532
3678
 
3533
3679
  // src/index.ts
3534
- var PAFI_ISSUER_SDK_VERSION = "0.4.0";
3680
+ var PAFI_ISSUER_SDK_VERSION = true ? "0.7.1" : "dev";
3535
3681
  // Annotate the CommonJS export names for ESM import in node:
3536
3682
  0 && (module.exports = {
3683
+ AdapterMisconfiguredError,
3537
3684
  AuthError,
3538
3685
  AuthService,
3539
3686
  BalanceAggregator,
3540
3687
  BundlerNotConfiguredError,
3541
3688
  BundlerRejectedError,
3542
3689
  BurnIndexer,
3690
+ ConfigurationError,
3543
3691
  DefaultPolicyEngine,
3544
3692
  FeeManager,
3545
3693
  InMemoryCursorStore,
@@ -3567,6 +3715,7 @@ var PAFI_ISSUER_SDK_VERSION = "0.4.0";
3567
3715
  PointIndexer,
3568
3716
  RelayError,
3569
3717
  RelayService,
3718
+ ValidationError,
3570
3719
  authenticateRequest,
3571
3720
  createIssuerService,
3572
3721
  createNativePtQuoter,