@pafi-dev/issuer 0.5.33 → 0.5.35

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.js CHANGED
@@ -1698,6 +1698,683 @@ async function handleRedeemStatus(params) {
1698
1698
  };
1699
1699
  }
1700
1700
 
1701
+ // src/api/mobileHandlers.ts
1702
+ import {
1703
+ ENTRY_POINT_V08,
1704
+ parseEip7702DelegatedAddress
1705
+ } from "@pafi-dev/core";
1706
+
1707
+ // src/userop-store/serialize.ts
1708
+ import { serializeUserOpToJsonRpc } from "@pafi-dev/core";
1709
+ function serializeEntryToJsonRpc(entry, signature, variant = "sponsored") {
1710
+ if (variant === "fallback") {
1711
+ if (!entry.fallback) {
1712
+ throw new Error(
1713
+ "serializeEntryToJsonRpc: variant=fallback requested but the stored entry has no `fallback` branch \u2014 caller should resubmit with variant='sponsored' or re-prepare with a fee configured."
1714
+ );
1715
+ }
1716
+ return serializeUserOpToJsonRpc(
1717
+ {
1718
+ sender: entry.sender,
1719
+ nonce: BigInt(entry.nonce),
1720
+ callData: entry.fallback.callData,
1721
+ callGasLimit: BigInt(entry.fallback.callGasLimit),
1722
+ verificationGasLimit: BigInt(entry.fallback.verificationGasLimit),
1723
+ preVerificationGas: BigInt(entry.fallback.preVerificationGas),
1724
+ maxFeePerGas: BigInt(entry.maxFeePerGas),
1725
+ maxPriorityFeePerGas: BigInt(entry.maxPriorityFeePerGas)
1726
+ // intentionally no paymaster — user pays ETH gas
1727
+ },
1728
+ signature
1729
+ );
1730
+ }
1731
+ return serializeUserOpToJsonRpc(
1732
+ {
1733
+ sender: entry.sender,
1734
+ nonce: BigInt(entry.nonce),
1735
+ callData: entry.callData,
1736
+ callGasLimit: BigInt(entry.callGasLimit),
1737
+ verificationGasLimit: BigInt(entry.verificationGasLimit),
1738
+ preVerificationGas: BigInt(entry.preVerificationGas),
1739
+ maxFeePerGas: BigInt(entry.maxFeePerGas),
1740
+ maxPriorityFeePerGas: BigInt(entry.maxPriorityFeePerGas),
1741
+ paymaster: entry.paymaster,
1742
+ paymasterVerificationGasLimit: entry.paymasterVerificationGasLimit != null ? BigInt(entry.paymasterVerificationGasLimit) : void 0,
1743
+ paymasterPostOpGasLimit: entry.paymasterPostOpGasLimit != null ? BigInt(entry.paymasterPostOpGasLimit) : void 0,
1744
+ paymasterData: entry.paymasterData
1745
+ },
1746
+ signature
1747
+ );
1748
+ }
1749
+
1750
+ // src/userop-store/prepareUserOp.ts
1751
+ import {
1752
+ buildUserOpTypedData,
1753
+ computeUserOpHash
1754
+ } from "@pafi-dev/core";
1755
+ function serializeUserOpTypedData(td) {
1756
+ return {
1757
+ domain: td.domain,
1758
+ types: td.types,
1759
+ primaryType: td.primaryType,
1760
+ message: {
1761
+ sender: td.message.sender,
1762
+ nonce: `0x${td.message.nonce.toString(16)}`,
1763
+ initCode: td.message.initCode,
1764
+ callData: td.message.callData,
1765
+ accountGasLimits: td.message.accountGasLimits,
1766
+ preVerificationGas: `0x${td.message.preVerificationGas.toString(
1767
+ 16
1768
+ )}`,
1769
+ gasFees: td.message.gasFees,
1770
+ paymasterAndData: td.message.paymasterAndData
1771
+ }
1772
+ };
1773
+ }
1774
+ function mergePaymasterFields(userOp, paymasterFields) {
1775
+ if (!paymasterFields) return userOp;
1776
+ const merged = {
1777
+ ...userOp
1778
+ };
1779
+ for (const [k, v] of Object.entries(paymasterFields)) {
1780
+ if (v !== void 0) merged[k] = v;
1781
+ }
1782
+ return merged;
1783
+ }
1784
+ async function prepareMobileUserOp(params) {
1785
+ const userOp = mergePaymasterFields(
1786
+ params.partialUserOp,
1787
+ params.paymasterFields
1788
+ );
1789
+ const userOpHash = computeUserOpHash(userOp, params.chainId);
1790
+ const typedData = serializeUserOpTypedData(
1791
+ buildUserOpTypedData(userOp, params.chainId)
1792
+ );
1793
+ let fallback;
1794
+ let fallbackEntry;
1795
+ if (params.partialUserOpFallback) {
1796
+ const fallbackUserOp = {
1797
+ ...params.partialUserOpFallback,
1798
+ maxFeePerGas: userOp.maxFeePerGas,
1799
+ maxPriorityFeePerGas: userOp.maxPriorityFeePerGas
1800
+ };
1801
+ const fallbackHash = computeUserOpHash(fallbackUserOp, params.chainId);
1802
+ const fallbackTypedData = serializeUserOpTypedData(
1803
+ buildUserOpTypedData(fallbackUserOp, params.chainId)
1804
+ );
1805
+ fallback = {
1806
+ userOp: fallbackUserOp,
1807
+ userOpHash: fallbackHash,
1808
+ typedData: fallbackTypedData
1809
+ };
1810
+ fallbackEntry = {
1811
+ callData: fallbackUserOp.callData,
1812
+ callGasLimit: fallbackUserOp.callGasLimit.toString(),
1813
+ verificationGasLimit: fallbackUserOp.verificationGasLimit.toString(),
1814
+ preVerificationGas: fallbackUserOp.preVerificationGas.toString(),
1815
+ userOpHash: fallbackHash
1816
+ };
1817
+ }
1818
+ const entry = {
1819
+ sender: userOp.sender,
1820
+ nonce: userOp.nonce.toString(),
1821
+ callData: userOp.callData,
1822
+ callGasLimit: userOp.callGasLimit.toString(),
1823
+ verificationGasLimit: userOp.verificationGasLimit.toString(),
1824
+ preVerificationGas: userOp.preVerificationGas.toString(),
1825
+ maxFeePerGas: userOp.maxFeePerGas.toString(),
1826
+ maxPriorityFeePerGas: userOp.maxPriorityFeePerGas.toString(),
1827
+ paymaster: userOp.paymaster,
1828
+ paymasterVerificationGasLimit: userOp.paymasterVerificationGasLimit?.toString(),
1829
+ paymasterPostOpGasLimit: userOp.paymasterPostOpGasLimit?.toString(),
1830
+ paymasterData: userOp.paymasterData,
1831
+ chainId: params.chainId,
1832
+ userOpHash,
1833
+ fallback: fallbackEntry
1834
+ };
1835
+ await params.store.save(params.lockId, entry, params.ttlSeconds);
1836
+ return {
1837
+ sponsored: { userOp, userOpHash, typedData },
1838
+ fallback,
1839
+ entry
1840
+ };
1841
+ }
1842
+
1843
+ // src/pafi-backend/helpers.ts
1844
+ var BundlerNotConfiguredError = class extends Error {
1845
+ code = "BUNDLER_NOT_CONFIGURED";
1846
+ constructor() {
1847
+ super(
1848
+ "PAFI backend client not configured \u2014 set PAFI_BACKEND_URL, PAFI_ISSUER_ID, PAFI_API_KEY to enable mobile submit."
1849
+ );
1850
+ this.name = "BundlerNotConfiguredError";
1851
+ }
1852
+ };
1853
+ var BundlerRejectedError = class extends Error {
1854
+ code = "BUNDLER_REJECTED";
1855
+ cause;
1856
+ constructor(message, cause) {
1857
+ super(message);
1858
+ this.name = "BundlerRejectedError";
1859
+ this.cause = cause;
1860
+ }
1861
+ };
1862
+ async function requestPaymaster(params) {
1863
+ if (!params.client) return void 0;
1864
+ const fn = params.functionName ?? defaultFunctionForScenario(params.scenario);
1865
+ try {
1866
+ return await params.client.requestSponsorship({
1867
+ chainId: params.chainId,
1868
+ scenario: params.scenario,
1869
+ userOp: params.userOp,
1870
+ target: {
1871
+ contract: params.pointTokenAddress,
1872
+ function: fn,
1873
+ pointToken: params.pointTokenAddress
1874
+ }
1875
+ });
1876
+ } catch (err) {
1877
+ const msg = err instanceof Error ? err.message : String(err);
1878
+ params.onWarning?.(`Paymaster sponsorship declined: ${msg}`);
1879
+ return void 0;
1880
+ }
1881
+ }
1882
+ function defaultFunctionForScenario(scenario) {
1883
+ switch (scenario) {
1884
+ case "mint":
1885
+ return "mint";
1886
+ case "burn":
1887
+ return "burn";
1888
+ case "swap":
1889
+ return "swap";
1890
+ case "perp-deposit":
1891
+ return "deposit";
1892
+ default:
1893
+ return scenario;
1894
+ }
1895
+ }
1896
+ async function relayUserOp(params) {
1897
+ if (!params.client) {
1898
+ throw new BundlerNotConfiguredError();
1899
+ }
1900
+ try {
1901
+ const result = await params.client.relayUserOperation({
1902
+ userOp: params.userOp,
1903
+ entryPoint: params.entryPoint,
1904
+ eip7702Auth: params.eip7702Auth
1905
+ });
1906
+ return { userOpHash: result.userOpHash };
1907
+ } catch (err) {
1908
+ const msg = err instanceof Error ? err.message : String(err);
1909
+ throw new BundlerRejectedError(msg, err);
1910
+ }
1911
+ }
1912
+
1913
+ // src/api/mobileHandlers.ts
1914
+ var PendingUserOpNotFoundError = class extends Error {
1915
+ code = "PENDING_USEROP_NOT_FOUND";
1916
+ constructor(lockId) {
1917
+ super(
1918
+ `No pending UserOp found for lockId ${lockId} \u2014 it may have expired or already been submitted.`
1919
+ );
1920
+ this.name = "PendingUserOpNotFoundError";
1921
+ }
1922
+ };
1923
+ async function handleMobilePrepare(params) {
1924
+ const [fees, userCode] = await Promise.all([
1925
+ params.provider.estimateFeesPerGas(),
1926
+ params.provider.getCode({ address: params.userAddress })
1927
+ ]);
1928
+ const needsDelegation = parseEip7702DelegatedAddress(userCode) === null;
1929
+ const sponsoredOp = {
1930
+ ...params.partialUserOp,
1931
+ maxFeePerGas: fees.maxFeePerGas ?? params.partialUserOp.maxFeePerGas ?? 0n,
1932
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? params.partialUserOp.maxPriorityFeePerGas ?? 0n
1933
+ };
1934
+ const paymasterFields = await requestPaymaster({
1935
+ client: params.pafiBackendClient,
1936
+ chainId: params.chainId,
1937
+ scenario: params.scenario,
1938
+ userOp: sponsoredOp,
1939
+ pointTokenAddress: params.pointTokenAddress,
1940
+ onWarning: params.onWarning
1941
+ });
1942
+ const prepared = await prepareMobileUserOp({
1943
+ lockId: params.lockId,
1944
+ partialUserOp: sponsoredOp,
1945
+ partialUserOpFallback: params.partialUserOpFallback,
1946
+ paymasterFields,
1947
+ chainId: params.chainId,
1948
+ store: params.store,
1949
+ ttlSeconds: params.ttlSeconds
1950
+ });
1951
+ return {
1952
+ ...prepared,
1953
+ isSponsored: !!paymasterFields,
1954
+ needsDelegation
1955
+ };
1956
+ }
1957
+ async function handleMobileSubmit(params) {
1958
+ const entry = await params.store.get(params.lockId);
1959
+ if (!entry) {
1960
+ throw new PendingUserOpNotFoundError(params.lockId);
1961
+ }
1962
+ const variant = params.variant ?? "sponsored";
1963
+ const userOpJson = serializeEntryToJsonRpc(entry, params.signature, variant);
1964
+ const result = await relayUserOp({
1965
+ client: params.pafiBackendClient,
1966
+ userOp: userOpJson,
1967
+ entryPoint: params.entryPoint ?? ENTRY_POINT_V08
1968
+ });
1969
+ await params.bindUserOpHash(params.lockId, result.userOpHash);
1970
+ await params.store.delete(params.lockId);
1971
+ return { userOpHash: result.userOpHash };
1972
+ }
1973
+
1974
+ // src/api/handlers/ptClaimHandler.ts
1975
+ import { getAddress as getAddress8 } from "viem";
1976
+ import {
1977
+ decodeBatchExecuteCalls,
1978
+ getContractAddresses as getContractAddresses2
1979
+ } from "@pafi-dev/core";
1980
+
1981
+ // src/issuer-state/types.ts
1982
+ var IssuerStateError = class extends Error {
1983
+ constructor(code, message, details) {
1984
+ super(message);
1985
+ this.code = code;
1986
+ this.details = details;
1987
+ this.name = "IssuerStateError";
1988
+ }
1989
+ code;
1990
+ details;
1991
+ };
1992
+
1993
+ // src/api/handlers/ptClaimHandler.ts
1994
+ var PTClaimError = class extends Error {
1995
+ constructor(code, message, details) {
1996
+ super(message);
1997
+ this.code = code;
1998
+ this.details = details;
1999
+ this.name = "PTClaimError";
2000
+ }
2001
+ code;
2002
+ details;
2003
+ };
2004
+ var DEFAULT_LOCK_MS = 15 * 60 * 1e3;
2005
+ var DEFAULT_SIG_DEADLINE_SEC2 = 15 * 60;
2006
+ var PTClaimHandler = class {
2007
+ cfg;
2008
+ constructor(config) {
2009
+ this.cfg = {
2010
+ ...config,
2011
+ lockDurationMs: config.lockDurationMs ?? DEFAULT_LOCK_MS,
2012
+ signatureDeadlineSeconds: config.signatureDeadlineSeconds ?? DEFAULT_SIG_DEADLINE_SEC2,
2013
+ now: config.now ?? (() => Date.now())
2014
+ };
2015
+ }
2016
+ async handle(request) {
2017
+ if (getAddress8(request.authenticatedAddress) !== getAddress8(request.userAddress)) {
2018
+ throw new PTClaimError(
2019
+ "VALIDATION_FAILED",
2020
+ `userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
2021
+ );
2022
+ }
2023
+ if (request.amount <= 0n) {
2024
+ throw new PTClaimError("INVALID_AMOUNT", "claim amount must be positive");
2025
+ }
2026
+ if (this.cfg.issuerStateValidator) {
2027
+ try {
2028
+ await this.cfg.issuerStateValidator.preValidateMint(
2029
+ request.pointTokenAddress,
2030
+ request.amount
2031
+ );
2032
+ } catch (err) {
2033
+ if (err instanceof IssuerStateError) throw err;
2034
+ throw new PTClaimError(
2035
+ "VALIDATION_FAILED",
2036
+ `issuer-state pre-validate failed: ${err instanceof Error ? err.message : String(err)}`
2037
+ );
2038
+ }
2039
+ }
2040
+ const { batchExecutor: batchExecutorAddress } = getContractAddresses2(
2041
+ request.chainId
2042
+ );
2043
+ const lockId = await this.cfg.ledger.lockForMinting(
2044
+ request.userAddress,
2045
+ request.amount,
2046
+ this.cfg.lockDurationMs,
2047
+ request.pointTokenAddress
2048
+ );
2049
+ const signatureDeadline = BigInt(
2050
+ Math.floor(this.cfg.now() / 1e3) + this.cfg.signatureDeadlineSeconds
2051
+ );
2052
+ const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee() : 0n;
2053
+ const domain = {
2054
+ name: this.cfg.pointTokenDomainName,
2055
+ chainId: request.chainId,
2056
+ verifyingContract: request.pointTokenAddress
2057
+ };
2058
+ let userOp;
2059
+ try {
2060
+ userOp = await this.cfg.relayService.prepareMint({
2061
+ userAddress: request.userAddress,
2062
+ aaNonce: request.aaNonce,
2063
+ batchExecutorAddress,
2064
+ pointTokenAddress: request.pointTokenAddress,
2065
+ amount: request.amount,
2066
+ issuerSignerWallet: this.cfg.issuerSignerWallet,
2067
+ domain,
2068
+ mintRequestNonce: request.mintRequestNonce,
2069
+ deadline: signatureDeadline
2070
+ // No feeAmount/feeRecipient — RelayService auto-resolves.
2071
+ });
2072
+ } catch (err) {
2073
+ throw new PTClaimError(
2074
+ "BUILD_FAILED",
2075
+ `prepareMint failed: ${err instanceof Error ? err.message : String(err)}`
2076
+ );
2077
+ }
2078
+ let fallback;
2079
+ if (feeAmount > 0n) {
2080
+ try {
2081
+ fallback = await this.cfg.relayService.prepareMint({
2082
+ userAddress: request.userAddress,
2083
+ aaNonce: request.aaNonce,
2084
+ batchExecutorAddress,
2085
+ pointTokenAddress: request.pointTokenAddress,
2086
+ amount: request.amount,
2087
+ issuerSignerWallet: this.cfg.issuerSignerWallet,
2088
+ domain,
2089
+ mintRequestNonce: request.mintRequestNonce,
2090
+ deadline: signatureDeadline,
2091
+ feeAmount: 0n
2092
+ });
2093
+ } catch (err) {
2094
+ throw new PTClaimError(
2095
+ "BUILD_FAILED",
2096
+ `prepareMint (fallback) failed: ${err instanceof Error ? err.message : String(err)}`
2097
+ );
2098
+ }
2099
+ }
2100
+ const calls = decodeBatchExecuteCalls(userOp.callData);
2101
+ const callsFallback = fallback ? decodeBatchExecuteCalls(fallback.callData) : void 0;
2102
+ return {
2103
+ userOp,
2104
+ fallback,
2105
+ lockId,
2106
+ feeAmount,
2107
+ signatureDeadline,
2108
+ expiresInSeconds: Math.floor(this.cfg.lockDurationMs / 1e3),
2109
+ calls,
2110
+ callsFallback
2111
+ };
2112
+ }
2113
+ };
2114
+
2115
+ // src/api/handlers/swapHandler.ts
2116
+ import {
2117
+ buildSwapWithGasDeduction,
2118
+ decodeBatchExecuteCalls as decodeBatchExecuteCalls2,
2119
+ findBestQuote,
2120
+ getContractAddresses as getContractAddresses3
2121
+ } from "@pafi-dev/core";
2122
+ var SwapError = class extends Error {
2123
+ constructor(code, message) {
2124
+ super(message);
2125
+ this.code = code;
2126
+ this.name = "SwapError";
2127
+ }
2128
+ code;
2129
+ };
2130
+ var DEFAULT_SLIPPAGE_BPS = 50;
2131
+ var DEFAULT_SWAP_DEADLINE_SEC = 5 * 60;
2132
+ var SwapHandler = class {
2133
+ cfg;
2134
+ constructor(config) {
2135
+ this.cfg = {
2136
+ ...config,
2137
+ defaultSlippageBps: config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS,
2138
+ defaultSwapDeadlineSeconds: config.defaultSwapDeadlineSeconds ?? DEFAULT_SWAP_DEADLINE_SEC,
2139
+ now: config.now ?? (() => Date.now())
2140
+ };
2141
+ }
2142
+ async handle(request) {
2143
+ if (request.amountIn <= 0n) {
2144
+ throw new SwapError("INVALID_AMOUNT", "amountIn must be positive");
2145
+ }
2146
+ const slippageBps = request.slippageBps ?? this.cfg.defaultSlippageBps;
2147
+ const { usdt, pafiFeeRecipient, universalRouter } = getContractAddresses3(
2148
+ request.chainId
2149
+ );
2150
+ const poolsResponse = await this.cfg.poolsProvider({
2151
+ chainId: request.chainId,
2152
+ pointTokenAddress: request.pointTokenAddress
2153
+ });
2154
+ if (poolsResponse.pools.length === 0) {
2155
+ throw new SwapError(
2156
+ "QUOTE_UNAVAILABLE",
2157
+ "no liquidity pool found for this point token"
2158
+ );
2159
+ }
2160
+ let fallbackQuote;
2161
+ try {
2162
+ fallbackQuote = await findBestQuote(
2163
+ this.cfg.provider,
2164
+ request.chainId,
2165
+ request.pointTokenAddress,
2166
+ usdt,
2167
+ request.amountIn,
2168
+ poolsResponse.pools
2169
+ );
2170
+ } catch {
2171
+ throw new SwapError(
2172
+ "QUOTE_UNAVAILABLE",
2173
+ "no swap path found for this point token"
2174
+ );
2175
+ }
2176
+ const estimatedUsdtOutFallback = fallbackQuote.bestRoute.amountOut;
2177
+ const minAmountOutFallback = estimatedUsdtOutFallback * BigInt(1e4 - slippageBps) / 10000n;
2178
+ const deadline = request.deadline ?? BigInt(
2179
+ Math.floor(this.cfg.now() / 1e3) + this.cfg.defaultSwapDeadlineSeconds
2180
+ );
2181
+ const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee() : 0n;
2182
+ if (feeAmount > 0n && feeAmount >= request.amountIn) {
2183
+ throw new SwapError(
2184
+ "FEE_EXCEEDS_AMOUNT",
2185
+ `gas fee (${feeAmount}) must be strictly less than swap amount (${request.amountIn})`
2186
+ );
2187
+ }
2188
+ const sponsoredAmountIn = request.amountIn - feeAmount;
2189
+ let estimatedUsdtOutSponsored = estimatedUsdtOutFallback;
2190
+ let sponsoredPath = fallbackQuote.bestRoute.path;
2191
+ if (feeAmount > 0n) {
2192
+ try {
2193
+ const sponsoredQuote = await findBestQuote(
2194
+ this.cfg.provider,
2195
+ request.chainId,
2196
+ request.pointTokenAddress,
2197
+ usdt,
2198
+ sponsoredAmountIn,
2199
+ poolsResponse.pools
2200
+ );
2201
+ estimatedUsdtOutSponsored = sponsoredQuote.bestRoute.amountOut;
2202
+ sponsoredPath = sponsoredQuote.bestRoute.path;
2203
+ } catch {
2204
+ throw new SwapError(
2205
+ "QUOTE_UNAVAILABLE",
2206
+ "no swap path found for sponsored amount (after fee deduction)"
2207
+ );
2208
+ }
2209
+ }
2210
+ const minAmountOutSponsored = estimatedUsdtOutSponsored * BigInt(1e4 - slippageBps) / 10000n;
2211
+ const sponsoredOp = buildSwapWithGasDeduction({
2212
+ userAddress: request.userAddress,
2213
+ aaNonce: request.aaNonce,
2214
+ pointTokenAddress: request.pointTokenAddress,
2215
+ outputTokenAddress: usdt,
2216
+ universalRouterAddress: universalRouter,
2217
+ amountIn: sponsoredAmountIn,
2218
+ minAmountOut: minAmountOutSponsored,
2219
+ swapPath: sponsoredPath,
2220
+ deadline,
2221
+ gasFeePt: feeAmount,
2222
+ feeRecipient: pafiFeeRecipient
2223
+ });
2224
+ const fallbackOp = feeAmount > 0n ? buildSwapWithGasDeduction({
2225
+ userAddress: request.userAddress,
2226
+ aaNonce: request.aaNonce,
2227
+ pointTokenAddress: request.pointTokenAddress,
2228
+ outputTokenAddress: usdt,
2229
+ universalRouterAddress: universalRouter,
2230
+ amountIn: request.amountIn,
2231
+ minAmountOut: minAmountOutFallback,
2232
+ swapPath: fallbackQuote.bestRoute.path,
2233
+ deadline,
2234
+ gasFeePt: 0n,
2235
+ feeRecipient: pafiFeeRecipient
2236
+ }) : void 0;
2237
+ return {
2238
+ userOp: sponsoredOp,
2239
+ fallback: fallbackOp,
2240
+ feeAmount,
2241
+ estimatedUsdtOut: estimatedUsdtOutSponsored,
2242
+ minAmountOut: minAmountOutSponsored,
2243
+ estimatedUsdtOutFallback: fallbackOp ? estimatedUsdtOutFallback : void 0,
2244
+ minAmountOutFallback: fallbackOp ? minAmountOutFallback : void 0,
2245
+ deadline,
2246
+ calls: decodeBatchExecuteCalls2(sponsoredOp.callData),
2247
+ callsFallback: fallbackOp ? decodeBatchExecuteCalls2(fallbackOp.callData) : void 0
2248
+ };
2249
+ }
2250
+ };
2251
+
2252
+ // src/api/handlers/perpDepositHandler.ts
2253
+ import {
2254
+ BROKER_HASHES,
2255
+ ORDERLY_RELAY_ABI,
2256
+ ORDERLY_VAULT_ABI,
2257
+ ORDERLY_VAULT_ADDRESSES,
2258
+ TOKEN_HASHES,
2259
+ buildPerpDepositViaRelay,
2260
+ computeAccountId,
2261
+ decodeBatchExecuteCalls as decodeBatchExecuteCalls3,
2262
+ getContractAddresses as getContractAddresses4
2263
+ } from "@pafi-dev/core";
2264
+ var PerpDepositError = class extends Error {
2265
+ constructor(code, message) {
2266
+ super(message);
2267
+ this.code = code;
2268
+ this.name = "PerpDepositError";
2269
+ }
2270
+ code;
2271
+ };
2272
+ var DEFAULT_MAX_FEE_PREMIUM_BPS = 5e3;
2273
+ var PerpDepositHandler = class {
2274
+ cfg;
2275
+ constructor(config) {
2276
+ this.cfg = {
2277
+ ...config,
2278
+ maxFeePremiumBps: config.maxFeePremiumBps ?? DEFAULT_MAX_FEE_PREMIUM_BPS
2279
+ };
2280
+ }
2281
+ async handle(request) {
2282
+ if (request.amount <= 0n) {
2283
+ throw new PerpDepositError("INVALID_AMOUNT", "amount must be positive");
2284
+ }
2285
+ const brokerHash = BROKER_HASHES[request.brokerId];
2286
+ const tokenHash = TOKEN_HASHES.USDC;
2287
+ const vault = ORDERLY_VAULT_ADDRESSES[request.chainId];
2288
+ if (!vault) {
2289
+ throw new PerpDepositError(
2290
+ "PERP_DEPOSIT_UNAVAILABLE",
2291
+ `no Orderly Vault for chainId ${request.chainId}`
2292
+ );
2293
+ }
2294
+ const { orderlyRelay: relayAddress, pafiFeeRecipient } = getContractAddresses4(request.chainId);
2295
+ const [usdcAddress, brokerAllowed] = await Promise.all([
2296
+ this.cfg.provider.readContract({
2297
+ address: vault,
2298
+ abi: ORDERLY_VAULT_ABI,
2299
+ functionName: "getAllowedToken",
2300
+ args: [tokenHash]
2301
+ }),
2302
+ this.cfg.provider.readContract({
2303
+ address: vault,
2304
+ abi: ORDERLY_VAULT_ABI,
2305
+ functionName: "getAllowedBroker",
2306
+ args: [brokerHash]
2307
+ })
2308
+ ]);
2309
+ if (!brokerAllowed) {
2310
+ throw new PerpDepositError(
2311
+ "BROKER_NOT_WHITELISTED",
2312
+ `broker "${request.brokerId}" is not whitelisted on Orderly Vault`
2313
+ );
2314
+ }
2315
+ const accountId = computeAccountId(request.userAddress, brokerHash);
2316
+ const requestForQuote = {
2317
+ token: usdcAddress,
2318
+ receiver: request.userAddress,
2319
+ brokerHash,
2320
+ totalAmount: request.amount,
2321
+ maxFee: 0n
2322
+ };
2323
+ const [relayTokenFee, ptGasFee] = await Promise.all([
2324
+ this.cfg.provider.readContract({
2325
+ address: relayAddress,
2326
+ abi: ORDERLY_RELAY_ABI,
2327
+ functionName: "quoteTokenFee",
2328
+ args: [requestForQuote]
2329
+ }),
2330
+ this.cfg.feeService ? this.cfg.feeService.estimateGasFee() : Promise.resolve(0n)
2331
+ ]);
2332
+ if (relayTokenFee >= request.amount) {
2333
+ throw new PerpDepositError(
2334
+ "RELAY_FEE_EXCEEDS_AMOUNT",
2335
+ `Relay quoted fee ${relayTokenFee} >= deposit amount ${request.amount}`
2336
+ );
2337
+ }
2338
+ const maxFee = relayTokenFee * BigInt(1e4 + this.cfg.maxFeePremiumBps) / 10000n;
2339
+ const depositReq = {
2340
+ token: usdcAddress,
2341
+ receiver: request.userAddress,
2342
+ brokerHash,
2343
+ totalAmount: request.amount,
2344
+ maxFee
2345
+ };
2346
+ const sponsoredOp = buildPerpDepositViaRelay({
2347
+ userAddress: request.userAddress,
2348
+ aaNonce: request.aaNonce,
2349
+ relayAddress,
2350
+ request: depositReq,
2351
+ pointTokenAddress: this.cfg.pointTokenAddress,
2352
+ gasFeePt: ptGasFee,
2353
+ gasFeePtRecipient: pafiFeeRecipient
2354
+ });
2355
+ const fallbackOp = ptGasFee > 0n ? buildPerpDepositViaRelay({
2356
+ userAddress: request.userAddress,
2357
+ aaNonce: request.aaNonce,
2358
+ relayAddress,
2359
+ request: depositReq
2360
+ }) : void 0;
2361
+ return {
2362
+ userOp: sponsoredOp,
2363
+ fallback: fallbackOp,
2364
+ feeAmount: ptGasFee,
2365
+ relayTokenFee,
2366
+ maxFee,
2367
+ netDeposit: request.amount - relayTokenFee,
2368
+ accountId,
2369
+ brokerHash,
2370
+ usdcAddress,
2371
+ relayAddress,
2372
+ calls: decodeBatchExecuteCalls3(sponsoredOp.callData),
2373
+ callsFallback: fallbackOp ? decodeBatchExecuteCalls3(fallbackOp.callData) : void 0
2374
+ };
2375
+ }
2376
+ };
2377
+
1701
2378
  // src/pools/subgraphPoolsProvider.ts
1702
2379
  import { isAddress } from "viem";
1703
2380
  import { PAFI_SUBGRAPH_URL } from "@pafi-dev/core";
@@ -2316,8 +2993,8 @@ var PafiBackendClient = class {
2316
2993
  };
2317
2994
 
2318
2995
  // src/config.ts
2319
- import { getAddress as getAddress8 } from "viem";
2320
- import { getContractAddresses as getContractAddresses2 } from "@pafi-dev/core";
2996
+ import { getAddress as getAddress9 } from "viem";
2997
+ import { getContractAddresses as getContractAddresses5 } from "@pafi-dev/core";
2321
2998
  function createIssuerService(config) {
2322
2999
  if (!config.provider) {
2323
3000
  throw new Error("createIssuerService: provider is required");
@@ -2337,7 +3014,7 @@ function createIssuerService(config) {
2337
3014
  "createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
2338
3015
  );
2339
3016
  }
2340
- const tokenAddresses = rawAddresses.map((a) => getAddress8(a));
3017
+ const tokenAddresses = rawAddresses.map((a) => getAddress9(a));
2341
3018
  const ledger = config.ledger;
2342
3019
  const sessionStore = config.sessionStore ?? new MemorySessionStore();
2343
3020
  const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
@@ -2387,7 +3064,7 @@ function createIssuerService(config) {
2387
3064
  indexers.set(tokenAddress, new PointIndexer(indexerConfig));
2388
3065
  }
2389
3066
  const firstIndexer = indexers.get(tokenAddresses[0]);
2390
- const chainAddresses = getContractAddresses2(config.chainId);
3067
+ const chainAddresses = getContractAddresses5(config.chainId);
2391
3068
  const resolvedContracts = {
2392
3069
  batchExecutor: chainAddresses.batchExecutor,
2393
3070
  usdt: chainAddresses.usdt,
@@ -2434,163 +3111,13 @@ function createIssuerService(config) {
2434
3111
  };
2435
3112
  }
2436
3113
 
2437
- // src/userop-store/serialize.ts
2438
- import { serializeUserOpToJsonRpc } from "@pafi-dev/core";
2439
- function serializeEntryToJsonRpc(entry, signature, variant = "sponsored") {
2440
- if (variant === "fallback") {
2441
- if (!entry.fallback) {
2442
- throw new Error(
2443
- "serializeEntryToJsonRpc: variant=fallback requested but the stored entry has no `fallback` branch \u2014 caller should resubmit with variant='sponsored' or re-prepare with a fee configured."
2444
- );
2445
- }
2446
- return serializeUserOpToJsonRpc(
2447
- {
2448
- sender: entry.sender,
2449
- nonce: BigInt(entry.nonce),
2450
- callData: entry.fallback.callData,
2451
- callGasLimit: BigInt(entry.fallback.callGasLimit),
2452
- verificationGasLimit: BigInt(entry.fallback.verificationGasLimit),
2453
- preVerificationGas: BigInt(entry.fallback.preVerificationGas),
2454
- maxFeePerGas: BigInt(entry.maxFeePerGas),
2455
- maxPriorityFeePerGas: BigInt(entry.maxPriorityFeePerGas)
2456
- // intentionally no paymaster — user pays ETH gas
2457
- },
2458
- signature
2459
- );
2460
- }
2461
- return serializeUserOpToJsonRpc(
2462
- {
2463
- sender: entry.sender,
2464
- nonce: BigInt(entry.nonce),
2465
- callData: entry.callData,
2466
- callGasLimit: BigInt(entry.callGasLimit),
2467
- verificationGasLimit: BigInt(entry.verificationGasLimit),
2468
- preVerificationGas: BigInt(entry.preVerificationGas),
2469
- maxFeePerGas: BigInt(entry.maxFeePerGas),
2470
- maxPriorityFeePerGas: BigInt(entry.maxPriorityFeePerGas),
2471
- paymaster: entry.paymaster,
2472
- paymasterVerificationGasLimit: entry.paymasterVerificationGasLimit != null ? BigInt(entry.paymasterVerificationGasLimit) : void 0,
2473
- paymasterPostOpGasLimit: entry.paymasterPostOpGasLimit != null ? BigInt(entry.paymasterPostOpGasLimit) : void 0,
2474
- paymasterData: entry.paymasterData
2475
- },
2476
- signature
2477
- );
2478
- }
2479
-
2480
- // src/userop-store/prepareUserOp.ts
2481
- import {
2482
- buildUserOpTypedData,
2483
- computeUserOpHash
2484
- } from "@pafi-dev/core";
2485
- function serializeUserOpTypedData(td) {
2486
- return {
2487
- domain: td.domain,
2488
- types: td.types,
2489
- primaryType: td.primaryType,
2490
- message: {
2491
- sender: td.message.sender,
2492
- nonce: `0x${td.message.nonce.toString(16)}`,
2493
- initCode: td.message.initCode,
2494
- callData: td.message.callData,
2495
- accountGasLimits: td.message.accountGasLimits,
2496
- preVerificationGas: `0x${td.message.preVerificationGas.toString(
2497
- 16
2498
- )}`,
2499
- gasFees: td.message.gasFees,
2500
- paymasterAndData: td.message.paymasterAndData
2501
- }
2502
- };
2503
- }
2504
- function mergePaymasterFields(userOp, paymasterFields) {
2505
- if (!paymasterFields) return userOp;
2506
- const merged = {
2507
- ...userOp
2508
- };
2509
- for (const [k, v] of Object.entries(paymasterFields)) {
2510
- if (v !== void 0) merged[k] = v;
2511
- }
2512
- return merged;
2513
- }
2514
- async function prepareMobileUserOp(params) {
2515
- const userOp = mergePaymasterFields(
2516
- params.partialUserOp,
2517
- params.paymasterFields
2518
- );
2519
- const userOpHash = computeUserOpHash(userOp, params.chainId);
2520
- const typedData = serializeUserOpTypedData(
2521
- buildUserOpTypedData(userOp, params.chainId)
2522
- );
2523
- let fallback;
2524
- let fallbackEntry;
2525
- if (params.partialUserOpFallback) {
2526
- const fallbackUserOp = {
2527
- ...params.partialUserOpFallback,
2528
- maxFeePerGas: userOp.maxFeePerGas,
2529
- maxPriorityFeePerGas: userOp.maxPriorityFeePerGas
2530
- };
2531
- const fallbackHash = computeUserOpHash(fallbackUserOp, params.chainId);
2532
- const fallbackTypedData = serializeUserOpTypedData(
2533
- buildUserOpTypedData(fallbackUserOp, params.chainId)
2534
- );
2535
- fallback = {
2536
- userOp: fallbackUserOp,
2537
- userOpHash: fallbackHash,
2538
- typedData: fallbackTypedData
2539
- };
2540
- fallbackEntry = {
2541
- callData: fallbackUserOp.callData,
2542
- callGasLimit: fallbackUserOp.callGasLimit.toString(),
2543
- verificationGasLimit: fallbackUserOp.verificationGasLimit.toString(),
2544
- preVerificationGas: fallbackUserOp.preVerificationGas.toString(),
2545
- userOpHash: fallbackHash
2546
- };
2547
- }
2548
- const entry = {
2549
- sender: userOp.sender,
2550
- nonce: userOp.nonce.toString(),
2551
- callData: userOp.callData,
2552
- callGasLimit: userOp.callGasLimit.toString(),
2553
- verificationGasLimit: userOp.verificationGasLimit.toString(),
2554
- preVerificationGas: userOp.preVerificationGas.toString(),
2555
- maxFeePerGas: userOp.maxFeePerGas.toString(),
2556
- maxPriorityFeePerGas: userOp.maxPriorityFeePerGas.toString(),
2557
- paymaster: userOp.paymaster,
2558
- paymasterVerificationGasLimit: userOp.paymasterVerificationGasLimit?.toString(),
2559
- paymasterPostOpGasLimit: userOp.paymasterPostOpGasLimit?.toString(),
2560
- paymasterData: userOp.paymasterData,
2561
- chainId: params.chainId,
2562
- userOpHash,
2563
- fallback: fallbackEntry
2564
- };
2565
- await params.store.save(params.lockId, entry, params.ttlSeconds);
2566
- return {
2567
- sponsored: { userOp, userOpHash, typedData },
2568
- fallback,
2569
- entry
2570
- };
2571
- }
2572
-
2573
3114
  // src/issuer-state/validator.ts
2574
- import { getAddress as getAddress9 } from "viem";
3115
+ import { getAddress as getAddress10 } from "viem";
2575
3116
  import {
2576
3117
  POINT_TOKEN_V2_ABI as POINT_TOKEN_V2_ABI3,
2577
3118
  issuerRegistryGetIssuerFlatAbi,
2578
- getContractAddresses as getContractAddresses3
3119
+ getContractAddresses as getContractAddresses6
2579
3120
  } from "@pafi-dev/core";
2580
-
2581
- // src/issuer-state/types.ts
2582
- var IssuerStateError = class extends Error {
2583
- constructor(code, message, details) {
2584
- super(message);
2585
- this.code = code;
2586
- this.details = details;
2587
- this.name = "IssuerStateError";
2588
- }
2589
- code;
2590
- details;
2591
- };
2592
-
2593
- // src/issuer-state/validator.ts
2594
3121
  var ISSUER_RECORD_TTL_MS = 3e4;
2595
3122
  var IssuerStateValidator = class _IssuerStateValidator {
2596
3123
  constructor(provider, registryAddress) {
@@ -2607,7 +3134,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
2607
3134
  * `CONTRACT_ADDRESSES` map for the given chain.
2608
3135
  */
2609
3136
  static forChain(provider, chainId) {
2610
- const { issuerRegistry } = getContractAddresses3(chainId);
3137
+ const { issuerRegistry } = getContractAddresses6(chainId);
2611
3138
  return new _IssuerStateValidator(provider, issuerRegistry);
2612
3139
  }
2613
3140
  /**
@@ -2616,7 +3143,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
2616
3143
  */
2617
3144
  invalidate(pointToken) {
2618
3145
  if (pointToken) {
2619
- const key = getAddress9(pointToken);
3146
+ const key = getAddress10(pointToken);
2620
3147
  this.pointTokenIssuerCache.delete(key);
2621
3148
  this.stateCache.delete(key);
2622
3149
  this.inflight.delete(key);
@@ -2631,7 +3158,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
2631
3158
  * The issuer field is set at `initialize()` and never changes.
2632
3159
  */
2633
3160
  async getIssuerAddressForPointToken(pointToken) {
2634
- const key = getAddress9(pointToken);
3161
+ const key = getAddress10(pointToken);
2635
3162
  const cached = this.pointTokenIssuerCache.get(key);
2636
3163
  if (cached) return cached;
2637
3164
  const issuer = await this.provider.readContract({
@@ -2639,15 +3166,15 @@ var IssuerStateValidator = class _IssuerStateValidator {
2639
3166
  abi: POINT_TOKEN_V2_ABI3,
2640
3167
  functionName: "issuer"
2641
3168
  });
2642
- this.pointTokenIssuerCache.set(key, getAddress9(issuer));
2643
- return getAddress9(issuer);
3169
+ this.pointTokenIssuerCache.set(key, getAddress10(issuer));
3170
+ return getAddress10(issuer);
2644
3171
  }
2645
3172
  /**
2646
3173
  * Read registry record + totalSupply, with 30s cache and in-flight
2647
3174
  * deduplication. Does NOT throw on inactive/missing — returns raw state.
2648
3175
  */
2649
3176
  async getIssuerState(pointToken) {
2650
- const tokenAddr = getAddress9(pointToken);
3177
+ const tokenAddr = getAddress10(pointToken);
2651
3178
  const now = Date.now();
2652
3179
  const cached = this.stateCache.get(tokenAddr);
2653
3180
  if (cached && cached.expiresAt > now) return cached.value;
@@ -2750,6 +3277,8 @@ export {
2750
3277
  AuthError,
2751
3278
  AuthService,
2752
3279
  BalanceAggregator,
3280
+ BundlerNotConfiguredError,
3281
+ BundlerRejectedError,
2753
3282
  BurnIndexer,
2754
3283
  DefaultPolicyEngine,
2755
3284
  FeeManager,
@@ -2762,13 +3291,20 @@ export {
2762
3291
  NonceManager,
2763
3292
  PAFI_ISSUER_SDK_VERSION,
2764
3293
  PAFI_SUBGRAPH_URL,
3294
+ PTClaimError,
3295
+ PTClaimHandler,
2765
3296
  PTRedeemError,
2766
3297
  PTRedeemHandler,
2767
3298
  PafiBackendClient,
2768
3299
  PafiBackendError,
3300
+ PendingUserOpNotFoundError,
3301
+ PerpDepositError,
3302
+ PerpDepositHandler,
2769
3303
  PointIndexer,
2770
3304
  RelayError,
2771
3305
  RelayService,
3306
+ SwapError,
3307
+ SwapHandler,
2772
3308
  TopUpRedemptionError,
2773
3309
  TopUpRedemptionHandler,
2774
3310
  authenticateRequest,
@@ -2777,9 +3313,13 @@ export {
2777
3313
  createSubgraphNativeUsdtQuoter,
2778
3314
  createSubgraphPoolsProvider,
2779
3315
  handleClaimStatus,
3316
+ handleMobilePrepare,
3317
+ handleMobileSubmit,
2780
3318
  handleRedeemStatus,
2781
3319
  mergePaymasterFields,
2782
3320
  prepareMobileUserOp,
3321
+ relayUserOp,
3322
+ requestPaymaster,
2783
3323
  serializeEntryToJsonRpc,
2784
3324
  serializeUserOpTypedData
2785
3325
  };