@pafi-dev/issuer 0.5.6 → 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.d.cts CHANGED
@@ -1125,8 +1125,8 @@ interface PTRedeemResponse {
1125
1125
  signatureDeadline: bigint;
1126
1126
  }
1127
1127
  declare class PTRedeemError extends Error {
1128
- code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED";
1129
- constructor(code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED", message: string);
1128
+ code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "NONCE_IN_FLIGHT" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED";
1129
+ constructor(code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "NONCE_IN_FLIGHT" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED", message: string);
1130
1130
  }
1131
1131
  declare class PTRedeemHandler {
1132
1132
  private readonly ledger;
@@ -1140,8 +1140,23 @@ declare class PTRedeemHandler {
1140
1140
  private readonly redeemLockDurationMs;
1141
1141
  private readonly signatureDeadlineSeconds;
1142
1142
  private readonly now;
1143
+ /**
1144
+ * Per-user in-flight nonce guard (single-process only).
1145
+ *
1146
+ * Prevents two concurrent requests from reading the same on-chain
1147
+ * burnRequestNonce before either has completed, which would produce two
1148
+ * signed UserOps with the same nonce — only one succeeds on-chain; the
1149
+ * other leaves an orphaned pending credit and a wasted signer call.
1150
+ *
1151
+ * NOTE: This guard is effective only within a single Node.js process. For
1152
+ * multi-instance deployments (k8s, PM2 cluster), enforce mutual exclusion
1153
+ * via a distributed lock (Redis SETNX / Postgres advisory lock) keyed on
1154
+ * `(userAddress, pointTokenAddress)` BEFORE calling `handle()`.
1155
+ */
1156
+ private readonly inFlightNonces;
1143
1157
  constructor(config: PTRedeemHandlerConfig);
1144
1158
  handle(request: PTRedeemRequest): Promise<PTRedeemResponse>;
1159
+ private _handleAfterNonceLock;
1145
1160
  }
1146
1161
 
1147
1162
  /**
@@ -1411,6 +1426,21 @@ declare class PafiBackendError extends Error {
1411
1426
  get safeToRetry(): boolean;
1412
1427
  }
1413
1428
 
1429
+ interface RelayUserOpRequest {
1430
+ userOp: Record<string, string | null>;
1431
+ entryPoint: string;
1432
+ eip7702Auth?: {
1433
+ chainId: string;
1434
+ address: string;
1435
+ nonce: string;
1436
+ r: string;
1437
+ s: string;
1438
+ yParity: string;
1439
+ };
1440
+ }
1441
+ interface RelayUserOpResponse {
1442
+ userOpHash: Hex;
1443
+ }
1414
1444
  interface SponsorshipUserOp {
1415
1445
  sender: Address;
1416
1446
  nonce: bigint;
@@ -1443,6 +1473,7 @@ declare class PafiBackendClient {
1443
1473
  private readonly config;
1444
1474
  constructor(config: PafiBackendConfig);
1445
1475
  requestSponsorship(request: SponsorshipRequest): Promise<SponsorshipResponse>;
1476
+ relayUserOperation(request: RelayUserOpRequest): Promise<RelayUserOpResponse>;
1446
1477
  private _doRequest;
1447
1478
  }
1448
1479
 
@@ -1571,4 +1602,4 @@ declare function createIssuerService(config: IssuerServiceConfig): IssuerService
1571
1602
  /** SDK package version — bumped on every release */
1572
1603
  declare const PAFI_ISSUER_SDK_VERSION = "0.4.0";
1573
1604
 
1574
- export { type ApiClaimRequest, type ApiClaimResponse, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedeemRequest, type ApiRedeemResponse, type ApiTopUpRequest, type ApiTopUpResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, BalanceAggregator, type BalanceAggregatorConfig, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type CombinedBalance, DefaultPolicyEngine, type DefaultPolicyEngineOptions, FeeManager, type FeeManagerConfig, type IIndexerCursorStore, type IPointLedger, type IPolicyEngine, type ISessionStore, InMemoryCursorStore, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerService, type IssuerServiceConfig, type LockedMintRequest, type LoginResult, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintingStatus, NonceManager, PAFI_ISSUER_SDK_VERSION, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, type PoolsProvider, type PrepareBurnDirectParams, type PrepareBurnParams, type PrepareBurnWithSigParams, type PrepareMintParams, RelayError, type RelayErrorCode, RelayService, type RetryConfig, type Session, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, TopUpRedemptionError, TopUpRedemptionHandler, type TopUpRedemptionHandlerConfig, type TopUpRedemptionRequest, type TopUpRedemptionResponse, authenticateRequest, createIssuerService, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider };
1605
+ export { type ApiClaimRequest, type ApiClaimResponse, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedeemRequest, type ApiRedeemResponse, type ApiTopUpRequest, type ApiTopUpResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, BalanceAggregator, type BalanceAggregatorConfig, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type CombinedBalance, DefaultPolicyEngine, type DefaultPolicyEngineOptions, FeeManager, type FeeManagerConfig, type IIndexerCursorStore, type IPointLedger, type IPolicyEngine, type ISessionStore, InMemoryCursorStore, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerService, type IssuerServiceConfig, type LockedMintRequest, type LoginResult, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintingStatus, NonceManager, PAFI_ISSUER_SDK_VERSION, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, type PoolsProvider, type PrepareBurnDirectParams, type PrepareBurnParams, type PrepareBurnWithSigParams, type PrepareMintParams, RelayError, type RelayErrorCode, RelayService, type RelayUserOpRequest, type RelayUserOpResponse, type RetryConfig, type Session, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, TopUpRedemptionError, TopUpRedemptionHandler, type TopUpRedemptionHandlerConfig, type TopUpRedemptionRequest, type TopUpRedemptionResponse, authenticateRequest, createIssuerService, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider };
package/dist/index.d.ts CHANGED
@@ -1125,8 +1125,8 @@ interface PTRedeemResponse {
1125
1125
  signatureDeadline: bigint;
1126
1126
  }
1127
1127
  declare class PTRedeemError extends Error {
1128
- code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED";
1129
- constructor(code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED", message: string);
1128
+ code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "NONCE_IN_FLIGHT" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED";
1129
+ constructor(code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "NONCE_IN_FLIGHT" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED", message: string);
1130
1130
  }
1131
1131
  declare class PTRedeemHandler {
1132
1132
  private readonly ledger;
@@ -1140,8 +1140,23 @@ declare class PTRedeemHandler {
1140
1140
  private readonly redeemLockDurationMs;
1141
1141
  private readonly signatureDeadlineSeconds;
1142
1142
  private readonly now;
1143
+ /**
1144
+ * Per-user in-flight nonce guard (single-process only).
1145
+ *
1146
+ * Prevents two concurrent requests from reading the same on-chain
1147
+ * burnRequestNonce before either has completed, which would produce two
1148
+ * signed UserOps with the same nonce — only one succeeds on-chain; the
1149
+ * other leaves an orphaned pending credit and a wasted signer call.
1150
+ *
1151
+ * NOTE: This guard is effective only within a single Node.js process. For
1152
+ * multi-instance deployments (k8s, PM2 cluster), enforce mutual exclusion
1153
+ * via a distributed lock (Redis SETNX / Postgres advisory lock) keyed on
1154
+ * `(userAddress, pointTokenAddress)` BEFORE calling `handle()`.
1155
+ */
1156
+ private readonly inFlightNonces;
1143
1157
  constructor(config: PTRedeemHandlerConfig);
1144
1158
  handle(request: PTRedeemRequest): Promise<PTRedeemResponse>;
1159
+ private _handleAfterNonceLock;
1145
1160
  }
1146
1161
 
1147
1162
  /**
@@ -1411,6 +1426,21 @@ declare class PafiBackendError extends Error {
1411
1426
  get safeToRetry(): boolean;
1412
1427
  }
1413
1428
 
1429
+ interface RelayUserOpRequest {
1430
+ userOp: Record<string, string | null>;
1431
+ entryPoint: string;
1432
+ eip7702Auth?: {
1433
+ chainId: string;
1434
+ address: string;
1435
+ nonce: string;
1436
+ r: string;
1437
+ s: string;
1438
+ yParity: string;
1439
+ };
1440
+ }
1441
+ interface RelayUserOpResponse {
1442
+ userOpHash: Hex;
1443
+ }
1414
1444
  interface SponsorshipUserOp {
1415
1445
  sender: Address;
1416
1446
  nonce: bigint;
@@ -1443,6 +1473,7 @@ declare class PafiBackendClient {
1443
1473
  private readonly config;
1444
1474
  constructor(config: PafiBackendConfig);
1445
1475
  requestSponsorship(request: SponsorshipRequest): Promise<SponsorshipResponse>;
1476
+ relayUserOperation(request: RelayUserOpRequest): Promise<RelayUserOpResponse>;
1446
1477
  private _doRequest;
1447
1478
  }
1448
1479
 
@@ -1571,4 +1602,4 @@ declare function createIssuerService(config: IssuerServiceConfig): IssuerService
1571
1602
  /** SDK package version — bumped on every release */
1572
1603
  declare const PAFI_ISSUER_SDK_VERSION = "0.4.0";
1573
1604
 
1574
- export { type ApiClaimRequest, type ApiClaimResponse, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedeemRequest, type ApiRedeemResponse, type ApiTopUpRequest, type ApiTopUpResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, BalanceAggregator, type BalanceAggregatorConfig, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type CombinedBalance, DefaultPolicyEngine, type DefaultPolicyEngineOptions, FeeManager, type FeeManagerConfig, type IIndexerCursorStore, type IPointLedger, type IPolicyEngine, type ISessionStore, InMemoryCursorStore, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerService, type IssuerServiceConfig, type LockedMintRequest, type LoginResult, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintingStatus, NonceManager, PAFI_ISSUER_SDK_VERSION, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, type PoolsProvider, type PrepareBurnDirectParams, type PrepareBurnParams, type PrepareBurnWithSigParams, type PrepareMintParams, RelayError, type RelayErrorCode, RelayService, type RetryConfig, type Session, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, TopUpRedemptionError, TopUpRedemptionHandler, type TopUpRedemptionHandlerConfig, type TopUpRedemptionRequest, type TopUpRedemptionResponse, authenticateRequest, createIssuerService, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider };
1605
+ export { type ApiClaimRequest, type ApiClaimResponse, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedeemRequest, type ApiRedeemResponse, type ApiTopUpRequest, type ApiTopUpResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, BalanceAggregator, type BalanceAggregatorConfig, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type CombinedBalance, DefaultPolicyEngine, type DefaultPolicyEngineOptions, FeeManager, type FeeManagerConfig, type IIndexerCursorStore, type IPointLedger, type IPolicyEngine, type ISessionStore, InMemoryCursorStore, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerService, type IssuerServiceConfig, type LockedMintRequest, type LoginResult, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintingStatus, NonceManager, PAFI_ISSUER_SDK_VERSION, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, type PoolsProvider, type PrepareBurnDirectParams, type PrepareBurnParams, type PrepareBurnWithSigParams, type PrepareMintParams, RelayError, type RelayErrorCode, RelayService, type RelayUserOpRequest, type RelayUserOpResponse, type RetryConfig, type Session, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, TopUpRedemptionError, TopUpRedemptionHandler, type TopUpRedemptionHandlerConfig, type TopUpRedemptionRequest, type TopUpRedemptionResponse, authenticateRequest, createIssuerService, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider };
package/dist/index.js CHANGED
@@ -1263,6 +1263,20 @@ var PTRedeemHandler = class {
1263
1263
  redeemLockDurationMs;
1264
1264
  signatureDeadlineSeconds;
1265
1265
  now;
1266
+ /**
1267
+ * Per-user in-flight nonce guard (single-process only).
1268
+ *
1269
+ * Prevents two concurrent requests from reading the same on-chain
1270
+ * burnRequestNonce before either has completed, which would produce two
1271
+ * signed UserOps with the same nonce — only one succeeds on-chain; the
1272
+ * other leaves an orphaned pending credit and a wasted signer call.
1273
+ *
1274
+ * NOTE: This guard is effective only within a single Node.js process. For
1275
+ * multi-instance deployments (k8s, PM2 cluster), enforce mutual exclusion
1276
+ * via a distributed lock (Redis SETNX / Postgres advisory lock) keyed on
1277
+ * `(userAddress, pointTokenAddress)` BEFORE calling `handle()`.
1278
+ */
1279
+ inFlightNonces = /* @__PURE__ */ new Map();
1266
1280
  constructor(config) {
1267
1281
  if (!config.ledger.reservePendingCredit) {
1268
1282
  throw new PTRedeemError(
@@ -1315,6 +1329,27 @@ var PTRedeemHandler = class {
1315
1329
  `failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
1316
1330
  );
1317
1331
  }
1332
+ const userKey = getAddress6(request.userAddress).toLowerCase();
1333
+ let userNonces = this.inFlightNonces.get(userKey);
1334
+ if (!userNonces) {
1335
+ userNonces = /* @__PURE__ */ new Set();
1336
+ this.inFlightNonces.set(userKey, userNonces);
1337
+ }
1338
+ if (userNonces.has(burnNonce)) {
1339
+ throw new PTRedeemError(
1340
+ "NONCE_IN_FLIGHT",
1341
+ `A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress}. Retry after the current request completes.`
1342
+ );
1343
+ }
1344
+ userNonces.add(burnNonce);
1345
+ try {
1346
+ return await this._handleAfterNonceLock(request, burnNonce);
1347
+ } finally {
1348
+ userNonces.delete(burnNonce);
1349
+ if (userNonces.size === 0) this.inFlightNonces.delete(userKey);
1350
+ }
1351
+ }
1352
+ async _handleAfterNonceLock(request, burnNonce) {
1318
1353
  const onChainBalance = await getPointTokenBalance2(
1319
1354
  this.provider,
1320
1355
  this.pointTokenAddress,
@@ -1821,6 +1856,40 @@ var PafiBackendClient = class {
1821
1856
  }
1822
1857
  throw lastError;
1823
1858
  }
1859
+ async relayUserOperation(request) {
1860
+ const fetchFn = this.config.fetchImpl ?? fetch;
1861
+ const url = `${this.config.url}/bundler/relay`;
1862
+ let response;
1863
+ try {
1864
+ response = await fetchFn(url, {
1865
+ method: "POST",
1866
+ headers: {
1867
+ "Content-Type": "application/json",
1868
+ Authorization: `Bearer ${this.config.apiKey}`,
1869
+ "X-Issuer-Id": this.config.issuerId
1870
+ },
1871
+ body: JSON.stringify(request)
1872
+ });
1873
+ } catch (err) {
1874
+ throw new PafiBackendError(
1875
+ "NETWORK_ERROR",
1876
+ `Network error: ${err instanceof Error ? err.message : String(err)}`,
1877
+ 0
1878
+ );
1879
+ }
1880
+ const text = await response.text();
1881
+ let json = {};
1882
+ try {
1883
+ json = JSON.parse(text);
1884
+ } catch {
1885
+ }
1886
+ if (!response.ok) {
1887
+ const code = json.code ?? "INTERNAL_ERROR";
1888
+ const message = json.message ?? `HTTP ${response.status}`;
1889
+ throw new PafiBackendError(code, message, response.status, json);
1890
+ }
1891
+ return { userOpHash: json.userOpHash };
1892
+ }
1824
1893
  async _doRequest(request) {
1825
1894
  const fetchFn = this.config.fetchImpl ?? fetch;
1826
1895
  const url = `${this.config.url}/paymaster/sponsor`;