@pafi-dev/issuer 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -621,6 +621,35 @@ declare class RelayService {
621
621
  * issuer-signed `BurnRequest` path.
622
622
  */
623
623
  prepareBurn(params: PrepareBurnParams): Promise<PartialUserOperation>;
624
+ /**
625
+ * Build a dummy `PartialUserOperation` for the mint scenario, suitable
626
+ * for `feeManager.estimateGasFee({ partialUserOp, ... })`. NO signing —
627
+ * uses a 65-byte zero signature placeholder.
628
+ */
629
+ previewMintUserOp(params: PreviewMintParams): PartialUserOperation;
630
+ /** Burn-side mirror of `previewMintUserOp`. */
631
+ previewBurnUserOp(params: PreviewBurnParams): PartialUserOperation;
632
+ }
633
+ /**
634
+ * Inputs for `previewMintUserOp` — strict subset of `PrepareMintParams`
635
+ * that excludes the signing wallet, EIP-712 domain, and on-chain
636
+ * `mintRequestNonces` lookup since those don't affect calldata shape
637
+ * (the bundler estimate is shape-invariant under their values).
638
+ */
639
+ interface PreviewMintParams {
640
+ userAddress: Address;
641
+ aaNonce: bigint;
642
+ pointTokenAddress: Address;
643
+ amount: bigint;
644
+ deadline: bigint;
645
+ mintFeeWrapperAddress?: Address;
646
+ }
647
+ interface PreviewBurnParams {
648
+ userAddress: Address;
649
+ aaNonce: bigint;
650
+ pointTokenAddress: Address;
651
+ amount: bigint;
652
+ deadline: bigint;
624
653
  }
625
654
  /**
626
655
  * Sig-gated `PointToken.mint(to, amount, deadline, minterSig)`.
@@ -708,66 +737,206 @@ interface PrepareBurnParams {
708
737
  preVerificationGas?: bigint;
709
738
  }
710
739
 
740
+ /**
741
+ * Caller-supplied fetch implementation, matching the WHATWG Fetch API
742
+ * shape. Defaults to `globalThis.fetch` when omitted; tests and
743
+ * non-browser/node environments can plug in their own.
744
+ */
745
+ type FetchImpl = (input: string, init?: RequestInit) => Promise<Response>;
746
+ /**
747
+ * Duck-typed estimator interface. `FeeManager` accepts any object
748
+ * satisfying this surface so the SDK stays decoupled from where the
749
+ * bundler estimate physically lives (PAFI sponsor-relayer in production,
750
+ * a mock in tests, a local Pimlico proxy in dev).
751
+ *
752
+ * The estimator's job: given the preview UserOp shape, return the gas
753
+ * units to multiply by `gasPrice` and feed into the caller's fee
754
+ * quoter. Premium and PM overhead are the estimator's responsibility,
755
+ * NOT the SDK's — preventing double-padding.
756
+ */
757
+ interface BundlerEstimatorClient {
758
+ /**
759
+ * Resolve gas units for a sponsored UserOp.
760
+ *
761
+ * @throws — implementations should throw on transport / auth /
762
+ * protocol errors so `FeeManager` can degrade to its hardcoded
763
+ * fallback. Network failures must NOT silently return a default,
764
+ * since that would mask a misconfiguration as a successful estimate.
765
+ */
766
+ getGasUnits(input: {
767
+ scenario: string;
768
+ contractAddress: Address;
769
+ paymasterAddress?: Address;
770
+ partialUserOp: {
771
+ sender: Address;
772
+ nonce: bigint;
773
+ callData: Hex;
774
+ signature?: Hex;
775
+ };
776
+ }): Promise<{
777
+ gasUnits: bigint;
778
+ source: "cache" | "bundler" | "fallback";
779
+ expiresAt: number;
780
+ }>;
781
+ }
782
+ interface PafiEstimatorClientConfig {
783
+ /**
784
+ * Base URL of the PAFI sponsor-relayer (no trailing slash). The
785
+ * adapter appends `/v1/estimate-gas-fee` — issuer infrastructure
786
+ * never talks to Pimlico directly.
787
+ */
788
+ baseUrl: string;
789
+ /** Issuer's PAFI API key — the same one used for `/paymaster/sponsor`. */
790
+ apiKey: string;
791
+ /** Issuer ID used in `X-Issuer-Id` header. */
792
+ issuerId: string;
793
+ /** Custom fetch (e.g. `undici` in tests). Defaults to `globalThis.fetch`. */
794
+ fetchImpl?: FetchImpl;
795
+ }
796
+ declare class PafiEstimatorHttpError extends Error {
797
+ readonly status: number;
798
+ readonly body: unknown;
799
+ constructor(status: number, body: unknown, message?: string);
800
+ }
801
+ /**
802
+ * HTTP adapter that hits PAFI sponsor-relayer's `/v1/estimate-gas-fee`
803
+ * endpoint. The SDK never imports Pimlico-specific code or holds the
804
+ * Pimlico API key — the bundler call happens server-side at PAFI.
805
+ *
806
+ * Authentication mirrors `/paymaster/sponsor`:
807
+ * - `Authorization: Bearer <apiKey>`
808
+ * - `X-Issuer-Id: <issuerId>`
809
+ *
810
+ * Issuers wire this once at boot via `createPafiEstimatorClient({
811
+ * baseUrl, apiKey, issuerId })` and pass it to `FeeManager` as
812
+ * `bundlerClient`. The same `baseUrl`/`apiKey`/`issuerId` already
813
+ * authenticate other PAFI calls — no new secrets required.
814
+ */
815
+ declare function createPafiEstimatorClient(config: PafiEstimatorClientConfig): BundlerEstimatorClient;
816
+
817
+ type GasFeeSource = "estimator" | "fallback" | "cached-fee";
818
+ /**
819
+ * Hooks for observability. Sync, best-effort — errors thrown by hooks
820
+ * are swallowed so they cannot break the user-facing fee flow.
821
+ */
822
+ interface FeeManagerMetrics {
823
+ onEstimate?: (info: {
824
+ source: GasFeeSource;
825
+ scenario?: string;
826
+ gasUnits: bigint;
827
+ latencyMs: number;
828
+ }) => void;
829
+ onEstimatorError?: (info: {
830
+ scenario?: string;
831
+ reason: string;
832
+ }) => void;
833
+ }
711
834
  interface FeeManagerConfig {
712
- /** Provider used for gas price reads. */
835
+ /** Provider used for gas-price reads. */
713
836
  provider: PublicClient;
714
837
  /**
715
- * Typical gas used by a single sponsored UserOp. Default: 500_000.
716
- * The manager multiplies this by current gas price to get native
717
- * cost, then converts via the injected `quoteNativeToFee`.
838
+ * Hardcoded fallback gas units, used when (a) no `bundlerClient` is
839
+ * configured, OR (b) the bundler call fails and `partialUserOp` was
840
+ * not supplied. Default: 500_000n. Sized as a safe over-estimate vs
841
+ * the historically observed mint/burn cost so a fallback never
842
+ * under-charges the sponsor.
718
843
  */
719
844
  gasUnits?: bigint;
720
845
  /**
721
- * Safety margin applied before charging the user, as basis points.
722
- * 12_000 = 120%. Default: 12_000.
846
+ * SDK-side premium in basis points. Default: 10_000 (100% = no extra
847
+ * padding). Pimlico's bundler pads ~10-15% internally, and PAFI
848
+ * sponsor-relayer applies its own premium (110%) before returning
849
+ * gasUnits, so adding more here would compound the over-charge that
850
+ * the v0.20 refactor was designed to remove. Override only when the
851
+ * caller's `bundlerClient` returns raw bundler values without a
852
+ * premium applied.
723
853
  */
724
854
  gasPremiumBps?: number;
725
855
  /**
726
856
  * Quote function — given an amount of native wei, return the
727
- * equivalent amount in the fee currency (typically PT raw units;
728
- * USDT 6-decimal for swap / perp deposit flows).
729
- *
730
- * Injected so the manager stays chain- and token-agnostic. Issuers
731
- * wire it to `@pafi-dev/core` Quoter helpers, a PAFI subgraph query,
732
- * or an oracle feed.
857
+ * equivalent amount in the fee currency (PT raw units for mint/burn,
858
+ * USDT/USDC 6-decimal for swap / perp deposit). Chain- and
859
+ * token-agnostic by design; wire to `createNativePtQuoter` for PT or
860
+ * `quoteOperatorFeeUsdt` for stables.
733
861
  */
734
862
  quoteNativeToFee: (amountNative: bigint) => Promise<bigint>;
863
+ /**
864
+ * Optional bundler-driven gas-units estimator. When set,
865
+ * `estimateGasFee({ partialUserOp, scenario, contractAddress })` calls
866
+ * the estimator for per-UserOp accuracy. When unset, the legacy
867
+ * hardcoded path runs — backward-compatible with v0.16/0.19 callers.
868
+ */
869
+ bundlerClient?: BundlerEstimatorClient;
870
+ /** Optional observability hooks. Throws inside hooks are swallowed. */
871
+ metrics?: FeeManagerMetrics;
872
+ }
873
+ /**
874
+ * Parameters for a single bundler-driven `estimateGasFee` call. All
875
+ * fields optional for backwards compatibility — when `partialUserOp` is
876
+ * absent the SDK falls back to the hardcoded path.
877
+ */
878
+ interface EstimateGasFeeOptions {
879
+ partialUserOp?: {
880
+ sender: Address;
881
+ nonce: bigint;
882
+ callData: Hex;
883
+ signature?: Hex;
884
+ };
885
+ scenario?: string;
886
+ contractAddress?: Address;
887
+ paymasterAddress?: Address;
735
888
  }
736
889
  /**
737
- * Computes how much fee to collect from the user to cover the gas cost
738
- * of a sponsored UserOp.
890
+ * Computes the operator fee the issuer charges users for sponsored gas.
891
+ *
892
+ * Fee currency is whatever the injected `quoteNativeToFee` returns —
893
+ * PT for mint/burn, USDT/USDC for swap & perp deposit.
894
+ *
895
+ * Two execution paths:
896
+ *
897
+ * 1. **Estimator path** (recommended) — caller wires `bundlerClient`
898
+ * (typically `createPafiEstimatorClient`). Each call hits the PAFI
899
+ * sponsor-relayer's `/v1/estimate-gas-fee` which caches by
900
+ * `(scenario, contract codehash, paymaster)` and applies its own
901
+ * premium. Result is the most accurate fee available and matches
902
+ * what sponsor-relayer's verify path expects, eliminating
903
+ * `INSUFFICIENT_FEE` rejects from formula drift.
739
904
  *
740
- * The fee is expressed in the **fee currency** chosen by the caller
741
- * (PT for mint/burn, USDT for swap/perp_deposit) — not hardcoded.
905
+ * 2. **Fallback path** no bundler client OR estimator threw. Use
906
+ * hardcoded `gasUnits × premium`. Same shape as v0.16/0.19 so
907
+ * legacy integrations get identical behaviour.
742
908
  *
743
- * **No operator rebalancing.** The operator does not hold ETH directly;
744
- * gas is paid by PAFI sponsor-relayer via the paymaster-proxy (see
745
- * [SPONSORED_PATH_FLOW.md]). The fee collected here is an
746
- * application-level ERC-20 transfer inside the same UserOp batch, not a
747
- * reimbursement to a wallet that needs topping up.
909
+ * **No operator rebalancing**: the operator does NOT hold ETH; gas is
910
+ * paid by PAFI's Pimlico paymaster. The fee here is an
911
+ * application-level ERC-20 transfer in the same UserOp batch.
748
912
  */
749
913
  declare class FeeManager {
750
914
  private readonly provider;
751
- private readonly gasUnits;
915
+ private readonly fallbackGasUnits;
752
916
  private readonly gasPremiumBps;
753
917
  private readonly quoteNativeToFee;
918
+ private readonly bundlerClient;
919
+ private readonly metrics;
754
920
  private cachedFee;
755
921
  private cacheExpiresAt;
756
- private static readonly CACHE_TTL_MS;
922
+ private static readonly FEE_CACHE_TTL_MS;
757
923
  constructor(config: FeeManagerConfig);
758
924
  /**
759
- * Estimate the fee (in the caller's fee currency) to charge for the
760
- * next sponsored UserOp:
925
+ * Estimate the operator fee for the next sponsored UserOp.
761
926
  *
762
- * nativeCost = gasUnits × gasPrice
763
- * withPremium = nativeCost × premiumBps / 10_000
764
- * fee = quoteNativeToFee(withPremium)
927
+ * Without `opts` → legacy path: `gasUnits × gasPrice × premium →
928
+ * quoteNativeToFee`. Cached for 10 s to absorb bursts.
765
929
  *
766
- * For backward compatibility with v0.2.x code that reads `gasFeeUsdt`
767
- * from the response, the name `estimateGasFee` is kept but the
768
- * currency depends on how the caller wired `quoteNativeToFee`.
930
+ * With `opts` AND `bundlerClient` → estimator path. Each call may
931
+ * hit a different bundler-cached result; the SDK does NOT add its
932
+ * own value cache because the estimator's cache TTL is the source
933
+ * of truth for "how long is this estimate good for".
769
934
  */
770
- estimateGasFee(): Promise<bigint>;
935
+ estimateGasFee(opts?: EstimateGasFeeOptions): Promise<bigint>;
936
+ /** Manually purge the legacy 10s fee cache. */
937
+ invalidateCache(): void;
938
+ private resolveGasUnits;
939
+ private safeEmit;
771
940
  }
772
941
 
773
942
  /** Decoded Transfer(from=0x0 → to) event used to finalize a mint. */
@@ -3487,4 +3656,4 @@ declare class MemoryRedemptionHistoryStore implements IRedemptionHistoryStore {
3487
3656
 
3488
3657
  declare const PAFI_ISSUER_SDK_VERSION: string;
3489
3658
 
3490
- export { AdapterMisconfiguredError, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedemptionEvaluateRequest, type ApiRedemptionEvaluateResponse, type ApiRedemptionPreviewRequest, type ApiRedemptionPreviewResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type ClaimDto, type ConfigDto, ConfigurationError, DEFAULT_REDEMPTION_POLICY, type DecodedCallDto, DefaultPolicyEngine, type DefaultPolicyEngineOptions, type DelegatePrepareDto, type DelegateStatusDto, type EvaluateInput, FeeManager, type FeeManagerConfig, type FetchFailureReason, type FetchResult, type GasFeeDto, type HandleDelegateSubmitParams, type HandleDelegateSubmitResult, type HandleMobilePrepareParams, type HandleMobilePrepareResult, type HandleMobileSubmitParams, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type IRateLimiter, type IRedemptionHistoryStore, type ISessionStore, InMemoryCursorStore, IssuerApiAdapter, type IssuerApiAdapterConfig, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemoryPendingUserOpStore, MemoryRateLimiter, MemoryRedemptionHistoryStore, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type MobilePrepareDto, type MobileSubmitDto, type NativePtQuoterConfig, NonceManager, NoopRateLimiter, PAFI_ISSUER_SDK_VERSION, PTClaimError, PTClaimHandler, type PTClaimHandlerConfig, type PTClaimRequest, type PTClaimResponse, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PaymasterGasEstimates, type PendingCredit, type PendingUserOpEntry, PendingUserOpForbiddenError, PendingUserOpNotFoundError, type PerpDepositDto, PerpDepositError, PerpDepositHandler, type PerpDepositHandlerConfig, type PerpDepositRequest, type PerpDepositResponse, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, PolicyProvider, type PolicyProviderConfig, type PoolsDto, type PoolsProvider, type PreValidateMintResult, type PrepareBurnParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, REDEMPTION_HISTORY_WINDOW_SEC, type RateLimitAction, type RateLimiterConfig, type RedeemDto, type RedeemPrepareDto, RedemptionService, type RedemptionServiceConfig, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type ResolvedPolicy, type RetryConfig, type SdkErrorBody, type SdkErrorMapperFactories, type SdkErrorStatus, type SerializedUserOpTypedData, type Session, SettlementClient, type SettlementClientConfig, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type UserDto, type UserHistory, applyPaymasterGasEstimates, authenticateRequest, buildSdkErrorBody, createIssuerService, createNativePtQuoter, createSdkErrorMapper, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, defaultPolicyFor, evaluateRedemption, handleClaimStatus, handleDelegateSubmit, handleMobilePrepare, handleMobileSubmit, handleRedeemStatus, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };
3659
+ export { AdapterMisconfiguredError, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedemptionEvaluateRequest, type ApiRedemptionEvaluateResponse, type ApiRedemptionPreviewRequest, type ApiRedemptionPreviewResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, type BundlerEstimatorClient, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type ClaimDto, type ConfigDto, ConfigurationError, DEFAULT_REDEMPTION_POLICY, type DecodedCallDto, DefaultPolicyEngine, type DefaultPolicyEngineOptions, type DelegatePrepareDto, type DelegateStatusDto, type EstimateGasFeeOptions, type EvaluateInput, FeeManager, type FeeManagerConfig, type FeeManagerMetrics, type FetchFailureReason, type FetchImpl, type FetchResult, type GasFeeDto, type GasFeeSource, type HandleDelegateSubmitParams, type HandleDelegateSubmitResult, type HandleMobilePrepareParams, type HandleMobilePrepareResult, type HandleMobileSubmitParams, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type IRateLimiter, type IRedemptionHistoryStore, type ISessionStore, InMemoryCursorStore, IssuerApiAdapter, type IssuerApiAdapterConfig, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemoryPendingUserOpStore, MemoryRateLimiter, MemoryRedemptionHistoryStore, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type MobilePrepareDto, type MobileSubmitDto, type NativePtQuoterConfig, NonceManager, NoopRateLimiter, PAFI_ISSUER_SDK_VERSION, PTClaimError, PTClaimHandler, type PTClaimHandlerConfig, type PTClaimRequest, type PTClaimResponse, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PafiEstimatorClientConfig, PafiEstimatorHttpError, type PaymasterGasEstimates, type PendingCredit, type PendingUserOpEntry, PendingUserOpForbiddenError, PendingUserOpNotFoundError, type PerpDepositDto, PerpDepositError, PerpDepositHandler, type PerpDepositHandlerConfig, type PerpDepositRequest, type PerpDepositResponse, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, PolicyProvider, type PolicyProviderConfig, type PoolsDto, type PoolsProvider, type PreValidateMintResult, type PrepareBurnParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, type PreviewBurnParams, type PreviewMintParams, REDEMPTION_HISTORY_WINDOW_SEC, type RateLimitAction, type RateLimiterConfig, type RedeemDto, type RedeemPrepareDto, RedemptionService, type RedemptionServiceConfig, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type ResolvedPolicy, type RetryConfig, type SdkErrorBody, type SdkErrorMapperFactories, type SdkErrorStatus, type SerializedUserOpTypedData, type Session, SettlementClient, type SettlementClientConfig, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type UserDto, type UserHistory, applyPaymasterGasEstimates, authenticateRequest, buildSdkErrorBody, createIssuerService, createNativePtQuoter, createPafiEstimatorClient, createSdkErrorMapper, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, defaultPolicyFor, evaluateRedemption, handleClaimStatus, handleDelegateSubmit, handleMobilePrepare, handleMobileSubmit, handleRedeemStatus, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };
package/dist/index.d.ts CHANGED
@@ -621,6 +621,35 @@ declare class RelayService {
621
621
  * issuer-signed `BurnRequest` path.
622
622
  */
623
623
  prepareBurn(params: PrepareBurnParams): Promise<PartialUserOperation>;
624
+ /**
625
+ * Build a dummy `PartialUserOperation` for the mint scenario, suitable
626
+ * for `feeManager.estimateGasFee({ partialUserOp, ... })`. NO signing —
627
+ * uses a 65-byte zero signature placeholder.
628
+ */
629
+ previewMintUserOp(params: PreviewMintParams): PartialUserOperation;
630
+ /** Burn-side mirror of `previewMintUserOp`. */
631
+ previewBurnUserOp(params: PreviewBurnParams): PartialUserOperation;
632
+ }
633
+ /**
634
+ * Inputs for `previewMintUserOp` — strict subset of `PrepareMintParams`
635
+ * that excludes the signing wallet, EIP-712 domain, and on-chain
636
+ * `mintRequestNonces` lookup since those don't affect calldata shape
637
+ * (the bundler estimate is shape-invariant under their values).
638
+ */
639
+ interface PreviewMintParams {
640
+ userAddress: Address;
641
+ aaNonce: bigint;
642
+ pointTokenAddress: Address;
643
+ amount: bigint;
644
+ deadline: bigint;
645
+ mintFeeWrapperAddress?: Address;
646
+ }
647
+ interface PreviewBurnParams {
648
+ userAddress: Address;
649
+ aaNonce: bigint;
650
+ pointTokenAddress: Address;
651
+ amount: bigint;
652
+ deadline: bigint;
624
653
  }
625
654
  /**
626
655
  * Sig-gated `PointToken.mint(to, amount, deadline, minterSig)`.
@@ -708,66 +737,206 @@ interface PrepareBurnParams {
708
737
  preVerificationGas?: bigint;
709
738
  }
710
739
 
740
+ /**
741
+ * Caller-supplied fetch implementation, matching the WHATWG Fetch API
742
+ * shape. Defaults to `globalThis.fetch` when omitted; tests and
743
+ * non-browser/node environments can plug in their own.
744
+ */
745
+ type FetchImpl = (input: string, init?: RequestInit) => Promise<Response>;
746
+ /**
747
+ * Duck-typed estimator interface. `FeeManager` accepts any object
748
+ * satisfying this surface so the SDK stays decoupled from where the
749
+ * bundler estimate physically lives (PAFI sponsor-relayer in production,
750
+ * a mock in tests, a local Pimlico proxy in dev).
751
+ *
752
+ * The estimator's job: given the preview UserOp shape, return the gas
753
+ * units to multiply by `gasPrice` and feed into the caller's fee
754
+ * quoter. Premium and PM overhead are the estimator's responsibility,
755
+ * NOT the SDK's — preventing double-padding.
756
+ */
757
+ interface BundlerEstimatorClient {
758
+ /**
759
+ * Resolve gas units for a sponsored UserOp.
760
+ *
761
+ * @throws — implementations should throw on transport / auth /
762
+ * protocol errors so `FeeManager` can degrade to its hardcoded
763
+ * fallback. Network failures must NOT silently return a default,
764
+ * since that would mask a misconfiguration as a successful estimate.
765
+ */
766
+ getGasUnits(input: {
767
+ scenario: string;
768
+ contractAddress: Address;
769
+ paymasterAddress?: Address;
770
+ partialUserOp: {
771
+ sender: Address;
772
+ nonce: bigint;
773
+ callData: Hex;
774
+ signature?: Hex;
775
+ };
776
+ }): Promise<{
777
+ gasUnits: bigint;
778
+ source: "cache" | "bundler" | "fallback";
779
+ expiresAt: number;
780
+ }>;
781
+ }
782
+ interface PafiEstimatorClientConfig {
783
+ /**
784
+ * Base URL of the PAFI sponsor-relayer (no trailing slash). The
785
+ * adapter appends `/v1/estimate-gas-fee` — issuer infrastructure
786
+ * never talks to Pimlico directly.
787
+ */
788
+ baseUrl: string;
789
+ /** Issuer's PAFI API key — the same one used for `/paymaster/sponsor`. */
790
+ apiKey: string;
791
+ /** Issuer ID used in `X-Issuer-Id` header. */
792
+ issuerId: string;
793
+ /** Custom fetch (e.g. `undici` in tests). Defaults to `globalThis.fetch`. */
794
+ fetchImpl?: FetchImpl;
795
+ }
796
+ declare class PafiEstimatorHttpError extends Error {
797
+ readonly status: number;
798
+ readonly body: unknown;
799
+ constructor(status: number, body: unknown, message?: string);
800
+ }
801
+ /**
802
+ * HTTP adapter that hits PAFI sponsor-relayer's `/v1/estimate-gas-fee`
803
+ * endpoint. The SDK never imports Pimlico-specific code or holds the
804
+ * Pimlico API key — the bundler call happens server-side at PAFI.
805
+ *
806
+ * Authentication mirrors `/paymaster/sponsor`:
807
+ * - `Authorization: Bearer <apiKey>`
808
+ * - `X-Issuer-Id: <issuerId>`
809
+ *
810
+ * Issuers wire this once at boot via `createPafiEstimatorClient({
811
+ * baseUrl, apiKey, issuerId })` and pass it to `FeeManager` as
812
+ * `bundlerClient`. The same `baseUrl`/`apiKey`/`issuerId` already
813
+ * authenticate other PAFI calls — no new secrets required.
814
+ */
815
+ declare function createPafiEstimatorClient(config: PafiEstimatorClientConfig): BundlerEstimatorClient;
816
+
817
+ type GasFeeSource = "estimator" | "fallback" | "cached-fee";
818
+ /**
819
+ * Hooks for observability. Sync, best-effort — errors thrown by hooks
820
+ * are swallowed so they cannot break the user-facing fee flow.
821
+ */
822
+ interface FeeManagerMetrics {
823
+ onEstimate?: (info: {
824
+ source: GasFeeSource;
825
+ scenario?: string;
826
+ gasUnits: bigint;
827
+ latencyMs: number;
828
+ }) => void;
829
+ onEstimatorError?: (info: {
830
+ scenario?: string;
831
+ reason: string;
832
+ }) => void;
833
+ }
711
834
  interface FeeManagerConfig {
712
- /** Provider used for gas price reads. */
835
+ /** Provider used for gas-price reads. */
713
836
  provider: PublicClient;
714
837
  /**
715
- * Typical gas used by a single sponsored UserOp. Default: 500_000.
716
- * The manager multiplies this by current gas price to get native
717
- * cost, then converts via the injected `quoteNativeToFee`.
838
+ * Hardcoded fallback gas units, used when (a) no `bundlerClient` is
839
+ * configured, OR (b) the bundler call fails and `partialUserOp` was
840
+ * not supplied. Default: 500_000n. Sized as a safe over-estimate vs
841
+ * the historically observed mint/burn cost so a fallback never
842
+ * under-charges the sponsor.
718
843
  */
719
844
  gasUnits?: bigint;
720
845
  /**
721
- * Safety margin applied before charging the user, as basis points.
722
- * 12_000 = 120%. Default: 12_000.
846
+ * SDK-side premium in basis points. Default: 10_000 (100% = no extra
847
+ * padding). Pimlico's bundler pads ~10-15% internally, and PAFI
848
+ * sponsor-relayer applies its own premium (110%) before returning
849
+ * gasUnits, so adding more here would compound the over-charge that
850
+ * the v0.20 refactor was designed to remove. Override only when the
851
+ * caller's `bundlerClient` returns raw bundler values without a
852
+ * premium applied.
723
853
  */
724
854
  gasPremiumBps?: number;
725
855
  /**
726
856
  * Quote function — given an amount of native wei, return the
727
- * equivalent amount in the fee currency (typically PT raw units;
728
- * USDT 6-decimal for swap / perp deposit flows).
729
- *
730
- * Injected so the manager stays chain- and token-agnostic. Issuers
731
- * wire it to `@pafi-dev/core` Quoter helpers, a PAFI subgraph query,
732
- * or an oracle feed.
857
+ * equivalent amount in the fee currency (PT raw units for mint/burn,
858
+ * USDT/USDC 6-decimal for swap / perp deposit). Chain- and
859
+ * token-agnostic by design; wire to `createNativePtQuoter` for PT or
860
+ * `quoteOperatorFeeUsdt` for stables.
733
861
  */
734
862
  quoteNativeToFee: (amountNative: bigint) => Promise<bigint>;
863
+ /**
864
+ * Optional bundler-driven gas-units estimator. When set,
865
+ * `estimateGasFee({ partialUserOp, scenario, contractAddress })` calls
866
+ * the estimator for per-UserOp accuracy. When unset, the legacy
867
+ * hardcoded path runs — backward-compatible with v0.16/0.19 callers.
868
+ */
869
+ bundlerClient?: BundlerEstimatorClient;
870
+ /** Optional observability hooks. Throws inside hooks are swallowed. */
871
+ metrics?: FeeManagerMetrics;
872
+ }
873
+ /**
874
+ * Parameters for a single bundler-driven `estimateGasFee` call. All
875
+ * fields optional for backwards compatibility — when `partialUserOp` is
876
+ * absent the SDK falls back to the hardcoded path.
877
+ */
878
+ interface EstimateGasFeeOptions {
879
+ partialUserOp?: {
880
+ sender: Address;
881
+ nonce: bigint;
882
+ callData: Hex;
883
+ signature?: Hex;
884
+ };
885
+ scenario?: string;
886
+ contractAddress?: Address;
887
+ paymasterAddress?: Address;
735
888
  }
736
889
  /**
737
- * Computes how much fee to collect from the user to cover the gas cost
738
- * of a sponsored UserOp.
890
+ * Computes the operator fee the issuer charges users for sponsored gas.
891
+ *
892
+ * Fee currency is whatever the injected `quoteNativeToFee` returns —
893
+ * PT for mint/burn, USDT/USDC for swap & perp deposit.
894
+ *
895
+ * Two execution paths:
896
+ *
897
+ * 1. **Estimator path** (recommended) — caller wires `bundlerClient`
898
+ * (typically `createPafiEstimatorClient`). Each call hits the PAFI
899
+ * sponsor-relayer's `/v1/estimate-gas-fee` which caches by
900
+ * `(scenario, contract codehash, paymaster)` and applies its own
901
+ * premium. Result is the most accurate fee available and matches
902
+ * what sponsor-relayer's verify path expects, eliminating
903
+ * `INSUFFICIENT_FEE` rejects from formula drift.
739
904
  *
740
- * The fee is expressed in the **fee currency** chosen by the caller
741
- * (PT for mint/burn, USDT for swap/perp_deposit) — not hardcoded.
905
+ * 2. **Fallback path** no bundler client OR estimator threw. Use
906
+ * hardcoded `gasUnits × premium`. Same shape as v0.16/0.19 so
907
+ * legacy integrations get identical behaviour.
742
908
  *
743
- * **No operator rebalancing.** The operator does not hold ETH directly;
744
- * gas is paid by PAFI sponsor-relayer via the paymaster-proxy (see
745
- * [SPONSORED_PATH_FLOW.md]). The fee collected here is an
746
- * application-level ERC-20 transfer inside the same UserOp batch, not a
747
- * reimbursement to a wallet that needs topping up.
909
+ * **No operator rebalancing**: the operator does NOT hold ETH; gas is
910
+ * paid by PAFI's Pimlico paymaster. The fee here is an
911
+ * application-level ERC-20 transfer in the same UserOp batch.
748
912
  */
749
913
  declare class FeeManager {
750
914
  private readonly provider;
751
- private readonly gasUnits;
915
+ private readonly fallbackGasUnits;
752
916
  private readonly gasPremiumBps;
753
917
  private readonly quoteNativeToFee;
918
+ private readonly bundlerClient;
919
+ private readonly metrics;
754
920
  private cachedFee;
755
921
  private cacheExpiresAt;
756
- private static readonly CACHE_TTL_MS;
922
+ private static readonly FEE_CACHE_TTL_MS;
757
923
  constructor(config: FeeManagerConfig);
758
924
  /**
759
- * Estimate the fee (in the caller's fee currency) to charge for the
760
- * next sponsored UserOp:
925
+ * Estimate the operator fee for the next sponsored UserOp.
761
926
  *
762
- * nativeCost = gasUnits × gasPrice
763
- * withPremium = nativeCost × premiumBps / 10_000
764
- * fee = quoteNativeToFee(withPremium)
927
+ * Without `opts` → legacy path: `gasUnits × gasPrice × premium →
928
+ * quoteNativeToFee`. Cached for 10 s to absorb bursts.
765
929
  *
766
- * For backward compatibility with v0.2.x code that reads `gasFeeUsdt`
767
- * from the response, the name `estimateGasFee` is kept but the
768
- * currency depends on how the caller wired `quoteNativeToFee`.
930
+ * With `opts` AND `bundlerClient` → estimator path. Each call may
931
+ * hit a different bundler-cached result; the SDK does NOT add its
932
+ * own value cache because the estimator's cache TTL is the source
933
+ * of truth for "how long is this estimate good for".
769
934
  */
770
- estimateGasFee(): Promise<bigint>;
935
+ estimateGasFee(opts?: EstimateGasFeeOptions): Promise<bigint>;
936
+ /** Manually purge the legacy 10s fee cache. */
937
+ invalidateCache(): void;
938
+ private resolveGasUnits;
939
+ private safeEmit;
771
940
  }
772
941
 
773
942
  /** Decoded Transfer(from=0x0 → to) event used to finalize a mint. */
@@ -3487,4 +3656,4 @@ declare class MemoryRedemptionHistoryStore implements IRedemptionHistoryStore {
3487
3656
 
3488
3657
  declare const PAFI_ISSUER_SDK_VERSION: string;
3489
3658
 
3490
- export { AdapterMisconfiguredError, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedemptionEvaluateRequest, type ApiRedemptionEvaluateResponse, type ApiRedemptionPreviewRequest, type ApiRedemptionPreviewResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type ClaimDto, type ConfigDto, ConfigurationError, DEFAULT_REDEMPTION_POLICY, type DecodedCallDto, DefaultPolicyEngine, type DefaultPolicyEngineOptions, type DelegatePrepareDto, type DelegateStatusDto, type EvaluateInput, FeeManager, type FeeManagerConfig, type FetchFailureReason, type FetchResult, type GasFeeDto, type HandleDelegateSubmitParams, type HandleDelegateSubmitResult, type HandleMobilePrepareParams, type HandleMobilePrepareResult, type HandleMobileSubmitParams, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type IRateLimiter, type IRedemptionHistoryStore, type ISessionStore, InMemoryCursorStore, IssuerApiAdapter, type IssuerApiAdapterConfig, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemoryPendingUserOpStore, MemoryRateLimiter, MemoryRedemptionHistoryStore, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type MobilePrepareDto, type MobileSubmitDto, type NativePtQuoterConfig, NonceManager, NoopRateLimiter, PAFI_ISSUER_SDK_VERSION, PTClaimError, PTClaimHandler, type PTClaimHandlerConfig, type PTClaimRequest, type PTClaimResponse, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PaymasterGasEstimates, type PendingCredit, type PendingUserOpEntry, PendingUserOpForbiddenError, PendingUserOpNotFoundError, type PerpDepositDto, PerpDepositError, PerpDepositHandler, type PerpDepositHandlerConfig, type PerpDepositRequest, type PerpDepositResponse, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, PolicyProvider, type PolicyProviderConfig, type PoolsDto, type PoolsProvider, type PreValidateMintResult, type PrepareBurnParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, REDEMPTION_HISTORY_WINDOW_SEC, type RateLimitAction, type RateLimiterConfig, type RedeemDto, type RedeemPrepareDto, RedemptionService, type RedemptionServiceConfig, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type ResolvedPolicy, type RetryConfig, type SdkErrorBody, type SdkErrorMapperFactories, type SdkErrorStatus, type SerializedUserOpTypedData, type Session, SettlementClient, type SettlementClientConfig, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type UserDto, type UserHistory, applyPaymasterGasEstimates, authenticateRequest, buildSdkErrorBody, createIssuerService, createNativePtQuoter, createSdkErrorMapper, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, defaultPolicyFor, evaluateRedemption, handleClaimStatus, handleDelegateSubmit, handleMobilePrepare, handleMobileSubmit, handleRedeemStatus, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };
3659
+ export { AdapterMisconfiguredError, type ApiConfigResponse, type ApiGasFeeResponse, type ApiLoginRequest, type ApiLoginResponse, type ApiNonceResponse, type ApiPoolsRequest, type ApiPoolsResponse, type ApiRedemptionEvaluateRequest, type ApiRedemptionEvaluateResponse, type ApiRedemptionPreviewRequest, type ApiRedemptionPreviewResponse, type ApiUserRequest, type ApiUserResponse, type AuthContext, AuthError, type AuthErrorCode, AuthService, type AuthServiceConfig, type BundlerEstimatorClient, BundlerNotConfiguredError, BundlerRejectedError, type BurnEvent, BurnIndexer, type BurnIndexerConfig, type BurnStatusParams, type BurnStatusResponse, type ClaimDto, type ConfigDto, ConfigurationError, DEFAULT_REDEMPTION_POLICY, type DecodedCallDto, DefaultPolicyEngine, type DefaultPolicyEngineOptions, type DelegatePrepareDto, type DelegateStatusDto, type EstimateGasFeeOptions, type EvaluateInput, FeeManager, type FeeManagerConfig, type FeeManagerMetrics, type FetchFailureReason, type FetchImpl, type FetchResult, type GasFeeDto, type GasFeeSource, type HandleDelegateSubmitParams, type HandleDelegateSubmitResult, type HandleMobilePrepareParams, type HandleMobilePrepareResult, type HandleMobileSubmitParams, type IIndexerCursorStore, type IPendingUserOpStore, type IPointLedger, type IPolicyEngine, type IRateLimiter, type IRedemptionHistoryStore, type ISessionStore, InMemoryCursorStore, IssuerApiAdapter, type IssuerApiAdapterConfig, IssuerApiHandlers, type IssuerApiHandlersConfig, type IssuerRegistryRecord, type IssuerService, type IssuerServiceConfig, IssuerStateError, IssuerStateValidator, LockNotFoundError, type LockedMintRequest, type LoginResult, MemoryPendingUserOpStore, MemoryRateLimiter, MemoryRedemptionHistoryStore, MemorySessionStore, type MemorySessionStoreOptions, type MintEvent, type MintStatusParams, type MintStatusResponse, type MintingStatus, type MobilePrepareDto, type MobileSubmitDto, type NativePtQuoterConfig, NonceManager, NoopRateLimiter, PAFI_ISSUER_SDK_VERSION, PTClaimError, PTClaimHandler, type PTClaimHandlerConfig, type PTClaimRequest, type PTClaimResponse, PTRedeemError, PTRedeemHandler, type PTRedeemHandlerConfig, type PTRedeemRequest, type PTRedeemResponse, PafiBackendClient, type PafiBackendConfig, PafiBackendError, type PafiBackendErrorCode, type PafiEstimatorClientConfig, PafiEstimatorHttpError, type PaymasterGasEstimates, type PendingCredit, type PendingUserOpEntry, PendingUserOpForbiddenError, PendingUserOpNotFoundError, type PerpDepositDto, PerpDepositError, PerpDepositHandler, type PerpDepositHandlerConfig, type PerpDepositRequest, type PerpDepositResponse, PointIndexer, type PointIndexerConfig, type PolicyDecision, type PolicyEvalRequest, PolicyProvider, type PolicyProviderConfig, type PoolsDto, type PoolsProvider, type PreValidateMintResult, type PrepareBurnParams, type PrepareMintParams, type PrepareMobileUserOpParams, type PrepareMobileUserOpResult, type PreparedUserOp, type PreviewBurnParams, type PreviewMintParams, REDEMPTION_HISTORY_WINDOW_SEC, type RateLimitAction, type RateLimiterConfig, type RedeemDto, type RedeemPrepareDto, RedemptionService, type RedemptionServiceConfig, RelayError, type RelayErrorCode, RelayService, type RelayUserOpParams, type RelayUserOpRequest, type RelayUserOpResponse, type RequestPaymasterParams, type ResolvedPolicy, type RetryConfig, type SdkErrorBody, type SdkErrorMapperFactories, type SdkErrorStatus, type SerializedUserOpTypedData, type Session, SettlementClient, type SettlementClientConfig, type SponsorshipRequest, type SponsorshipResponse, type SponsorshipTarget, type SponsorshipUserOp, type SubgraphNativeUsdtQuoterConfig, type SubgraphPoolsProviderConfig, type UserDto, type UserHistory, applyPaymasterGasEstimates, authenticateRequest, buildSdkErrorBody, createIssuerService, createNativePtQuoter, createPafiEstimatorClient, createSdkErrorMapper, createSubgraphNativeUsdtQuoter, createSubgraphPoolsProvider, defaultPolicyFor, evaluateRedemption, handleClaimStatus, handleDelegateSubmit, handleMobilePrepare, handleMobileSubmit, handleRedeemStatus, mergePaymasterFields, prepareMobileUserOp, relayUserOp, requestPaymaster, serializeEntryToJsonRpc, serializeUserOpTypedData };