@pafi-dev/issuer 0.18.0 → 0.20.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.cjs CHANGED
@@ -30,7 +30,6 @@ __export(index_exports, {
30
30
  DEFAULT_REDEMPTION_POLICY: () => DEFAULT_REDEMPTION_POLICY,
31
31
  DefaultPolicyEngine: () => DefaultPolicyEngine,
32
32
  FeeManager: () => FeeManager,
33
- GasUnitsCache: () => GasUnitsCache,
34
33
  InMemoryCursorStore: () => InMemoryCursorStore,
35
34
  IssuerApiAdapter: () => IssuerApiAdapter,
36
35
  IssuerApiHandlers: () => IssuerApiHandlers,
@@ -51,6 +50,7 @@ __export(index_exports, {
51
50
  PTRedeemHandler: () => PTRedeemHandler,
52
51
  PafiBackendClient: () => PafiBackendClient,
53
52
  PafiBackendError: () => PafiBackendError,
53
+ PafiEstimatorHttpError: () => PafiEstimatorHttpError,
54
54
  PafiSdkError: () => import_core.PafiSdkError,
55
55
  PendingUserOpForbiddenError: () => PendingUserOpForbiddenError,
56
56
  PendingUserOpNotFoundError: () => PendingUserOpNotFoundError,
@@ -71,6 +71,7 @@ __export(index_exports, {
71
71
  buildSdkErrorBody: () => buildSdkErrorBody,
72
72
  createIssuerService: () => createIssuerService,
73
73
  createNativePtQuoter: () => createNativePtQuoter,
74
+ createPafiEstimatorClient: () => createPafiEstimatorClient,
74
75
  createSdkErrorMapper: () => createSdkErrorMapper,
75
76
  createSubgraphNativeUsdtQuoter: () => createSubgraphNativeUsdtQuoter,
76
77
  createSubgraphPoolsProvider: () => createSubgraphPoolsProvider,
@@ -1097,21 +1098,21 @@ var RelayService = class {
1097
1098
  // =========================================================================
1098
1099
  // Preview methods — produce bundler-ready partial UserOps WITHOUT signing.
1099
1100
  //
1100
- // These exist so callers can compute an accurate gas estimate via
1101
- // `bundlerClient.estimateUserOperationGas(...)` BEFORE committing to a
1102
- // signed MintRequest / BurnRequest (signing is HSM-backed and expensive
1103
- // in prod). The returned `callData` matches the SHAPE of the real call;
1104
- // the EIP-712 signature bytes are placeholder. Bundler simulation
1105
- // doesn't validate the signature, so the gas units come back accurate.
1101
+ // These exist so callers can fetch an accurate gas estimate from PAFI's
1102
+ // `/v1/estimate-gas-fee` BEFORE committing to a signed MintRequest /
1103
+ // BurnRequest (HSM/KMS signing is expensive in production). The
1104
+ // returned `callData` matches the SHAPE of the real call; the EIP-712
1105
+ // signature bytes are a placeholder. Bundler simulation doesn't
1106
+ // validate the signature, so the gas estimate comes back accurate.
1106
1107
  //
1107
- // Cache-wise: same SC version + same scenario same calldata shape →
1108
- // same bundler-returned gas units. The first call seeds the cache; the
1109
- // rest hit it.
1108
+ // Cache-wise: same SC version + same scenario produce identical
1109
+ // calldata shape → identical bundler-returned gas units. The first
1110
+ // call seeds the cache; subsequent ones hit it.
1110
1111
  // =========================================================================
1111
1112
  /**
1112
1113
  * Build a dummy `PartialUserOperation` for the mint scenario, suitable
1113
1114
  * for `feeManager.estimateGasFee({ partialUserOp, ... })`. NO signing —
1114
- * uses a 65-byte zero signature in place of the real minter sig.
1115
+ * uses a 65-byte zero signature placeholder.
1115
1116
  */
1116
1117
  previewMintUserOp(params) {
1117
1118
  const useWrapper = params.mintFeeWrapperAddress !== void 0;
@@ -1148,7 +1149,11 @@ var RelayService = class {
1148
1149
  nonce: params.aaNonce,
1149
1150
  operations: [{ target: mintTarget, value: 0n, data: mintCallData }],
1150
1151
  // Gas limits ignored by bundler estimate — it computes them.
1151
- gasLimits: { callGasLimit: 1n, verificationGasLimit: 1n, preVerificationGas: 1n }
1152
+ gasLimits: {
1153
+ callGasLimit: 1n,
1154
+ verificationGasLimit: 1n,
1155
+ preVerificationGas: 1n
1156
+ }
1152
1157
  });
1153
1158
  }
1154
1159
  /** Burn-side mirror of `previewMintUserOp`. */
@@ -1169,7 +1174,11 @@ var RelayService = class {
1169
1174
  operations: [
1170
1175
  { target: params.pointTokenAddress, value: 0n, data: burnCallData }
1171
1176
  ],
1172
- gasLimits: { callGasLimit: 1n, verificationGasLimit: 1n, preVerificationGas: 1n }
1177
+ gasLimits: {
1178
+ callGasLimit: 1n,
1179
+ verificationGasLimit: 1n,
1180
+ preVerificationGas: 1n
1181
+ }
1173
1182
  });
1174
1183
  }
1175
1184
  };
@@ -1178,85 +1187,21 @@ function errorMessage(err) {
1178
1187
  return err instanceof Error ? err.message : String(err);
1179
1188
  }
1180
1189
 
1181
- // src/relay/gasUnitsCache.ts
1182
- var import_viem4 = require("viem");
1183
- var DEFAULT_TTL_MS = 5 * 6e4;
1184
- var DEFAULT_CODEHASH_TTL_MS = 60 * 6e4;
1185
- var DEFAULT_MAX_ENTRIES = 100;
1186
- var GasUnitsCache = class {
1187
- entries = /* @__PURE__ */ new Map();
1188
- codehashEntries = /* @__PURE__ */ new Map();
1189
- ttlMs;
1190
- codehashTtlMs;
1191
- maxEntries;
1192
- constructor(config = {}) {
1193
- this.ttlMs = config.ttlMs ?? DEFAULT_TTL_MS;
1194
- this.codehashTtlMs = config.codehashTtlMs ?? DEFAULT_CODEHASH_TTL_MS;
1195
- this.maxEntries = config.maxEntries ?? DEFAULT_MAX_ENTRIES;
1196
- }
1197
- async buildKey(params) {
1198
- const codehash = await this.getCodehash(
1199
- params.provider,
1200
- params.contractAddress
1201
- );
1202
- const pm = params.paymasterAddress?.toLowerCase() ?? "0x0";
1203
- return `${params.scenario}:${codehash}:${pm}`;
1204
- }
1205
- get(key, now = Date.now()) {
1206
- const entry = this.entries.get(key);
1207
- if (!entry) return null;
1208
- if (entry.expiresAt <= now) {
1209
- this.entries.delete(key);
1210
- return null;
1211
- }
1212
- return entry.gasUnits;
1213
- }
1214
- set(key, gasUnits, now = Date.now()) {
1215
- if (this.entries.size >= this.maxEntries && !this.entries.has(key)) {
1216
- const eldest = this.entries.keys().next().value;
1217
- if (eldest !== void 0) this.entries.delete(eldest);
1218
- }
1219
- this.entries.set(key, { gasUnits, expiresAt: now + this.ttlMs });
1220
- }
1221
- invalidate() {
1222
- this.entries.clear();
1223
- this.codehashEntries.clear();
1224
- }
1225
- size() {
1226
- return this.entries.size;
1227
- }
1228
- async getCodehash(provider, address) {
1229
- const lower = address.toLowerCase();
1230
- const now = Date.now();
1231
- const cached = this.codehashEntries.get(lower);
1232
- if (cached && cached.expiresAt > now) return cached.codehash;
1233
- const code = await provider.getCode({ address });
1234
- const codehash = code ? (0, import_viem4.keccak256)(code) : "0x0";
1235
- this.codehashEntries.set(lower, {
1236
- codehash,
1237
- expiresAt: now + this.codehashTtlMs
1238
- });
1239
- return codehash;
1240
- }
1241
- };
1242
-
1243
1190
  // src/relay/feeManager.ts
1244
1191
  var DEFAULT_GAS_UNITS = 500000n;
1245
1192
  var DEFAULT_PREMIUM_BPS = 1e4;
1246
- var DEFAULT_PAYMASTER_OVERHEAD = 80000n;
1247
- var DUMMY_SIGNATURE = "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c";
1248
1193
  var FeeManager = class _FeeManager {
1249
1194
  provider;
1250
- gasUnits;
1195
+ fallbackGasUnits;
1251
1196
  gasPremiumBps;
1252
1197
  quoteNativeToFee;
1253
1198
  bundlerClient;
1254
- cache;
1255
- paymasterOverheadGas;
1256
1199
  metrics;
1257
- // Short-lived in-flight fee cache (legacy behavior). Distinct from
1258
- // `cache` that one stores gasUnits per scenario; this one stores the
1259
- // FULL computed fee value, valid for 10s to absorb burst calls.
1200
+ // Short-lived fee-value cache. Distinct from the estimator's cache:
1201
+ // this absorbs burst calls (e.g. 5 user requests in 5s all hit the
1202
+ // /gas-fee endpoint) by remembering the COMPUTED PT amount, not the
1203
+ // gas units. Only used by the no-opts legacy path; estimator path
1204
+ // gets its caching from the PAFI side instead.
1260
1205
  cachedFee = null;
1261
1206
  cacheExpiresAt = 0;
1262
1207
  static FEE_CACHE_TTL_MS = 1e4;
@@ -1265,29 +1210,26 @@ var FeeManager = class _FeeManager {
1265
1210
  if (!config.quoteNativeToFee)
1266
1211
  throw new Error("FeeManager: quoteNativeToFee required");
1267
1212
  this.provider = config.provider;
1268
- this.gasUnits = config.gasUnits ?? DEFAULT_GAS_UNITS;
1213
+ this.fallbackGasUnits = config.gasUnits ?? DEFAULT_GAS_UNITS;
1269
1214
  this.gasPremiumBps = config.gasPremiumBps ?? DEFAULT_PREMIUM_BPS;
1270
1215
  this.quoteNativeToFee = config.quoteNativeToFee;
1271
1216
  this.bundlerClient = config.bundlerClient;
1272
- this.cache = new GasUnitsCache(config.cache);
1273
- this.paymasterOverheadGas = config.paymasterOverheadGas ?? DEFAULT_PAYMASTER_OVERHEAD;
1274
1217
  this.metrics = config.metrics;
1275
1218
  }
1276
1219
  /**
1277
- * Estimate the fee (in the caller's fee currency) to charge for the
1278
- * next sponsored UserOp.
1220
+ * Estimate the operator fee for the next sponsored UserOp.
1279
1221
  *
1280
- * gasUnits = bundler-estimated (cached) or hardcoded fallback
1281
- * nativeCost = gasUnits × gasPrice
1282
- * withPremium = nativeCost × premiumBps / 10_000
1283
- * fee = quoteNativeToFee(withPremium)
1222
+ * Without `opts` → legacy path: `gasUnits × gasPrice × premium
1223
+ * quoteNativeToFee`. Cached for 10 s to absorb bursts.
1284
1224
  *
1285
- * When `opts.partialUserOp` is omitted, behaves exactly like v0.16.x:
1286
- * uses the hardcoded `gasUnits` default.
1225
+ * With `opts` AND `bundlerClient` estimator path. Each call may
1226
+ * hit a different bundler-cached result; the SDK does NOT add its
1227
+ * own value cache because the estimator's cache TTL is the source
1228
+ * of truth for "how long is this estimate good for".
1287
1229
  */
1288
1230
  async estimateGasFee(opts = {}) {
1289
- const now = Date.now();
1290
1231
  const isLegacyCall = !opts.partialUserOp && !opts.scenario && !opts.contractAddress;
1232
+ const now = Date.now();
1291
1233
  if (isLegacyCall && this.cachedFee !== null && now < this.cacheExpiresAt) {
1292
1234
  return this.cachedFee;
1293
1235
  }
@@ -1312,49 +1254,32 @@ var FeeManager = class _FeeManager {
1312
1254
  }
1313
1255
  return fee;
1314
1256
  }
1315
- /**
1316
- * Manually purge the per-scenario gas-units cache. Useful after an SC
1317
- * upgrade when ops wants the next estimate to refresh immediately
1318
- * (the codehash check would catch it on the NEXT call anyway, but
1319
- * this forces it now).
1320
- */
1257
+ /** Manually purge the legacy 10s fee cache. */
1321
1258
  invalidateCache() {
1322
- this.cache.invalidate();
1323
1259
  this.cachedFee = null;
1324
1260
  this.cacheExpiresAt = 0;
1325
1261
  }
1326
1262
  async resolveGasUnits(opts) {
1327
1263
  if (!this.bundlerClient || !opts.partialUserOp || !opts.scenario || !opts.contractAddress) {
1328
- return { gasUnits: this.gasUnits, source: "fallback" };
1264
+ return { gasUnits: this.fallbackGasUnits, source: "fallback" };
1329
1265
  }
1330
1266
  try {
1331
- const cacheKey = await this.cache.buildKey({
1267
+ const result = await this.bundlerClient.getGasUnits({
1332
1268
  scenario: opts.scenario,
1333
1269
  contractAddress: opts.contractAddress,
1334
1270
  paymasterAddress: opts.paymasterAddress,
1335
- provider: this.provider
1271
+ partialUserOp: opts.partialUserOp
1336
1272
  });
1337
- const cached = this.cache.get(cacheKey);
1338
- if (cached !== null) {
1339
- return { gasUnits: cached, source: "cache" };
1340
- }
1341
- const estimate = await this.bundlerClient.estimateUserOperationGas({
1342
- sender: opts.partialUserOp.sender,
1343
- nonce: opts.partialUserOp.nonce,
1344
- callData: opts.partialUserOp.callData,
1345
- signature: opts.partialUserOp.signature ?? DUMMY_SIGNATURE
1346
- // Intentionally NO paymaster fields — avoids chicken-and-egg
1347
- // (paymasterData depends on gasLimits). Overhead added below.
1348
- });
1349
- const gasUnits = estimate.callGasLimit + estimate.verificationGasLimit + estimate.preVerificationGas + (estimate.paymasterVerificationGasLimit ?? 0n) + (estimate.paymasterPostOpGasLimit ?? 0n) + this.paymasterOverheadGas;
1350
- this.cache.set(cacheKey, gasUnits);
1351
- return { gasUnits, source: "bundler" };
1273
+ return { gasUnits: result.gasUnits, source: "estimator" };
1352
1274
  } catch (err) {
1353
1275
  const reason = err instanceof Error ? err.message : String(err);
1354
1276
  this.safeEmit(
1355
- () => this.metrics?.onBundlerError?.({ scenario: opts.scenario, reason })
1277
+ () => this.metrics?.onEstimatorError?.({
1278
+ scenario: opts.scenario,
1279
+ reason
1280
+ })
1356
1281
  );
1357
- return { gasUnits: this.gasUnits, source: "fallback" };
1282
+ return { gasUnits: this.fallbackGasUnits, source: "fallback" };
1358
1283
  }
1359
1284
  }
1360
1285
  safeEmit(fn) {
@@ -1365,6 +1290,70 @@ var FeeManager = class _FeeManager {
1365
1290
  }
1366
1291
  };
1367
1292
 
1293
+ // src/relay/bundlerEstimator.ts
1294
+ var PafiEstimatorHttpError = class extends Error {
1295
+ status;
1296
+ body;
1297
+ constructor(status, body, message) {
1298
+ super(message ?? `PAFI estimator HTTP ${status}`);
1299
+ this.status = status;
1300
+ this.body = body;
1301
+ }
1302
+ };
1303
+ function createPafiEstimatorClient(config) {
1304
+ const { baseUrl, apiKey, issuerId } = config;
1305
+ if (!baseUrl) throw new Error("createPafiEstimatorClient: baseUrl required");
1306
+ if (!apiKey) throw new Error("createPafiEstimatorClient: apiKey required");
1307
+ if (!issuerId) throw new Error("createPafiEstimatorClient: issuerId required");
1308
+ const fetchImpl = config.fetchImpl ?? globalThis.fetch;
1309
+ if (!fetchImpl) {
1310
+ throw new Error(
1311
+ "createPafiEstimatorClient: no fetch implementation available \u2014 pass `fetchImpl`"
1312
+ );
1313
+ }
1314
+ const url = `${baseUrl.replace(/\/$/, "")}/v1/estimate-gas-fee`;
1315
+ return {
1316
+ async getGasUnits(input) {
1317
+ const body = JSON.stringify({
1318
+ partialUserOp: {
1319
+ sender: input.partialUserOp.sender,
1320
+ // Hex-encode bigint for JSON safety. Sponsor-relayer parses
1321
+ // back to bigint at the DTO layer.
1322
+ nonce: `0x${input.partialUserOp.nonce.toString(16)}`,
1323
+ callData: input.partialUserOp.callData,
1324
+ signature: input.partialUserOp.signature
1325
+ },
1326
+ scenario: input.scenario,
1327
+ contractAddress: input.contractAddress,
1328
+ paymasterAddress: input.paymasterAddress
1329
+ });
1330
+ const res = await fetchImpl(url, {
1331
+ method: "POST",
1332
+ headers: {
1333
+ "content-type": "application/json",
1334
+ authorization: `Bearer ${apiKey}`,
1335
+ "x-issuer-id": issuerId
1336
+ },
1337
+ body
1338
+ });
1339
+ if (!res.ok) {
1340
+ let errBody = null;
1341
+ try {
1342
+ errBody = await res.json();
1343
+ } catch {
1344
+ }
1345
+ throw new PafiEstimatorHttpError(res.status, errBody);
1346
+ }
1347
+ const json = await res.json();
1348
+ return {
1349
+ gasUnits: BigInt(json.gasUnits),
1350
+ source: json.source,
1351
+ expiresAt: json.expiresAt
1352
+ };
1353
+ }
1354
+ };
1355
+ }
1356
+
1368
1357
  // src/indexer/types.ts
1369
1358
  var InMemoryCursorStore = class {
1370
1359
  cursor;
@@ -1377,11 +1366,11 @@ var InMemoryCursorStore = class {
1377
1366
  };
1378
1367
 
1379
1368
  // src/indexer/pointIndexer.ts
1380
- var import_viem5 = require("viem");
1381
- var TRANSFER_EVENT = (0, import_viem5.parseAbiItem)(
1369
+ var import_viem4 = require("viem");
1370
+ var TRANSFER_EVENT = (0, import_viem4.parseAbiItem)(
1382
1371
  "event Transfer(address indexed from, address indexed to, uint256 value)"
1383
1372
  );
1384
- var MINT_WITH_FEE_EVENT = (0, import_viem5.parseAbiItem)(
1373
+ var MINT_WITH_FEE_EVENT = (0, import_viem4.parseAbiItem)(
1385
1374
  "event MintWithFee(address indexed pointToken, address indexed to, uint256 grossAmount, uint256 netAmount, uint256 feeAmount)"
1386
1375
  );
1387
1376
  var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
@@ -1391,7 +1380,7 @@ var DEFAULT_BATCH_SIZE = 2000n;
1391
1380
  var DEFAULT_POLL_INTERVAL_MS = 5e3;
1392
1381
  function isNoWrapper(addr) {
1393
1382
  if (!addr) return true;
1394
- const checksummed = (0, import_viem5.getAddress)(addr);
1383
+ const checksummed = (0, import_viem4.getAddress)(addr);
1395
1384
  return checksummed === ZERO_ADDRESS || checksummed === DEAD_ADDRESS;
1396
1385
  }
1397
1386
  var PointIndexer = class {
@@ -1413,8 +1402,8 @@ var PointIndexer = class {
1413
1402
  throw new Error("PointIndexer: pointTokenAddress required");
1414
1403
  if (!config.ledger) throw new Error("PointIndexer: ledger required");
1415
1404
  this.provider = config.provider;
1416
- this.pointTokenAddress = (0, import_viem5.getAddress)(config.pointTokenAddress);
1417
- this.mintFeeWrapperAddress = isNoWrapper(config.mintFeeWrapperAddress) ? void 0 : (0, import_viem5.getAddress)(config.mintFeeWrapperAddress);
1405
+ this.pointTokenAddress = (0, import_viem4.getAddress)(config.pointTokenAddress);
1406
+ this.mintFeeWrapperAddress = isNoWrapper(config.mintFeeWrapperAddress) ? void 0 : (0, import_viem4.getAddress)(config.mintFeeWrapperAddress);
1418
1407
  this.ledger = config.ledger;
1419
1408
  this.cursorStore = config.cursorStore ?? new InMemoryCursorStore();
1420
1409
  this.startBlock = config.fromBlock ?? 0n;
@@ -1533,9 +1522,9 @@ var PointIndexer = class {
1533
1522
  if (!args.pointToken || !args.to || args.grossAmount === void 0 || log.blockNumber === null || log.transactionHash === null) {
1534
1523
  continue;
1535
1524
  }
1536
- if ((0, import_viem5.getAddress)(args.pointToken) !== this.pointTokenAddress) continue;
1525
+ if ((0, import_viem4.getAddress)(args.pointToken) !== this.pointTokenAddress) continue;
1537
1526
  out.push({
1538
- to: (0, import_viem5.getAddress)(args.to),
1527
+ to: (0, import_viem4.getAddress)(args.to),
1539
1528
  amount: args.grossAmount,
1540
1529
  blockNumber: log.blockNumber,
1541
1530
  txHash: log.transactionHash,
@@ -1563,10 +1552,10 @@ var PointIndexer = class {
1563
1552
  for (const log of logs) {
1564
1553
  const args = log.args;
1565
1554
  if (!args.from || !args.to || args.value === void 0) continue;
1566
- if ((0, import_viem5.getAddress)(args.from) !== ZERO_ADDRESS) continue;
1555
+ if ((0, import_viem4.getAddress)(args.from) !== ZERO_ADDRESS) continue;
1567
1556
  if (log.blockNumber === null || log.transactionHash === null) continue;
1568
1557
  out.push({
1569
- to: (0, import_viem5.getAddress)(args.to),
1558
+ to: (0, import_viem4.getAddress)(args.to),
1570
1559
  amount: args.value,
1571
1560
  blockNumber: log.blockNumber,
1572
1561
  txHash: log.transactionHash,
@@ -1625,8 +1614,8 @@ function pickMatchingLock(locks, amount) {
1625
1614
  }
1626
1615
 
1627
1616
  // src/indexer/burnIndexer.ts
1628
- var import_viem6 = require("viem");
1629
- var TRANSFER_EVENT2 = (0, import_viem6.parseAbiItem)(
1617
+ var import_viem5 = require("viem");
1618
+ var TRANSFER_EVENT2 = (0, import_viem5.parseAbiItem)(
1630
1619
  "event Transfer(address indexed from, address indexed to, uint256 value)"
1631
1620
  );
1632
1621
  var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
@@ -1757,10 +1746,10 @@ var BurnIndexer = class {
1757
1746
  for (const log of logs) {
1758
1747
  const args = log.args;
1759
1748
  if (!args.from || !args.to || args.value === void 0) continue;
1760
- if ((0, import_viem6.getAddress)(args.to) !== ZERO_ADDRESS2) continue;
1749
+ if ((0, import_viem5.getAddress)(args.to) !== ZERO_ADDRESS2) continue;
1761
1750
  if (log.blockNumber === null || log.transactionHash === null) continue;
1762
1751
  out.push({
1763
- from: (0, import_viem6.getAddress)(args.from),
1752
+ from: (0, import_viem5.getAddress)(args.from),
1764
1753
  amount: args.value,
1765
1754
  blockNumber: log.blockNumber,
1766
1755
  txHash: log.transactionHash,
@@ -1795,7 +1784,7 @@ var BurnIndexer = class {
1795
1784
  };
1796
1785
 
1797
1786
  // src/api/handlers.ts
1798
- var import_viem7 = require("viem");
1787
+ var import_viem6 = require("viem");
1799
1788
  var import_core6 = require("@pafi-dev/core");
1800
1789
  var IssuerApiHandlers = class _IssuerApiHandlers {
1801
1790
  authService;
@@ -1832,7 +1821,7 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
1832
1821
  "IssuerApiHandlers: pointTokenAddress or pointTokenAddresses required"
1833
1822
  );
1834
1823
  }
1835
- const normalized = raw.map((a) => (0, import_viem7.getAddress)(a));
1824
+ const normalized = raw.map((a) => (0, import_viem6.getAddress)(a));
1836
1825
  this.supportedTokens = new Set(normalized);
1837
1826
  this.chainId = config.chainId;
1838
1827
  this.contracts = config.contracts;
@@ -1841,7 +1830,7 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
1841
1830
  if (config.poolsProvider) this.poolsProvider = config.poolsProvider;
1842
1831
  if (config.redemption) this.redemption = config.redemption;
1843
1832
  if (config.mintFeeWrapperAddress) {
1844
- this.mintFeeWrapperAddress = (0, import_viem7.getAddress)(config.mintFeeWrapperAddress);
1833
+ this.mintFeeWrapperAddress = (0, import_viem6.getAddress)(config.mintFeeWrapperAddress);
1845
1834
  }
1846
1835
  }
1847
1836
  // =========================================================================
@@ -2048,8 +2037,8 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
2048
2037
  { requested: request.chainId, supported: this.chainId }
2049
2038
  );
2050
2039
  }
2051
- const normalizedAuthed = (0, import_viem7.getAddress)(userAddress);
2052
- const normalizedRequest = (0, import_viem7.getAddress)(request.userAddress);
2040
+ const normalizedAuthed = (0, import_viem6.getAddress)(userAddress);
2041
+ const normalizedRequest = (0, import_viem6.getAddress)(request.userAddress);
2053
2042
  if (normalizedAuthed !== normalizedRequest) {
2054
2043
  throw new import_core3.ValidationError(
2055
2044
  "USER_ADDRESS_MISMATCH",
@@ -2057,7 +2046,7 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
2057
2046
  { authenticated: normalizedAuthed, requested: normalizedRequest }
2058
2047
  );
2059
2048
  }
2060
- const pointToken = (0, import_viem7.getAddress)(request.pointTokenAddress);
2049
+ const pointToken = (0, import_viem6.getAddress)(request.pointTokenAddress);
2061
2050
  if (!this.supportedTokens.has(pointToken)) {
2062
2051
  throw new import_core3.ValidationError(
2063
2052
  "UNSUPPORTED_POINT_TOKEN",
@@ -2098,9 +2087,9 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
2098
2087
  "handleRedemptionPreview: redemption is not configured on this issuer"
2099
2088
  );
2100
2089
  }
2101
- const tokenAddress = request.pointTokenAddress ? this.requireSupportedToken((0, import_viem7.getAddress)(request.pointTokenAddress), "handleRedemptionPreview") : void 0;
2090
+ const tokenAddress = request.pointTokenAddress ? this.requireSupportedToken((0, import_viem6.getAddress)(request.pointTokenAddress), "handleRedemptionPreview") : void 0;
2102
2091
  const preview = await this.redemption.preview(
2103
- (0, import_viem7.getAddress)(userAddress),
2092
+ (0, import_viem6.getAddress)(userAddress),
2104
2093
  tokenAddress
2105
2094
  );
2106
2095
  return preview;
@@ -2129,9 +2118,9 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
2129
2118
  { amountPt: request.amountPt.toString() }
2130
2119
  );
2131
2120
  }
2132
- const tokenAddress = request.pointTokenAddress ? this.requireSupportedToken((0, import_viem7.getAddress)(request.pointTokenAddress), "handleRedemptionEvaluate") : void 0;
2121
+ const tokenAddress = request.pointTokenAddress ? this.requireSupportedToken((0, import_viem6.getAddress)(request.pointTokenAddress), "handleRedemptionEvaluate") : void 0;
2133
2122
  const decision = await this.redemption.evaluate(
2134
- (0, import_viem7.getAddress)(userAddress),
2123
+ (0, import_viem6.getAddress)(userAddress),
2135
2124
  request.amountPt,
2136
2125
  tokenAddress
2137
2126
  );
@@ -2155,7 +2144,7 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
2155
2144
  };
2156
2145
 
2157
2146
  // src/api/handlers/ptRedeemHandler.ts
2158
- var import_viem8 = require("viem");
2147
+ var import_viem7 = require("viem");
2159
2148
  var import_core7 = require("@pafi-dev/core");
2160
2149
  var DEFAULT_REDEEM_LOCK_MS = 15 * 60 * 1e3;
2161
2150
  var DEFAULT_SIG_DEADLINE_SEC = 15 * 60;
@@ -2216,8 +2205,8 @@ var PTRedeemHandler = class {
2216
2205
  this.relayService = config.relayService;
2217
2206
  this.provider = config.provider;
2218
2207
  this.feeService = config.feeService;
2219
- this.pointTokenAddress = (0, import_viem8.getAddress)(config.pointTokenAddress);
2220
- this.batchExecutorAddress = (0, import_viem8.getAddress)(config.batchExecutorAddress);
2208
+ this.pointTokenAddress = (0, import_viem7.getAddress)(config.pointTokenAddress);
2209
+ this.batchExecutorAddress = (0, import_viem7.getAddress)(config.batchExecutorAddress);
2221
2210
  this.chainId = config.chainId;
2222
2211
  this.domain = config.domain;
2223
2212
  this.burnerSignerWallet = config.burnerSignerWallet;
@@ -2232,7 +2221,7 @@ var PTRedeemHandler = class {
2232
2221
  }
2233
2222
  }
2234
2223
  async handle(request) {
2235
- if ((0, import_viem8.getAddress)(request.authenticatedAddress) !== (0, import_viem8.getAddress)(request.userAddress)) {
2224
+ if ((0, import_viem7.getAddress)(request.authenticatedAddress) !== (0, import_viem7.getAddress)(request.userAddress)) {
2236
2225
  throw new PTRedeemError(
2237
2226
  "UNAUTHORIZED",
2238
2227
  `userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
@@ -2270,7 +2259,7 @@ var PTRedeemHandler = class {
2270
2259
  `failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
2271
2260
  );
2272
2261
  }
2273
- const userKey = (0, import_viem8.getAddress)(request.userAddress).toLowerCase();
2262
+ const userKey = (0, import_viem7.getAddress)(request.userAddress).toLowerCase();
2274
2263
  let userNonces = this.inFlightNonces.get(userKey);
2275
2264
  if (!userNonces) {
2276
2265
  userNonces = /* @__PURE__ */ new Set();
@@ -2552,7 +2541,7 @@ async function handleRedeemStatus(params) {
2552
2541
  }
2553
2542
 
2554
2543
  // src/api/mobileHandlers.ts
2555
- var import_viem9 = require("viem");
2544
+ var import_viem8 = require("viem");
2556
2545
  var import_core10 = require("@pafi-dev/core");
2557
2546
 
2558
2547
  // src/userop-store/serialize.ts
@@ -2898,7 +2887,7 @@ async function handleMobileSubmit(params) {
2898
2887
  if (!entry) {
2899
2888
  throw new PendingUserOpNotFoundError(params.lockId);
2900
2889
  }
2901
- if ((0, import_viem9.getAddress)(entry.sender) !== (0, import_viem9.getAddress)(params.authenticatedAddress)) {
2890
+ if ((0, import_viem8.getAddress)(entry.sender) !== (0, import_viem8.getAddress)(params.authenticatedAddress)) {
2902
2891
  throw new PendingUserOpForbiddenError(params.lockId);
2903
2892
  }
2904
2893
  const variant = params.variant ?? "sponsored";
@@ -2914,7 +2903,7 @@ async function handleMobileSubmit(params) {
2914
2903
  }
2915
2904
 
2916
2905
  // src/api/handlers/ptClaimHandler.ts
2917
- var import_viem10 = require("viem");
2906
+ var import_viem9 = require("viem");
2918
2907
  var import_core11 = require("@pafi-dev/core");
2919
2908
 
2920
2909
  // src/issuer-state/types.ts
@@ -2960,7 +2949,7 @@ var PTClaimHandler = class {
2960
2949
  };
2961
2950
  }
2962
2951
  async handle(request) {
2963
- if ((0, import_viem10.getAddress)(request.authenticatedAddress) !== (0, import_viem10.getAddress)(request.userAddress)) {
2952
+ if ((0, import_viem9.getAddress)(request.authenticatedAddress) !== (0, import_viem9.getAddress)(request.userAddress)) {
2964
2953
  throw new PTClaimError(
2965
2954
  "VALIDATION_FAILED",
2966
2955
  `userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
@@ -3214,7 +3203,7 @@ var PerpDepositHandler = class {
3214
3203
 
3215
3204
  // src/api/delegateHandler.ts
3216
3205
  var import_core13 = require("@pafi-dev/core");
3217
- var import_viem11 = require("viem");
3206
+ var import_viem10 = require("viem");
3218
3207
  var DEFAULT_DELEGATE_GAS = {
3219
3208
  callGasLimit: 100000n,
3220
3209
  verificationGasLimit: 150000n,
@@ -3301,7 +3290,7 @@ async function handleDelegateSubmit(params) {
3301
3290
  if (!entry) {
3302
3291
  throw new PendingUserOpNotFoundError(params.lockId);
3303
3292
  }
3304
- if ((0, import_viem11.getAddress)(entry.sender) !== (0, import_viem11.getAddress)(params.authenticatedAddress)) {
3293
+ if ((0, import_viem10.getAddress)(entry.sender) !== (0, import_viem10.getAddress)(params.authenticatedAddress)) {
3305
3294
  throw new PendingUserOpForbiddenError(params.lockId);
3306
3295
  }
3307
3296
  if (!entry.eip7702Auth) {
@@ -3322,7 +3311,7 @@ async function handleDelegateSubmit(params) {
3322
3311
 
3323
3312
  // src/api/issuerApiAdapter.ts
3324
3313
  var import_node_crypto3 = require("crypto");
3325
- var import_viem12 = require("viem");
3314
+ var import_viem11 = require("viem");
3326
3315
  var import_core14 = require("@pafi-dev/core");
3327
3316
  var AdapterMisconfiguredError = class extends Error {
3328
3317
  code = "ADAPTER_MISCONFIGURED";
@@ -3380,7 +3369,7 @@ var IssuerApiAdapter = class {
3380
3369
  async pools(authenticatedAddress, chainId, pointTokenAddress) {
3381
3370
  const result = await this.cfg.issuerService.api.handlePools(
3382
3371
  authenticatedAddress,
3383
- { chainId, pointTokenAddress: (0, import_viem12.getAddress)(pointTokenAddress) }
3372
+ { chainId, pointTokenAddress: (0, import_viem11.getAddress)(pointTokenAddress) }
3384
3373
  );
3385
3374
  return { pools: result.pools };
3386
3375
  }
@@ -3389,8 +3378,8 @@ var IssuerApiAdapter = class {
3389
3378
  authenticatedAddress,
3390
3379
  {
3391
3380
  chainId,
3392
- userAddress: (0, import_viem12.getAddress)(userAddress),
3393
- pointTokenAddress: (0, import_viem12.getAddress)(pointTokenAddress)
3381
+ userAddress: (0, import_viem11.getAddress)(userAddress),
3382
+ pointTokenAddress: (0, import_viem11.getAddress)(pointTokenAddress)
3394
3383
  }
3395
3384
  );
3396
3385
  return {
@@ -3411,7 +3400,7 @@ var IssuerApiAdapter = class {
3411
3400
  "ptClaimHandler",
3412
3401
  "claim"
3413
3402
  );
3414
- const pointTokenAddress = (0, import_viem12.getAddress)(input.pointTokenAddress);
3403
+ const pointTokenAddress = (0, import_viem11.getAddress)(input.pointTokenAddress);
3415
3404
  const result = await ptClaimHandler.handle({
3416
3405
  authenticatedAddress: input.authenticatedAddress,
3417
3406
  userAddress: input.authenticatedAddress,
@@ -3506,7 +3495,7 @@ var IssuerApiAdapter = class {
3506
3495
  "ptClaimHandler",
3507
3496
  "claimPrepare"
3508
3497
  );
3509
- const pointTokenAddress = (0, import_viem12.getAddress)(input.pointTokenAddress);
3498
+ const pointTokenAddress = (0, import_viem11.getAddress)(input.pointTokenAddress);
3510
3499
  const claimResult = await ptClaimHandler.handle({
3511
3500
  authenticatedAddress: input.authenticatedAddress,
3512
3501
  userAddress: input.authenticatedAddress,
@@ -3552,7 +3541,7 @@ var IssuerApiAdapter = class {
3552
3541
  }
3553
3542
  async redeemPrepare(input) {
3554
3543
  this.assertRedeemHandler();
3555
- const pointTokenAddress = (0, import_viem12.getAddress)(input.pointTokenAddress);
3544
+ const pointTokenAddress = (0, import_viem11.getAddress)(input.pointTokenAddress);
3556
3545
  const redeemResponse = await this.cfg.ptRedeemHandler.handle({
3557
3546
  userAddress: input.authenticatedAddress,
3558
3547
  authenticatedAddress: input.authenticatedAddress,
@@ -3742,7 +3731,7 @@ var IssuerApiAdapter = class {
3742
3731
  };
3743
3732
 
3744
3733
  // src/pools/subgraphPoolsProvider.ts
3745
- var import_viem13 = require("viem");
3734
+ var import_viem12 = require("viem");
3746
3735
  var import_core15 = require("@pafi-dev/core");
3747
3736
  var DEFAULT_CACHE_TTL_MS = 3e4;
3748
3737
  var MAX_REASONABLE_FEE_TIER = 1e6;
@@ -3865,7 +3854,7 @@ async function fetchPoolsFromSubgraph(fetchImpl, subgraphUrl, pointTokenAddress,
3865
3854
  return [];
3866
3855
  }
3867
3856
  const { pool } = token;
3868
- if (!(0, import_viem13.isAddress)(pool.token0.id) || !(0, import_viem13.isAddress)(pool.token1.id)) {
3857
+ if (!(0, import_viem12.isAddress)(pool.token0.id) || !(0, import_viem12.isAddress)(pool.token1.id)) {
3869
3858
  const error = new Error(
3870
3859
  "[PAFI] SubgraphPoolsProvider: invalid token address in response"
3871
3860
  );
@@ -4018,8 +4007,8 @@ function toUsdtPerNative(priceFloat, usdtDecimals) {
4018
4007
  }
4019
4008
 
4020
4009
  // src/pools/nativePtQuoter.ts
4021
- var import_viem14 = require("viem");
4022
- var CHAINLINK_ABI = (0, import_viem14.parseAbi)([
4010
+ var import_viem13 = require("viem");
4011
+ var CHAINLINK_ABI = (0, import_viem13.parseAbi)([
4023
4012
  "function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)"
4024
4013
  ]);
4025
4014
  var CHAINLINK_MAX_AGE_S = 3600n;
@@ -4303,7 +4292,7 @@ var PafiBackendClient = class {
4303
4292
  };
4304
4293
 
4305
4294
  // src/config.ts
4306
- var import_viem15 = require("viem");
4295
+ var import_viem14 = require("viem");
4307
4296
  var import_core18 = require("@pafi-dev/core");
4308
4297
 
4309
4298
  // src/redemption/evaluator.ts
@@ -4630,7 +4619,7 @@ function createIssuerService(config) {
4630
4619
  "createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
4631
4620
  );
4632
4621
  }
4633
- const tokenAddresses = rawAddresses.map((a) => (0, import_viem15.getAddress)(a));
4622
+ const tokenAddresses = rawAddresses.map((a) => (0, import_viem14.getAddress)(a));
4634
4623
  const ledger = config.ledger;
4635
4624
  const sessionStore = config.sessionStore ?? new MemorySessionStore();
4636
4625
  const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
@@ -4749,7 +4738,7 @@ function createIssuerService(config) {
4749
4738
  }
4750
4739
 
4751
4740
  // src/issuer-state/validator.ts
4752
- var import_viem16 = require("viem");
4741
+ var import_viem15 = require("viem");
4753
4742
  var import_core19 = require("@pafi-dev/core");
4754
4743
  var ISSUER_RECORD_TTL_MS = 3e4;
4755
4744
  var IssuerStateValidator = class _IssuerStateValidator {
@@ -4776,7 +4765,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
4776
4765
  */
4777
4766
  invalidate(pointToken) {
4778
4767
  if (pointToken) {
4779
- const key = (0, import_viem16.getAddress)(pointToken);
4768
+ const key = (0, import_viem15.getAddress)(pointToken);
4780
4769
  this.pointTokenIssuerCache.delete(key);
4781
4770
  this.stateCache.delete(key);
4782
4771
  this.inflight.delete(key);
@@ -4791,7 +4780,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
4791
4780
  * The issuer field is set at `initialize()` and never changes.
4792
4781
  */
4793
4782
  async getIssuerAddressForPointToken(pointToken) {
4794
- const key = (0, import_viem16.getAddress)(pointToken);
4783
+ const key = (0, import_viem15.getAddress)(pointToken);
4795
4784
  const cached = this.pointTokenIssuerCache.get(key);
4796
4785
  if (cached) return cached;
4797
4786
  const issuer = await this.provider.readContract({
@@ -4799,15 +4788,15 @@ var IssuerStateValidator = class _IssuerStateValidator {
4799
4788
  abi: import_core19.POINT_TOKEN_ABI,
4800
4789
  functionName: "issuer"
4801
4790
  });
4802
- this.pointTokenIssuerCache.set(key, (0, import_viem16.getAddress)(issuer));
4803
- return (0, import_viem16.getAddress)(issuer);
4791
+ this.pointTokenIssuerCache.set(key, (0, import_viem15.getAddress)(issuer));
4792
+ return (0, import_viem15.getAddress)(issuer);
4804
4793
  }
4805
4794
  /**
4806
4795
  * Read registry record + totalSupply, with 30s cache and in-flight
4807
4796
  * deduplication. Does NOT throw on inactive/missing — returns raw state.
4808
4797
  */
4809
4798
  async getIssuerState(pointToken) {
4810
- const tokenAddr = (0, import_viem16.getAddress)(pointToken);
4799
+ const tokenAddr = (0, import_viem15.getAddress)(pointToken);
4811
4800
  const now = Date.now();
4812
4801
  const cached = this.stateCache.get(tokenAddr);
4813
4802
  if (cached && cached.expiresAt > now) return cached.value;
@@ -4950,7 +4939,7 @@ var MemoryRedemptionHistoryStore = class {
4950
4939
  };
4951
4940
 
4952
4941
  // src/index.ts
4953
- var PAFI_ISSUER_SDK_VERSION = true ? "0.18.0" : "dev";
4942
+ var PAFI_ISSUER_SDK_VERSION = true ? "0.20.0" : "dev";
4954
4943
  // Annotate the CommonJS export names for ESM import in node:
4955
4944
  0 && (module.exports = {
4956
4945
  AdapterMisconfiguredError,
@@ -4963,7 +4952,6 @@ var PAFI_ISSUER_SDK_VERSION = true ? "0.18.0" : "dev";
4963
4952
  DEFAULT_REDEMPTION_POLICY,
4964
4953
  DefaultPolicyEngine,
4965
4954
  FeeManager,
4966
- GasUnitsCache,
4967
4955
  InMemoryCursorStore,
4968
4956
  IssuerApiAdapter,
4969
4957
  IssuerApiHandlers,
@@ -4984,6 +4972,7 @@ var PAFI_ISSUER_SDK_VERSION = true ? "0.18.0" : "dev";
4984
4972
  PTRedeemHandler,
4985
4973
  PafiBackendClient,
4986
4974
  PafiBackendError,
4975
+ PafiEstimatorHttpError,
4987
4976
  PafiSdkError,
4988
4977
  PendingUserOpForbiddenError,
4989
4978
  PendingUserOpNotFoundError,
@@ -5004,6 +4993,7 @@ var PAFI_ISSUER_SDK_VERSION = true ? "0.18.0" : "dev";
5004
4993
  buildSdkErrorBody,
5005
4994
  createIssuerService,
5006
4995
  createNativePtQuoter,
4996
+ createPafiEstimatorClient,
5007
4997
  createSdkErrorMapper,
5008
4998
  createSubgraphNativeUsdtQuoter,
5009
4999
  createSubgraphPoolsProvider,