@pafi-dev/issuer 0.5.5 → 0.5.7

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
@@ -31,7 +31,7 @@ __export(index_exports, {
31
31
  MemorySessionStore: () => MemorySessionStore,
32
32
  NonceManager: () => NonceManager,
33
33
  PAFI_ISSUER_SDK_VERSION: () => PAFI_ISSUER_SDK_VERSION,
34
- PAFI_SUBGRAPH_URL: () => PAFI_SUBGRAPH_URL,
34
+ PAFI_SUBGRAPH_URL: () => import_core6.PAFI_SUBGRAPH_URL,
35
35
  PTRedeemError: () => PTRedeemError,
36
36
  PTRedeemHandler: () => PTRedeemHandler,
37
37
  PafiBackendClient: () => PafiBackendClient,
@@ -1300,6 +1300,20 @@ var PTRedeemHandler = class {
1300
1300
  redeemLockDurationMs;
1301
1301
  signatureDeadlineSeconds;
1302
1302
  now;
1303
+ /**
1304
+ * Per-user in-flight nonce guard (single-process only).
1305
+ *
1306
+ * Prevents two concurrent requests from reading the same on-chain
1307
+ * burnRequestNonce before either has completed, which would produce two
1308
+ * signed UserOps with the same nonce — only one succeeds on-chain; the
1309
+ * other leaves an orphaned pending credit and a wasted signer call.
1310
+ *
1311
+ * NOTE: This guard is effective only within a single Node.js process. For
1312
+ * multi-instance deployments (k8s, PM2 cluster), enforce mutual exclusion
1313
+ * via a distributed lock (Redis SETNX / Postgres advisory lock) keyed on
1314
+ * `(userAddress, pointTokenAddress)` BEFORE calling `handle()`.
1315
+ */
1316
+ inFlightNonces = /* @__PURE__ */ new Map();
1303
1317
  constructor(config) {
1304
1318
  if (!config.ledger.reservePendingCredit) {
1305
1319
  throw new PTRedeemError(
@@ -1352,6 +1366,27 @@ var PTRedeemHandler = class {
1352
1366
  `failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
1353
1367
  );
1354
1368
  }
1369
+ const userKey = (0, import_viem7.getAddress)(request.userAddress).toLowerCase();
1370
+ let userNonces = this.inFlightNonces.get(userKey);
1371
+ if (!userNonces) {
1372
+ userNonces = /* @__PURE__ */ new Set();
1373
+ this.inFlightNonces.set(userKey, userNonces);
1374
+ }
1375
+ if (userNonces.has(burnNonce)) {
1376
+ throw new PTRedeemError(
1377
+ "NONCE_IN_FLIGHT",
1378
+ `A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress}. Retry after the current request completes.`
1379
+ );
1380
+ }
1381
+ userNonces.add(burnNonce);
1382
+ try {
1383
+ return await this._handleAfterNonceLock(request, burnNonce);
1384
+ } finally {
1385
+ userNonces.delete(burnNonce);
1386
+ if (userNonces.size === 0) this.inFlightNonces.delete(userKey);
1387
+ }
1388
+ }
1389
+ async _handleAfterNonceLock(request, burnNonce) {
1355
1390
  const onChainBalance = await (0, import_core4.getPointTokenBalance)(
1356
1391
  this.provider,
1357
1392
  this.pointTokenAddress,
@@ -1481,7 +1516,7 @@ var TopUpRedemptionHandler = class {
1481
1516
 
1482
1517
  // src/pools/subgraphPoolsProvider.ts
1483
1518
  var import_viem9 = require("viem");
1484
- var PAFI_SUBGRAPH_URL = "https://graph-base-mainnet.pacificfinance.org/subgraphs/name/pafi-subgraph-v2";
1519
+ var import_core6 = require("@pafi-dev/core");
1485
1520
  var DEFAULT_CACHE_TTL_MS = 3e4;
1486
1521
  var POOL_QUERY = `
1487
1522
  query GetPoolForPointToken($id: ID!) {
@@ -1499,7 +1534,7 @@ var POOL_QUERY = `
1499
1534
  }
1500
1535
  `;
1501
1536
  function createSubgraphPoolsProvider(config = {}) {
1502
- const subgraphUrl = config.subgraphUrl ?? PAFI_SUBGRAPH_URL;
1537
+ const subgraphUrl = config.subgraphUrl ?? import_core6.PAFI_SUBGRAPH_URL;
1503
1538
  try {
1504
1539
  const parsed = new URL(subgraphUrl);
1505
1540
  if (process.env.NODE_ENV === "production" && parsed.protocol !== "https:") {
@@ -1631,7 +1666,7 @@ var PRICE_QUERY = `
1631
1666
  }
1632
1667
  `;
1633
1668
  function createSubgraphNativeUsdtQuoter(config = {}) {
1634
- const subgraphUrl = config.subgraphUrl ?? PAFI_SUBGRAPH_URL;
1669
+ const subgraphUrl = config.subgraphUrl ?? import_core6.PAFI_SUBGRAPH_URL;
1635
1670
  try {
1636
1671
  const parsed = new URL(subgraphUrl);
1637
1672
  if (process.env.NODE_ENV === "production" && parsed.protocol !== "https:") {
@@ -1739,7 +1774,7 @@ function toUsdtPerNative(priceFloat, usdtDecimals) {
1739
1774
  }
1740
1775
 
1741
1776
  // src/balance/balanceAggregator.ts
1742
- var import_core6 = require("@pafi-dev/core");
1777
+ var import_core7 = require("@pafi-dev/core");
1743
1778
  var BalanceAggregator = class {
1744
1779
  provider;
1745
1780
  ledger;
@@ -1760,7 +1795,7 @@ var BalanceAggregator = class {
1760
1795
  async getCombinedBalance(user, pointToken) {
1761
1796
  const [offChain, onChain] = await Promise.all([
1762
1797
  this.ledger.getBalance(user, pointToken),
1763
- (0, import_core6.getPointTokenBalance)(this.provider, pointToken, user)
1798
+ (0, import_core7.getPointTokenBalance)(this.provider, pointToken, user)
1764
1799
  ]);
1765
1800
  return {
1766
1801
  offChain,
@@ -1858,6 +1893,40 @@ var PafiBackendClient = class {
1858
1893
  }
1859
1894
  throw lastError;
1860
1895
  }
1896
+ async relayUserOperation(request) {
1897
+ const fetchFn = this.config.fetchImpl ?? fetch;
1898
+ const url = `${this.config.url}/bundler/relay`;
1899
+ let response;
1900
+ try {
1901
+ response = await fetchFn(url, {
1902
+ method: "POST",
1903
+ headers: {
1904
+ "Content-Type": "application/json",
1905
+ Authorization: `Bearer ${this.config.apiKey}`,
1906
+ "X-Issuer-Id": this.config.issuerId
1907
+ },
1908
+ body: JSON.stringify(request)
1909
+ });
1910
+ } catch (err) {
1911
+ throw new PafiBackendError(
1912
+ "NETWORK_ERROR",
1913
+ `Network error: ${err instanceof Error ? err.message : String(err)}`,
1914
+ 0
1915
+ );
1916
+ }
1917
+ const text = await response.text();
1918
+ let json = {};
1919
+ try {
1920
+ json = JSON.parse(text);
1921
+ } catch {
1922
+ }
1923
+ if (!response.ok) {
1924
+ const code = json.code ?? "INTERNAL_ERROR";
1925
+ const message = json.message ?? `HTTP ${response.status}`;
1926
+ throw new PafiBackendError(code, message, response.status, json);
1927
+ }
1928
+ return { userOpHash: json.userOpHash };
1929
+ }
1861
1930
  async _doRequest(request) {
1862
1931
  const fetchFn = this.config.fetchImpl ?? fetch;
1863
1932
  const url = `${this.config.url}/paymaster/sponsor`;
@@ -1910,7 +1979,7 @@ var PafiBackendClient = class {
1910
1979
 
1911
1980
  // src/config.ts
1912
1981
  var import_viem10 = require("viem");
1913
- var import_core7 = require("@pafi-dev/core");
1982
+ var import_core8 = require("@pafi-dev/core");
1914
1983
  function createIssuerService(config) {
1915
1984
  if (!config.provider) {
1916
1985
  throw new Error("createIssuerService: provider is required");
@@ -1977,7 +2046,7 @@ function createIssuerService(config) {
1977
2046
  indexers.set(tokenAddress, new PointIndexer(indexerConfig));
1978
2047
  }
1979
2048
  const firstIndexer = indexers.get(tokenAddresses[0]);
1980
- const chainAddresses = (0, import_core7.getContractAddresses)(config.chainId);
2049
+ const chainAddresses = (0, import_core8.getContractAddresses)(config.chainId);
1981
2050
  const resolvedContracts = {
1982
2051
  batchExecutor: chainAddresses.batchExecutor,
1983
2052
  usdt: chainAddresses.usdt,