@pafi-dev/core 0.17.0 → 0.19.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
@@ -695,6 +695,59 @@ interface BuildPerpDepositViaRelayParams {
695
695
  */
696
696
  declare function buildPerpDepositViaRelay(params: BuildPerpDepositViaRelayParams): PartialUserOperation;
697
697
 
698
+ /**
699
+ * Builder for the `erc20-transfer` sponsored scenario.
700
+ *
701
+ * Produces two UserOps from one request — same nonce, same sender:
702
+ *
703
+ * sponsored (2 calls):
704
+ * [ token.transfer(PAFI_FEE_RECIPIENT, fee), token.transfer(recipient, amount) ]
705
+ *
706
+ * fallback (1 call):
707
+ * [ token.transfer(recipient, amount) ]
708
+ *
709
+ * Both calls use the SAME token contract — the fee currency equals the
710
+ * token being sent (decision documented in
711
+ * `docs/SPONSORED_ERC20_TRANSFER_SPEC.md`). Caller is responsible for
712
+ * validating the token is in the sponsorship allowlist; this function
713
+ * just builds calldata, no policy enforcement.
714
+ *
715
+ * The sponsor-relayer's IntentValidator enforces:
716
+ * - exactly 2 calls
717
+ * - both target the same token address
718
+ * - call[0] = transfer to PAFI_FEE_RECIPIENT, amount >= 95% quoted
719
+ * - call[1] = transfer to recipient (not fee recipient, not 0x0)
720
+ *
721
+ * Fallback variant (1 call, no fee) submits no-paymaster — caller's
722
+ * `sendWithPaymasterFallback` routes there when sponsorship is refused.
723
+ */
724
+ interface BuildErc20TransferParams {
725
+ /** EIP-7702 delegated user EOA (== smart account sender). */
726
+ userAddress: Address;
727
+ /** ERC-4337 nonce from the EntryPoint. */
728
+ aaNonce: bigint;
729
+ /** Whitelisted ERC-20 to transfer. */
730
+ tokenAddress: Address;
731
+ /** Final recipient. Must NOT equal `feeRecipient` or `0x0...0`. */
732
+ recipient: Address;
733
+ /** Amount to send (token-native units; 6-dec for stables, 18-dec for PT). */
734
+ amount: bigint;
735
+ /**
736
+ * Operator fee in the SAME token. Sponsored variant only — pass
737
+ * `undefined` or 0n to skip and build the no-fee variant.
738
+ */
739
+ feeAmount?: bigint;
740
+ /** Required when `feeAmount > 0`. */
741
+ feeRecipient?: Address;
742
+ /** Override gas limits if you have a tighter Pimlico estimate. */
743
+ gasLimits?: {
744
+ callGasLimit?: bigint;
745
+ verificationGasLimit?: bigint;
746
+ preVerificationGas?: bigint;
747
+ };
748
+ }
749
+ declare function buildErc20TransferUserOp(params: BuildErc20TransferParams): PartialUserOperation;
750
+
698
751
  /**
699
752
  * Build an ERC-20 `transfer(to, amount)` operation. Used inside a batch
700
753
  * to move fee tokens from the user to the fee recipient atomically with
@@ -1021,7 +1074,7 @@ interface PaymasterConfig {
1021
1074
  * validates the request's target contracts + function selectors
1022
1075
  * match the expected pattern for the scenario.
1023
1076
  */
1024
- type SponsorshipScenario = "mint" | "burn" | "swap" | "perp-deposit" | "delegate";
1077
+ type SponsorshipScenario = "mint" | "burn" | "swap" | "perp-deposit" | "delegate" | "erc20-transfer";
1025
1078
 
1026
1079
  /**
1027
1080
  * Submission path chosen by `checkEthAndBranch`.
@@ -1576,7 +1629,8 @@ declare function sendWithPaymasterFallback(params: SendWithPaymasterFallbackPara
1576
1629
  *
1577
1630
  * Pass an explicit `gasUnits` to override.
1578
1631
  */
1579
- type FeeScenario = "mint" | "burn" | "swap" | "perp-deposit" | "delegate";
1632
+ type FeeScenario = "mint" | "burn" | "swap" | "perp-deposit" | "delegate" | "erc20-transfer";
1633
+ declare const SCENARIO_GAS_UNITS: Record<FeeScenario, bigint>;
1580
1634
  interface QuoteOperatorFeePtConfig {
1581
1635
  provider: PublicClient;
1582
1636
  chainId: number;
@@ -1689,6 +1743,43 @@ interface QuoteOperatorFeeUsdtConfig {
1689
1743
  */
1690
1744
  declare function quoteOperatorFeeUsdt(config: QuoteOperatorFeeUsdtConfig): Promise<bigint>;
1691
1745
  declare function quoteOperatorFeePt(config: QuoteOperatorFeePtConfig): Promise<bigint>;
1746
+ /**
1747
+ * Router quoter for the `erc20-transfer` sponsored scenario — picks
1748
+ * the correct underlying quoter based on the token being transferred:
1749
+ *
1750
+ * USDC / USDT → `quoteOperatorFeeUsdt` (Chainlink-based, 6-dec)
1751
+ * active PT → `quoteOperatorFeePt` (Chainlink + V3 pool quote)
1752
+ *
1753
+ * Fee is denominated in the SAME token the user is sending — no extra
1754
+ * approval / swap step needed. Caller is responsible for verifying the
1755
+ * token IS in the sponsored allowlist before calling; this function
1756
+ * just routes by address equality.
1757
+ *
1758
+ * Returns the fee in the token's native units (6-dec for USDC/USDT,
1759
+ * 18-dec for PT). The sponsor-relayer reproduces the same quote
1760
+ * server-side with a 5% slack window.
1761
+ *
1762
+ * @example
1763
+ * ```ts
1764
+ * const fee = await quoteOperatorFeeForTransfer({
1765
+ * provider, chainId: 8453,
1766
+ * tokenAddress: USDC_ADDRESS,
1767
+ * });
1768
+ * ```
1769
+ */
1770
+ interface QuoteOperatorFeeForTransferConfig extends Omit<QuoteOperatorFeeUsdtConfig, "scenario"> {
1771
+ /** The ERC-20 the user is transferring. Drives both quote logic + fee currency. */
1772
+ tokenAddress: Address;
1773
+ /**
1774
+ * Subgraph URL override — only consumed when `tokenAddress` resolves
1775
+ * to a PT (USDC/USDT paths don't hit the subgraph).
1776
+ */
1777
+ subgraphUrl?: string;
1778
+ /** PT fallback (only used when `tokenAddress` is a PT and subgraph fails). */
1779
+ fallbackPtPriceUsdt?: number;
1780
+ fetchImpl?: typeof fetch;
1781
+ }
1782
+ declare function quoteOperatorFeeForTransfer(config: QuoteOperatorFeeForTransferConfig): Promise<bigint>;
1692
1783
 
1693
1784
  declare const BATCH_EXECUTOR_ADDRESS_BASE_MAINNET: `0x${string}`;
1694
1785
  declare const BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA: `0x${string}`;
@@ -1714,7 +1805,8 @@ declare const BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA: `0x${string}`;
1714
1805
  * chainlinkEthUsd — ETH/USD price feed used by FeeManager
1715
1806
  * orderlyRelay — Orderly perp-deposit Relay
1716
1807
  * pafiFeeRecipient — PAFI-controlled fee recipient (sponsored gas reimbursement)
1717
- * universalRouter — Uniswap V4 swap entry point
1808
+ * universalRouter — PAFI v3 fork UniversalRouter
1809
+ * permit2 — PAFI fork Permit2 (paired with the PAFI UR above)
1718
1810
  *
1719
1811
  * PointToken addresses are per-issuer and resolved from the issuer's
1720
1812
  * config (env / DB / PointTokenFactory readout). PointTokenFactory and
@@ -1761,6 +1853,15 @@ interface ContractAddresses {
1761
1853
  pafiFeeRecipient: Address;
1762
1854
  /** Uniswap V4 UniversalRouter — swap entry point for the swap handler. */
1763
1855
  universalRouter: Address;
1856
+ /**
1857
+ * PAFI fork of Uniswap's Permit2. Acts as the unified token-approval
1858
+ * authority for UniversalRouter swaps and any other PAFI-paired flow
1859
+ * that needs gasless allowances. Differs from Uniswap canonical
1860
+ * Permit2 (`0x000000000022D473...`) — must use the PAFI fork because
1861
+ * the fork UR was deployed against this Permit2 instance. Keep in
1862
+ * sync with `PERMIT2_ADDRESS` top-level export.
1863
+ */
1864
+ permit2: Address;
1764
1865
  }
1765
1866
  declare const CONTRACT_ADDRESSES: Record<number, ContractAddresses>;
1766
1867
  /**
@@ -2131,4 +2232,4 @@ declare class PafiSDK {
2131
2232
  signLoginMessage(message: string): Promise<Hex>;
2132
2233
  }
2133
2234
 
2134
- export { ApiError, BATCH_EXECUTOR_7702_IMPL, BATCH_EXECUTOR_ABI, BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA, BROKER_HASHES, type BuildDelegationUserOpParams, type BuildPartialUserOpParams, type BuildPerpDepositViaRelayParams, type BuildPerpDepositWithGasDeductionParams, COMMON_POOLS, COMMON_TOKENS, CONTRACT_ADDRESSES, ChainConfig, type CheckEthAndBranchParams, ConfigurationError, type ContractAddresses, DUMMY_SIGNATURE_V07, type DelegateDirectParams, type DelegateDirectResult, type DelegateImpl, EIP712Signature, ENTRY_POINT_V07, ENTRY_POINT_V08, type Eip7702AuthorizationJsonRpc, LoginMessageParams, MintRequest, type ModalOpenOptions, ORDERLY_RELAY_ABI, ORDERLY_VAULT_ABI, ORDERLY_VAULT_ADDRESSES, ORDERLY_VAULT_BASE_MAINNET, type Operation, OracleStaleError, type OrderlyRelayDepositRequest, PAFI_SERVICE_URLS, PAFI_SUBGRAPH_URL, PERMIT2_ADDRESS, POINT_TOKEN_FACTORY_ADDRESSES, POINT_TOKEN_IMPL_ADDRESSES, POINT_TOKEN_POOLS, type PackedUserOperationMessage, type PafiErrorType, type PafiProxyTransportParams, PafiSDK, PafiSDKConfig, PafiSdkError, type PafiServiceUrls, type PafiWebModalAdapter, type PafiWebModalHandle, type PartialUserOperation, type PaymasterConfig, type PaymasterFields, PointTokenDomainConfig, PoolKey, QUOTER_V2_ADDRESSES, type QuoteOperatorFeePtConfig, type QuoteOperatorFeeUsdtConfig, SDK_ERROR_HTTP_STATUS_CODE, SIMPLE_7702_IMPL_BASE_MAINNET, SUPPORTED_CHAINS, type SdkErrorHttpStatus, type SendWithPaymasterFallbackParams, type SignAuthorizationFn, type SignatureStruct, SignatureVerification, SignatureVerifyOptions, type SignedAuthorization, SigningError, SimulationError, type SmartAccountSender, type SponsorshipScenario, type SubmissionPath, TOKEN_HASHES, UNIVERSAL_ROUTER_ADDRESSES, type UserOpReceipt, type UserOpTypedData, type UserOperation, V3Path, V3_FACTORY_ADDRESSES, V3_POOL_INIT_CODE_HASH, V3_SWAP_ROUTER_ADDRESSES, ValidationError, type VaultDepositFE, ZERO_VALUE, assembleUserOperation, buildDelegationUserOp, buildEip7702Authorization, buildPartialUserOperation, buildPerpDepositViaRelay, buildPerpDepositWithGasDeduction, buildUserOpTypedData, burnRequestTypes, checkDelegation, checkEthAndBranch, computeAccountId, computeAuthorizationHash, computeUserOpHash, computeV3PoolAddress, createPafiProxyTransport, decodeBatchExecuteCalls, defaultErrorTypeForStatus, delegateDirect, detectDelegateImpl, encodeBatchExecute, encodeV3Path, encodeV3PathReversed, erc20ApproveOp, erc20BurnOp, erc20TransferOp, fetchPafiPools, getAaNonce, getContractAddresses, getDummySignatureFor7702, getPafiServiceUrls, getPafiWebModalAdapter, isDelegatedTo, isDelegatedToTarget, isPaymasterError, mintRequestTypes, openPafiWebModal, openWebPopup, parseEip7702DelegatedAddress, quoteOperatorFeePt, quoteOperatorFeeUsdt, rawCallOp, sendWithPaymasterFallback, serializeUserOpToJsonRpc, setPafiWebModalAdapter, splitAuthorizationSig, webPopupAdapter };
2235
+ export { ApiError, BATCH_EXECUTOR_7702_IMPL, BATCH_EXECUTOR_ABI, BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA, BROKER_HASHES, type BuildDelegationUserOpParams, type BuildErc20TransferParams, type BuildPartialUserOpParams, type BuildPerpDepositViaRelayParams, type BuildPerpDepositWithGasDeductionParams, COMMON_POOLS, COMMON_TOKENS, CONTRACT_ADDRESSES, ChainConfig, type CheckEthAndBranchParams, ConfigurationError, type ContractAddresses, DUMMY_SIGNATURE_V07, type DelegateDirectParams, type DelegateDirectResult, type DelegateImpl, EIP712Signature, ENTRY_POINT_V07, ENTRY_POINT_V08, type Eip7702AuthorizationJsonRpc, type FallbackInfo, type FeeScenario, LoginMessageParams, MintRequest, type ModalOpenOptions, ORDERLY_RELAY_ABI, ORDERLY_VAULT_ABI, ORDERLY_VAULT_ADDRESSES, ORDERLY_VAULT_BASE_MAINNET, type Operation, OracleStaleError, type OrderlyRelayDepositRequest, PAFI_SERVICE_URLS, PAFI_SUBGRAPH_URL, PERMIT2_ADDRESS, POINT_TOKEN_FACTORY_ADDRESSES, POINT_TOKEN_IMPL_ADDRESSES, POINT_TOKEN_POOLS, type PackedUserOperationMessage, type PafiErrorType, type PafiProxyTransportParams, PafiSDK, PafiSDKConfig, PafiSdkError, type PafiServiceUrls, type PafiWebModalAdapter, type PafiWebModalHandle, type PartialUserOperation, type PaymasterConfig, type PaymasterFields, PointTokenDomainConfig, PoolKey, QUOTER_V2_ADDRESSES, type QuoteOperatorFeeForTransferConfig, type QuoteOperatorFeePtConfig, type QuoteOperatorFeeUsdtConfig, SCENARIO_GAS_UNITS, SDK_ERROR_HTTP_STATUS_CODE, SIMPLE_7702_IMPL_BASE_MAINNET, SUPPORTED_CHAINS, type SdkErrorHttpStatus, type SendWithPaymasterFallbackParams, type SignAuthorizationFn, type SignatureStruct, SignatureVerification, SignatureVerifyOptions, type SignedAuthorization, SigningError, SimulationError, type SmartAccountSender, type SponsorshipScenario, type SubmissionPath, TOKEN_HASHES, UNIVERSAL_ROUTER_ADDRESSES, type UserOpReceipt, type UserOpTypedData, type UserOperation, V3Path, V3_FACTORY_ADDRESSES, V3_POOL_INIT_CODE_HASH, V3_SWAP_ROUTER_ADDRESSES, ValidationError, type VaultDepositFE, ZERO_VALUE, assembleUserOperation, buildDelegationUserOp, buildEip7702Authorization, buildErc20TransferUserOp, buildPartialUserOperation, buildPerpDepositViaRelay, buildPerpDepositWithGasDeduction, buildUserOpTypedData, burnRequestTypes, checkDelegation, checkEthAndBranch, computeAccountId, computeAuthorizationHash, computeUserOpHash, computeV3PoolAddress, createPafiProxyTransport, decodeBatchExecuteCalls, defaultErrorTypeForStatus, delegateDirect, detectDelegateImpl, encodeBatchExecute, encodeV3Path, encodeV3PathReversed, erc20ApproveOp, erc20BurnOp, erc20TransferOp, fetchPafiPools, getAaNonce, getContractAddresses, getDummySignatureFor7702, getPafiServiceUrls, getPafiWebModalAdapter, isDelegatedTo, isDelegatedToTarget, isPaymasterError, mintRequestTypes, openPafiWebModal, openWebPopup, parseEip7702DelegatedAddress, quoteOperatorFeeForTransfer, quoteOperatorFeePt, quoteOperatorFeeUsdt, rawCallOp, sendWithPaymasterFallback, serializeUserOpToJsonRpc, setPafiWebModalAdapter, splitAuthorizationSig, webPopupAdapter };
package/dist/index.d.ts CHANGED
@@ -695,6 +695,59 @@ interface BuildPerpDepositViaRelayParams {
695
695
  */
696
696
  declare function buildPerpDepositViaRelay(params: BuildPerpDepositViaRelayParams): PartialUserOperation;
697
697
 
698
+ /**
699
+ * Builder for the `erc20-transfer` sponsored scenario.
700
+ *
701
+ * Produces two UserOps from one request — same nonce, same sender:
702
+ *
703
+ * sponsored (2 calls):
704
+ * [ token.transfer(PAFI_FEE_RECIPIENT, fee), token.transfer(recipient, amount) ]
705
+ *
706
+ * fallback (1 call):
707
+ * [ token.transfer(recipient, amount) ]
708
+ *
709
+ * Both calls use the SAME token contract — the fee currency equals the
710
+ * token being sent (decision documented in
711
+ * `docs/SPONSORED_ERC20_TRANSFER_SPEC.md`). Caller is responsible for
712
+ * validating the token is in the sponsorship allowlist; this function
713
+ * just builds calldata, no policy enforcement.
714
+ *
715
+ * The sponsor-relayer's IntentValidator enforces:
716
+ * - exactly 2 calls
717
+ * - both target the same token address
718
+ * - call[0] = transfer to PAFI_FEE_RECIPIENT, amount >= 95% quoted
719
+ * - call[1] = transfer to recipient (not fee recipient, not 0x0)
720
+ *
721
+ * Fallback variant (1 call, no fee) submits no-paymaster — caller's
722
+ * `sendWithPaymasterFallback` routes there when sponsorship is refused.
723
+ */
724
+ interface BuildErc20TransferParams {
725
+ /** EIP-7702 delegated user EOA (== smart account sender). */
726
+ userAddress: Address;
727
+ /** ERC-4337 nonce from the EntryPoint. */
728
+ aaNonce: bigint;
729
+ /** Whitelisted ERC-20 to transfer. */
730
+ tokenAddress: Address;
731
+ /** Final recipient. Must NOT equal `feeRecipient` or `0x0...0`. */
732
+ recipient: Address;
733
+ /** Amount to send (token-native units; 6-dec for stables, 18-dec for PT). */
734
+ amount: bigint;
735
+ /**
736
+ * Operator fee in the SAME token. Sponsored variant only — pass
737
+ * `undefined` or 0n to skip and build the no-fee variant.
738
+ */
739
+ feeAmount?: bigint;
740
+ /** Required when `feeAmount > 0`. */
741
+ feeRecipient?: Address;
742
+ /** Override gas limits if you have a tighter Pimlico estimate. */
743
+ gasLimits?: {
744
+ callGasLimit?: bigint;
745
+ verificationGasLimit?: bigint;
746
+ preVerificationGas?: bigint;
747
+ };
748
+ }
749
+ declare function buildErc20TransferUserOp(params: BuildErc20TransferParams): PartialUserOperation;
750
+
698
751
  /**
699
752
  * Build an ERC-20 `transfer(to, amount)` operation. Used inside a batch
700
753
  * to move fee tokens from the user to the fee recipient atomically with
@@ -1021,7 +1074,7 @@ interface PaymasterConfig {
1021
1074
  * validates the request's target contracts + function selectors
1022
1075
  * match the expected pattern for the scenario.
1023
1076
  */
1024
- type SponsorshipScenario = "mint" | "burn" | "swap" | "perp-deposit" | "delegate";
1077
+ type SponsorshipScenario = "mint" | "burn" | "swap" | "perp-deposit" | "delegate" | "erc20-transfer";
1025
1078
 
1026
1079
  /**
1027
1080
  * Submission path chosen by `checkEthAndBranch`.
@@ -1576,7 +1629,8 @@ declare function sendWithPaymasterFallback(params: SendWithPaymasterFallbackPara
1576
1629
  *
1577
1630
  * Pass an explicit `gasUnits` to override.
1578
1631
  */
1579
- type FeeScenario = "mint" | "burn" | "swap" | "perp-deposit" | "delegate";
1632
+ type FeeScenario = "mint" | "burn" | "swap" | "perp-deposit" | "delegate" | "erc20-transfer";
1633
+ declare const SCENARIO_GAS_UNITS: Record<FeeScenario, bigint>;
1580
1634
  interface QuoteOperatorFeePtConfig {
1581
1635
  provider: PublicClient;
1582
1636
  chainId: number;
@@ -1689,6 +1743,43 @@ interface QuoteOperatorFeeUsdtConfig {
1689
1743
  */
1690
1744
  declare function quoteOperatorFeeUsdt(config: QuoteOperatorFeeUsdtConfig): Promise<bigint>;
1691
1745
  declare function quoteOperatorFeePt(config: QuoteOperatorFeePtConfig): Promise<bigint>;
1746
+ /**
1747
+ * Router quoter for the `erc20-transfer` sponsored scenario — picks
1748
+ * the correct underlying quoter based on the token being transferred:
1749
+ *
1750
+ * USDC / USDT → `quoteOperatorFeeUsdt` (Chainlink-based, 6-dec)
1751
+ * active PT → `quoteOperatorFeePt` (Chainlink + V3 pool quote)
1752
+ *
1753
+ * Fee is denominated in the SAME token the user is sending — no extra
1754
+ * approval / swap step needed. Caller is responsible for verifying the
1755
+ * token IS in the sponsored allowlist before calling; this function
1756
+ * just routes by address equality.
1757
+ *
1758
+ * Returns the fee in the token's native units (6-dec for USDC/USDT,
1759
+ * 18-dec for PT). The sponsor-relayer reproduces the same quote
1760
+ * server-side with a 5% slack window.
1761
+ *
1762
+ * @example
1763
+ * ```ts
1764
+ * const fee = await quoteOperatorFeeForTransfer({
1765
+ * provider, chainId: 8453,
1766
+ * tokenAddress: USDC_ADDRESS,
1767
+ * });
1768
+ * ```
1769
+ */
1770
+ interface QuoteOperatorFeeForTransferConfig extends Omit<QuoteOperatorFeeUsdtConfig, "scenario"> {
1771
+ /** The ERC-20 the user is transferring. Drives both quote logic + fee currency. */
1772
+ tokenAddress: Address;
1773
+ /**
1774
+ * Subgraph URL override — only consumed when `tokenAddress` resolves
1775
+ * to a PT (USDC/USDT paths don't hit the subgraph).
1776
+ */
1777
+ subgraphUrl?: string;
1778
+ /** PT fallback (only used when `tokenAddress` is a PT and subgraph fails). */
1779
+ fallbackPtPriceUsdt?: number;
1780
+ fetchImpl?: typeof fetch;
1781
+ }
1782
+ declare function quoteOperatorFeeForTransfer(config: QuoteOperatorFeeForTransferConfig): Promise<bigint>;
1692
1783
 
1693
1784
  declare const BATCH_EXECUTOR_ADDRESS_BASE_MAINNET: `0x${string}`;
1694
1785
  declare const BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA: `0x${string}`;
@@ -1714,7 +1805,8 @@ declare const BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA: `0x${string}`;
1714
1805
  * chainlinkEthUsd — ETH/USD price feed used by FeeManager
1715
1806
  * orderlyRelay — Orderly perp-deposit Relay
1716
1807
  * pafiFeeRecipient — PAFI-controlled fee recipient (sponsored gas reimbursement)
1717
- * universalRouter — Uniswap V4 swap entry point
1808
+ * universalRouter — PAFI v3 fork UniversalRouter
1809
+ * permit2 — PAFI fork Permit2 (paired with the PAFI UR above)
1718
1810
  *
1719
1811
  * PointToken addresses are per-issuer and resolved from the issuer's
1720
1812
  * config (env / DB / PointTokenFactory readout). PointTokenFactory and
@@ -1761,6 +1853,15 @@ interface ContractAddresses {
1761
1853
  pafiFeeRecipient: Address;
1762
1854
  /** Uniswap V4 UniversalRouter — swap entry point for the swap handler. */
1763
1855
  universalRouter: Address;
1856
+ /**
1857
+ * PAFI fork of Uniswap's Permit2. Acts as the unified token-approval
1858
+ * authority for UniversalRouter swaps and any other PAFI-paired flow
1859
+ * that needs gasless allowances. Differs from Uniswap canonical
1860
+ * Permit2 (`0x000000000022D473...`) — must use the PAFI fork because
1861
+ * the fork UR was deployed against this Permit2 instance. Keep in
1862
+ * sync with `PERMIT2_ADDRESS` top-level export.
1863
+ */
1864
+ permit2: Address;
1764
1865
  }
1765
1866
  declare const CONTRACT_ADDRESSES: Record<number, ContractAddresses>;
1766
1867
  /**
@@ -2131,4 +2232,4 @@ declare class PafiSDK {
2131
2232
  signLoginMessage(message: string): Promise<Hex>;
2132
2233
  }
2133
2234
 
2134
- export { ApiError, BATCH_EXECUTOR_7702_IMPL, BATCH_EXECUTOR_ABI, BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA, BROKER_HASHES, type BuildDelegationUserOpParams, type BuildPartialUserOpParams, type BuildPerpDepositViaRelayParams, type BuildPerpDepositWithGasDeductionParams, COMMON_POOLS, COMMON_TOKENS, CONTRACT_ADDRESSES, ChainConfig, type CheckEthAndBranchParams, ConfigurationError, type ContractAddresses, DUMMY_SIGNATURE_V07, type DelegateDirectParams, type DelegateDirectResult, type DelegateImpl, EIP712Signature, ENTRY_POINT_V07, ENTRY_POINT_V08, type Eip7702AuthorizationJsonRpc, LoginMessageParams, MintRequest, type ModalOpenOptions, ORDERLY_RELAY_ABI, ORDERLY_VAULT_ABI, ORDERLY_VAULT_ADDRESSES, ORDERLY_VAULT_BASE_MAINNET, type Operation, OracleStaleError, type OrderlyRelayDepositRequest, PAFI_SERVICE_URLS, PAFI_SUBGRAPH_URL, PERMIT2_ADDRESS, POINT_TOKEN_FACTORY_ADDRESSES, POINT_TOKEN_IMPL_ADDRESSES, POINT_TOKEN_POOLS, type PackedUserOperationMessage, type PafiErrorType, type PafiProxyTransportParams, PafiSDK, PafiSDKConfig, PafiSdkError, type PafiServiceUrls, type PafiWebModalAdapter, type PafiWebModalHandle, type PartialUserOperation, type PaymasterConfig, type PaymasterFields, PointTokenDomainConfig, PoolKey, QUOTER_V2_ADDRESSES, type QuoteOperatorFeePtConfig, type QuoteOperatorFeeUsdtConfig, SDK_ERROR_HTTP_STATUS_CODE, SIMPLE_7702_IMPL_BASE_MAINNET, SUPPORTED_CHAINS, type SdkErrorHttpStatus, type SendWithPaymasterFallbackParams, type SignAuthorizationFn, type SignatureStruct, SignatureVerification, SignatureVerifyOptions, type SignedAuthorization, SigningError, SimulationError, type SmartAccountSender, type SponsorshipScenario, type SubmissionPath, TOKEN_HASHES, UNIVERSAL_ROUTER_ADDRESSES, type UserOpReceipt, type UserOpTypedData, type UserOperation, V3Path, V3_FACTORY_ADDRESSES, V3_POOL_INIT_CODE_HASH, V3_SWAP_ROUTER_ADDRESSES, ValidationError, type VaultDepositFE, ZERO_VALUE, assembleUserOperation, buildDelegationUserOp, buildEip7702Authorization, buildPartialUserOperation, buildPerpDepositViaRelay, buildPerpDepositWithGasDeduction, buildUserOpTypedData, burnRequestTypes, checkDelegation, checkEthAndBranch, computeAccountId, computeAuthorizationHash, computeUserOpHash, computeV3PoolAddress, createPafiProxyTransport, decodeBatchExecuteCalls, defaultErrorTypeForStatus, delegateDirect, detectDelegateImpl, encodeBatchExecute, encodeV3Path, encodeV3PathReversed, erc20ApproveOp, erc20BurnOp, erc20TransferOp, fetchPafiPools, getAaNonce, getContractAddresses, getDummySignatureFor7702, getPafiServiceUrls, getPafiWebModalAdapter, isDelegatedTo, isDelegatedToTarget, isPaymasterError, mintRequestTypes, openPafiWebModal, openWebPopup, parseEip7702DelegatedAddress, quoteOperatorFeePt, quoteOperatorFeeUsdt, rawCallOp, sendWithPaymasterFallback, serializeUserOpToJsonRpc, setPafiWebModalAdapter, splitAuthorizationSig, webPopupAdapter };
2235
+ export { ApiError, BATCH_EXECUTOR_7702_IMPL, BATCH_EXECUTOR_ABI, BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA, BROKER_HASHES, type BuildDelegationUserOpParams, type BuildErc20TransferParams, type BuildPartialUserOpParams, type BuildPerpDepositViaRelayParams, type BuildPerpDepositWithGasDeductionParams, COMMON_POOLS, COMMON_TOKENS, CONTRACT_ADDRESSES, ChainConfig, type CheckEthAndBranchParams, ConfigurationError, type ContractAddresses, DUMMY_SIGNATURE_V07, type DelegateDirectParams, type DelegateDirectResult, type DelegateImpl, EIP712Signature, ENTRY_POINT_V07, ENTRY_POINT_V08, type Eip7702AuthorizationJsonRpc, type FallbackInfo, type FeeScenario, LoginMessageParams, MintRequest, type ModalOpenOptions, ORDERLY_RELAY_ABI, ORDERLY_VAULT_ABI, ORDERLY_VAULT_ADDRESSES, ORDERLY_VAULT_BASE_MAINNET, type Operation, OracleStaleError, type OrderlyRelayDepositRequest, PAFI_SERVICE_URLS, PAFI_SUBGRAPH_URL, PERMIT2_ADDRESS, POINT_TOKEN_FACTORY_ADDRESSES, POINT_TOKEN_IMPL_ADDRESSES, POINT_TOKEN_POOLS, type PackedUserOperationMessage, type PafiErrorType, type PafiProxyTransportParams, PafiSDK, PafiSDKConfig, PafiSdkError, type PafiServiceUrls, type PafiWebModalAdapter, type PafiWebModalHandle, type PartialUserOperation, type PaymasterConfig, type PaymasterFields, PointTokenDomainConfig, PoolKey, QUOTER_V2_ADDRESSES, type QuoteOperatorFeeForTransferConfig, type QuoteOperatorFeePtConfig, type QuoteOperatorFeeUsdtConfig, SCENARIO_GAS_UNITS, SDK_ERROR_HTTP_STATUS_CODE, SIMPLE_7702_IMPL_BASE_MAINNET, SUPPORTED_CHAINS, type SdkErrorHttpStatus, type SendWithPaymasterFallbackParams, type SignAuthorizationFn, type SignatureStruct, SignatureVerification, SignatureVerifyOptions, type SignedAuthorization, SigningError, SimulationError, type SmartAccountSender, type SponsorshipScenario, type SubmissionPath, TOKEN_HASHES, UNIVERSAL_ROUTER_ADDRESSES, type UserOpReceipt, type UserOpTypedData, type UserOperation, V3Path, V3_FACTORY_ADDRESSES, V3_POOL_INIT_CODE_HASH, V3_SWAP_ROUTER_ADDRESSES, ValidationError, type VaultDepositFE, ZERO_VALUE, assembleUserOperation, buildDelegationUserOp, buildEip7702Authorization, buildErc20TransferUserOp, buildPartialUserOperation, buildPerpDepositViaRelay, buildPerpDepositWithGasDeduction, buildUserOpTypedData, burnRequestTypes, checkDelegation, checkEthAndBranch, computeAccountId, computeAuthorizationHash, computeUserOpHash, computeV3PoolAddress, createPafiProxyTransport, decodeBatchExecuteCalls, defaultErrorTypeForStatus, delegateDirect, detectDelegateImpl, encodeBatchExecute, encodeV3Path, encodeV3PathReversed, erc20ApproveOp, erc20BurnOp, erc20TransferOp, fetchPafiPools, getAaNonce, getContractAddresses, getDummySignatureFor7702, getPafiServiceUrls, getPafiWebModalAdapter, isDelegatedTo, isDelegatedToTarget, isPaymasterError, mintRequestTypes, openPafiWebModal, openWebPopup, parseEip7702DelegatedAddress, quoteOperatorFeeForTransfer, quoteOperatorFeePt, quoteOperatorFeeUsdt, rawCallOp, sendWithPaymasterFallback, serializeUserOpToJsonRpc, setPafiWebModalAdapter, splitAuthorizationSig, webPopupAdapter };
package/dist/index.js CHANGED
@@ -532,6 +532,43 @@ function buildPerpDepositViaRelay(params) {
532
532
  });
533
533
  }
534
534
 
535
+ // src/transfer/buildErc20Transfer.ts
536
+ function buildErc20TransferUserOp(params) {
537
+ if (params.amount <= 0n) {
538
+ throw new Error("buildErc20TransferUserOp: amount must be positive");
539
+ }
540
+ const operations = [];
541
+ if (params.feeAmount && params.feeAmount > 0n) {
542
+ if (!params.feeRecipient) {
543
+ throw new Error(
544
+ "buildErc20TransferUserOp: feeRecipient required when feeAmount > 0"
545
+ );
546
+ }
547
+ operations.push(
548
+ erc20TransferOp(
549
+ params.tokenAddress,
550
+ params.feeRecipient,
551
+ params.feeAmount
552
+ )
553
+ );
554
+ }
555
+ operations.push(
556
+ erc20TransferOp(params.tokenAddress, params.recipient, params.amount)
557
+ );
558
+ return buildPartialUserOperation({
559
+ sender: params.userAddress,
560
+ nonce: params.aaNonce,
561
+ operations,
562
+ gasLimits: {
563
+ // 2 simple ERC-20 transfers + 7702 batch overhead. ~70-90k actual;
564
+ // 200k matches the `delegate` scenario budget and covers premium.
565
+ callGasLimit: params.gasLimits?.callGasLimit ?? 200000n,
566
+ verificationGasLimit: params.gasLimits?.verificationGasLimit ?? 150000n,
567
+ preVerificationGas: params.gasLimits?.preVerificationGas ?? 50000n
568
+ }
569
+ });
570
+ }
571
+
535
572
  // src/userop/types.ts
536
573
  var ZERO_VALUE = 0n;
537
574
 
@@ -843,7 +880,13 @@ var CONTRACT_ADDRESSES = {
843
880
  // (`0x6fF5693b...`). The PAFI fork has its own factory + poolInitCodeHash,
844
881
  // so only this UR can route through PAFI v3 fork pools. Keep in sync with
845
882
  // `UNIVERSAL_ROUTER_ADDRESSES` in `chains.ts`.
846
- universalRouter: "0x008887C992A5bDC24097E717Bfb71CE89483c5A2"
883
+ universalRouter: "0x008887C992A5bDC24097E717Bfb71CE89483c5A2",
884
+ // PAFI fork Permit2 — DIFFERS from Uniswap canonical Permit2
885
+ // (`0x000000000022D473...`). PAFI's UR was deployed against this
886
+ // Permit2, so swaps signed against the canonical Permit2 fail
887
+ // signature verification. Keep in sync with `PERMIT2_ADDRESS`
888
+ // top-level export.
889
+ permit2: "0xEB450d21ae68D3303Cf5775A54Cc84EE7c3fC8eC"
847
890
  },
848
891
  // Base Sepolia — not in active use; placeholders kept so the map
849
892
  // compiles for tooling that enumerates chains.
@@ -856,7 +899,8 @@ var CONTRACT_ADDRESSES = {
856
899
  chainlinkEthUsd: PLACEHOLDER_DEAD("de02"),
857
900
  orderlyRelay: PLACEHOLDER_DEAD("de03"),
858
901
  pafiFeeRecipient: PLACEHOLDER_DEAD("de04"),
859
- universalRouter: PLACEHOLDER_DEAD("de05")
902
+ universalRouter: PLACEHOLDER_DEAD("de05"),
903
+ permit2: PLACEHOLDER_DEAD("de06")
860
904
  }
861
905
  };
862
906
  var POINT_TOKEN_FACTORY_ADDRESSES = {
@@ -1112,7 +1156,11 @@ var SCENARIO_GAS_UNITS = {
1112
1156
  burn: 500000n,
1113
1157
  swap: 700000n,
1114
1158
  "perp-deposit": 800000n,
1115
- delegate: 200000n
1159
+ delegate: 200000n,
1160
+ // 2-call batch: 1 ERC-20 fee transfer + 1 ERC-20 transfer + 7702
1161
+ // delegation overhead. ~70-90k empirical; 200k matches `delegate`
1162
+ // budget and gives headroom for premium variance.
1163
+ "erc20-transfer": 200000n
1116
1164
  };
1117
1165
  var DEFAULT_GAS_UNITS = 500000n;
1118
1166
  var DEFAULT_PREMIUM_BPS = 12e3;
@@ -1177,6 +1225,42 @@ async function quoteOperatorFeePt(config) {
1177
1225
  ]);
1178
1226
  return withPremium * ethPrice8dec * ptPerUsdt18dec / 10n ** 26n;
1179
1227
  }
1228
+ async function quoteOperatorFeeForTransfer(config) {
1229
+ const { provider, chainId, tokenAddress } = config;
1230
+ const { usdc, usdt } = getContractAddresses(chainId);
1231
+ const tokenLower = tokenAddress.toLowerCase();
1232
+ const isStable = usdc && tokenLower === usdc.toLowerCase() || tokenLower === usdt.toLowerCase();
1233
+ if (isStable) {
1234
+ return quoteOperatorFeeUsdt({
1235
+ provider,
1236
+ chainId,
1237
+ scenario: "erc20-transfer",
1238
+ gasUnits: config.gasUnits,
1239
+ premiumBps: config.premiumBps,
1240
+ chainlinkFeedAddress: config.chainlinkFeedAddress,
1241
+ usdtDecimals: config.usdtDecimals,
1242
+ allowStaleFallback: config.allowStaleFallback,
1243
+ fallbackEthPriceUsd: config.fallbackEthPriceUsd,
1244
+ onFallback: config.onFallback
1245
+ });
1246
+ }
1247
+ return quoteOperatorFeePt({
1248
+ provider,
1249
+ chainId,
1250
+ pointTokenAddress: tokenAddress,
1251
+ scenario: "erc20-transfer",
1252
+ gasUnits: config.gasUnits,
1253
+ premiumBps: config.premiumBps,
1254
+ chainlinkFeedAddress: config.chainlinkFeedAddress,
1255
+ subgraphUrl: config.subgraphUrl,
1256
+ usdtDecimals: config.usdtDecimals,
1257
+ allowStaleFallback: config.allowStaleFallback,
1258
+ fallbackEthPriceUsd: config.fallbackEthPriceUsd,
1259
+ fallbackPtPriceUsdt: config.fallbackPtPriceUsdt,
1260
+ onFallback: config.onFallback,
1261
+ fetchImpl: config.fetchImpl
1262
+ });
1263
+ }
1180
1264
  async function getEthPrice8dec(provider, feed, fallback, onFallback) {
1181
1265
  try {
1182
1266
  const result = await provider.readContract({
@@ -1592,6 +1676,7 @@ export {
1592
1676
  PafiSDK,
1593
1677
  PafiSdkError,
1594
1678
  QUOTER_V2_ADDRESSES,
1679
+ SCENARIO_GAS_UNITS,
1595
1680
  SDK_ERROR_HTTP_STATUS_CODE,
1596
1681
  SIMPLE_7702_IMPL_BASE_MAINNET,
1597
1682
  SPONSOR_AUTH_DOMAIN_ANCHOR_BASE_MAINNET,
@@ -1614,6 +1699,7 @@ export {
1614
1699
  buildDelegationUserOp,
1615
1700
  buildDomain,
1616
1701
  buildEip7702Authorization,
1702
+ buildErc20TransferUserOp,
1617
1703
  buildMintRequestTypedData,
1618
1704
  buildPartialUserOperation,
1619
1705
  buildPerpDepositViaRelay,
@@ -1677,6 +1763,7 @@ export {
1677
1763
  permit2Abi,
1678
1764
  pointTokenAbi,
1679
1765
  pointTokenFactoryAbi,
1766
+ quoteOperatorFeeForTransfer,
1680
1767
  quoteOperatorFeePt,
1681
1768
  quoteOperatorFeeUsdt,
1682
1769
  rawCallOp,