@pafi-dev/issuer 0.3.0-beta.8 → 0.4.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/README.md +164 -292
- package/dist/index.cjs +394 -489
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +132 -308
- package/dist/index.d.ts +132 -308
- package/dist/index.js +370 -465
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Address, Hex, PublicClient,
|
|
2
|
-
import { PointTokenDomainConfig,
|
|
1
|
+
import { Address, Hex, PublicClient, WalletClient } from 'viem';
|
|
2
|
+
import { PointTokenDomainConfig, PartialUserOperation, BurnRequest, ReceiverConsent, PathKey, PoolKey } from '@pafi-dev/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Lifecycle of a minting request as tracked by the issuer's point ledger.
|
|
@@ -62,11 +62,7 @@ interface IPointLedger {
|
|
|
62
62
|
lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number, tokenAddress?: Address): Promise<string>;
|
|
63
63
|
/** Release a previously created lock (e.g. on tx failure / cancel). */
|
|
64
64
|
releaseLock(lockId: string): Promise<void>;
|
|
65
|
-
/**
|
|
66
|
-
* Permanently deduct an amount from a user's balance after the on-chain
|
|
67
|
-
* mint has been observed by the indexer. Should also resolve any matching
|
|
68
|
-
* lock so the funds aren't double-counted.
|
|
69
|
-
*/
|
|
65
|
+
/** Deduct balance after a confirmed on-chain mint. Idempotent on `txHash`. */
|
|
70
66
|
deductBalance(userAddress: Address, amount: bigint, txHash: Hex, tokenAddress?: Address): Promise<void>;
|
|
71
67
|
/** Credit points to a user's balance (e.g. from merchant activity). */
|
|
72
68
|
creditBalance(userAddress: Address, amount: bigint, reason: string, tokenAddress?: Address): Promise<void>;
|
|
@@ -97,47 +93,6 @@ interface IPointLedger {
|
|
|
97
93
|
resolveCreditByBurnTx?(lockId: string, txHash: Hex): Promise<void>;
|
|
98
94
|
}
|
|
99
95
|
|
|
100
|
-
/**
|
|
101
|
-
* In-memory IPointLedger implementation for development and tests.
|
|
102
|
-
*
|
|
103
|
-
* NOT for production — state is lost on restart. Issuers should ship their
|
|
104
|
-
* own database-backed implementation.
|
|
105
|
-
*
|
|
106
|
-
* Concurrency model: single-process, single-threaded (Node.js event loop).
|
|
107
|
-
* The lock check + insert is atomic within a tick because no awaits sit
|
|
108
|
-
* between balance read and lock write.
|
|
109
|
-
*
|
|
110
|
-
* **Multi-token (0.2.0):** Balances are keyed by `(user, token)`. If callers
|
|
111
|
-
* omit `tokenAddress`, the literal string "default" is used — that keeps
|
|
112
|
-
* single-token usage working exactly like 0.1.x.
|
|
113
|
-
*/
|
|
114
|
-
declare class MemoryPointLedger implements IPointLedger {
|
|
115
|
-
private balances;
|
|
116
|
-
private locks;
|
|
117
|
-
private nextLockId;
|
|
118
|
-
private now;
|
|
119
|
-
constructor(opts?: {
|
|
120
|
-
now?: () => number;
|
|
121
|
-
});
|
|
122
|
-
getBalance(userAddress: Address, tokenAddress?: Address): Promise<bigint>;
|
|
123
|
-
getLockedRequests(userAddress: Address, tokenAddress?: Address): Promise<LockedMintRequest[]>;
|
|
124
|
-
creditBalance(userAddress: Address, amount: bigint, _reason: string, tokenAddress?: Address): Promise<void>;
|
|
125
|
-
lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number, tokenAddress?: Address): Promise<string>;
|
|
126
|
-
releaseLock(lockId: string): Promise<void>;
|
|
127
|
-
deductBalance(userAddress: Address, amount: bigint, txHash: Hex, tokenAddress?: Address): Promise<void>;
|
|
128
|
-
updateMintStatus(lockId: string, status: MintingStatus, txHash?: Hex): Promise<void>;
|
|
129
|
-
private pendingCredits;
|
|
130
|
-
private nextCreditId;
|
|
131
|
-
reservePendingCredit(userAddress: Address, amount: bigint, durationMs: number, tokenAddress?: Address): Promise<string>;
|
|
132
|
-
resolveCreditByBurnTx(lockId: string, txHash: Hex): Promise<void>;
|
|
133
|
-
/**
|
|
134
|
-
* Auto-expire any PENDING lock past its expiry. Called lazily on every
|
|
135
|
-
* read/write so the in-memory state stays self-cleaning without a timer.
|
|
136
|
-
*/
|
|
137
|
-
private purgeExpired;
|
|
138
|
-
private lockedTotalFor;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
96
|
/**
|
|
142
97
|
* Input to a policy evaluation. Policy engines use this to decide whether
|
|
143
98
|
* a user's mint request should be approved.
|
|
@@ -213,56 +168,6 @@ declare class DefaultPolicyEngine implements IPolicyEngine {
|
|
|
213
168
|
evaluate(request: PolicyEvalRequest): Promise<PolicyDecision>;
|
|
214
169
|
}
|
|
215
170
|
|
|
216
|
-
/**
|
|
217
|
-
* Issuer signer — produces the MintRequest EIP-712 signature that the Relay
|
|
218
|
-
* contract verifies against the issuer's on-chain authorized minter.
|
|
219
|
-
*
|
|
220
|
-
* This is a trust boundary: the default `PrivateKeySigner` holds the key in
|
|
221
|
-
* process memory and is intended for local development only. Production
|
|
222
|
-
* issuers replace this with an HSM/KMS/MPC integration.
|
|
223
|
-
*/
|
|
224
|
-
interface IIssuerSigner {
|
|
225
|
-
/**
|
|
226
|
-
* Sign a `MintRequest` message against the point token's EIP-712 domain.
|
|
227
|
-
* The returned signature is what the Relay contract passes to
|
|
228
|
-
* `PointToken.verify` during `mintAndSwap`.
|
|
229
|
-
*/
|
|
230
|
-
signMintRequest(domain: PointTokenDomainConfig, message: MintRequest): Promise<EIP712Signature>;
|
|
231
|
-
/** Get the signer's on-chain address (used for verification / logging). */
|
|
232
|
-
getAddress(): Promise<Address>;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
interface PrivateKeySignerOptions {
|
|
236
|
-
/** 0x-prefixed 32-byte hex private key */
|
|
237
|
-
privateKey: Hex;
|
|
238
|
-
/**
|
|
239
|
-
* Chain metadata for the viem WalletClient. Only the chain id is actually
|
|
240
|
-
* used for signing; a minimal stub is acceptable (it does not need to
|
|
241
|
-
* match the deployed chain config beyond id).
|
|
242
|
-
*/
|
|
243
|
-
chain: Chain;
|
|
244
|
-
/**
|
|
245
|
-
* Optional RPC URL. `signTypedData` is offline, so this can usually be
|
|
246
|
-
* left unset — viem only requires a transport to construct the client.
|
|
247
|
-
*/
|
|
248
|
-
rpcUrl?: string;
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Local-key implementation of `IIssuerSigner`. Wraps viem's `signTypedData`
|
|
252
|
-
* via the shared `@pafi/core` `signMintRequest` helper.
|
|
253
|
-
*
|
|
254
|
-
* ⚠️ **NOT for production use.** The private key lives in process memory
|
|
255
|
-
* and is trivially extractable from a compromised host. Replace with an
|
|
256
|
-
* HSM/KMS/MPC-backed `IIssuerSigner` before deployment.
|
|
257
|
-
*/
|
|
258
|
-
declare class PrivateKeySigner implements IIssuerSigner {
|
|
259
|
-
private readonly account;
|
|
260
|
-
private readonly walletClient;
|
|
261
|
-
constructor(opts: PrivateKeySignerOptions);
|
|
262
|
-
signMintRequest(domain: PointTokenDomainConfig, message: MintRequest): Promise<EIP712Signature>;
|
|
263
|
-
getAddress(): Promise<Address>;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
171
|
/**
|
|
267
172
|
* A server-issued session created after a successful wallet login. The
|
|
268
173
|
* token id is embedded in the JWT so sessions can be revoked without
|
|
@@ -609,6 +514,9 @@ declare class FeeManager {
|
|
|
609
514
|
private readonly gasUnits;
|
|
610
515
|
private readonly gasPremiumBps;
|
|
611
516
|
private readonly quoteNativeToFee;
|
|
517
|
+
private cachedFee;
|
|
518
|
+
private cacheExpiresAt;
|
|
519
|
+
private static readonly CACHE_TTL_MS;
|
|
612
520
|
constructor(config: FeeManagerConfig);
|
|
613
521
|
/**
|
|
614
522
|
* Estimate the fee (in the caller's fee currency) to charge for the
|
|
@@ -784,6 +692,15 @@ interface BurnIndexerConfig {
|
|
|
784
692
|
/** Polling interval (ms). Default: 5000. */
|
|
785
693
|
pollIntervalMs?: number;
|
|
786
694
|
now?: () => number;
|
|
695
|
+
/**
|
|
696
|
+
* Map a burn event to the pending credit lockId that should be resolved.
|
|
697
|
+
* Return `undefined` to skip this burn event (no credit granted).
|
|
698
|
+
*
|
|
699
|
+
* REQUIRED — there is no default implementation. Issuers with a Postgres
|
|
700
|
+
* ledger typically JOIN on `(from, amount, status=PENDING)`. The in-memory
|
|
701
|
+
* ledger uses a lookup by lockId supplied out-of-band from the claim flow.
|
|
702
|
+
*/
|
|
703
|
+
matchLockId: (event: BurnEvent) => Promise<string | undefined>;
|
|
787
704
|
}
|
|
788
705
|
/**
|
|
789
706
|
* Mirror of `PointIndexer` for the reverse direction — watches
|
|
@@ -814,17 +731,6 @@ declare class BurnIndexer {
|
|
|
814
731
|
private readonly confirmations;
|
|
815
732
|
private readonly batchSize;
|
|
816
733
|
private readonly pollIntervalMs;
|
|
817
|
-
/**
|
|
818
|
-
* Caller-supplied matcher. Return the lockId to resolve for a given
|
|
819
|
-
* burn event, or `undefined` to skip. Runs synchronously via the
|
|
820
|
-
* ledger's query path.
|
|
821
|
-
*
|
|
822
|
-
* Default: try `ledger.resolveCreditByBurnTx` keyed on a synthetic
|
|
823
|
-
* lock id `burn-${from}-${amount}` — the in-memory ledger assigns
|
|
824
|
-
* incrementing IDs so callers with the memory ledger must provide a
|
|
825
|
-
* custom matcher. Real DB-backed ledgers override this to JOIN on
|
|
826
|
-
* their `pending_credits` table.
|
|
827
|
-
*/
|
|
828
734
|
matchLockId: (event: BurnEvent) => Promise<string | undefined>;
|
|
829
735
|
private running;
|
|
830
736
|
private timer;
|
|
@@ -974,6 +880,27 @@ interface ApiClaimAndSwapResponse {
|
|
|
974
880
|
blockNumber?: bigint;
|
|
975
881
|
gasUsed?: bigint;
|
|
976
882
|
}
|
|
883
|
+
interface ApiClaimRequest {
|
|
884
|
+
chainId: number;
|
|
885
|
+
pointTokenAddress: Address;
|
|
886
|
+
/** PT amount to mint. */
|
|
887
|
+
amount: bigint;
|
|
888
|
+
/** ERC-4337 account nonce for the user's EOA (from EntryPoint). */
|
|
889
|
+
aaNonce: bigint;
|
|
890
|
+
/** Unix seconds — when the MintRequest signature expires. */
|
|
891
|
+
deadline: bigint;
|
|
892
|
+
/** Optional operator fee (PT) deducted inside the same UserOp batch. */
|
|
893
|
+
feeAmount?: bigint;
|
|
894
|
+
feeRecipient?: Address;
|
|
895
|
+
}
|
|
896
|
+
interface ApiClaimResponse {
|
|
897
|
+
/** Off-chain lock id — poll `/user` to track PENDING → MINTED. */
|
|
898
|
+
lockId: string;
|
|
899
|
+
/** Unsigned UserOp — attach paymaster data + user signature, then submit. */
|
|
900
|
+
userOp: PartialUserOperation;
|
|
901
|
+
/** Seconds until the off-chain lock expires if the UserOp is not submitted. */
|
|
902
|
+
expiresInSeconds: number;
|
|
903
|
+
}
|
|
977
904
|
interface ApiBuildConsentTypedDataRequest {
|
|
978
905
|
chainId: number;
|
|
979
906
|
pointTokenAddress: Address;
|
|
@@ -1024,6 +951,20 @@ interface IssuerApiHandlersConfig {
|
|
|
1024
951
|
feeManager?: FeeManager;
|
|
1025
952
|
/** Required by `handlePools`; omit to disable the endpoint. */
|
|
1026
953
|
poolsProvider?: PoolsProvider;
|
|
954
|
+
/**
|
|
955
|
+
* Required by `handleClaim`; omit to disable the endpoint.
|
|
956
|
+
* Wires policy evaluation + ledger locking + MintRequest signing
|
|
957
|
+
* into a single atomic handler so callers cannot accidentally skip
|
|
958
|
+
* the policy check.
|
|
959
|
+
*/
|
|
960
|
+
claim?: {
|
|
961
|
+
policy: IPolicyEngine;
|
|
962
|
+
relayService: RelayService;
|
|
963
|
+
issuerSignerWallet: WalletClient;
|
|
964
|
+
batchExecutorAddress: Address;
|
|
965
|
+
/** How long to hold the off-chain lock while the UserOp is in flight. Default: 15 min. */
|
|
966
|
+
lockDurationMs?: number;
|
|
967
|
+
};
|
|
1027
968
|
}
|
|
1028
969
|
/**
|
|
1029
970
|
* Framework-agnostic HTTP handlers that match the endpoints a `PafiSDK`
|
|
@@ -1045,15 +986,12 @@ declare class IssuerApiHandlers {
|
|
|
1045
986
|
* validate the request's `pointTokenAddress` against this set.
|
|
1046
987
|
*/
|
|
1047
988
|
private readonly supportedTokens;
|
|
1048
|
-
/** First supported token — used as default when a handler doesn't
|
|
1049
|
-
* receive a `pointTokenAddress` in the request (shouldn't happen in
|
|
1050
|
-
* practice, but keeps type-narrowing happy). */
|
|
1051
|
-
private readonly defaultToken;
|
|
1052
989
|
private readonly chainId;
|
|
1053
990
|
private readonly contracts;
|
|
1054
991
|
private readonly pafiWebUrl?;
|
|
1055
992
|
private readonly feeManager?;
|
|
1056
993
|
private readonly poolsProvider?;
|
|
994
|
+
private readonly claim?;
|
|
1057
995
|
constructor(config: IssuerApiHandlersConfig);
|
|
1058
996
|
/** `GET /auth/nonce` */
|
|
1059
997
|
handleGetNonce(): Promise<ApiNonceResponse>;
|
|
@@ -1099,6 +1037,22 @@ declare class IssuerApiHandlers {
|
|
|
1099
1037
|
* mobile apps — no app store review needed.
|
|
1100
1038
|
*/
|
|
1101
1039
|
handleBuildConsentTypedData(userAddress: Address, request: ApiBuildConsentTypedDataRequest): Promise<ApiBuildConsentTypedDataResponse>;
|
|
1040
|
+
/**
|
|
1041
|
+
* `POST /claim`
|
|
1042
|
+
*
|
|
1043
|
+
* Policy gate + ledger lock + MintRequest signing in a single atomic
|
|
1044
|
+
* step. Returns an unsigned UserOp the frontend attaches paymaster data
|
|
1045
|
+
* to and submits via EIP-7702 + Bundler.
|
|
1046
|
+
*
|
|
1047
|
+
* Order of operations:
|
|
1048
|
+
* 1. Validate request fields.
|
|
1049
|
+
* 2. policy.evaluate() — throws if denied; cannot be bypassed.
|
|
1050
|
+
* 3. ledger.lockForMinting() — reserves the balance.
|
|
1051
|
+
* 4. Read on-chain mintRequestNonce + token name in parallel.
|
|
1052
|
+
* 5. relayService.prepareMint() — sign MintRequest + encode UserOp.
|
|
1053
|
+
* 6. On any error after step 3, release the lock before re-throwing.
|
|
1054
|
+
*/
|
|
1055
|
+
handleClaim(userAddress: Address, request: ApiClaimRequest): Promise<ApiClaimResponse>;
|
|
1102
1056
|
}
|
|
1103
1057
|
|
|
1104
1058
|
/**
|
|
@@ -1167,6 +1121,8 @@ interface PTRedeemHandlerConfig {
|
|
|
1167
1121
|
now?: () => number;
|
|
1168
1122
|
}
|
|
1169
1123
|
interface PTRedeemRequest {
|
|
1124
|
+
/** Address extracted from the verified JWT — must match `userAddress`. */
|
|
1125
|
+
authenticatedAddress: Address;
|
|
1170
1126
|
userAddress: Address;
|
|
1171
1127
|
amount: bigint;
|
|
1172
1128
|
/** ERC-4337 account nonce for the user's EOA. */
|
|
@@ -1183,8 +1139,8 @@ interface PTRedeemResponse {
|
|
|
1183
1139
|
signatureDeadline: bigint;
|
|
1184
1140
|
}
|
|
1185
1141
|
declare class PTRedeemError extends Error {
|
|
1186
|
-
code: "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED";
|
|
1187
|
-
constructor(code: "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED", message: string);
|
|
1142
|
+
code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED";
|
|
1143
|
+
constructor(code: "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED", message: string);
|
|
1188
1144
|
}
|
|
1189
1145
|
declare class PTRedeemHandler {
|
|
1190
1146
|
private readonly ledger;
|
|
@@ -1232,6 +1188,8 @@ interface TopUpRedemptionHandlerConfig {
|
|
|
1232
1188
|
pointTokenAddress: Address;
|
|
1233
1189
|
}
|
|
1234
1190
|
interface TopUpRedemptionRequest {
|
|
1191
|
+
/** Address extracted from the verified JWT — must match `userAddress`. */
|
|
1192
|
+
authenticatedAddress: Address;
|
|
1235
1193
|
userAddress: Address;
|
|
1236
1194
|
/** Total points the voucher redemption requires off-chain. */
|
|
1237
1195
|
requiredAmount: bigint;
|
|
@@ -1252,8 +1210,8 @@ type TopUpRedemptionResponse = {
|
|
|
1252
1210
|
redeem: PTRedeemResponse;
|
|
1253
1211
|
};
|
|
1254
1212
|
declare class TopUpRedemptionError extends Error {
|
|
1255
|
-
code: "INSUFFICIENT_ONCHAIN_BALANCE" | "LEDGER_NOT_SUPPORTED";
|
|
1256
|
-
constructor(code: "INSUFFICIENT_ONCHAIN_BALANCE" | "LEDGER_NOT_SUPPORTED", message: string);
|
|
1213
|
+
code: "UNAUTHORIZED" | "INSUFFICIENT_ONCHAIN_BALANCE" | "LEDGER_NOT_SUPPORTED";
|
|
1214
|
+
constructor(code: "UNAUTHORIZED" | "INSUFFICIENT_ONCHAIN_BALANCE" | "LEDGER_NOT_SUPPORTED", message: string);
|
|
1257
1215
|
}
|
|
1258
1216
|
declare class TopUpRedemptionHandler {
|
|
1259
1217
|
private readonly ledger;
|
|
@@ -1438,221 +1396,66 @@ declare class BalanceAggregator {
|
|
|
1438
1396
|
}
|
|
1439
1397
|
|
|
1440
1398
|
interface RetryConfig {
|
|
1441
|
-
/**
|
|
1442
|
-
* Max total attempts including the first try. Default: 1 (no retry).
|
|
1443
|
-
* Set to 3 to get 2 retries after the initial call.
|
|
1444
|
-
*
|
|
1445
|
-
* Only applies when the server error body carries `safeToRetry: true`
|
|
1446
|
-
* or the failure is a transient network/timeout error.
|
|
1447
|
-
*/
|
|
1448
1399
|
maxAttempts?: number;
|
|
1449
|
-
/**
|
|
1450
|
-
* Initial backoff delay in ms. Default: 500. Each subsequent retry
|
|
1451
|
-
* doubles this (exponential backoff) and adds ±20% jitter.
|
|
1452
|
-
*/
|
|
1453
1400
|
initialDelayMs?: number;
|
|
1454
|
-
/**
|
|
1455
|
-
* Hard ceiling for a single backoff (ms). Default: 10_000.
|
|
1456
|
-
*/
|
|
1457
1401
|
maxDelayMs?: number;
|
|
1458
|
-
/**
|
|
1459
|
-
* Upper bound on `retryAfter` from the server. If the server asks us
|
|
1460
|
-
* to wait longer than this (e.g. rate limit until UTC midnight), the
|
|
1461
|
-
* client gives up rather than blocking. Default: 30_000.
|
|
1462
|
-
*/
|
|
1463
1402
|
maxRetryAfterMs?: number;
|
|
1464
1403
|
}
|
|
1465
1404
|
interface PafiBackendConfig {
|
|
1466
|
-
/**
|
|
1467
|
-
* PAFI Backend API base URL. Example:
|
|
1468
|
-
* https://api.pacificfinance.org
|
|
1469
|
-
* https://staging-api.pacificfinance.org
|
|
1470
|
-
*/
|
|
1471
1405
|
url: string;
|
|
1472
|
-
/** PAFI-assigned issuer ID (e.g., "gg56"). Sent in X-Issuer-Id header. */
|
|
1473
1406
|
issuerId: string;
|
|
1474
|
-
/** Per-issuer API key (or JWT) for the Authorization header. */
|
|
1475
1407
|
apiKey: string;
|
|
1476
|
-
/** Optional fetch override for tests. */
|
|
1477
1408
|
fetchImpl?: typeof fetch;
|
|
1478
|
-
/**
|
|
1479
|
-
* Timeout (ms) for each request. Default: 10_000. PAFI Backend should
|
|
1480
|
-
* respond in <1s for the happy path; this is just the sanity bound.
|
|
1481
|
-
*/
|
|
1482
1409
|
timeoutMs?: number;
|
|
1483
|
-
/**
|
|
1484
|
-
* Retry policy for transient failures (5xx, 429, timeouts, network).
|
|
1485
|
-
* Omit or pass `{ maxAttempts: 1 }` to disable retry entirely.
|
|
1486
|
-
*/
|
|
1487
1410
|
retry?: RetryConfig;
|
|
1488
1411
|
}
|
|
1489
|
-
|
|
1490
|
-
* @deprecated Coinbase paymaster path — replaced by SponsorAuth in beta.8.
|
|
1491
|
-
* Kept for back-compat with consumers still on Coinbase. Will be removed
|
|
1492
|
-
* in 1.0.
|
|
1493
|
-
*/
|
|
1494
|
-
interface SponsorshipRequest {
|
|
1495
|
-
chainId: number;
|
|
1496
|
-
scenario: SponsorshipScenario;
|
|
1497
|
-
userOp: PartialUserOperation;
|
|
1498
|
-
target: {
|
|
1499
|
-
contract: Address;
|
|
1500
|
-
function: string;
|
|
1501
|
-
pointToken?: Address;
|
|
1502
|
-
};
|
|
1503
|
-
}
|
|
1504
|
-
/** @deprecated See `SponsorshipRequest`. Use `SponsorAuthResponse` in beta.8+. */
|
|
1505
|
-
interface SponsorshipResponse {
|
|
1506
|
-
paymaster: Address;
|
|
1507
|
-
paymasterData: Hex;
|
|
1508
|
-
paymasterVerificationGasLimit: bigint;
|
|
1509
|
-
paymasterPostOpGasLimit: bigint;
|
|
1510
|
-
expiresAt: number;
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
/**
|
|
1514
|
-
* Request body for `POST /sponsor-auth` against PAFI sponsor-relayer.
|
|
1515
|
-
* See `pafi-backend/docs/SPONSOR_AUTH_DESIGN.md` for the full spec.
|
|
1516
|
-
*
|
|
1517
|
-
* The endpoint:
|
|
1518
|
-
* 1. Authenticates user (JWT) + issuer (API key)
|
|
1519
|
-
* 2. Per-(user, scenario) rate-limits
|
|
1520
|
-
* 3. Per-issuer daily budget check
|
|
1521
|
-
* 4. Scenario-specific intent validation (mint cap, KYC, etc.)
|
|
1522
|
-
* 5. Allocates a unique nonce
|
|
1523
|
-
* 6. Signs the SponsorAuth payload via KMS-backed PAFI key
|
|
1524
|
-
* 7. Returns `{ sponsorAuth, payload }` for FE to forward to Privy
|
|
1525
|
-
*/
|
|
1526
|
-
interface SponsorAuthRequest {
|
|
1527
|
-
chainId: number;
|
|
1528
|
-
scenario: SponsorAuthScenario;
|
|
1529
|
-
/** The exact UserOp the FE intends to submit. callDataHash binds to this. */
|
|
1530
|
-
userOp: PartialUserOperation;
|
|
1531
|
-
/** Issuer ID this sponsorship is billed against (e.g. "gg56"). */
|
|
1532
|
-
issuerId: string;
|
|
1533
|
-
/**
|
|
1534
|
-
* Scenario-specific context for intent validation. The relayer reads
|
|
1535
|
-
* these to apply scenario rules — e.g. preValidateMint reads
|
|
1536
|
-
* pointToken + amount to check the issuer cap.
|
|
1537
|
-
*/
|
|
1538
|
-
context?: {
|
|
1539
|
-
pointToken?: Address;
|
|
1540
|
-
/** Decimal string representation (handles bigint over JSON). */
|
|
1541
|
-
amount?: string;
|
|
1542
|
-
brokerHash?: Hex;
|
|
1543
|
-
};
|
|
1544
|
-
}
|
|
1545
|
-
/**
|
|
1546
|
-
* Response from `POST /sponsor-auth`.
|
|
1547
|
-
*
|
|
1548
|
-
* `payload` is returned alongside `sponsorAuth` so the FE doesn't have
|
|
1549
|
-
* to reconstruct the message — just forwards both to Privy. Privy
|
|
1550
|
-
* verifier checks `signature` recovers to PAFI's registered pubkey
|
|
1551
|
-
* over `payload`.
|
|
1552
|
-
*/
|
|
1553
|
-
interface SponsorAuthResponse {
|
|
1554
|
-
/** EIP-712 signature, hex-encoded 65 bytes. */
|
|
1555
|
-
sponsorAuth: Hex;
|
|
1556
|
-
/** The payload that was signed. Pass to Privy alongside the signature. */
|
|
1557
|
-
payload: SponsorAuthPayload;
|
|
1558
|
-
}
|
|
1559
|
-
/**
|
|
1560
|
-
* Machine-readable error codes returned by PAFI sponsor-relayer.
|
|
1561
|
-
*
|
|
1562
|
-
* Source of truth: `apps/sponsor-relayer/src/**` (renamed from
|
|
1563
|
-
* paymaster-proxy in beta.8). Keep in sync.
|
|
1564
|
-
*/
|
|
1565
|
-
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" | "KMS_UNAVAILABLE" | "SPONSOR_AUTH_SIGNING_FAILED" | "PAYMASTER_REJECTED" | "PAYMASTER_UNAVAILABLE" | "PAYMASTER_TIMEOUT" | "CALLDATA_INVALID" | "CALLDATA_EMPTY_BATCH" | "TARGET_NOT_ALLOWLISTED" | "FUNCTION_NOT_ALLOWED" | "SCENARIO_MISMATCH" | "SCENARIO_DISABLED" | "BAD_REQUEST" | "INTERNAL_ERROR" | "TIMEOUT" | "NETWORK_ERROR";
|
|
1412
|
+
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 & {});
|
|
1566
1413
|
declare class PafiBackendError extends Error {
|
|
1567
1414
|
code: PafiBackendErrorCode;
|
|
1568
1415
|
httpStatus: number;
|
|
1569
1416
|
details?: unknown | undefined;
|
|
1570
|
-
/**
|
|
1571
|
-
* Seconds to wait before retry. Populated from the server body
|
|
1572
|
-
* (e.g. rate limit returns the number of seconds until UTC midnight).
|
|
1573
|
-
*/
|
|
1574
1417
|
readonly retryAfter?: number;
|
|
1575
|
-
/**
|
|
1576
|
-
* `safeToRetry` as reported by the server body. Prefer this over the
|
|
1577
|
-
* code-based heuristic when available — the server knows more about
|
|
1578
|
-
* whether the same request will succeed on retry.
|
|
1579
|
-
*/
|
|
1580
1418
|
private readonly serverSafeToRetry?;
|
|
1581
1419
|
constructor(code: PafiBackendErrorCode, message: string, httpStatus: number, details?: unknown | undefined, opts?: {
|
|
1582
1420
|
retryAfter?: number;
|
|
1583
1421
|
safeToRetry?: boolean;
|
|
1584
1422
|
});
|
|
1585
|
-
/**
|
|
1586
|
-
* Whether the caller can safely retry the same request.
|
|
1587
|
-
*
|
|
1588
|
-
* If the server provided `safeToRetry` in the body, trust that.
|
|
1589
|
-
* Otherwise fall back to a code-based heuristic.
|
|
1590
|
-
*/
|
|
1591
1423
|
get safeToRetry(): boolean;
|
|
1592
1424
|
}
|
|
1593
1425
|
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1426
|
+
interface SponsorshipUserOp {
|
|
1427
|
+
sender: Address;
|
|
1428
|
+
nonce: bigint;
|
|
1429
|
+
callData: Hex;
|
|
1430
|
+
callGasLimit: bigint;
|
|
1431
|
+
verificationGasLimit: bigint;
|
|
1432
|
+
preVerificationGas: bigint;
|
|
1433
|
+
maxFeePerGas: bigint;
|
|
1434
|
+
maxPriorityFeePerGas: bigint;
|
|
1435
|
+
}
|
|
1436
|
+
interface SponsorshipTarget {
|
|
1437
|
+
contract: Address;
|
|
1438
|
+
function: string;
|
|
1439
|
+
pointToken: Address;
|
|
1440
|
+
}
|
|
1441
|
+
interface SponsorshipRequest {
|
|
1442
|
+
chainId: number;
|
|
1443
|
+
scenario: string;
|
|
1444
|
+
userOp: SponsorshipUserOp;
|
|
1445
|
+
target: SponsorshipTarget;
|
|
1446
|
+
}
|
|
1447
|
+
interface SponsorshipResponse {
|
|
1448
|
+
paymaster: Address;
|
|
1449
|
+
paymasterData: Hex;
|
|
1450
|
+
paymasterVerificationGasLimit: bigint;
|
|
1451
|
+
paymasterPostOpGasLimit: bigint;
|
|
1452
|
+
expiresAt: number;
|
|
1453
|
+
}
|
|
1602
1454
|
declare class PafiBackendClient {
|
|
1603
|
-
private readonly
|
|
1604
|
-
private readonly issuerId;
|
|
1605
|
-
private readonly apiKey;
|
|
1606
|
-
private readonly fetchImpl;
|
|
1607
|
-
private readonly timeoutMs;
|
|
1608
|
-
private readonly retry;
|
|
1455
|
+
private readonly config;
|
|
1609
1456
|
constructor(config: PafiBackendConfig);
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
*
|
|
1613
|
-
* The relayer:
|
|
1614
|
-
* 1. Authenticates user (JWT) + issuer (API key)
|
|
1615
|
-
* 2. Per-(user, scenario) rate limit + per-issuer daily budget
|
|
1616
|
-
* 3. Scenario-specific intent validation (mint cap, KYC, etc.)
|
|
1617
|
-
* 4. Allocates nonce + signs SponsorAuth payload via KMS PAFI key
|
|
1618
|
-
* 5. Returns `{ sponsorAuth, payload }` for the FE to forward to
|
|
1619
|
-
* Privy's `signUserOperation({ sponsorAuth, payload })`.
|
|
1620
|
-
*
|
|
1621
|
-
* Retries on transient failures (5xx, timeouts, KMS unavailable,
|
|
1622
|
-
* rate-limit-with-retryAfter). 4xx that are not `safeToRetry` fail fast.
|
|
1623
|
-
*
|
|
1624
|
-
* See `pafi-backend/docs/SPONSOR_AUTH_DESIGN.md` for the full spec.
|
|
1625
|
-
*
|
|
1626
|
-
* @throws PafiBackendError on final failure after exhausting retries
|
|
1627
|
-
*/
|
|
1628
|
-
requestSponsorAuth(req: SponsorAuthRequest): Promise<SponsorAuthResponse>;
|
|
1629
|
-
/**
|
|
1630
|
-
* @deprecated Coinbase paymaster path — replaced by `requestSponsorAuth`
|
|
1631
|
-
* in beta.8. Will be removed in 1.0. Migrate by:
|
|
1632
|
-
* 1. Switch to `requestSponsorAuth` returning `{ sponsorAuth, payload }`
|
|
1633
|
-
* 2. Pass both to Privy `signUserOperation` instead of merging
|
|
1634
|
-
* paymasterData into the UserOp callData
|
|
1635
|
-
*/
|
|
1636
|
-
requestSponsorship(req: SponsorshipRequest): Promise<SponsorshipResponse>;
|
|
1637
|
-
private postWithRetry;
|
|
1638
|
-
/**
|
|
1639
|
-
* Pick the delay before the next retry.
|
|
1640
|
-
* - If the server sent `retryAfter` (seconds), honor it (capped by
|
|
1641
|
-
* `maxRetryAfterMs`) — returns null if the server wait exceeds the
|
|
1642
|
-
* cap, signalling the caller should give up.
|
|
1643
|
-
* - Otherwise: exponential backoff with ±20% jitter, capped at
|
|
1644
|
-
* `maxDelayMs`.
|
|
1645
|
-
*/
|
|
1646
|
-
private computeBackoff;
|
|
1647
|
-
private sleep;
|
|
1648
|
-
private post;
|
|
1649
|
-
/** JSON replacer that stringifies bigints. Paired with bigintReviver. */
|
|
1650
|
-
private bigintReplacer;
|
|
1651
|
-
/**
|
|
1652
|
-
* JSON reviver that coerces specific numeric-string fields back to
|
|
1653
|
-
* bigint. The server must send these fields as decimal strings.
|
|
1654
|
-
*/
|
|
1655
|
-
private bigintReviver;
|
|
1457
|
+
requestSponsorship(request: SponsorshipRequest): Promise<SponsorshipResponse>;
|
|
1458
|
+
private _doRequest;
|
|
1656
1459
|
}
|
|
1657
1460
|
|
|
1658
1461
|
/**
|
|
@@ -1691,8 +1494,20 @@ interface IssuerServiceConfig {
|
|
|
1691
1494
|
/** Passed straight to `jose` (`"24h"`, `"7d"`, …). Default `"24h"`. */
|
|
1692
1495
|
jwtExpiresIn?: string;
|
|
1693
1496
|
};
|
|
1694
|
-
|
|
1497
|
+
/**
|
|
1498
|
+
* Off-chain point ledger — the source of truth for user balances and
|
|
1499
|
+
* in-flight minting locks. Every issuer provides their own database-backed
|
|
1500
|
+
* implementation (Postgres, Redis, etc.) that implements `IPointLedger`.
|
|
1501
|
+
* The SDK does not ship a production ledger; each issuer's data model and
|
|
1502
|
+
* infrastructure are different.
|
|
1503
|
+
*/
|
|
1504
|
+
ledger: IPointLedger;
|
|
1505
|
+
/**
|
|
1506
|
+
* Policy engine — optional, defaults to `DefaultPolicyEngine` which checks
|
|
1507
|
+
* off-chain balance. Extend or replace to add KYC, volume caps, etc.
|
|
1508
|
+
*/
|
|
1695
1509
|
policy?: IPolicyEngine;
|
|
1510
|
+
/** Session store — optional, defaults to `MemorySessionStore` (dev/test only). */
|
|
1696
1511
|
sessionStore?: ISessionStore;
|
|
1697
1512
|
/**
|
|
1698
1513
|
* Fee management config. If omitted the `handleGasFee` endpoint will
|
|
@@ -1704,6 +1519,16 @@ interface IssuerServiceConfig {
|
|
|
1704
1519
|
* throws "not configured" at request time.
|
|
1705
1520
|
*/
|
|
1706
1521
|
poolsProvider?: PoolsProvider;
|
|
1522
|
+
/**
|
|
1523
|
+
* Enables `handleClaim`. The factory combines these with the shared
|
|
1524
|
+
* `policy` + `relayService` instances already wired by the factory.
|
|
1525
|
+
* Omit to disable the `/claim` endpoint.
|
|
1526
|
+
*/
|
|
1527
|
+
claim?: {
|
|
1528
|
+
issuerSignerWallet: WalletClient;
|
|
1529
|
+
batchExecutorAddress: Address;
|
|
1530
|
+
lockDurationMs?: number;
|
|
1531
|
+
};
|
|
1707
1532
|
indexer?: {
|
|
1708
1533
|
fromBlock?: bigint;
|
|
1709
1534
|
cursorStore?: IIndexerCursorStore;
|
|
@@ -1737,8 +1562,7 @@ interface IssuerService {
|
|
|
1737
1562
|
* Wire a fully-functional issuer service from a single config object.
|
|
1738
1563
|
*
|
|
1739
1564
|
* Defaults:
|
|
1740
|
-
* - `
|
|
1741
|
-
* - `sessionStore` → `MemorySessionStore`
|
|
1565
|
+
* - `sessionStore` → `MemorySessionStore` (dev/test only — replace in prod)
|
|
1742
1566
|
* - `policy` → `DefaultPolicyEngine({ ledger })`
|
|
1743
1567
|
* - `feeManager` → undefined (handleGasFee throws until configured)
|
|
1744
1568
|
* - `poolsProvider` → undefined (handlePools throws until configured)
|
|
@@ -1751,4 +1575,4 @@ declare function createIssuerService(config: IssuerServiceConfig): IssuerService
|
|
|
1751
1575
|
/** SDK package version — bumped on every release */
|
|
1752
1576
|
declare const PAFI_ISSUER_SDK_VERSION = "0.1.0";
|
|
1753
1577
|
|
|
1754
|
-
export { type ApiBuildConsentTypedDataRequest, type ApiBuildConsentTypedDataResponse, type ApiClaimAndSwapRequest, type ApiClaimAndSwapResponse, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, 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
|
|
1578
|
+
export { type ApiBuildConsentTypedDataRequest, type ApiBuildConsentTypedDataResponse, type ApiClaimAndSwapRequest, type ApiClaimAndSwapResponse, type ApiClaimRequest, type ApiClaimResponse, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, 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 };
|