@pafi-dev/issuer 0.5.32 → 0.5.34

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.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Address, Hex, PublicClient, WalletClient } from 'viem';
2
- import { PointTokenDomainConfig, PartialUserOperation, BurnRequest, PoolKey } from '@pafi-dev/core';
2
+ import { PointTokenDomainConfig, PartialUserOperation, BurnRequest, PoolKey, ENTRY_POINT_V08, UserOpTypedData } from '@pafi-dev/core';
3
3
  export { PAFI_SUBGRAPH_URL } from '@pafi-dev/core';
4
4
 
5
5
  /**
@@ -32,6 +32,28 @@ interface LockedMintRequest {
32
32
  expiresAt: number;
33
33
  /** On-chain transaction hash, set once the mint is confirmed */
34
34
  txHash?: Hex;
35
+ /**
36
+ * ERC-4337 userOpHash returned by the bundler at submit time. Bound
37
+ * to the lock by `bindMintUserOpHash` so status endpoints can poll
38
+ * the bundler receipt directly — bypasses `PointIndexer`'s
39
+ * amount-based matching, which races when several PENDING locks
40
+ * share the same amount.
41
+ */
42
+ userOpHash?: Hex;
43
+ }
44
+ /** A pending off-chain credit (burn → credit reverse flow). */
45
+ interface PendingCredit {
46
+ lockId: string;
47
+ userAddress: Address;
48
+ amount: bigint;
49
+ tokenAddress?: Address;
50
+ status: "PENDING" | "RESOLVED" | "EXPIRED";
51
+ createdAt: number;
52
+ expiresAt: number;
53
+ txHash?: Hex;
54
+ resolvedAt?: number;
55
+ /** Bundler-returned userOpHash. See `LockedMintRequest.userOpHash`. */
56
+ userOpHash?: Hex;
35
57
  }
36
58
  /**
37
59
  * Issuer point ledger interface — the source of truth for off-chain user
@@ -92,6 +114,29 @@ interface IPointLedger {
92
114
  * Throws if the lockId is unknown or already resolved.
93
115
  */
94
116
  resolveCreditByBurnTx?(lockId: string, txHash: Hex): Promise<void>;
117
+ /**
118
+ * Persist the bundler-returned userOpHash for a mint lock at
119
+ * submit time. Called once per `/claim/submit` after the bundler
120
+ * accepts the UserOp.
121
+ */
122
+ bindMintUserOpHash?(lockId: string, userOpHash: Hex): Promise<void>;
123
+ /**
124
+ * Persist the bundler-returned userOpHash for a pending credit at
125
+ * `/redeem/submit` time.
126
+ */
127
+ bindCreditUserOpHash?(lockId: string, userOpHash: Hex): Promise<void>;
128
+ /**
129
+ * Look up a mint lock by id. Returns `null` if the lock doesn't
130
+ * exist or doesn't belong to the supplied user (when provided).
131
+ * Used by `handleClaimStatus` for status polling. OPTIONAL — when
132
+ * absent, callers must implement status endpoints themselves.
133
+ */
134
+ getMintLock?(lockId: string, userAddress?: Address): Promise<LockedMintRequest | null>;
135
+ /**
136
+ * Look up a pending credit by id. Symmetric counterpart of
137
+ * `getMintLock` for the burn/redeem flow.
138
+ */
139
+ getPendingCredit?(lockId: string, userAddress?: Address): Promise<PendingCredit | null>;
95
140
  }
96
141
 
97
142
  /**
@@ -1294,6 +1339,194 @@ declare class TopUpRedemptionHandler {
1294
1339
  handle(request: TopUpRedemptionRequest): Promise<TopUpRedemptionResponse>;
1295
1340
  }
1296
1341
 
1342
+ interface RetryConfig {
1343
+ maxAttempts?: number;
1344
+ initialDelayMs?: number;
1345
+ maxDelayMs?: number;
1346
+ maxRetryAfterMs?: number;
1347
+ }
1348
+ interface PafiBackendConfig {
1349
+ url: string;
1350
+ issuerId: string;
1351
+ apiKey: string;
1352
+ fetchImpl?: typeof fetch;
1353
+ timeoutMs?: number;
1354
+ retry?: RetryConfig;
1355
+ }
1356
+ type PafiBackendErrorCode = "MISSING_ISSUER_ID" | "MISSING_API_KEY" | "ISSUER_UNAUTHORIZED" | "USER_UNAUTHORIZED" | "INTENT_REJECTED" | "MINT_CAP_EXCEEDED" | "ISSUER_INACTIVE" | "BROKER_NOT_WHITELISTED" | "RATE_LIMIT_EXCEEDED" | "RATE_LIMIT_EXCEEDED_DAILY" | "RATE_LIMIT_EXCEEDED_PER_USER" | "ISSUER_BUDGET_EXCEEDED" | "RATE_LIMITER_UNAVAILABLE" | "PAYMASTER_UNAVAILABLE" | "TARGET_NOT_ALLOWLISTED" | "BAD_REQUEST" | "INTERNAL_ERROR" | "TIMEOUT" | "NETWORK_ERROR" | (string & {});
1357
+ declare class PafiBackendError extends Error {
1358
+ code: PafiBackendErrorCode;
1359
+ httpStatus: number;
1360
+ details?: unknown | undefined;
1361
+ readonly retryAfter?: number;
1362
+ private readonly serverSafeToRetry?;
1363
+ constructor(code: PafiBackendErrorCode, message: string, httpStatus: number, details?: unknown | undefined, opts?: {
1364
+ retryAfter?: number;
1365
+ safeToRetry?: boolean;
1366
+ });
1367
+ get safeToRetry(): boolean;
1368
+ }
1369
+
1370
+ interface RelayUserOpRequest {
1371
+ userOp: Record<string, string | null>;
1372
+ entryPoint: string;
1373
+ eip7702Auth?: {
1374
+ chainId: string;
1375
+ address: string;
1376
+ nonce: string;
1377
+ r: string;
1378
+ s: string;
1379
+ yParity: string;
1380
+ };
1381
+ }
1382
+ interface RelayUserOpResponse {
1383
+ userOpHash: Hex;
1384
+ }
1385
+ interface SponsorshipUserOp {
1386
+ sender: Address;
1387
+ nonce: bigint;
1388
+ callData: Hex;
1389
+ callGasLimit: bigint;
1390
+ verificationGasLimit: bigint;
1391
+ preVerificationGas: bigint;
1392
+ maxFeePerGas: bigint;
1393
+ maxPriorityFeePerGas: bigint;
1394
+ }
1395
+ interface SponsorshipTarget {
1396
+ contract: Address;
1397
+ function: string;
1398
+ pointToken: Address;
1399
+ }
1400
+ interface SponsorshipRequest {
1401
+ chainId: number;
1402
+ scenario: string;
1403
+ userOp: SponsorshipUserOp;
1404
+ target: SponsorshipTarget;
1405
+ }
1406
+ interface SponsorshipResponse {
1407
+ paymaster: Address;
1408
+ paymasterData: Hex;
1409
+ paymasterVerificationGasLimit: bigint;
1410
+ paymasterPostOpGasLimit: bigint;
1411
+ /**
1412
+ * Pimlico's `pm_sponsorUserOperation` re-estimates these gas fields
1413
+ * and signs its paymaster signature over the new values. Callers
1414
+ * MUST overwrite the matching userOp fields with these BEFORE
1415
+ * computing the EIP-712 userOpHash and submitting to the bundler.
1416
+ * Otherwise both `AA34` (paymaster sig) and `AA24` (sender sig) will
1417
+ * fire — the EntryPoint hashes the actual on-chain field values, and
1418
+ * a mismatch invalidates every sig over the hash.
1419
+ */
1420
+ callGasLimit?: bigint;
1421
+ verificationGasLimit?: bigint;
1422
+ preVerificationGas?: bigint;
1423
+ /**
1424
+ * Bundler-required gas price (Pimlico's `pimlico_getUserOperationGasPrice`
1425
+ * fast tier). Pimlico's paymaster signs over these — caller MUST apply
1426
+ * to the userOp before computing the EIP-712 userOpHash. Base RPC's
1427
+ * `eth_feeHistory` underestimates the bundler floor by 10-15 %, so
1428
+ * relying on it produces "maxFeePerGas must be at least ..." rejections.
1429
+ */
1430
+ maxFeePerGas?: bigint;
1431
+ maxPriorityFeePerGas?: bigint;
1432
+ expiresAt: number;
1433
+ }
1434
+ declare class PafiBackendClient {
1435
+ private readonly config;
1436
+ constructor(config: PafiBackendConfig);
1437
+ requestSponsorship(request: SponsorshipRequest): Promise<SponsorshipResponse>;
1438
+ /**
1439
+ * Fetch ERC-4337 UserOp receipt via PAFI's authenticated bundler proxy.
1440
+ * Returns `null` when the bundler hasn't seen the userOp yet — caller
1441
+ * should keep polling. Used by status endpoints to short-circuit the
1442
+ * on-chain indexer when several PENDING locks share the same amount.
1443
+ */
1444
+ getUserOpReceipt(userOpHash: Hex): Promise<{
1445
+ success: boolean;
1446
+ txHash: Hex;
1447
+ blockNumber: string;
1448
+ } | null>;
1449
+ relayUserOperation(request: RelayUserOpRequest): Promise<RelayUserOpResponse>;
1450
+ private _doRequest;
1451
+ }
1452
+
1453
+ /**
1454
+ * Status snapshot returned to mobile clients polling a mint lock.
1455
+ * Matches the legacy gg56 shape so existing FE code keeps working.
1456
+ */
1457
+ interface MintStatusResponse {
1458
+ lockId: string;
1459
+ status: "PENDING" | "MINTED" | "EXPIRED" | "FAILED";
1460
+ txHash: Hex | null;
1461
+ amount: string;
1462
+ createdAt: string;
1463
+ expiresAt: string;
1464
+ }
1465
+ interface BurnStatusResponse {
1466
+ lockId: string;
1467
+ status: "PENDING" | "RESOLVED" | "EXPIRED";
1468
+ txHash: Hex | null;
1469
+ amount: string;
1470
+ createdAt: string;
1471
+ expiresAt: string;
1472
+ resolvedAt?: string | null;
1473
+ }
1474
+ interface MintStatusParams {
1475
+ lockId: string;
1476
+ /** User EOA from auth — handler returns 404 if the lock isn't theirs. */
1477
+ userAddress: Address;
1478
+ ledger: IPointLedger;
1479
+ /**
1480
+ * PAFI backend client for the bundler-receipt fallback. Optional —
1481
+ * when omitted, status only reflects what the on-chain `PointIndexer`
1482
+ * has finalized into the ledger. With it, the handler queries the
1483
+ * bundler receipt when:
1484
+ * - lock.status === "PENDING"
1485
+ * - lock.userOpHash is bound (set by `/claim/submit`)
1486
+ *
1487
+ * If the bundler reports the UserOp confirmed, the handler updates
1488
+ * the ledger lock + returns `MINTED` immediately, bypassing
1489
+ * `PointIndexer`'s amount-based race (multiple PENDING locks with
1490
+ * the same amount can be matched to the wrong tx_hash).
1491
+ */
1492
+ pafiBackendClient?: PafiBackendClient;
1493
+ /** Optional logger for "ledger update failed" warnings. */
1494
+ onWarning?: (msg: string) => void;
1495
+ }
1496
+ interface BurnStatusParams {
1497
+ lockId: string;
1498
+ userAddress: Address;
1499
+ ledger: IPointLedger;
1500
+ pafiBackendClient?: PafiBackendClient;
1501
+ onWarning?: (msg: string) => void;
1502
+ }
1503
+ declare class LockNotFoundError extends Error {
1504
+ readonly code = "LOCK_NOT_FOUND";
1505
+ constructor();
1506
+ }
1507
+
1508
+ /**
1509
+ * Handle GET /claim/status/:lockId.
1510
+ *
1511
+ * Returns the lock's current state from the ledger. When the ledger
1512
+ * supports `userOpHash` binding AND the lock is still PENDING, falls
1513
+ * back to the bundler receipt — the canonical truth that bypasses
1514
+ * `PointIndexer`'s amount-based matching race.
1515
+ *
1516
+ * Throws `LockNotFoundError` when the lock doesn't exist or doesn't
1517
+ * belong to `userAddress`. Caller maps to HTTP 404.
1518
+ */
1519
+ declare function handleClaimStatus(params: MintStatusParams): Promise<MintStatusResponse>;
1520
+ /**
1521
+ * Handle GET /redeem/status/:lockId. Symmetric to `handleClaimStatus`
1522
+ * for the burn → off-chain credit flow.
1523
+ *
1524
+ * Bundler-receipt fallback resolves the credit via
1525
+ * `ledger.resolveCreditByBurnTx` when the bundler reports the burn
1526
+ * UserOp succeeded.
1527
+ */
1528
+ declare function handleRedeemStatus(params: BurnStatusParams): Promise<BurnStatusResponse>;
1529
+
1297
1530
  /**
1298
1531
  * Config for `createSubgraphPoolsProvider`.
1299
1532
  */
@@ -1511,37 +1744,70 @@ declare class BalanceAggregator {
1511
1744
  getCombinedBalanceMulti(user: Address, pointTokens: Address[]): Promise<Map<Address, CombinedBalance>>;
1512
1745
  }
1513
1746
 
1514
- interface RetryConfig {
1515
- maxAttempts?: number;
1516
- initialDelayMs?: number;
1517
- maxDelayMs?: number;
1518
- maxRetryAfterMs?: number;
1519
- }
1520
- interface PafiBackendConfig {
1521
- url: string;
1522
- issuerId: string;
1523
- apiKey: string;
1524
- fetchImpl?: typeof fetch;
1525
- timeoutMs?: number;
1526
- retry?: RetryConfig;
1527
- }
1528
- type PafiBackendErrorCode = "MISSING_ISSUER_ID" | "MISSING_API_KEY" | "ISSUER_UNAUTHORIZED" | "USER_UNAUTHORIZED" | "INTENT_REJECTED" | "MINT_CAP_EXCEEDED" | "ISSUER_INACTIVE" | "BROKER_NOT_WHITELISTED" | "RATE_LIMIT_EXCEEDED" | "RATE_LIMIT_EXCEEDED_DAILY" | "RATE_LIMIT_EXCEEDED_PER_USER" | "ISSUER_BUDGET_EXCEEDED" | "RATE_LIMITER_UNAVAILABLE" | "PAYMASTER_UNAVAILABLE" | "TARGET_NOT_ALLOWLISTED" | "BAD_REQUEST" | "INTERNAL_ERROR" | "TIMEOUT" | "NETWORK_ERROR" | (string & {});
1529
- declare class PafiBackendError extends Error {
1530
- code: PafiBackendErrorCode;
1531
- httpStatus: number;
1532
- details?: unknown | undefined;
1533
- readonly retryAfter?: number;
1534
- private readonly serverSafeToRetry?;
1535
- constructor(code: PafiBackendErrorCode, message: string, httpStatus: number, details?: unknown | undefined, opts?: {
1536
- retryAfter?: number;
1537
- safeToRetry?: boolean;
1538
- });
1539
- get safeToRetry(): boolean;
1747
+ /**
1748
+ * Typed errors thrown by the helpers below — issuer controllers map
1749
+ * these to the appropriate HTTP status. We don't depend on @nestjs/common
1750
+ * here because the SDK is framework-agnostic; the consuming controller
1751
+ * wraps each one in its preferred exception class.
1752
+ */
1753
+ declare class BundlerNotConfiguredError extends Error {
1754
+ readonly code = "BUNDLER_NOT_CONFIGURED";
1755
+ constructor();
1756
+ }
1757
+ declare class BundlerRejectedError extends Error {
1758
+ readonly code = "BUNDLER_REJECTED";
1759
+ readonly cause: unknown;
1760
+ constructor(message: string, cause: unknown);
1761
+ }
1762
+ interface RequestPaymasterParams {
1763
+ /** PAFI backend client. When `null` / `undefined` → returns `undefined`. */
1764
+ client: PafiBackendClient | null | undefined;
1765
+ chainId: number;
1766
+ scenario: string;
1767
+ /** UserOp skeleton — must have all gas + fee fields set. */
1768
+ userOp: {
1769
+ sender: Address;
1770
+ nonce: bigint;
1771
+ callData: Hex;
1772
+ callGasLimit: bigint;
1773
+ verificationGasLimit: bigint;
1774
+ preVerificationGas: bigint;
1775
+ maxFeePerGas: bigint;
1776
+ maxPriorityFeePerGas: bigint;
1777
+ };
1778
+ /** Target contract (typically the PointToken). */
1779
+ pointTokenAddress: Address;
1780
+ /**
1781
+ * Function name to surface in audit / paymaster context. Defaults to
1782
+ * a per-scenario sensible value (`mint` / `burn` / `swap` / generic
1783
+ * scenario name).
1784
+ */
1785
+ functionName?: string;
1786
+ /** Optional logger for the "sponsorship declined" warning. */
1787
+ onWarning?: (msg: string) => void;
1540
1788
  }
1541
-
1542
- interface RelayUserOpRequest {
1789
+ /**
1790
+ * Thin wrapper around `PafiBackendClient.requestSponsorship` with the
1791
+ * "non-fatal on failure" semantics every issuer wants:
1792
+ *
1793
+ * - When the client is missing → returns `undefined` (the caller falls
1794
+ * back to a self-funded UserOp).
1795
+ * - When the network call throws OR PAFI declines (rate limit, intent
1796
+ * rejection, paymaster outage) → returns `undefined` after logging,
1797
+ * so the controller doesn't hard-fail. The caller's
1798
+ * `prepareMobileUserOp` / `mergePaymasterFields` will gracefully
1799
+ * produce the unsponsored variant.
1800
+ *
1801
+ * Replaces ~30 LoC of try/catch + scenario-to-function mapping every
1802
+ * issuer would copy.
1803
+ */
1804
+ declare function requestPaymaster(params: RequestPaymasterParams): Promise<Awaited<ReturnType<PafiBackendClient["requestSponsorship"]>> | undefined>;
1805
+ interface RelayUserOpParams {
1806
+ client: PafiBackendClient | null | undefined;
1807
+ /** EntryPoint address — typically `ENTRY_POINT_V08` from core. */
1808
+ entryPoint: typeof ENTRY_POINT_V08 | string;
1543
1809
  userOp: Record<string, string | null>;
1544
- entryPoint: string;
1810
+ /** EIP-7702 authorization (delegation UserOps only). */
1545
1811
  eip7702Auth?: {
1546
1812
  chainId: string;
1547
1813
  address: string;
@@ -1551,76 +1817,24 @@ interface RelayUserOpRequest {
1551
1817
  yParity: string;
1552
1818
  };
1553
1819
  }
1554
- interface RelayUserOpResponse {
1820
+ /**
1821
+ * Submit a serialized UserOp to the Pimlico bundler via PAFI's
1822
+ * sponsor-relayer. Handles the "client missing" / "bundler rejected"
1823
+ * branches as typed errors so the controller can map to HTTP cleanly.
1824
+ *
1825
+ * Every issuer mobile flow has this exact wrapper — moved into SDK
1826
+ * to drop ~30 LoC of try/catch + error-shape boilerplate per
1827
+ * controller.
1828
+ *
1829
+ * Throws:
1830
+ * - `BundlerNotConfiguredError` — caller didn't configure
1831
+ * `PafiBackendClient`. Map to 503.
1832
+ * - `BundlerRejectedError` — bundler returned an error. Map to 422
1833
+ * (the FE can show the reason — usually `AA21` / `AA34` / etc.).
1834
+ */
1835
+ declare function relayUserOp(params: RelayUserOpParams): Promise<{
1555
1836
  userOpHash: Hex;
1556
- }
1557
- interface SponsorshipUserOp {
1558
- sender: Address;
1559
- nonce: bigint;
1560
- callData: Hex;
1561
- callGasLimit: bigint;
1562
- verificationGasLimit: bigint;
1563
- preVerificationGas: bigint;
1564
- maxFeePerGas: bigint;
1565
- maxPriorityFeePerGas: bigint;
1566
- }
1567
- interface SponsorshipTarget {
1568
- contract: Address;
1569
- function: string;
1570
- pointToken: Address;
1571
- }
1572
- interface SponsorshipRequest {
1573
- chainId: number;
1574
- scenario: string;
1575
- userOp: SponsorshipUserOp;
1576
- target: SponsorshipTarget;
1577
- }
1578
- interface SponsorshipResponse {
1579
- paymaster: Address;
1580
- paymasterData: Hex;
1581
- paymasterVerificationGasLimit: bigint;
1582
- paymasterPostOpGasLimit: bigint;
1583
- /**
1584
- * Pimlico's `pm_sponsorUserOperation` re-estimates these gas fields
1585
- * and signs its paymaster signature over the new values. Callers
1586
- * MUST overwrite the matching userOp fields with these BEFORE
1587
- * computing the EIP-712 userOpHash and submitting to the bundler.
1588
- * Otherwise both `AA34` (paymaster sig) and `AA24` (sender sig) will
1589
- * fire — the EntryPoint hashes the actual on-chain field values, and
1590
- * a mismatch invalidates every sig over the hash.
1591
- */
1592
- callGasLimit?: bigint;
1593
- verificationGasLimit?: bigint;
1594
- preVerificationGas?: bigint;
1595
- /**
1596
- * Bundler-required gas price (Pimlico's `pimlico_getUserOperationGasPrice`
1597
- * fast tier). Pimlico's paymaster signs over these — caller MUST apply
1598
- * to the userOp before computing the EIP-712 userOpHash. Base RPC's
1599
- * `eth_feeHistory` underestimates the bundler floor by 10-15 %, so
1600
- * relying on it produces "maxFeePerGas must be at least ..." rejections.
1601
- */
1602
- maxFeePerGas?: bigint;
1603
- maxPriorityFeePerGas?: bigint;
1604
- expiresAt: number;
1605
- }
1606
- declare class PafiBackendClient {
1607
- private readonly config;
1608
- constructor(config: PafiBackendConfig);
1609
- requestSponsorship(request: SponsorshipRequest): Promise<SponsorshipResponse>;
1610
- /**
1611
- * Fetch ERC-4337 UserOp receipt via PAFI's authenticated bundler proxy.
1612
- * Returns `null` when the bundler hasn't seen the userOp yet — caller
1613
- * should keep polling. Used by status endpoints to short-circuit the
1614
- * on-chain indexer when several PENDING locks share the same amount.
1615
- */
1616
- getUserOpReceipt(userOpHash: Hex): Promise<{
1617
- success: boolean;
1618
- txHash: Hex;
1619
- blockNumber: string;
1620
- } | null>;
1621
- relayUserOperation(request: RelayUserOpRequest): Promise<RelayUserOpResponse>;
1622
- private _doRequest;
1623
- }
1837
+ }>;
1624
1838
 
1625
1839
  /**
1626
1840
  * Top-level configuration for `createIssuerService`.
@@ -1814,6 +2028,154 @@ interface IPendingUserOpStore {
1814
2028
  */
1815
2029
  declare function serializeEntryToJsonRpc(entry: PendingUserOpEntry, signature: Hex, variant?: "sponsored" | "fallback"): Record<string, string | null>;
1816
2030
 
2031
+ /**
2032
+ * Re-shape `UserOpTypedData` so all `bigint` fields become hex strings —
2033
+ * required for JSON transport over HTTP. Mirrors the inverse of what
2034
+ * `walletClient.signTypedData` accepts on the client (it auto-coerces hex
2035
+ * strings back to bigints for `uint256` fields).
2036
+ */
2037
+ type SerializedUserOpTypedData = {
2038
+ domain: UserOpTypedData["domain"];
2039
+ types: UserOpTypedData["types"];
2040
+ primaryType: UserOpTypedData["primaryType"];
2041
+ message: {
2042
+ sender: Address;
2043
+ nonce: Hex;
2044
+ initCode: Hex;
2045
+ callData: Hex;
2046
+ accountGasLimits: Hex;
2047
+ preVerificationGas: Hex;
2048
+ gasFees: Hex;
2049
+ paymasterAndData: Hex;
2050
+ };
2051
+ };
2052
+ /**
2053
+ * Convert a `UserOpTypedData` payload into the JSON-safe wire form
2054
+ * (bigint → hex string). The mobile client passes this directly to
2055
+ * `eth_signTypedData_v4` / viem's `signTypedData`.
2056
+ */
2057
+ declare function serializeUserOpTypedData(td: UserOpTypedData): SerializedUserOpTypedData;
2058
+ /**
2059
+ * Merge Pimlico's paymaster-sponsorship response into a UserOp
2060
+ * skeleton, applying only fields that are actually defined.
2061
+ *
2062
+ * `pm_sponsorUserOperation` returns:
2063
+ * - `paymaster` / `paymasterData` — required for the sponsored sig
2064
+ * - `paymasterVerificationGasLimit` / `paymasterPostOpGasLimit`
2065
+ * - **re-estimated** `callGasLimit` / `verificationGasLimit` /
2066
+ * `preVerificationGas` — the paymaster signature is computed over
2067
+ * these new values
2068
+ * - **bundler-required** `maxFeePerGas` / `maxPriorityFeePerGas` —
2069
+ * Base RPC's `eth_feeHistory` underestimates, bundler rejects
2070
+ *
2071
+ * Callers MUST re-merge ALL of these into the userOp BEFORE computing
2072
+ * the EIP-712 userOpHash — otherwise both the paymaster signature
2073
+ * (AA34) and the user signature (AA24, recovered against a different
2074
+ * hash) fail at validation.
2075
+ *
2076
+ * Skips fields that are undefined so legacy paymaster responses
2077
+ * (without re-estimated gas) don't accidentally zero out the original
2078
+ * estimates.
2079
+ */
2080
+ declare function mergePaymasterFields<T extends object>(userOp: T, paymasterFields: {
2081
+ paymaster: Address;
2082
+ paymasterData: Hex;
2083
+ paymasterVerificationGasLimit: bigint;
2084
+ paymasterPostOpGasLimit: bigint;
2085
+ callGasLimit?: bigint;
2086
+ verificationGasLimit?: bigint;
2087
+ preVerificationGas?: bigint;
2088
+ maxFeePerGas?: bigint;
2089
+ maxPriorityFeePerGas?: bigint;
2090
+ } | undefined): T;
2091
+ interface PreparedUserOp {
2092
+ /** The bundler-ready UserOp (with paymaster + Pimlico-quoted gas). */
2093
+ userOp: PartialUserOperation & {
2094
+ maxFeePerGas: bigint;
2095
+ maxPriorityFeePerGas: bigint;
2096
+ paymaster?: Address;
2097
+ paymasterData?: Hex;
2098
+ paymasterVerificationGasLimit?: bigint;
2099
+ paymasterPostOpGasLimit?: bigint;
2100
+ };
2101
+ /** Hex-encoded EIP-712 digest. Equals `EntryPoint.getUserOpHash`. */
2102
+ userOpHash: Hex;
2103
+ /** Typed-data payload — pass directly to `eth_signTypedData_v4`. */
2104
+ typedData: SerializedUserOpTypedData;
2105
+ }
2106
+ interface PrepareMobileUserOpParams {
2107
+ /** Lock id (issuer-generated) keying both store entry + ledger row. */
2108
+ lockId: string;
2109
+ /**
2110
+ * Sponsored variant — built with the PT operator-fee transfer
2111
+ * included. SDK or caller should set `partialUserOp.maxFeePerGas` /
2112
+ * `maxPriorityFeePerGas` from `provider.estimateFeesPerGas()` before
2113
+ * calling — they get overridden by Pimlico's quote anyway, but
2114
+ * they must be valid bigints for the merge.
2115
+ */
2116
+ partialUserOp: PartialUserOperation & {
2117
+ maxFeePerGas: bigint;
2118
+ maxPriorityFeePerGas: bigint;
2119
+ };
2120
+ /**
2121
+ * Optional fee-stripped fallback variant — built with `gasFeePt: 0n`
2122
+ * (no PT operator-fee transfer). Submitted when paymaster refuses
2123
+ * sponsorship and the user pays ETH gas directly.
2124
+ */
2125
+ partialUserOpFallback?: PartialUserOperation & {
2126
+ maxFeePerGas?: bigint;
2127
+ maxPriorityFeePerGas?: bigint;
2128
+ };
2129
+ /** Paymaster sponsorship response, or `undefined` if PAFI declined. */
2130
+ paymasterFields?: {
2131
+ paymaster: Address;
2132
+ paymasterData: Hex;
2133
+ paymasterVerificationGasLimit: bigint;
2134
+ paymasterPostOpGasLimit: bigint;
2135
+ callGasLimit?: bigint;
2136
+ verificationGasLimit?: bigint;
2137
+ preVerificationGas?: bigint;
2138
+ maxFeePerGas?: bigint;
2139
+ maxPriorityFeePerGas?: bigint;
2140
+ };
2141
+ chainId: number;
2142
+ /** Pending-userop store implementation (Redis/Postgres/Memory). */
2143
+ store: IPendingUserOpStore;
2144
+ /** TTL the store entry should outlive — typically the MintRequest deadline. */
2145
+ ttlSeconds: number;
2146
+ }
2147
+ interface PrepareMobileUserOpResult {
2148
+ sponsored: PreparedUserOp;
2149
+ /**
2150
+ * Set when `partialUserOpFallback` was supplied AND the PT fee was
2151
+ * non-zero (i.e. sponsored ≠ fallback). Mobile client picks which
2152
+ * variant to sign + submit; SDK's `serializeEntryToJsonRpc` reads
2153
+ * the `variant` flag to dispatch.
2154
+ */
2155
+ fallback?: PreparedUserOp;
2156
+ /** What got persisted into the pending-userop store. */
2157
+ entry: PendingUserOpEntry;
2158
+ }
2159
+ /**
2160
+ * Build the sponsored UserOp + (optional) fee-stripped fallback for the
2161
+ * mobile prepare/submit flow.
2162
+ *
2163
+ * What this does, end-to-end:
2164
+ * 1. Merge Pimlico's paymaster sponsorship + re-quoted gas into the
2165
+ * caller's `partialUserOp` skeleton.
2166
+ * 2. Compute the EIP-712 userOpHash + the typed-data payload (in
2167
+ * JSON-safe form for HTTP transport).
2168
+ * 3. (Optional) Repeat for the `partialUserOpFallback` skeleton with
2169
+ * no paymaster fields — produces a separate hash + typed-data so
2170
+ * the client can re-sign if it falls back.
2171
+ * 4. Persist a single store entry containing BOTH callData variants
2172
+ * keyed by lockId. `serializeEntryToJsonRpc` reads the `variant`
2173
+ * param at submit time.
2174
+ *
2175
+ * Replaces ~100 LoC of glue per scenario in issuer controllers.
2176
+ */
2177
+ declare function prepareMobileUserOp(params: PrepareMobileUserOpParams): Promise<PrepareMobileUserOpResult>;
2178
+
1817
2179
  interface IssuerRegistryRecord {
1818
2180
  issuerAddress: Address;
1819
2181
  signerAddress: Address;
@@ -1907,4 +2269,4 @@ declare class IssuerStateValidator {
1907
2269
  /** SDK package version — bumped on every release */
1908
2270
  declare const PAFI_ISSUER_SDK_VERSION = "0.4.0";
1909
2271
 
1910
- 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 IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type ISessionStore, InMemoryCursorStore, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, type LockedMintRequest, type LoginResult, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintingStatus, type NativePtQuoterConfig, NonceManager, PAFI_ISSUER_SDK_VERSION, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PendingUserOpEntry, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, type PoolsProvider, type PreValidateMintResult, 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, createNativePtQuoter, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, serializeEntryToJsonRpc };
2272
+ 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, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type CombinedBalance, DefaultPolicyEngine, type DefaultPolicyEngineOptions, FeeManager, type FeeManagerConfig, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type ISessionStore, InMemoryCursorStore, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type NativePtQuoterConfig, NonceManager, PAFI_ISSUER_SDK_VERSION, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PendingCredit, type PendingUserOpEntry, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, type PoolsProvider, type PreValidateMintResult, type PrepareBurnDirectParams, type PrepareBurnParams, type PrepareBurnWithSigParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type RetryConfig, type SerializedUserOpTypedData, type Session, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, TopUpRedemptionError, TopUpRedemptionHandler, type TopUpRedemptionHandlerConfig, type TopUpRedemptionRequest, type TopUpRedemptionResponse, authenticateRequest, createIssuerService, createNativePtQuoter, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, handleClaimStatus, handleRedeemStatus, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };