@pafi-dev/issuer 0.20.0 → 0.22.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.d.cts CHANGED
@@ -983,6 +983,56 @@ declare class InMemoryCursorStore implements IIndexerCursorStore {
983
983
  load(): Promise<bigint | undefined>;
984
984
  save(blockNumber: bigint): Promise<void>;
985
985
  }
986
+ /**
987
+ * Lock handle returned by a successful `ISingletonLock.acquire()`.
988
+ *
989
+ * The holder is the leader for the keyed indexer; non-holders MUST NOT
990
+ * call `indexer.start()` (else they race against the leader's polling
991
+ * loop and last-writer-wins cursor save corrupts replay state).
992
+ *
993
+ * `release()` is called by the leader on graceful shutdown. If the
994
+ * leader crashes without calling release, the lock implementation
995
+ * MUST drop the lock automatically (e.g. Postgres advisory locks
996
+ * auto-release on connection close).
997
+ */
998
+ interface SingletonLockHandle {
999
+ release(): Promise<void>;
1000
+ }
1001
+ /**
1002
+ * Leader-election primitive for indexer singletons.
1003
+ *
1004
+ * **Why this exists (audit finding H-04):** the `BurnIndexer` and
1005
+ * `PointIndexer` classes are stateful polling loops with a cursor
1006
+ * stored in an external store. Running them on multiple replicas
1007
+ * simultaneously causes:
1008
+ *
1009
+ * 1. Double-credit — both replicas see the same Transfer event
1010
+ * and call `ledger.resolveCreditByBurnTx` twice.
1011
+ * 2. Cursor rewind — last-writer-wins `save()` causes the newer
1012
+ * cursor to be overwritten by a lagging replica's older value,
1013
+ * causing the next poll to replay events.
1014
+ * 3. Skipped blocks — chunked range reads can leave gaps when two
1015
+ * replicas race-advance the cursor.
1016
+ *
1017
+ * **Adoption:** pass `singletonLock` into `IssuerServiceConfig.indexer`
1018
+ * (or wire it directly in your provider). The factory will only call
1019
+ * `indexer.start()` on the indexers it successfully acquires a lock
1020
+ * for; the rest stay idle and take over instantly on lock release.
1021
+ *
1022
+ * **Implementations:** `makePostgresSingletonLock(dataSource)` (recommended
1023
+ * — uses `pg_try_advisory_lock`, auto-releases on connection close).
1024
+ * You can also bring your own: Redis SETNX with TTL, etcd lease, etc.
1025
+ */
1026
+ interface ISingletonLock {
1027
+ /**
1028
+ * Attempt to acquire the lock for `key`. Returns a handle on success
1029
+ * (caller is the leader), or `null` if another holder owns it.
1030
+ *
1031
+ * Implementations MUST be non-blocking — if the lock is held, return
1032
+ * null immediately. The factory polls / retries at a higher layer.
1033
+ */
1034
+ acquire(key: string): Promise<SingletonLockHandle | null>;
1035
+ }
986
1036
 
987
1037
  interface PointIndexerConfig {
988
1038
  provider: PublicClient;
@@ -1190,7 +1240,11 @@ interface BurnIndexerConfig {
1190
1240
  */
1191
1241
  declare class BurnIndexer {
1192
1242
  private readonly provider;
1193
- private readonly pointTokenAddress;
1243
+ /**
1244
+ * The PointToken this indexer watches. Exposed so callers can key
1245
+ * leader-election locks / cursor stores by token (audit H-04 fix).
1246
+ */
1247
+ readonly pointTokenAddress: Address;
1194
1248
  private readonly ledger;
1195
1249
  private readonly cursorStore;
1196
1250
  private readonly startBlock;
@@ -1222,6 +1276,60 @@ declare class BurnIndexer {
1222
1276
  private finalize;
1223
1277
  }
1224
1278
 
1279
+ /**
1280
+ * Minimal duck-typed adapter so we don't drag a TypeORM / pg dep into
1281
+ * the SDK. The caller passes anything that can execute a parameterised
1282
+ * query and yield `{ got: boolean }` rows — TypeORM `DataSource.query`,
1283
+ * a raw `pg` client, knex's `raw`, kysely, etc.
1284
+ *
1285
+ * The helper requires `int8` (signed 64-bit) lock IDs. We derive them
1286
+ * from the lock `key` via a deterministic hash so different replicas
1287
+ * agree on the int regardless of casing / whitespace drift.
1288
+ */
1289
+ interface PostgresQueryRunner {
1290
+ query(sql: string, params?: unknown[]): Promise<unknown>;
1291
+ }
1292
+ /**
1293
+ * Build an `ISingletonLock` backed by PostgreSQL session-level advisory
1294
+ * locks (`pg_try_advisory_lock`).
1295
+ *
1296
+ * Why advisory locks (vs. row-level locks or a leases table):
1297
+ * 1. **Non-blocking** — `pg_try_advisory_lock` returns immediately;
1298
+ * perfect for the "try then idle" pattern the SDK uses on boot.
1299
+ * 2. **Auto-release on connection drop** — if the leader pod crashes,
1300
+ * its PG connection closes, and the lock is freed automatically.
1301
+ * No timeout tuning, no lease renewal loops, no zombie locks.
1302
+ * 3. **Zero schema** — no extra table, no migration, no contention
1303
+ * with application reads.
1304
+ *
1305
+ * **Caveat — long-lived connection required.** Session-level locks are
1306
+ * held only for the lifetime of the PG connection that called
1307
+ * `pg_try_advisory_lock`. PgBouncer in `transaction` pooling mode will
1308
+ * release the lock between statements; you MUST run in `session`
1309
+ * pooling or skip the bouncer for the indexer process. The advisory
1310
+ * `transaction` variant exists but doesn't fit the "hold while polling"
1311
+ * pattern — for that you'd need a periodic re-acquire, which we don't
1312
+ * implement here.
1313
+ *
1314
+ * @example
1315
+ * import { DataSource } from "typeorm";
1316
+ * import { makePostgresSingletonLock } from "@pafi-dev/issuer";
1317
+ *
1318
+ * const dataSource = new DataSource({ ... });
1319
+ * const lock = makePostgresSingletonLock({
1320
+ * query: (sql, params) => dataSource.query(sql, params),
1321
+ * });
1322
+ *
1323
+ * createIssuerService({
1324
+ * ...,
1325
+ * indexer: {
1326
+ * autoStart: true,
1327
+ * singletonLock: lock,
1328
+ * },
1329
+ * });
1330
+ */
1331
+ declare function makePostgresSingletonLock(runner: PostgresQueryRunner): ISingletonLock;
1332
+
1225
1333
  interface ApiConfigResponse {
1226
1334
  chainId: number;
1227
1335
  contracts: {
@@ -1652,6 +1760,43 @@ declare class IssuerApiHandlers {
1652
1760
  private requireSupportedToken;
1653
1761
  }
1654
1762
 
1763
+ /**
1764
+ * Resolves the EIP-712 domain `name` for a PointToken. Used by handlers
1765
+ * (PTClaimHandler / PTRedeemHandler) that sign requests for multiple
1766
+ * PointTokens within the same issuer backend.
1767
+ *
1768
+ * Resolution order per address:
1769
+ * 1. Cache hit → return immediately (no RPC)
1770
+ * 2. Static override map (config.overrides) → cache + return
1771
+ * 3. On-chain `name()` lookup → cache + return
1772
+ *
1773
+ * Cache is per-instance — share one resolver across handlers so mint +
1774
+ * redeem paths amortise the same set of lookups.
1775
+ *
1776
+ * Why the override map: production issuers often know their PTs at
1777
+ * boot (env / DB) and want to avoid RPC quota churn + tolerate transient
1778
+ * RPC failures. The on-chain fallback handles ad-hoc PTs not in the map
1779
+ * (e.g. newly deployed during the process lifetime).
1780
+ */
1781
+ interface PointTokenDomainResolverConfig {
1782
+ provider: PublicClient;
1783
+ /**
1784
+ * Optional pre-configured map — checksummed (or lowercased) PointToken
1785
+ * address → ERC-20 `name()` value. When a PT's address is in this map,
1786
+ * the resolver short-circuits without an RPC call.
1787
+ */
1788
+ overrides?: Record<string, string>;
1789
+ }
1790
+ declare class PointTokenDomainResolver {
1791
+ private readonly provider;
1792
+ private readonly overrides;
1793
+ private readonly cache;
1794
+ constructor(config: PointTokenDomainResolverConfig);
1795
+ resolve(pointTokenAddress: Address): Promise<string>;
1796
+ /** Invalidate one address (after deploy / proxy upgrade) or all. */
1797
+ invalidate(pointTokenAddress?: Address): void;
1798
+ }
1799
+
1655
1800
  /**
1656
1801
  * Reverse flow — user-initiated PT redeem.
1657
1802
  *
@@ -1681,22 +1826,20 @@ interface PTRedeemHandlerConfig {
1681
1826
  ledger: IPointLedger;
1682
1827
  relayService: RelayService;
1683
1828
  provider: PublicClient;
1684
- /** PointToken contract address (chain-specific). */
1685
- pointTokenAddress: Address;
1686
1829
  /** BatchExecutor delegation target (chain-specific). */
1687
1830
  batchExecutorAddress: Address;
1688
1831
  /** Chain id — used for the BurnRequest EIP-712 domain. */
1689
1832
  chainId: number;
1690
1833
  /**
1691
- * EIP-712 domain fields. Must match the on-chain PointToken's domain
1692
- * separator, or on-chain signature recovery fails. `name` is
1693
- * typically the PointToken's ERC-20 name. `verifyingContract`
1694
- * defaults to `pointTokenAddress`.
1834
+ * Resolver for the EIP-712 domain `name` (= PointToken.name() on-chain).
1835
+ * Required because mint/redeem now route the `pointTokenAddress` per
1836
+ * request the handler can't bind a single domain at construction.
1837
+ * Pass a shared resolver instance so mint + redeem amortise the same
1838
+ * cache. The handler always uses `pointTokenAddress` as the EIP-712
1839
+ * `verifyingContract` — no override (per-PT domain separator is
1840
+ * isolated by contract address).
1695
1841
  */
1696
- domain: {
1697
- name: string;
1698
- verifyingContract?: Address;
1699
- };
1842
+ domainResolver: PointTokenDomainResolver;
1700
1843
  /**
1701
1844
  * Issuer burner signer wallet — signs the `BurnRequest` EIP-712.
1702
1845
  * Must be whitelisted via `PointToken.addBurner(signerAddr)` at
@@ -1739,6 +1882,10 @@ interface PTRedeemRequest {
1739
1882
  authenticatedAddress: Address;
1740
1883
  userAddress: Address;
1741
1884
  amount: bigint;
1885
+ /** PointToken contract to redeem. The handler resolves the EIP-712
1886
+ * domain (name + verifyingContract) from this address via the
1887
+ * configured `domainResolver`. */
1888
+ pointTokenAddress: Address;
1742
1889
  /** ERC-4337 account nonce for the user's EOA. */
1743
1890
  aaNonce: bigint;
1744
1891
  /**
@@ -1806,10 +1953,9 @@ declare class PTRedeemHandler {
1806
1953
  private readonly relayService;
1807
1954
  private readonly provider;
1808
1955
  private readonly feeService?;
1809
- private readonly pointTokenAddress;
1810
1956
  private readonly batchExecutorAddress;
1811
1957
  private readonly chainId;
1812
- private readonly domain;
1958
+ private readonly domainResolver;
1813
1959
  private readonly burnerSignerWallet;
1814
1960
  private readonly redeemLockDurationMs;
1815
1961
  private readonly signatureDeadlineSeconds;
@@ -2524,11 +2670,14 @@ interface PTClaimHandlerConfig {
2524
2670
  /** Issuer minter signer wallet — passed through to RelayService.prepareMint. */
2525
2671
  issuerSignerWallet: WalletClient;
2526
2672
  /**
2527
- * EIP-712 domain `name` for `MintRequest`. Typically the PointToken
2528
- * ERC-20 name. RelayService will set chainId + verifyingContract from
2529
- * the request.
2673
+ * Resolver for the EIP-712 domain `name` (= PointToken.name() on-chain).
2674
+ * Required because mint routes the `pointTokenAddress` per request
2675
+ * the handler can't bind a single domain at construction. Pass a
2676
+ * shared resolver instance so mint + redeem amortise the same cache.
2677
+ * `chainId` + `verifyingContract` are derived from the request's
2678
+ * `chainId` + `pointTokenAddress` respectively.
2530
2679
  */
2531
- pointTokenDomainName: string;
2680
+ domainResolver: PointTokenDomainResolver;
2532
2681
  /** Optional — when wired, used to estimate the PT gas-reimbursement fee. */
2533
2682
  feeService?: FeeManager;
2534
2683
  /** Optional — pre-validates issuer status + cap before locking balance. */
@@ -2849,8 +2998,27 @@ interface IssuerServiceConfig {
2849
2998
  /**
2850
2999
  * If `true`, the factory calls `indexer.start()` before returning.
2851
3000
  * Default: `false` — the caller decides when to begin polling.
3001
+ *
3002
+ * **SAFETY (H-04):** in a multi-replica deployment, ALWAYS pair
3003
+ * `autoStart: true` with `singletonLock` below. Without leader
3004
+ * election, every replica starts its own indexer fleet and races
3005
+ * against the others — see `ISingletonLock` docs for the
3006
+ * consequences.
2852
3007
  */
2853
3008
  autoStart?: boolean;
3009
+ /**
3010
+ * Leader-election primitive. When provided, the factory wraps
3011
+ * `indexer.start()` with a `singletonLock.acquire(key)` call and
3012
+ * only starts the indexer if it wins the lock. Non-leaders stay
3013
+ * idle and take over on the next acquire attempt (when the leader
3014
+ * pod's connection drops, the lock auto-releases).
3015
+ *
3016
+ * The lock key includes the indexer kind + the PointToken address,
3017
+ * so different tokens can be sharded across replicas if desired.
3018
+ *
3019
+ * Recommended: `makePostgresSingletonLock(dataSource)`.
3020
+ */
3021
+ singletonLock?: ISingletonLock;
2854
3022
  /**
2855
3023
  * Override the MintFeeWrapper address used by the indexer. When
2856
3024
  * omitted, the factory auto-resolves from
@@ -2896,6 +3064,13 @@ interface IssuerService {
2896
3064
  fee: FeeManager | undefined;
2897
3065
  /** All indexers keyed by PointToken address. */
2898
3066
  indexers: Map<Address, PointIndexer>;
3067
+ /**
3068
+ * Lock handles for the indexers this replica was elected leader for.
3069
+ * Empty when `autoStart` is false, or when no `singletonLock` was
3070
+ * provided. Call `release()` on each during graceful shutdown so
3071
+ * peers can take over without waiting for the connection to die.
3072
+ */
3073
+ indexerLeaderLocks: SingletonLockHandle[];
2899
3074
  /** Framework-agnostic HTTP handlers — wire into Express / Fastify / Hono. */
2900
3075
  api: IssuerApiHandlers;
2901
3076
  /**
@@ -2917,7 +3092,7 @@ interface IssuerService {
2917
3092
  *
2918
3093
  * Throws synchronously if any required field is missing.
2919
3094
  */
2920
- declare function createIssuerService(config: IssuerServiceConfig): IssuerService;
3095
+ declare function createIssuerService(config: IssuerServiceConfig): Promise<IssuerService>;
2921
3096
 
2922
3097
  /**
2923
3098
  * Adapter that absorbs every "framework-agnostic" endpoint body into a
@@ -3096,6 +3271,7 @@ declare class IssuerApiAdapter {
3096
3271
  redeem(input: {
3097
3272
  authenticatedAddress: Address;
3098
3273
  chainId: number;
3274
+ pointTokenAddress: Address;
3099
3275
  amount: bigint;
3100
3276
  aaNonce: bigint;
3101
3277
  }): Promise<RedeemDto>;
@@ -3656,4 +3832,4 @@ declare class MemoryRedemptionHistoryStore implements IRedemptionHistoryStore {
3656
3832
 
3657
3833
  declare const PAFI_ISSUER_SDK_VERSION: string;
3658
3834
 
3659
- export { AdapterMisconfiguredError, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedemptionEvaluateRequest, type ApiRedemptionEvaluateResponse, type ApiRedemptionPreviewRequest, type ApiRedemptionPreviewResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, type BundlerEstimatorClient, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type ClaimDto, type ConfigDto, ConfigurationError, DEFAULT_REDEMPTION_POLICY, type DecodedCallDto, DefaultPolicyEngine, type DefaultPolicyEngineOptions, type DelegatePrepareDto, type DelegateStatusDto, type EstimateGasFeeOptions, type EvaluateInput, FeeManager, type FeeManagerConfig, type FeeManagerMetrics, type FetchFailureReason, type FetchImpl, type FetchResult, type GasFeeDto, type GasFeeSource, type HandleDelegateSubmitParams, type HandleDelegateSubmitResult, type HandleMobilePrepareParams, type HandleMobilePrepareResult, type HandleMobileSubmitParams, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type IRateLimiter, type IRedemptionHistoryStore, type ISessionStore, InMemoryCursorStore, IssuerApiAdapter, type IssuerApiAdapterConfig, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemoryPendingUserOpStore, MemoryRateLimiter, MemoryRedemptionHistoryStore, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type MobilePrepareDto, type MobileSubmitDto, type NativePtQuoterConfig, NonceManager, NoopRateLimiter, PAFI_ISSUER_SDK_VERSION, PTClaimError, PTClaimHandler, type PTClaimHandlerConfig, type PTClaimRequest, type PTClaimResponse, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PafiEstimatorClientConfig, PafiEstimatorHttpError, type PaymasterGasEstimates, type PendingCredit, type PendingUserOpEntry, PendingUserOpForbiddenError, PendingUserOpNotFoundError, type PerpDepositDto, PerpDepositError, PerpDepositHandler, type PerpDepositHandlerConfig, type PerpDepositRequest, type PerpDepositResponse, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, PolicyProvider, type PolicyProviderConfig, type PoolsDto, type PoolsProvider, type PreValidateMintResult, type PrepareBurnParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, type PreviewBurnParams, type PreviewMintParams, REDEMPTION_HISTORY_WINDOW_SEC, type RateLimitAction, type RateLimiterConfig, type RedeemDto, type RedeemPrepareDto, RedemptionService, type RedemptionServiceConfig, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type ResolvedPolicy, type RetryConfig, type SdkErrorBody, type SdkErrorMapperFactories, type SdkErrorStatus, type SerializedUserOpTypedData, type Session, SettlementClient, type SettlementClientConfig, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type UserDto, type UserHistory, applyPaymasterGasEstimates, authenticateRequest, buildSdkErrorBody, createIssuerService, createNativePtQuoter, createPafiEstimatorClient, createSdkErrorMapper, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, defaultPolicyFor, evaluateRedemption, handleClaimStatus, handleDelegateSubmit, handleMobilePrepare, handleMobileSubmit, handleRedeemStatus, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };
3835
+ export { AdapterMisconfiguredError, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedemptionEvaluateRequest, type ApiRedemptionEvaluateResponse, type ApiRedemptionPreviewRequest, type ApiRedemptionPreviewResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, type BundlerEstimatorClient, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type ClaimDto, type ConfigDto, ConfigurationError, DEFAULT_REDEMPTION_POLICY, type DecodedCallDto, DefaultPolicyEngine, type DefaultPolicyEngineOptions, type DelegatePrepareDto, type DelegateStatusDto, type EstimateGasFeeOptions, type EvaluateInput, FeeManager, type FeeManagerConfig, type FeeManagerMetrics, type FetchFailureReason, type FetchImpl, type FetchResult, type GasFeeDto, type GasFeeSource, type HandleDelegateSubmitParams, type HandleDelegateSubmitResult, type HandleMobilePrepareParams, type HandleMobilePrepareResult, type HandleMobileSubmitParams, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type IRateLimiter, type IRedemptionHistoryStore, type ISessionStore, type ISingletonLock, InMemoryCursorStore, IssuerApiAdapter, type IssuerApiAdapterConfig, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemoryPendingUserOpStore, MemoryRateLimiter, MemoryRedemptionHistoryStore, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type MobilePrepareDto, type MobileSubmitDto, type NativePtQuoterConfig, NonceManager, NoopRateLimiter, PAFI_ISSUER_SDK_VERSION, PTClaimError, PTClaimHandler, type PTClaimHandlerConfig, type PTClaimRequest, type PTClaimResponse, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PafiEstimatorClientConfig, PafiEstimatorHttpError, type PaymasterGasEstimates, type PendingCredit, type PendingUserOpEntry, PendingUserOpForbiddenError, PendingUserOpNotFoundError, type PerpDepositDto, PerpDepositError, PerpDepositHandler, type PerpDepositHandlerConfig, type PerpDepositRequest, type PerpDepositResponse, PointIndexer, type PointIndexerConfig, PointTokenDomainResolver, type PointTokenDomainResolverConfig, type PolicyDecision, type PolicyEvalRequest, PolicyProvider, type PolicyProviderConfig, type PoolsDto, type PoolsProvider, type PostgresQueryRunner, type PreValidateMintResult, type PrepareBurnParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, type PreviewBurnParams, type PreviewMintParams, REDEMPTION_HISTORY_WINDOW_SEC, type RateLimitAction, type RateLimiterConfig, type RedeemDto, type RedeemPrepareDto, RedemptionService, type RedemptionServiceConfig, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type ResolvedPolicy, type RetryConfig, type SdkErrorBody, type SdkErrorMapperFactories, type SdkErrorStatus, type SerializedUserOpTypedData, type Session, SettlementClient, type SettlementClientConfig, type SingletonLockHandle, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type UserDto, type UserHistory, applyPaymasterGasEstimates, authenticateRequest, buildSdkErrorBody, createIssuerService, createNativePtQuoter, createPafiEstimatorClient, createSdkErrorMapper, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, defaultPolicyFor, evaluateRedemption, handleClaimStatus, handleDelegateSubmit, handleMobilePrepare, handleMobileSubmit, handleRedeemStatus, makePostgresSingletonLock, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };
package/dist/index.d.ts CHANGED
@@ -983,6 +983,56 @@ declare class InMemoryCursorStore implements IIndexerCursorStore {
983
983
  load(): Promise<bigint | undefined>;
984
984
  save(blockNumber: bigint): Promise<void>;
985
985
  }
986
+ /**
987
+ * Lock handle returned by a successful `ISingletonLock.acquire()`.
988
+ *
989
+ * The holder is the leader for the keyed indexer; non-holders MUST NOT
990
+ * call `indexer.start()` (else they race against the leader's polling
991
+ * loop and last-writer-wins cursor save corrupts replay state).
992
+ *
993
+ * `release()` is called by the leader on graceful shutdown. If the
994
+ * leader crashes without calling release, the lock implementation
995
+ * MUST drop the lock automatically (e.g. Postgres advisory locks
996
+ * auto-release on connection close).
997
+ */
998
+ interface SingletonLockHandle {
999
+ release(): Promise<void>;
1000
+ }
1001
+ /**
1002
+ * Leader-election primitive for indexer singletons.
1003
+ *
1004
+ * **Why this exists (audit finding H-04):** the `BurnIndexer` and
1005
+ * `PointIndexer` classes are stateful polling loops with a cursor
1006
+ * stored in an external store. Running them on multiple replicas
1007
+ * simultaneously causes:
1008
+ *
1009
+ * 1. Double-credit — both replicas see the same Transfer event
1010
+ * and call `ledger.resolveCreditByBurnTx` twice.
1011
+ * 2. Cursor rewind — last-writer-wins `save()` causes the newer
1012
+ * cursor to be overwritten by a lagging replica's older value,
1013
+ * causing the next poll to replay events.
1014
+ * 3. Skipped blocks — chunked range reads can leave gaps when two
1015
+ * replicas race-advance the cursor.
1016
+ *
1017
+ * **Adoption:** pass `singletonLock` into `IssuerServiceConfig.indexer`
1018
+ * (or wire it directly in your provider). The factory will only call
1019
+ * `indexer.start()` on the indexers it successfully acquires a lock
1020
+ * for; the rest stay idle and take over instantly on lock release.
1021
+ *
1022
+ * **Implementations:** `makePostgresSingletonLock(dataSource)` (recommended
1023
+ * — uses `pg_try_advisory_lock`, auto-releases on connection close).
1024
+ * You can also bring your own: Redis SETNX with TTL, etcd lease, etc.
1025
+ */
1026
+ interface ISingletonLock {
1027
+ /**
1028
+ * Attempt to acquire the lock for `key`. Returns a handle on success
1029
+ * (caller is the leader), or `null` if another holder owns it.
1030
+ *
1031
+ * Implementations MUST be non-blocking — if the lock is held, return
1032
+ * null immediately. The factory polls / retries at a higher layer.
1033
+ */
1034
+ acquire(key: string): Promise<SingletonLockHandle | null>;
1035
+ }
986
1036
 
987
1037
  interface PointIndexerConfig {
988
1038
  provider: PublicClient;
@@ -1190,7 +1240,11 @@ interface BurnIndexerConfig {
1190
1240
  */
1191
1241
  declare class BurnIndexer {
1192
1242
  private readonly provider;
1193
- private readonly pointTokenAddress;
1243
+ /**
1244
+ * The PointToken this indexer watches. Exposed so callers can key
1245
+ * leader-election locks / cursor stores by token (audit H-04 fix).
1246
+ */
1247
+ readonly pointTokenAddress: Address;
1194
1248
  private readonly ledger;
1195
1249
  private readonly cursorStore;
1196
1250
  private readonly startBlock;
@@ -1222,6 +1276,60 @@ declare class BurnIndexer {
1222
1276
  private finalize;
1223
1277
  }
1224
1278
 
1279
+ /**
1280
+ * Minimal duck-typed adapter so we don't drag a TypeORM / pg dep into
1281
+ * the SDK. The caller passes anything that can execute a parameterised
1282
+ * query and yield `{ got: boolean }` rows — TypeORM `DataSource.query`,
1283
+ * a raw `pg` client, knex's `raw`, kysely, etc.
1284
+ *
1285
+ * The helper requires `int8` (signed 64-bit) lock IDs. We derive them
1286
+ * from the lock `key` via a deterministic hash so different replicas
1287
+ * agree on the int regardless of casing / whitespace drift.
1288
+ */
1289
+ interface PostgresQueryRunner {
1290
+ query(sql: string, params?: unknown[]): Promise<unknown>;
1291
+ }
1292
+ /**
1293
+ * Build an `ISingletonLock` backed by PostgreSQL session-level advisory
1294
+ * locks (`pg_try_advisory_lock`).
1295
+ *
1296
+ * Why advisory locks (vs. row-level locks or a leases table):
1297
+ * 1. **Non-blocking** — `pg_try_advisory_lock` returns immediately;
1298
+ * perfect for the "try then idle" pattern the SDK uses on boot.
1299
+ * 2. **Auto-release on connection drop** — if the leader pod crashes,
1300
+ * its PG connection closes, and the lock is freed automatically.
1301
+ * No timeout tuning, no lease renewal loops, no zombie locks.
1302
+ * 3. **Zero schema** — no extra table, no migration, no contention
1303
+ * with application reads.
1304
+ *
1305
+ * **Caveat — long-lived connection required.** Session-level locks are
1306
+ * held only for the lifetime of the PG connection that called
1307
+ * `pg_try_advisory_lock`. PgBouncer in `transaction` pooling mode will
1308
+ * release the lock between statements; you MUST run in `session`
1309
+ * pooling or skip the bouncer for the indexer process. The advisory
1310
+ * `transaction` variant exists but doesn't fit the "hold while polling"
1311
+ * pattern — for that you'd need a periodic re-acquire, which we don't
1312
+ * implement here.
1313
+ *
1314
+ * @example
1315
+ * import { DataSource } from "typeorm";
1316
+ * import { makePostgresSingletonLock } from "@pafi-dev/issuer";
1317
+ *
1318
+ * const dataSource = new DataSource({ ... });
1319
+ * const lock = makePostgresSingletonLock({
1320
+ * query: (sql, params) => dataSource.query(sql, params),
1321
+ * });
1322
+ *
1323
+ * createIssuerService({
1324
+ * ...,
1325
+ * indexer: {
1326
+ * autoStart: true,
1327
+ * singletonLock: lock,
1328
+ * },
1329
+ * });
1330
+ */
1331
+ declare function makePostgresSingletonLock(runner: PostgresQueryRunner): ISingletonLock;
1332
+
1225
1333
  interface ApiConfigResponse {
1226
1334
  chainId: number;
1227
1335
  contracts: {
@@ -1652,6 +1760,43 @@ declare class IssuerApiHandlers {
1652
1760
  private requireSupportedToken;
1653
1761
  }
1654
1762
 
1763
+ /**
1764
+ * Resolves the EIP-712 domain `name` for a PointToken. Used by handlers
1765
+ * (PTClaimHandler / PTRedeemHandler) that sign requests for multiple
1766
+ * PointTokens within the same issuer backend.
1767
+ *
1768
+ * Resolution order per address:
1769
+ * 1. Cache hit → return immediately (no RPC)
1770
+ * 2. Static override map (config.overrides) → cache + return
1771
+ * 3. On-chain `name()` lookup → cache + return
1772
+ *
1773
+ * Cache is per-instance — share one resolver across handlers so mint +
1774
+ * redeem paths amortise the same set of lookups.
1775
+ *
1776
+ * Why the override map: production issuers often know their PTs at
1777
+ * boot (env / DB) and want to avoid RPC quota churn + tolerate transient
1778
+ * RPC failures. The on-chain fallback handles ad-hoc PTs not in the map
1779
+ * (e.g. newly deployed during the process lifetime).
1780
+ */
1781
+ interface PointTokenDomainResolverConfig {
1782
+ provider: PublicClient;
1783
+ /**
1784
+ * Optional pre-configured map — checksummed (or lowercased) PointToken
1785
+ * address → ERC-20 `name()` value. When a PT's address is in this map,
1786
+ * the resolver short-circuits without an RPC call.
1787
+ */
1788
+ overrides?: Record<string, string>;
1789
+ }
1790
+ declare class PointTokenDomainResolver {
1791
+ private readonly provider;
1792
+ private readonly overrides;
1793
+ private readonly cache;
1794
+ constructor(config: PointTokenDomainResolverConfig);
1795
+ resolve(pointTokenAddress: Address): Promise<string>;
1796
+ /** Invalidate one address (after deploy / proxy upgrade) or all. */
1797
+ invalidate(pointTokenAddress?: Address): void;
1798
+ }
1799
+
1655
1800
  /**
1656
1801
  * Reverse flow — user-initiated PT redeem.
1657
1802
  *
@@ -1681,22 +1826,20 @@ interface PTRedeemHandlerConfig {
1681
1826
  ledger: IPointLedger;
1682
1827
  relayService: RelayService;
1683
1828
  provider: PublicClient;
1684
- /** PointToken contract address (chain-specific). */
1685
- pointTokenAddress: Address;
1686
1829
  /** BatchExecutor delegation target (chain-specific). */
1687
1830
  batchExecutorAddress: Address;
1688
1831
  /** Chain id — used for the BurnRequest EIP-712 domain. */
1689
1832
  chainId: number;
1690
1833
  /**
1691
- * EIP-712 domain fields. Must match the on-chain PointToken's domain
1692
- * separator, or on-chain signature recovery fails. `name` is
1693
- * typically the PointToken's ERC-20 name. `verifyingContract`
1694
- * defaults to `pointTokenAddress`.
1834
+ * Resolver for the EIP-712 domain `name` (= PointToken.name() on-chain).
1835
+ * Required because mint/redeem now route the `pointTokenAddress` per
1836
+ * request the handler can't bind a single domain at construction.
1837
+ * Pass a shared resolver instance so mint + redeem amortise the same
1838
+ * cache. The handler always uses `pointTokenAddress` as the EIP-712
1839
+ * `verifyingContract` — no override (per-PT domain separator is
1840
+ * isolated by contract address).
1695
1841
  */
1696
- domain: {
1697
- name: string;
1698
- verifyingContract?: Address;
1699
- };
1842
+ domainResolver: PointTokenDomainResolver;
1700
1843
  /**
1701
1844
  * Issuer burner signer wallet — signs the `BurnRequest` EIP-712.
1702
1845
  * Must be whitelisted via `PointToken.addBurner(signerAddr)` at
@@ -1739,6 +1882,10 @@ interface PTRedeemRequest {
1739
1882
  authenticatedAddress: Address;
1740
1883
  userAddress: Address;
1741
1884
  amount: bigint;
1885
+ /** PointToken contract to redeem. The handler resolves the EIP-712
1886
+ * domain (name + verifyingContract) from this address via the
1887
+ * configured `domainResolver`. */
1888
+ pointTokenAddress: Address;
1742
1889
  /** ERC-4337 account nonce for the user's EOA. */
1743
1890
  aaNonce: bigint;
1744
1891
  /**
@@ -1806,10 +1953,9 @@ declare class PTRedeemHandler {
1806
1953
  private readonly relayService;
1807
1954
  private readonly provider;
1808
1955
  private readonly feeService?;
1809
- private readonly pointTokenAddress;
1810
1956
  private readonly batchExecutorAddress;
1811
1957
  private readonly chainId;
1812
- private readonly domain;
1958
+ private readonly domainResolver;
1813
1959
  private readonly burnerSignerWallet;
1814
1960
  private readonly redeemLockDurationMs;
1815
1961
  private readonly signatureDeadlineSeconds;
@@ -2524,11 +2670,14 @@ interface PTClaimHandlerConfig {
2524
2670
  /** Issuer minter signer wallet — passed through to RelayService.prepareMint. */
2525
2671
  issuerSignerWallet: WalletClient;
2526
2672
  /**
2527
- * EIP-712 domain `name` for `MintRequest`. Typically the PointToken
2528
- * ERC-20 name. RelayService will set chainId + verifyingContract from
2529
- * the request.
2673
+ * Resolver for the EIP-712 domain `name` (= PointToken.name() on-chain).
2674
+ * Required because mint routes the `pointTokenAddress` per request
2675
+ * the handler can't bind a single domain at construction. Pass a
2676
+ * shared resolver instance so mint + redeem amortise the same cache.
2677
+ * `chainId` + `verifyingContract` are derived from the request's
2678
+ * `chainId` + `pointTokenAddress` respectively.
2530
2679
  */
2531
- pointTokenDomainName: string;
2680
+ domainResolver: PointTokenDomainResolver;
2532
2681
  /** Optional — when wired, used to estimate the PT gas-reimbursement fee. */
2533
2682
  feeService?: FeeManager;
2534
2683
  /** Optional — pre-validates issuer status + cap before locking balance. */
@@ -2849,8 +2998,27 @@ interface IssuerServiceConfig {
2849
2998
  /**
2850
2999
  * If `true`, the factory calls `indexer.start()` before returning.
2851
3000
  * Default: `false` — the caller decides when to begin polling.
3001
+ *
3002
+ * **SAFETY (H-04):** in a multi-replica deployment, ALWAYS pair
3003
+ * `autoStart: true` with `singletonLock` below. Without leader
3004
+ * election, every replica starts its own indexer fleet and races
3005
+ * against the others — see `ISingletonLock` docs for the
3006
+ * consequences.
2852
3007
  */
2853
3008
  autoStart?: boolean;
3009
+ /**
3010
+ * Leader-election primitive. When provided, the factory wraps
3011
+ * `indexer.start()` with a `singletonLock.acquire(key)` call and
3012
+ * only starts the indexer if it wins the lock. Non-leaders stay
3013
+ * idle and take over on the next acquire attempt (when the leader
3014
+ * pod's connection drops, the lock auto-releases).
3015
+ *
3016
+ * The lock key includes the indexer kind + the PointToken address,
3017
+ * so different tokens can be sharded across replicas if desired.
3018
+ *
3019
+ * Recommended: `makePostgresSingletonLock(dataSource)`.
3020
+ */
3021
+ singletonLock?: ISingletonLock;
2854
3022
  /**
2855
3023
  * Override the MintFeeWrapper address used by the indexer. When
2856
3024
  * omitted, the factory auto-resolves from
@@ -2896,6 +3064,13 @@ interface IssuerService {
2896
3064
  fee: FeeManager | undefined;
2897
3065
  /** All indexers keyed by PointToken address. */
2898
3066
  indexers: Map<Address, PointIndexer>;
3067
+ /**
3068
+ * Lock handles for the indexers this replica was elected leader for.
3069
+ * Empty when `autoStart` is false, or when no `singletonLock` was
3070
+ * provided. Call `release()` on each during graceful shutdown so
3071
+ * peers can take over without waiting for the connection to die.
3072
+ */
3073
+ indexerLeaderLocks: SingletonLockHandle[];
2899
3074
  /** Framework-agnostic HTTP handlers — wire into Express / Fastify / Hono. */
2900
3075
  api: IssuerApiHandlers;
2901
3076
  /**
@@ -2917,7 +3092,7 @@ interface IssuerService {
2917
3092
  *
2918
3093
  * Throws synchronously if any required field is missing.
2919
3094
  */
2920
- declare function createIssuerService(config: IssuerServiceConfig): IssuerService;
3095
+ declare function createIssuerService(config: IssuerServiceConfig): Promise<IssuerService>;
2921
3096
 
2922
3097
  /**
2923
3098
  * Adapter that absorbs every "framework-agnostic" endpoint body into a
@@ -3096,6 +3271,7 @@ declare class IssuerApiAdapter {
3096
3271
  redeem(input: {
3097
3272
  authenticatedAddress: Address;
3098
3273
  chainId: number;
3274
+ pointTokenAddress: Address;
3099
3275
  amount: bigint;
3100
3276
  aaNonce: bigint;
3101
3277
  }): Promise<RedeemDto>;
@@ -3656,4 +3832,4 @@ declare class MemoryRedemptionHistoryStore implements IRedemptionHistoryStore {
3656
3832
 
3657
3833
  declare const PAFI_ISSUER_SDK_VERSION: string;
3658
3834
 
3659
- export { AdapterMisconfiguredError, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedemptionEvaluateRequest, type ApiRedemptionEvaluateResponse, type ApiRedemptionPreviewRequest, type ApiRedemptionPreviewResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, type BundlerEstimatorClient, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type ClaimDto, type ConfigDto, ConfigurationError, DEFAULT_REDEMPTION_POLICY, type DecodedCallDto, DefaultPolicyEngine, type DefaultPolicyEngineOptions, type DelegatePrepareDto, type DelegateStatusDto, type EstimateGasFeeOptions, type EvaluateInput, FeeManager, type FeeManagerConfig, type FeeManagerMetrics, type FetchFailureReason, type FetchImpl, type FetchResult, type GasFeeDto, type GasFeeSource, type HandleDelegateSubmitParams, type HandleDelegateSubmitResult, type HandleMobilePrepareParams, type HandleMobilePrepareResult, type HandleMobileSubmitParams, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type IRateLimiter, type IRedemptionHistoryStore, type ISessionStore, InMemoryCursorStore, IssuerApiAdapter, type IssuerApiAdapterConfig, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemoryPendingUserOpStore, MemoryRateLimiter, MemoryRedemptionHistoryStore, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type MobilePrepareDto, type MobileSubmitDto, type NativePtQuoterConfig, NonceManager, NoopRateLimiter, PAFI_ISSUER_SDK_VERSION, PTClaimError, PTClaimHandler, type PTClaimHandlerConfig, type PTClaimRequest, type PTClaimResponse, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PafiEstimatorClientConfig, PafiEstimatorHttpError, type PaymasterGasEstimates, type PendingCredit, type PendingUserOpEntry, PendingUserOpForbiddenError, PendingUserOpNotFoundError, type PerpDepositDto, PerpDepositError, PerpDepositHandler, type PerpDepositHandlerConfig, type PerpDepositRequest, type PerpDepositResponse, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, PolicyProvider, type PolicyProviderConfig, type PoolsDto, type PoolsProvider, type PreValidateMintResult, type PrepareBurnParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, type PreviewBurnParams, type PreviewMintParams, REDEMPTION_HISTORY_WINDOW_SEC, type RateLimitAction, type RateLimiterConfig, type RedeemDto, type RedeemPrepareDto, RedemptionService, type RedemptionServiceConfig, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type ResolvedPolicy, type RetryConfig, type SdkErrorBody, type SdkErrorMapperFactories, type SdkErrorStatus, type SerializedUserOpTypedData, type Session, SettlementClient, type SettlementClientConfig, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type UserDto, type UserHistory, applyPaymasterGasEstimates, authenticateRequest, buildSdkErrorBody, createIssuerService, createNativePtQuoter, createPafiEstimatorClient, createSdkErrorMapper, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, defaultPolicyFor, evaluateRedemption, handleClaimStatus, handleDelegateSubmit, handleMobilePrepare, handleMobileSubmit, handleRedeemStatus, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };
3835
+ export { AdapterMisconfiguredError, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedemptionEvaluateRequest, type ApiRedemptionEvaluateResponse, type ApiRedemptionPreviewRequest, type ApiRedemptionPreviewResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, type BundlerEstimatorClient, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type ClaimDto, type ConfigDto, ConfigurationError, DEFAULT_REDEMPTION_POLICY, type DecodedCallDto, DefaultPolicyEngine, type DefaultPolicyEngineOptions, type DelegatePrepareDto, type DelegateStatusDto, type EstimateGasFeeOptions, type EvaluateInput, FeeManager, type FeeManagerConfig, type FeeManagerMetrics, type FetchFailureReason, type FetchImpl, type FetchResult, type GasFeeDto, type GasFeeSource, type HandleDelegateSubmitParams, type HandleDelegateSubmitResult, type HandleMobilePrepareParams, type HandleMobilePrepareResult, type HandleMobileSubmitParams, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type IRateLimiter, type IRedemptionHistoryStore, type ISessionStore, type ISingletonLock, InMemoryCursorStore, IssuerApiAdapter, type IssuerApiAdapterConfig, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemoryPendingUserOpStore, MemoryRateLimiter, MemoryRedemptionHistoryStore, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type MobilePrepareDto, type MobileSubmitDto, type NativePtQuoterConfig, NonceManager, NoopRateLimiter, PAFI_ISSUER_SDK_VERSION, PTClaimError, PTClaimHandler, type PTClaimHandlerConfig, type PTClaimRequest, type PTClaimResponse, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PafiEstimatorClientConfig, PafiEstimatorHttpError, type PaymasterGasEstimates, type PendingCredit, type PendingUserOpEntry, PendingUserOpForbiddenError, PendingUserOpNotFoundError, type PerpDepositDto, PerpDepositError, PerpDepositHandler, type PerpDepositHandlerConfig, type PerpDepositRequest, type PerpDepositResponse, PointIndexer, type PointIndexerConfig, PointTokenDomainResolver, type PointTokenDomainResolverConfig, type PolicyDecision, type PolicyEvalRequest, PolicyProvider, type PolicyProviderConfig, type PoolsDto, type PoolsProvider, type PostgresQueryRunner, type PreValidateMintResult, type PrepareBurnParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, type PreviewBurnParams, type PreviewMintParams, REDEMPTION_HISTORY_WINDOW_SEC, type RateLimitAction, type RateLimiterConfig, type RedeemDto, type RedeemPrepareDto, RedemptionService, type RedemptionServiceConfig, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type ResolvedPolicy, type RetryConfig, type SdkErrorBody, type SdkErrorMapperFactories, type SdkErrorStatus, type SerializedUserOpTypedData, type Session, SettlementClient, type SettlementClientConfig, type SingletonLockHandle, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type UserDto, type UserHistory, applyPaymasterGasEstimates, authenticateRequest, buildSdkErrorBody, createIssuerService, createNativePtQuoter, createPafiEstimatorClient, createSdkErrorMapper, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, defaultPolicyFor, evaluateRedemption, handleClaimStatus, handleDelegateSubmit, handleMobilePrepare, handleMobileSubmit, handleRedeemStatus, makePostgresSingletonLock, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };