@pafi-dev/issuer 0.2.0 → 0.3.0-alpha.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 +58 -0
- package/dist/index.cjs +532 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +418 -79
- package/dist/index.d.ts +418 -79
- package/dist/index.js +528 -81
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Address, Hex, PublicClient, Chain } from 'viem';
|
|
2
|
-
import { PointTokenDomainConfig, MintRequest, EIP712Signature, MintParams, SwapParams, ReceiverConsent, PathKey, PoolKey } from '@pafi-dev/core';
|
|
2
|
+
import { PointTokenDomainConfig, MintRequest, EIP712Signature, MintParams, SwapParams, ReceiverConsent, PathKey, PoolKey, SponsorshipScenario, PartialUserOperation } from '@pafi-dev/core';
|
|
3
3
|
export { encodeExtData } from '@pafi-dev/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -78,6 +78,24 @@ interface IPointLedger {
|
|
|
78
78
|
* supplied when the status is `MINTED`.
|
|
79
79
|
*/
|
|
80
80
|
updateMintStatus(lockId: string, status: MintingStatus, txHash?: Hex): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Reserve a pending off-chain credit before the burn tx is submitted.
|
|
83
|
+
*
|
|
84
|
+
* Returns a lockId that the burn indexer uses to correlate the
|
|
85
|
+
* on-chain burn event back to this credit request.
|
|
86
|
+
*
|
|
87
|
+
* Throws if the ledger doesn't support the reverse flow (legacy
|
|
88
|
+
* implementations) — callers should catch and fall back.
|
|
89
|
+
*/
|
|
90
|
+
reservePendingCredit?(userAddress: Address, amount: bigint, durationMs: number, tokenAddress?: Address): Promise<string>;
|
|
91
|
+
/**
|
|
92
|
+
* Finalize a reserved credit when the on-chain Burn event is seen by
|
|
93
|
+
* the BurnIndexer. Idempotent — safe to call multiple times with the
|
|
94
|
+
* same txHash (no double-credit).
|
|
95
|
+
*
|
|
96
|
+
* Throws if the lockId is unknown or already resolved.
|
|
97
|
+
*/
|
|
98
|
+
resolveCreditByBurnTx?(lockId: string, txHash: Hex): Promise<void>;
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
/**
|
|
@@ -109,6 +127,10 @@ declare class MemoryPointLedger implements IPointLedger {
|
|
|
109
127
|
releaseLock(lockId: string): Promise<void>;
|
|
110
128
|
deductBalance(userAddress: Address, amount: bigint, txHash: Hex, tokenAddress?: Address): Promise<void>;
|
|
111
129
|
updateMintStatus(lockId: string, status: MintingStatus, txHash?: Hex): Promise<void>;
|
|
130
|
+
private pendingCredits;
|
|
131
|
+
private nextCreditId;
|
|
132
|
+
reservePendingCredit(userAddress: Address, amount: bigint, durationMs: number, tokenAddress?: Address): Promise<string>;
|
|
133
|
+
resolveCreditByBurnTx(lockId: string, txHash: Hex): Promise<void>;
|
|
112
134
|
/**
|
|
113
135
|
* Auto-expire any PENDING lock past its expiry. Called lazily on every
|
|
114
136
|
* read/write so the in-memory state stays self-cleaning without a timer.
|
|
@@ -536,93 +558,76 @@ declare class RelayService {
|
|
|
536
558
|
* decide whether to release the ledger lock (`SUBMIT_FAILED` and
|
|
537
559
|
* `SIMULATION_FAILED` are safe to release; `TX_REVERTED` and `TIMEOUT`
|
|
538
560
|
* need manual review because the tx may still land).
|
|
561
|
+
*
|
|
562
|
+
* @deprecated Since 0.3.0 — will be replaced by `prepareMint()` +
|
|
563
|
+
* `prepareBurn()` in the v1.4 sponsored-UserOp flow. The SC team
|
|
564
|
+
* still needs to finalize Relayer v2 ABI before the replacements
|
|
565
|
+
* can ship (blocker B1). Kept for v0.2.x consumers. Removed in 2.0.
|
|
539
566
|
*/
|
|
540
567
|
submitMintAndSwap(params: SubmitMintAndSwapParams): Promise<RelayResult>;
|
|
541
568
|
}
|
|
542
569
|
|
|
543
570
|
interface FeeManagerConfig {
|
|
544
|
-
/** Provider used for gas price
|
|
571
|
+
/** Provider used for gas price reads. */
|
|
545
572
|
provider: PublicClient;
|
|
546
|
-
/** Operator wallet whose native balance the manager monitors. */
|
|
547
|
-
operatorWallet: OperatorWalletLike;
|
|
548
|
-
/** USDT token address on the target chain (used for rebalance swaps). */
|
|
549
|
-
usdtAddress: Address;
|
|
550
|
-
/** Wrapped-native token address (WETH on Base/Ethereum, WMATIC, etc). */
|
|
551
|
-
nativeWrappedAddress: Address;
|
|
552
573
|
/**
|
|
553
|
-
* Typical gas used by a
|
|
554
|
-
* manager multiplies this by current gas price to get
|
|
555
|
-
* then converts
|
|
574
|
+
* Typical gas used by a single sponsored UserOp. Default: 500_000.
|
|
575
|
+
* The manager multiplies this by current gas price to get native
|
|
576
|
+
* cost, then converts via the injected `quoteNativeToFee`.
|
|
556
577
|
*/
|
|
557
|
-
|
|
578
|
+
gasUnits?: bigint;
|
|
558
579
|
/**
|
|
559
|
-
* Safety margin applied
|
|
560
|
-
*
|
|
580
|
+
* Safety margin applied before charging the user, as basis points.
|
|
581
|
+
* 12_000 = 120%. Default: 12_000.
|
|
561
582
|
*/
|
|
562
583
|
gasPremiumBps?: number;
|
|
563
584
|
/**
|
|
564
|
-
*
|
|
565
|
-
* equivalent amount
|
|
566
|
-
*
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
*
|
|
571
|
-
* `rebalanceThresholdWei`, `rebalanceIfNeeded()` swaps `rebalanceUsdtAmount`
|
|
572
|
-
* worth of USDT into native. Both optional — omit to disable rebalancing.
|
|
573
|
-
*/
|
|
574
|
-
rebalanceThresholdWei?: bigint;
|
|
575
|
-
rebalanceUsdtAmount?: bigint;
|
|
576
|
-
/**
|
|
577
|
-
* Actual swap executor — the manager calls this when a rebalance is
|
|
578
|
-
* triggered. Injected so the SDK does not hard-code a DEX choice; the
|
|
579
|
-
* issuer wires it to the UniversalRouter (via `@pafi/core swap/`) or
|
|
580
|
-
* whatever liquidity venue they trust. Required iff the rebalance
|
|
581
|
-
* fields above are set.
|
|
585
|
+
* Quote function — given an amount of native wei, return the
|
|
586
|
+
* equivalent amount in the fee currency (PT raw units in v1.4,
|
|
587
|
+
* USDT 6-decimal in legacy v1.2 flows).
|
|
588
|
+
*
|
|
589
|
+
* Injected so the manager stays chain- and token-agnostic. Issuers
|
|
590
|
+
* wire it to `@pafi-dev/core` V4 quoting, a subgraph query, or an
|
|
591
|
+
* oracle feed.
|
|
582
592
|
*/
|
|
583
|
-
|
|
593
|
+
quoteNativeToFee: (amountNative: bigint) => Promise<bigint>;
|
|
584
594
|
}
|
|
585
595
|
/**
|
|
586
|
-
*
|
|
596
|
+
* Computes how much fee to collect from the user to cover the gas cost
|
|
597
|
+
* of a sponsored UserOp.
|
|
598
|
+
*
|
|
599
|
+
* ## v1.4 scope change
|
|
587
600
|
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
* 2. `rebalanceIfNeeded()` — when the operator's native balance gets
|
|
591
|
-
* low, swap some of the accumulated USDT fee back into native gas
|
|
592
|
-
* token so the operator never runs dry.
|
|
601
|
+
* The fee is now expressed in the **fee currency** chosen by the caller
|
|
602
|
+
* (PT for mint/burn, USDT for swap/perp_deposit) — not hardcoded to USDT.
|
|
593
603
|
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
604
|
+
* **Operator rebalancing is gone.** In v1.4 the operator no longer holds
|
|
605
|
+
* ETH directly — gas is paid by Coinbase Paymaster via the paymaster-proxy
|
|
606
|
+
* (see [SPONSORED_PATH_FLOW.md]). The fee collected here is an
|
|
607
|
+
* application-level ERC-20 transfer inside the same UserOp batch, not a
|
|
608
|
+
* reimbursement to a wallet that needs topping up.
|
|
609
|
+
*
|
|
610
|
+
* `rebalanceIfNeeded()` and `swapUsdtToNative` were removed in 0.3.0.
|
|
597
611
|
*/
|
|
598
612
|
declare class FeeManager {
|
|
599
613
|
private readonly provider;
|
|
600
|
-
private readonly
|
|
601
|
-
private readonly mintAndSwapGasUnits;
|
|
614
|
+
private readonly gasUnits;
|
|
602
615
|
private readonly gasPremiumBps;
|
|
603
|
-
private readonly
|
|
604
|
-
private readonly rebalanceThresholdWei?;
|
|
605
|
-
private readonly rebalanceUsdtAmount?;
|
|
606
|
-
private readonly swapUsdtToNative?;
|
|
616
|
+
private readonly quoteNativeToFee;
|
|
607
617
|
constructor(config: FeeManagerConfig);
|
|
608
618
|
/**
|
|
609
|
-
* Estimate the
|
|
610
|
-
*
|
|
619
|
+
* Estimate the fee (in the caller's fee currency) to charge for the
|
|
620
|
+
* next sponsored UserOp:
|
|
611
621
|
*
|
|
612
|
-
* nativeCost
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
*/
|
|
616
|
-
estimateGasFee(): Promise<bigint>;
|
|
617
|
-
/**
|
|
618
|
-
* Check the operator's native balance and, if it has dropped below the
|
|
619
|
-
* configured threshold, trigger a USDT→native rebalance via the injected
|
|
620
|
-
* `swapUsdtToNative` function.
|
|
622
|
+
* nativeCost = gasUnits × gasPrice
|
|
623
|
+
* withPremium = nativeCost × premiumBps / 10_000
|
|
624
|
+
* fee = quoteNativeToFee(withPremium)
|
|
621
625
|
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
626
|
+
* For backward compatibility with v0.2.x code that reads `gasFeeUsdt`
|
|
627
|
+
* from the response, the name `estimateGasFee` is kept — but the
|
|
628
|
+
* currency depends on how the caller wired `quoteNativeToFee`.
|
|
624
629
|
*/
|
|
625
|
-
|
|
630
|
+
estimateGasFee(): Promise<bigint>;
|
|
626
631
|
}
|
|
627
632
|
|
|
628
633
|
/**
|
|
@@ -751,6 +756,12 @@ declare class MintingGateway {
|
|
|
751
756
|
private readonly now;
|
|
752
757
|
private readonly defaultLockBufferMs;
|
|
753
758
|
constructor(config: MintingGatewayConfig);
|
|
759
|
+
/**
|
|
760
|
+
* @deprecated Since 0.3.0 — will be renamed to `processMint()` once
|
|
761
|
+
* the SC team finalizes Relayer v2 ABI. The new flow drops the
|
|
762
|
+
* swap steps entirely (no more single-call mint+swap); users swap
|
|
763
|
+
* separately on PAFI Web. Kept here for v0.2.x consumers. Removed in 2.0.
|
|
764
|
+
*/
|
|
754
765
|
processMintAndCashOut(request: MintAndCashOutRequest): Promise<MintAndCashOutResponse>;
|
|
755
766
|
private computeLockDurationMs;
|
|
756
767
|
/**
|
|
@@ -782,6 +793,16 @@ interface MintEvent {
|
|
|
782
793
|
/** Log index within the tx, for deterministic ordering */
|
|
783
794
|
logIndex: number;
|
|
784
795
|
}
|
|
796
|
+
/** Decoded Transfer(from=user → 0x0) event used to finalize a burn-for-credit. */
|
|
797
|
+
interface BurnEvent {
|
|
798
|
+
/** The burner — user whose PT was burned. */
|
|
799
|
+
from: Address;
|
|
800
|
+
/** Amount burned. */
|
|
801
|
+
amount: bigint;
|
|
802
|
+
blockNumber: bigint;
|
|
803
|
+
txHash: Hex;
|
|
804
|
+
logIndex: number;
|
|
805
|
+
}
|
|
785
806
|
/**
|
|
786
807
|
* Cursor persistence interface — the indexer reports the next block
|
|
787
808
|
* number it is about to process so the caller can write it to Redis /
|
|
@@ -901,10 +922,102 @@ declare class PointIndexer {
|
|
|
901
922
|
private finalize;
|
|
902
923
|
}
|
|
903
924
|
|
|
925
|
+
interface BurnIndexerConfig {
|
|
926
|
+
provider: PublicClient;
|
|
927
|
+
pointTokenAddress: Address;
|
|
928
|
+
ledger: IPointLedger;
|
|
929
|
+
/** Block to start from on first run. Ignored if cursor store has value. */
|
|
930
|
+
fromBlock?: bigint;
|
|
931
|
+
cursorStore?: IIndexerCursorStore;
|
|
932
|
+
/**
|
|
933
|
+
* Reorg safety — only treat events as final after this many
|
|
934
|
+
* confirmations. Default: 3.
|
|
935
|
+
*/
|
|
936
|
+
confirmations?: number;
|
|
937
|
+
/** Blocks per getLogs call. Default: 2000. */
|
|
938
|
+
batchSize?: number;
|
|
939
|
+
/** Polling interval (ms). Default: 5000. */
|
|
940
|
+
pollIntervalMs?: number;
|
|
941
|
+
now?: () => number;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Mirror of `PointIndexer` for the reverse direction — watches
|
|
945
|
+
* `Transfer(user → 0x0)` events (ERC-20 burns) on the PointToken
|
|
946
|
+
* contract and finalizes pending off-chain credits.
|
|
947
|
+
*
|
|
948
|
+
* Finalization flow:
|
|
949
|
+
* 1. For each Burn event at `{from, amount, txHash}`:
|
|
950
|
+
* 2. Call `ledger.resolveCreditByBurnTx(lockId, txHash)` where `lockId`
|
|
951
|
+
* is resolved by the caller's `onMatchCredit` hook or a
|
|
952
|
+
* ledger-specific lookup. The SDK does not prescribe the matching
|
|
953
|
+
* strategy — issuers with a Postgres ledger can JOIN by
|
|
954
|
+
* `(from, amount, status=PENDING)`; the in-memory ledger matches
|
|
955
|
+
* by `lockId` supplied out-of-band.
|
|
956
|
+
*
|
|
957
|
+
* When no pending credit matches an observed Burn event, the indexer
|
|
958
|
+
* logs + skips — **it never credits off-chain** from a Burn that was
|
|
959
|
+
* not first reserved via `reservePendingCredit()`. This prevents
|
|
960
|
+
* spurious credits from one-off admin burns or direct burn calls
|
|
961
|
+
* outside the issuer SDK.
|
|
962
|
+
*/
|
|
963
|
+
declare class BurnIndexer {
|
|
964
|
+
private readonly provider;
|
|
965
|
+
private readonly pointTokenAddress;
|
|
966
|
+
private readonly ledger;
|
|
967
|
+
private readonly cursorStore;
|
|
968
|
+
private readonly startBlock;
|
|
969
|
+
private readonly confirmations;
|
|
970
|
+
private readonly batchSize;
|
|
971
|
+
private readonly pollIntervalMs;
|
|
972
|
+
/**
|
|
973
|
+
* Caller-supplied matcher. Return the lockId to resolve for a given
|
|
974
|
+
* burn event, or `undefined` to skip. Runs synchronously via the
|
|
975
|
+
* ledger's query path.
|
|
976
|
+
*
|
|
977
|
+
* Default: try `ledger.resolveCreditByBurnTx` keyed on a synthetic
|
|
978
|
+
* lock id `burn-${from}-${amount}` — the in-memory ledger assigns
|
|
979
|
+
* incrementing IDs so callers with the memory ledger must provide a
|
|
980
|
+
* custom matcher. Real DB-backed ledgers override this to JOIN on
|
|
981
|
+
* their `pending_credits` table.
|
|
982
|
+
*/
|
|
983
|
+
matchLockId: (event: BurnEvent) => Promise<string | undefined>;
|
|
984
|
+
private running;
|
|
985
|
+
private timer;
|
|
986
|
+
constructor(config: BurnIndexerConfig);
|
|
987
|
+
start(): void;
|
|
988
|
+
stop(): void;
|
|
989
|
+
tick(): Promise<void>;
|
|
990
|
+
private scheduleNext;
|
|
991
|
+
/**
|
|
992
|
+
* Scan `[from, to]` inclusive for burn events. Callers can drive this
|
|
993
|
+
* directly to backfill a specific range without `start()`. Cursor is
|
|
994
|
+
* advanced to `to + 1` on completion.
|
|
995
|
+
*/
|
|
996
|
+
processBlockRange(from: bigint, to: bigint): Promise<void>;
|
|
997
|
+
private decodeBurnEvents;
|
|
998
|
+
/**
|
|
999
|
+
* Resolve a matching pending credit for this burn event and call
|
|
1000
|
+
* `ledger.resolveCreditByBurnTx(lockId, txHash)`. If no match found,
|
|
1001
|
+
* log + skip.
|
|
1002
|
+
*/
|
|
1003
|
+
private finalize;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
904
1006
|
interface ApiConfigResponse {
|
|
905
1007
|
chainId: number;
|
|
906
1008
|
contracts: {
|
|
1009
|
+
/**
|
|
1010
|
+
* Legacy single-token field — kept for backward compat with v0.1.x
|
|
1011
|
+
* frontends. Prefer `pointTokens` for multi-token issuers.
|
|
1012
|
+
*/
|
|
907
1013
|
pointToken?: Address;
|
|
1014
|
+
/**
|
|
1015
|
+
* All supported PointToken addresses (v0.2.0+). Single-token issuers
|
|
1016
|
+
* will have one entry that matches `pointToken`. Multi-token
|
|
1017
|
+
* issuers expose the full list here so the frontend can render a
|
|
1018
|
+
* token picker.
|
|
1019
|
+
*/
|
|
1020
|
+
pointTokens?: Address[];
|
|
908
1021
|
relay?: Address;
|
|
909
1022
|
issuerRegistry?: Address;
|
|
910
1023
|
pointTokenFactory?: Address;
|
|
@@ -912,6 +1025,17 @@ interface ApiConfigResponse {
|
|
|
912
1025
|
poolManager?: Address;
|
|
913
1026
|
usdt?: Address;
|
|
914
1027
|
};
|
|
1028
|
+
/**
|
|
1029
|
+
* Absolute URL that the Issuer App opens after a successful claim to
|
|
1030
|
+
* let the user swap PT → USDT or deposit into the perp DEX on PAFI
|
|
1031
|
+
* Web. Mobile opens this in an in-app browser
|
|
1032
|
+
* (SFSafariViewController / Chrome Custom Tabs). Desktop opens in a
|
|
1033
|
+
* popup. See [MOBILE_SDK_INTEGRATION.md] "PAFI Web Handoff" section.
|
|
1034
|
+
*
|
|
1035
|
+
* Optional — if omitted, the Issuer App should hide the "Open PAFI"
|
|
1036
|
+
* button.
|
|
1037
|
+
*/
|
|
1038
|
+
pafiWebUrl?: string;
|
|
915
1039
|
}
|
|
916
1040
|
interface ApiNonceResponse {
|
|
917
1041
|
nonce: string;
|
|
@@ -967,6 +1091,7 @@ interface ApiUserResponse {
|
|
|
967
1091
|
balance: bigint;
|
|
968
1092
|
isMinter: boolean;
|
|
969
1093
|
}
|
|
1094
|
+
/** @deprecated Since 0.3.0 — use `ApiClaimRequest` (mint-only) instead. Removed in 2.0. */
|
|
970
1095
|
interface ApiClaimAndSwapRequest {
|
|
971
1096
|
chainId: number;
|
|
972
1097
|
pointTokenAddress: Address;
|
|
@@ -985,6 +1110,7 @@ interface ApiClaimAndSwapRequest {
|
|
|
985
1110
|
/** Unix seconds. */
|
|
986
1111
|
swapDeadline: bigint;
|
|
987
1112
|
}
|
|
1113
|
+
/** @deprecated Since 0.3.0 — use `ApiClaimResponse` instead. Removed in 2.0. */
|
|
988
1114
|
interface ApiClaimAndSwapResponse {
|
|
989
1115
|
txHash: Hex;
|
|
990
1116
|
lockId: string;
|
|
@@ -1032,6 +1158,12 @@ interface IssuerApiHandlersConfig {
|
|
|
1032
1158
|
pointTokenAddresses?: Address[];
|
|
1033
1159
|
chainId: number;
|
|
1034
1160
|
contracts: ApiConfigResponse["contracts"];
|
|
1161
|
+
/**
|
|
1162
|
+
* Optional — URL that the Issuer App opens for PT→USDT swap or perp
|
|
1163
|
+
* deposit after a successful claim. Surfaced in `/config` response.
|
|
1164
|
+
* See [MOBILE_SDK_INTEGRATION.md] "PAFI Web Handoff".
|
|
1165
|
+
*/
|
|
1166
|
+
pafiWebUrl?: string;
|
|
1035
1167
|
/** Required by `handleGasFee`; omit to disable the endpoint. */
|
|
1036
1168
|
feeManager?: FeeManager;
|
|
1037
1169
|
/** Required by `handlePools`; omit to disable the endpoint. */
|
|
@@ -1065,6 +1197,7 @@ declare class IssuerApiHandlers {
|
|
|
1065
1197
|
private readonly defaultToken;
|
|
1066
1198
|
private readonly chainId;
|
|
1067
1199
|
private readonly contracts;
|
|
1200
|
+
private readonly pafiWebUrl?;
|
|
1068
1201
|
private readonly feeManager?;
|
|
1069
1202
|
private readonly poolsProvider?;
|
|
1070
1203
|
constructor(config: IssuerApiHandlersConfig);
|
|
@@ -1115,8 +1248,14 @@ declare class IssuerApiHandlers {
|
|
|
1115
1248
|
/**
|
|
1116
1249
|
* `POST /claim-and-swap`
|
|
1117
1250
|
*
|
|
1118
|
-
*
|
|
1119
|
-
*
|
|
1251
|
+
* @deprecated Since 0.3.0 — the single-call mint-then-swap flow is
|
|
1252
|
+
* retired in v1.4. Use the new `handleClaim()` (mint only) and let
|
|
1253
|
+
* the user swap separately on PAFI Web. See
|
|
1254
|
+
* [V1.4_V1.5_OVERVIEW.md §4] for the new scenario model. Will be
|
|
1255
|
+
* removed in 2.0.
|
|
1256
|
+
*
|
|
1257
|
+
* Legacy behavior: the terminal handler forwards the verified
|
|
1258
|
+
* consent to the MintingGateway, which runs the 11-step flow.
|
|
1120
1259
|
*/
|
|
1121
1260
|
handleClaimAndSwap(userAddress: Address, request: ApiClaimAndSwapRequest): Promise<ApiClaimAndSwapResponse>;
|
|
1122
1261
|
}
|
|
@@ -1219,26 +1358,29 @@ interface SubgraphNativeUsdtQuoterConfig {
|
|
|
1219
1358
|
now?: () => number;
|
|
1220
1359
|
}
|
|
1221
1360
|
/**
|
|
1222
|
-
* Create a
|
|
1223
|
-
* `Bundle.ethPriceUSD`.
|
|
1361
|
+
* Create a native→USDT quoter backed by the PAFI subgraph's
|
|
1362
|
+
* `Bundle.ethPriceUSD`. The returned function has the shape
|
|
1363
|
+
* `(amountNative: bigint) => Promise<bigint>` and can be passed as
|
|
1364
|
+
* `quoteNativeToFee` to `FeeManager` — in v1.4 the fee currency
|
|
1365
|
+
* is configured at the integration layer, not hardcoded here.
|
|
1224
1366
|
*
|
|
1225
|
-
* Used by `FeeManager.estimateGasFee()` to convert the
|
|
1226
|
-
*
|
|
1227
|
-
* precision is not critical
|
|
1228
|
-
* the
|
|
1367
|
+
* Used by `FeeManager.estimateGasFee()` to convert the gas cost into
|
|
1368
|
+
* an ERC-20 amount charged as part of the sponsored UserOp batch.
|
|
1369
|
+
* Price precision is not critical — a 1-2% drift is acceptable since
|
|
1370
|
+
* the fee manager applies a `gasPremiumBps` buffer.
|
|
1229
1371
|
*
|
|
1230
|
-
* The result is cached in-process with a short TTL (default 30s). If
|
|
1231
|
-
* subgraph is unreachable, falls back to `fallbackEthPriceUsd` so
|
|
1232
|
-
* estimation doesn't block
|
|
1372
|
+
* The result is cached in-process with a short TTL (default 30s). If
|
|
1373
|
+
* the subgraph is unreachable, falls back to `fallbackEthPriceUsd` so
|
|
1374
|
+
* gas estimation doesn't block user flow during a subgraph outage.
|
|
1233
1375
|
*
|
|
1234
1376
|
* @example
|
|
1235
1377
|
* ```ts
|
|
1236
|
-
* import { createSubgraphNativeUsdtQuoter, createIssuerService } from "@pafi/issuer";
|
|
1378
|
+
* import { createSubgraphNativeUsdtQuoter, createIssuerService } from "@pafi-dev/issuer";
|
|
1237
1379
|
*
|
|
1238
1380
|
* const service = createIssuerService({
|
|
1239
1381
|
* // ...other config
|
|
1240
1382
|
* fee: {
|
|
1241
|
-
*
|
|
1383
|
+
* quoteNativeToFee: createSubgraphNativeUsdtQuoter({
|
|
1242
1384
|
* subgraphUrl: "https://graph.pacificfinance.org/subgraphs/name/pafi",
|
|
1243
1385
|
* }),
|
|
1244
1386
|
* },
|
|
@@ -1247,6 +1389,204 @@ interface SubgraphNativeUsdtQuoterConfig {
|
|
|
1247
1389
|
*/
|
|
1248
1390
|
declare function createSubgraphNativeUsdtQuoter(config: SubgraphNativeUsdtQuoterConfig): (amountNative: bigint) => Promise<bigint>;
|
|
1249
1391
|
|
|
1392
|
+
/**
|
|
1393
|
+
* Combined off-chain + on-chain balance for a single user / token pair.
|
|
1394
|
+
*
|
|
1395
|
+
* - `offChain` — the issuer's ledger balance (available, excluding locks)
|
|
1396
|
+
* - `onChain` — the user's ERC-20 balance from `PointToken.balanceOf`
|
|
1397
|
+
* - `total` — `offChain + onChain` (what the Issuer App displays)
|
|
1398
|
+
*/
|
|
1399
|
+
interface CombinedBalance {
|
|
1400
|
+
offChain: bigint;
|
|
1401
|
+
onChain: bigint;
|
|
1402
|
+
total: bigint;
|
|
1403
|
+
}
|
|
1404
|
+
interface BalanceAggregatorConfig {
|
|
1405
|
+
provider: PublicClient;
|
|
1406
|
+
ledger: IPointLedger;
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* v1.4 utility — aggregates off-chain + on-chain point balances into a
|
|
1410
|
+
* single view for the "combined balance" UI in the Issuer App.
|
|
1411
|
+
*
|
|
1412
|
+
* The `/user` API handler uses this internally; the helper is exposed
|
|
1413
|
+
* publicly so Issuer Apps can call it directly without going through
|
|
1414
|
+
* the HTTP layer (e.g., for server-rendered pages or admin dashboards).
|
|
1415
|
+
*
|
|
1416
|
+
* See [REQUIREMENTS_V2.md] §1 — "The Issuer App displays a combined
|
|
1417
|
+
* balance (off-chain points + on-chain PT) and does not surface USDT."
|
|
1418
|
+
*/
|
|
1419
|
+
declare class BalanceAggregator {
|
|
1420
|
+
private readonly provider;
|
|
1421
|
+
private readonly ledger;
|
|
1422
|
+
constructor(config: BalanceAggregatorConfig);
|
|
1423
|
+
/**
|
|
1424
|
+
* Combined balance for a single (user, token) pair. Fetches off-chain
|
|
1425
|
+
* + on-chain in parallel.
|
|
1426
|
+
*/
|
|
1427
|
+
getCombinedBalance(user: Address, pointToken: Address): Promise<CombinedBalance>;
|
|
1428
|
+
/**
|
|
1429
|
+
* Combined balance for multiple tokens owned by the same user. Runs
|
|
1430
|
+
* all lookups in parallel. Returns a Map keyed by the token address
|
|
1431
|
+
* (same casing as supplied — caller should normalize if needed).
|
|
1432
|
+
*/
|
|
1433
|
+
getCombinedBalanceMulti(user: Address, pointTokens: Address[]): Promise<Map<Address, CombinedBalance>>;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
interface RetryConfig {
|
|
1437
|
+
/**
|
|
1438
|
+
* Max total attempts including the first try. Default: 1 (no retry).
|
|
1439
|
+
* Set to 3 to get 2 retries after the initial call.
|
|
1440
|
+
*
|
|
1441
|
+
* Only applies when the server error body carries `safeToRetry: true`
|
|
1442
|
+
* or the failure is a transient network/timeout error.
|
|
1443
|
+
*/
|
|
1444
|
+
maxAttempts?: number;
|
|
1445
|
+
/**
|
|
1446
|
+
* Initial backoff delay in ms. Default: 500. Each subsequent retry
|
|
1447
|
+
* doubles this (exponential backoff) and adds ±20% jitter.
|
|
1448
|
+
*/
|
|
1449
|
+
initialDelayMs?: number;
|
|
1450
|
+
/**
|
|
1451
|
+
* Hard ceiling for a single backoff (ms). Default: 10_000.
|
|
1452
|
+
*/
|
|
1453
|
+
maxDelayMs?: number;
|
|
1454
|
+
/**
|
|
1455
|
+
* Upper bound on `retryAfter` from the server. If the server asks us
|
|
1456
|
+
* to wait longer than this (e.g. rate limit until UTC midnight), the
|
|
1457
|
+
* client gives up rather than blocking. Default: 30_000.
|
|
1458
|
+
*/
|
|
1459
|
+
maxRetryAfterMs?: number;
|
|
1460
|
+
}
|
|
1461
|
+
interface PafiBackendConfig {
|
|
1462
|
+
/**
|
|
1463
|
+
* PAFI Backend API base URL. Example:
|
|
1464
|
+
* https://api.pacificfinance.org
|
|
1465
|
+
* https://staging-api.pacificfinance.org
|
|
1466
|
+
*/
|
|
1467
|
+
url: string;
|
|
1468
|
+
/** PAFI-assigned issuer ID (e.g., "gg56"). Sent in X-Issuer-Id header. */
|
|
1469
|
+
issuerId: string;
|
|
1470
|
+
/** Per-issuer API key (or JWT) for the Authorization header. */
|
|
1471
|
+
apiKey: string;
|
|
1472
|
+
/** Optional fetch override for tests. */
|
|
1473
|
+
fetchImpl?: typeof fetch;
|
|
1474
|
+
/**
|
|
1475
|
+
* Timeout (ms) for each request. Default: 10_000. PAFI Backend should
|
|
1476
|
+
* respond in <1s for the happy path; this is just the sanity bound.
|
|
1477
|
+
*/
|
|
1478
|
+
timeoutMs?: number;
|
|
1479
|
+
/**
|
|
1480
|
+
* Retry policy for transient failures (5xx, 429, timeouts, network).
|
|
1481
|
+
* Omit or pass `{ maxAttempts: 1 }` to disable retry entirely.
|
|
1482
|
+
*/
|
|
1483
|
+
retry?: RetryConfig;
|
|
1484
|
+
}
|
|
1485
|
+
/** Paired with `POST /paymaster/sponsor`. See SPONSORED_PATH_FLOW.md §4.1 */
|
|
1486
|
+
interface SponsorshipRequest {
|
|
1487
|
+
chainId: number;
|
|
1488
|
+
scenario: SponsorshipScenario;
|
|
1489
|
+
userOp: PartialUserOperation;
|
|
1490
|
+
target: {
|
|
1491
|
+
/** The allowlisted contract this batch call targets. */
|
|
1492
|
+
contract: Address;
|
|
1493
|
+
/** Function selector / name — validated against allowlist. */
|
|
1494
|
+
function: string;
|
|
1495
|
+
/** The PointToken involved (for scenario context). */
|
|
1496
|
+
pointToken?: Address;
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
interface SponsorshipResponse {
|
|
1500
|
+
paymaster: Address;
|
|
1501
|
+
paymasterData: Hex;
|
|
1502
|
+
paymasterVerificationGasLimit: bigint;
|
|
1503
|
+
paymasterPostOpGasLimit: bigint;
|
|
1504
|
+
/** Unix seconds when this sponsorship expires. Re-request after. */
|
|
1505
|
+
expiresAt: number;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Machine-readable error codes returned by PAFI Backend.
|
|
1509
|
+
*
|
|
1510
|
+
* Source of truth: `apps/paymaster-proxy` `CalldataValidationError`,
|
|
1511
|
+
* `RateLimitError`, `CoinbaseClientError`. Keep in sync.
|
|
1512
|
+
*/
|
|
1513
|
+
type PafiBackendErrorCode = "MISSING_ISSUER_ID" | "MISSING_API_KEY" | "ISSUER_UNAUTHORIZED" | "CALLDATA_INVALID" | "CALLDATA_EMPTY_BATCH" | "TARGET_NOT_ALLOWLISTED" | "FUNCTION_NOT_ALLOWED" | "SCENARIO_MISMATCH" | "SCENARIO_DISABLED" | "RATE_LIMIT_EXCEEDED" | "RATE_LIMIT_EXCEEDED_DAILY" | "RATE_LIMIT_EXCEEDED_PER_USER" | "RATE_LIMITER_UNAVAILABLE" | "PAYMASTER_REJECTED" | "PAYMASTER_UNAVAILABLE" | "PAYMASTER_TIMEOUT" | "BAD_REQUEST" | "INTERNAL_ERROR" | "TIMEOUT" | "NETWORK_ERROR";
|
|
1514
|
+
declare class PafiBackendError extends Error {
|
|
1515
|
+
code: PafiBackendErrorCode;
|
|
1516
|
+
httpStatus: number;
|
|
1517
|
+
details?: unknown | undefined;
|
|
1518
|
+
/**
|
|
1519
|
+
* Seconds to wait before retry. Populated from the server body
|
|
1520
|
+
* (e.g. rate limit returns the number of seconds until UTC midnight).
|
|
1521
|
+
*/
|
|
1522
|
+
readonly retryAfter?: number;
|
|
1523
|
+
/**
|
|
1524
|
+
* `safeToRetry` as reported by the server body. Prefer this over the
|
|
1525
|
+
* code-based heuristic when available — the server knows more about
|
|
1526
|
+
* whether the same request will succeed on retry.
|
|
1527
|
+
*/
|
|
1528
|
+
private readonly serverSafeToRetry?;
|
|
1529
|
+
constructor(code: PafiBackendErrorCode, message: string, httpStatus: number, details?: unknown | undefined, opts?: {
|
|
1530
|
+
retryAfter?: number;
|
|
1531
|
+
safeToRetry?: boolean;
|
|
1532
|
+
});
|
|
1533
|
+
/**
|
|
1534
|
+
* Whether the caller can safely retry the same request.
|
|
1535
|
+
*
|
|
1536
|
+
* If the server provided `safeToRetry` in the body, trust that.
|
|
1537
|
+
* Otherwise fall back to a code-based heuristic.
|
|
1538
|
+
*/
|
|
1539
|
+
get safeToRetry(): boolean;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* HTTP client for the PAFI Backend paymaster proxy service. See
|
|
1544
|
+
* [SPONSORED_PATH_FLOW.md] for the full flow + API contract.
|
|
1545
|
+
*
|
|
1546
|
+
* This client sits between `@pafi/issuer`'s RelayService and the
|
|
1547
|
+
* PAFI Backend. It does NOT talk to Coinbase Paymaster directly —
|
|
1548
|
+
* PAFI Backend holds that integration.
|
|
1549
|
+
*/
|
|
1550
|
+
declare class PafiBackendClient {
|
|
1551
|
+
private readonly url;
|
|
1552
|
+
private readonly issuerId;
|
|
1553
|
+
private readonly apiKey;
|
|
1554
|
+
private readonly fetchImpl;
|
|
1555
|
+
private readonly timeoutMs;
|
|
1556
|
+
private readonly retry;
|
|
1557
|
+
constructor(config: PafiBackendConfig);
|
|
1558
|
+
/**
|
|
1559
|
+
* Request paymaster sponsorship for a pre-built UserOperation.
|
|
1560
|
+
* See [SPONSORED_PATH_FLOW.md §4.1] for the API contract.
|
|
1561
|
+
*
|
|
1562
|
+
* Retries automatically on transient failures (5xx, timeouts, network
|
|
1563
|
+
* errors, and errors the server flags with `safeToRetry: true`) up to
|
|
1564
|
+
* `retry.maxAttempts`. 4xx errors that are not `safeToRetry` fail fast.
|
|
1565
|
+
*
|
|
1566
|
+
* @throws PafiBackendError on final failure after exhausting retries
|
|
1567
|
+
*/
|
|
1568
|
+
requestSponsorship(req: SponsorshipRequest): Promise<SponsorshipResponse>;
|
|
1569
|
+
private postWithRetry;
|
|
1570
|
+
/**
|
|
1571
|
+
* Pick the delay before the next retry.
|
|
1572
|
+
* - If the server sent `retryAfter` (seconds), honor it (capped by
|
|
1573
|
+
* `maxRetryAfterMs`) — returns null if the server wait exceeds the
|
|
1574
|
+
* cap, signalling the caller should give up.
|
|
1575
|
+
* - Otherwise: exponential backoff with ±20% jitter, capped at
|
|
1576
|
+
* `maxDelayMs`.
|
|
1577
|
+
*/
|
|
1578
|
+
private computeBackoff;
|
|
1579
|
+
private sleep;
|
|
1580
|
+
private post;
|
|
1581
|
+
/** JSON replacer that stringifies bigints. Paired with bigintReviver. */
|
|
1582
|
+
private bigintReplacer;
|
|
1583
|
+
/**
|
|
1584
|
+
* JSON reviver that coerces specific numeric-string fields back to
|
|
1585
|
+
* bigint. The server must send these fields as decimal strings.
|
|
1586
|
+
*/
|
|
1587
|
+
private bigintReviver;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1250
1590
|
/**
|
|
1251
1591
|
* Top-level configuration for `createIssuerService`. Everything except
|
|
1252
1592
|
* the chain metadata, wallets, auth secret, and `signer` is optional and
|
|
@@ -1304,10 +1644,9 @@ interface IssuerServiceConfig {
|
|
|
1304
1644
|
/**
|
|
1305
1645
|
* Fee management config. If omitted the `handleGasFee` endpoint will
|
|
1306
1646
|
* throw "not configured" at request time. Pass any subset of fields
|
|
1307
|
-
* to opt in — provider
|
|
1308
|
-
* config automatically.
|
|
1647
|
+
* to opt in — `provider` is inherited from the outer config.
|
|
1309
1648
|
*/
|
|
1310
|
-
fee?: Omit<FeeManagerConfig, "provider"
|
|
1649
|
+
fee?: Omit<FeeManagerConfig, "provider">;
|
|
1311
1650
|
/**
|
|
1312
1651
|
* Pool discovery function for `handlePools`. If omitted the endpoint
|
|
1313
1652
|
* throws "not configured" at request time.
|
|
@@ -1377,4 +1716,4 @@ declare function createIssuerService(config: IssuerServiceConfig): IssuerService
|
|
|
1377
1716
|
/** SDK package version — bumped on every release */
|
|
1378
1717
|
declare const PAFI_ISSUER_SDK_VERSION = "0.1.0";
|
|
1379
1718
|
|
|
1380
|
-
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, DefaultPolicyEngine, type DefaultPolicyEngineOptions, FeeManager, type FeeManagerConfig, type IIndexerCursorStore, type IIssuerSigner, type IPointLedger, type IPolicyEngine, type ISessionStore, InMemoryCursorStore, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerService, type IssuerServiceConfig, type LockedMintRequest, type LoginResult, MemoryPointLedger, MemorySessionStore, type MemorySessionStoreOptions, type MintAndCashOutRequest, type MintAndCashOutResponse, type MintEvent, MintingGateway, type MintingGatewayConfig, MintingGatewayError, type MintingGatewayErrorCode, type MintingStatus, NonceManager, type OperatorWalletLike, PAFI_ISSUER_SDK_VERSION, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, type PoolsProvider, PrivateKeySigner, type PrivateKeySignerOptions, RelayError, type RelayErrorCode, type RelayResult, RelayService, type RelayServiceConfig, type Session, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type SubmitMintAndSwapParams, authenticateRequest, createIssuerService, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider };
|
|
1719
|
+
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 IIssuerSigner, type IPointLedger, type IPolicyEngine, type ISessionStore, InMemoryCursorStore, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerService, type IssuerServiceConfig, type LockedMintRequest, type LoginResult, MemoryPointLedger, MemorySessionStore, type MemorySessionStoreOptions, type MintAndCashOutRequest, type MintAndCashOutResponse, type MintEvent, MintingGateway, type MintingGatewayConfig, MintingGatewayError, type MintingGatewayErrorCode, type MintingStatus, NonceManager, type OperatorWalletLike, PAFI_ISSUER_SDK_VERSION, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, type PoolsProvider, PrivateKeySigner, type PrivateKeySignerOptions, RelayError, type RelayErrorCode, type RelayResult, RelayService, type RelayServiceConfig, type Session, type SponsorshipRequest, type SponsorshipResponse, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type SubmitMintAndSwapParams, authenticateRequest, createIssuerService, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider };
|