@pafi-dev/trading 0.4.0 → 0.4.2
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.cjs +626 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +365 -18
- package/dist/index.d.ts +365 -18
- package/dist/index.js +618 -35
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -71,7 +71,7 @@ async function quoteExactInput(client, quoterAddress, exactCurrency, path, exact
|
|
|
71
71
|
address: quoterAddress,
|
|
72
72
|
abi: v4QuoterAbi,
|
|
73
73
|
functionName: "quoteExactInput",
|
|
74
|
-
args: [{ exactCurrency, path, exactAmount
|
|
74
|
+
args: [{ exactCurrency, path, exactAmount }]
|
|
75
75
|
});
|
|
76
76
|
return { amountOut, gasEstimate, path };
|
|
77
77
|
}
|
|
@@ -84,7 +84,7 @@ async function quoteExactInputSingle(client, quoterAddress, poolKey, zeroForOne,
|
|
|
84
84
|
{
|
|
85
85
|
poolKey,
|
|
86
86
|
zeroForOne,
|
|
87
|
-
exactAmount
|
|
87
|
+
exactAmount,
|
|
88
88
|
hookData
|
|
89
89
|
}
|
|
90
90
|
]
|
|
@@ -101,13 +101,7 @@ async function quoteBestRoute(client, quoterAddress, exactCurrency, routes, exac
|
|
|
101
101
|
{
|
|
102
102
|
exactCurrency,
|
|
103
103
|
path,
|
|
104
|
-
|
|
105
|
-
// signature takes `uint256` (a bigint at the wire level), but
|
|
106
|
-
// our pinned ABI types it as `number`. The runtime is fine
|
|
107
|
-
// (bigint serializes correctly) — this cast tells TS to skip
|
|
108
|
-
// the structural check until we re-generate the ABI with the
|
|
109
|
-
// correct uint256 type. Tracked: SDK_CORE_TRADING_AUDIT.md H11.
|
|
110
|
-
exactAmount: BigInt(exactAmount)
|
|
104
|
+
exactAmount
|
|
111
105
|
}
|
|
112
106
|
]
|
|
113
107
|
})),
|
|
@@ -148,6 +142,84 @@ async function findBestQuote(client, chainId, tokenIn, tokenOut, exactAmount, po
|
|
|
148
142
|
}
|
|
149
143
|
return quoteBestRoute(client, quoter, tokenIn, paths, exactAmount);
|
|
150
144
|
}
|
|
145
|
+
async function quoteExactOutput(client, quoterAddress, exactCurrency, path, exactAmount) {
|
|
146
|
+
const [amountIn, gasEstimate] = await client.readContract({
|
|
147
|
+
address: quoterAddress,
|
|
148
|
+
abi: v4QuoterAbi,
|
|
149
|
+
functionName: "quoteExactOutput",
|
|
150
|
+
args: [{ exactCurrency, path, exactAmount }]
|
|
151
|
+
});
|
|
152
|
+
return { amountIn, gasEstimate, path };
|
|
153
|
+
}
|
|
154
|
+
async function quoteExactOutputSingle(client, quoterAddress, poolKey, zeroForOne, exactAmount, hookData) {
|
|
155
|
+
const [amountIn, gasEstimate] = await client.readContract({
|
|
156
|
+
address: quoterAddress,
|
|
157
|
+
abi: v4QuoterAbi,
|
|
158
|
+
functionName: "quoteExactOutputSingle",
|
|
159
|
+
args: [
|
|
160
|
+
{
|
|
161
|
+
poolKey,
|
|
162
|
+
zeroForOne,
|
|
163
|
+
exactAmount,
|
|
164
|
+
hookData
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
});
|
|
168
|
+
return { amountIn, gasEstimate };
|
|
169
|
+
}
|
|
170
|
+
async function quoteBestRouteExactOut(client, quoterAddress, exactCurrency, routes, exactAmount) {
|
|
171
|
+
const results = await client.multicall({
|
|
172
|
+
contracts: routes.map((path) => ({
|
|
173
|
+
address: quoterAddress,
|
|
174
|
+
abi: v4QuoterAbi,
|
|
175
|
+
functionName: "quoteExactOutput",
|
|
176
|
+
args: [
|
|
177
|
+
{
|
|
178
|
+
exactCurrency,
|
|
179
|
+
path,
|
|
180
|
+
exactAmount
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
})),
|
|
184
|
+
allowFailure: true
|
|
185
|
+
});
|
|
186
|
+
const allRoutes = [];
|
|
187
|
+
let firstFailure;
|
|
188
|
+
for (let i = 0; i < results.length; i++) {
|
|
189
|
+
const r = results[i];
|
|
190
|
+
if (r.status === "success") {
|
|
191
|
+
const [amountIn, gasEstimate] = r.result;
|
|
192
|
+
allRoutes.push({ amountIn, gasEstimate, path: routes[i] });
|
|
193
|
+
} else if (firstFailure === void 0) {
|
|
194
|
+
const errMsg = r.error instanceof Error ? r.error.message : String(r.error ?? "unknown");
|
|
195
|
+
firstFailure = errMsg;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (allRoutes.length === 0) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`No valid exact-output routes found to ${exactCurrency} (${routes.length} candidates probed)` + (firstFailure ? `; first failure: ${firstFailure}` : "")
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const bestRoute = allRoutes.reduce(
|
|
204
|
+
(best, current) => current.amountIn < best.amountIn ? current : best
|
|
205
|
+
);
|
|
206
|
+
return { bestRoute, allRoutes };
|
|
207
|
+
}
|
|
208
|
+
async function findBestQuoteExactOut(client, chainId, tokenIn, tokenOut, exactAmount, pools = [], quoterAddress, maxHops = 3) {
|
|
209
|
+
const quoter = quoterAddress ?? V4_QUOTER_ADDRESSES[chainId];
|
|
210
|
+
if (!quoter) {
|
|
211
|
+
throw new Error(`No V4 Quoter address configured for chain ${chainId}`);
|
|
212
|
+
}
|
|
213
|
+
const commonPools = COMMON_POOLS2[chainId] ?? [];
|
|
214
|
+
const allPools = [...pools, ...commonPools];
|
|
215
|
+
const paths = buildAllPaths(allPools, tokenOut, tokenIn, maxHops);
|
|
216
|
+
if (paths.length === 0) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
`No exact-output paths found to ${tokenOut} from ${tokenIn}`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
return quoteBestRouteExactOut(client, quoter, tokenOut, paths, exactAmount);
|
|
222
|
+
}
|
|
151
223
|
|
|
152
224
|
// src/swap/approval.ts
|
|
153
225
|
import { encodeFunctionData } from "viem";
|
|
@@ -180,8 +252,11 @@ function buildPermit2ApprovalCalldata(token, spender, amount, expiration) {
|
|
|
180
252
|
import { encodeAbiParameters, encodePacked } from "viem";
|
|
181
253
|
var V4_SWAP = 16;
|
|
182
254
|
var SWAP_EXACT_IN = 7;
|
|
255
|
+
var SWAP_EXACT_OUT_SINGLE = 8;
|
|
256
|
+
var SWAP_EXACT_OUT = 9;
|
|
183
257
|
var SETTLE_ALL = 12;
|
|
184
258
|
var TAKE_ALL = 15;
|
|
259
|
+
var UINT128_MAX = 2n ** 128n - 1n;
|
|
185
260
|
var PATH_KEY_ABI_COMPONENTS = [
|
|
186
261
|
{ name: "intermediateCurrency", type: "address" },
|
|
187
262
|
{ name: "fee", type: "uint256" },
|
|
@@ -199,6 +274,16 @@ var EXACT_INPUT_PARAMS_ABI = [
|
|
|
199
274
|
{ name: "amountIn", type: "uint128" },
|
|
200
275
|
{ name: "amountOutMinimum", type: "uint128" }
|
|
201
276
|
];
|
|
277
|
+
var EXACT_OUTPUT_PARAMS_ABI = [
|
|
278
|
+
{ name: "currencyOut", type: "address" },
|
|
279
|
+
{
|
|
280
|
+
name: "path",
|
|
281
|
+
type: "tuple[]",
|
|
282
|
+
components: PATH_KEY_ABI_COMPONENTS
|
|
283
|
+
},
|
|
284
|
+
{ name: "amountOut", type: "uint128" },
|
|
285
|
+
{ name: "amountInMaximum", type: "uint128" }
|
|
286
|
+
];
|
|
202
287
|
function buildV4SwapInput(currencyIn, path, amountIn, minAmountOut, outputCurrency) {
|
|
203
288
|
const actions = encodePacked(
|
|
204
289
|
["uint8", "uint8", "uint8"],
|
|
@@ -250,7 +335,81 @@ function buildUniversalRouterExecuteArgs(currencyIn, path, amountIn, minAmountOu
|
|
|
250
335
|
];
|
|
251
336
|
return { commands, inputs };
|
|
252
337
|
}
|
|
338
|
+
function buildV4SwapInputExactOut(currencyOut, path, amountOut, maxAmountIn, inputCurrency) {
|
|
339
|
+
if (amountOut <= 0n || amountOut > UINT128_MAX) {
|
|
340
|
+
throw new Error(
|
|
341
|
+
`buildV4SwapInputExactOut: amountOut (${amountOut}) must be in (0, 2^128-1]`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
if (maxAmountIn <= 0n || maxAmountIn > UINT128_MAX) {
|
|
345
|
+
throw new Error(
|
|
346
|
+
`buildV4SwapInputExactOut: maxAmountIn (${maxAmountIn}) must be in (0, 2^128-1]`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
const actions = encodePacked(
|
|
350
|
+
["uint8", "uint8", "uint8"],
|
|
351
|
+
[SWAP_EXACT_OUT, SETTLE_ALL, TAKE_ALL]
|
|
352
|
+
);
|
|
353
|
+
const swapParam = encodeAbiParameters(
|
|
354
|
+
[{ name: "swap", type: "tuple", components: EXACT_OUTPUT_PARAMS_ABI }],
|
|
355
|
+
[
|
|
356
|
+
{
|
|
357
|
+
currencyOut,
|
|
358
|
+
path: path.map((p) => ({
|
|
359
|
+
intermediateCurrency: p.intermediateCurrency,
|
|
360
|
+
fee: BigInt(p.fee),
|
|
361
|
+
tickSpacing: p.tickSpacing,
|
|
362
|
+
hooks: p.hooks,
|
|
363
|
+
hookData: p.hookData
|
|
364
|
+
})),
|
|
365
|
+
amountOut,
|
|
366
|
+
amountInMaximum: maxAmountIn
|
|
367
|
+
}
|
|
368
|
+
]
|
|
369
|
+
);
|
|
370
|
+
const settleParam = encodeAbiParameters(
|
|
371
|
+
[
|
|
372
|
+
{ name: "currency", type: "address" },
|
|
373
|
+
{ name: "maxAmount", type: "uint256" }
|
|
374
|
+
],
|
|
375
|
+
[inputCurrency, maxAmountIn]
|
|
376
|
+
);
|
|
377
|
+
const takeParam = encodeAbiParameters(
|
|
378
|
+
[
|
|
379
|
+
{ name: "currency", type: "address" },
|
|
380
|
+
{ name: "minAmount", type: "uint256" }
|
|
381
|
+
],
|
|
382
|
+
[currencyOut, amountOut]
|
|
383
|
+
);
|
|
384
|
+
return encodeAbiParameters(
|
|
385
|
+
[
|
|
386
|
+
{ name: "actions", type: "bytes" },
|
|
387
|
+
{ name: "params", type: "bytes[]" }
|
|
388
|
+
],
|
|
389
|
+
[actions, [swapParam, settleParam, takeParam]]
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
function buildUniversalRouterExecuteArgsExactOut(currencyOut, path, amountOut, maxAmountIn, inputCurrency) {
|
|
393
|
+
const commands = encodePacked(["uint8"], [V4_SWAP]);
|
|
394
|
+
const inputs = [
|
|
395
|
+
buildV4SwapInputExactOut(
|
|
396
|
+
currencyOut,
|
|
397
|
+
path,
|
|
398
|
+
amountOut,
|
|
399
|
+
maxAmountIn,
|
|
400
|
+
inputCurrency
|
|
401
|
+
)
|
|
402
|
+
];
|
|
403
|
+
return { commands, inputs };
|
|
404
|
+
}
|
|
405
|
+
var _buildSwapFromQuoteWarned = false;
|
|
253
406
|
function buildSwapFromQuote(params) {
|
|
407
|
+
if (!_buildSwapFromQuoteWarned) {
|
|
408
|
+
_buildSwapFromQuoteWarned = true;
|
|
409
|
+
console.warn(
|
|
410
|
+
"[PAFI] DEPRECATION (v1.4+): `buildSwapFromQuote` from @pafi-dev/trading is deprecated and will be removed in v2.0. Use `buildUniversalRouterExecuteArgs` directly with `quote.bestRoute.path`."
|
|
411
|
+
);
|
|
412
|
+
}
|
|
254
413
|
return buildUniversalRouterExecuteArgs(
|
|
255
414
|
params.currencyIn,
|
|
256
415
|
params.quote.path,
|
|
@@ -358,6 +517,82 @@ function buildSwapUserOp(params) {
|
|
|
358
517
|
}
|
|
359
518
|
});
|
|
360
519
|
}
|
|
520
|
+
function buildSwapUserOpExactOut(params) {
|
|
521
|
+
if (params.amountOut <= 0n) {
|
|
522
|
+
throw new Error("buildSwapUserOpExactOut: amountOut must be positive");
|
|
523
|
+
}
|
|
524
|
+
if (params.maxAmountIn <= 0n) {
|
|
525
|
+
throw new Error("buildSwapUserOpExactOut: maxAmountIn must be positive");
|
|
526
|
+
}
|
|
527
|
+
if (params.gasFeeAmountInput < 0n) {
|
|
528
|
+
throw new Error(
|
|
529
|
+
"buildSwapUserOpExactOut: gasFeeAmountInput must be non-negative"
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
if (params.swapPath.length === 0) {
|
|
533
|
+
throw new Error(
|
|
534
|
+
"buildSwapUserOpExactOut: swapPath must contain at least one PathKey"
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
const PERMIT2_EXPIRATION_MAX = 2n ** 48n - 1n;
|
|
538
|
+
if (params.deadline <= 0n || params.deadline > PERMIT2_EXPIRATION_MAX) {
|
|
539
|
+
throw new Error(
|
|
540
|
+
`buildSwapUserOpExactOut: deadline (${params.deadline}) must be unix seconds in (0, 2^48-1]. Did you accidentally pass milliseconds?`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
const { commands, inputs } = buildUniversalRouterExecuteArgsExactOut(
|
|
544
|
+
params.outputTokenAddress,
|
|
545
|
+
params.swapPath,
|
|
546
|
+
params.amountOut,
|
|
547
|
+
params.maxAmountIn,
|
|
548
|
+
params.inputTokenAddress
|
|
549
|
+
);
|
|
550
|
+
const swapCallData = encodeFunctionData2({
|
|
551
|
+
abi: universalRouterAbi2,
|
|
552
|
+
functionName: "execute",
|
|
553
|
+
args: [commands, inputs, params.deadline]
|
|
554
|
+
});
|
|
555
|
+
const permit2ApproveData = buildPermit2ApprovalCalldata(
|
|
556
|
+
params.inputTokenAddress,
|
|
557
|
+
params.universalRouterAddress,
|
|
558
|
+
params.maxAmountIn,
|
|
559
|
+
Number(params.deadline)
|
|
560
|
+
);
|
|
561
|
+
const operations = [];
|
|
562
|
+
if (params.gasFeeAmountInput > 0n) {
|
|
563
|
+
operations.push(
|
|
564
|
+
erc20TransferOp(
|
|
565
|
+
params.inputTokenAddress,
|
|
566
|
+
params.feeRecipient,
|
|
567
|
+
params.gasFeeAmountInput
|
|
568
|
+
)
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
operations.push(
|
|
572
|
+
erc20ApproveOp(params.inputTokenAddress, PERMIT2_ADDRESS, params.maxAmountIn),
|
|
573
|
+
rawCallOp(PERMIT2_ADDRESS, permit2ApproveData),
|
|
574
|
+
rawCallOp(params.universalRouterAddress, swapCallData)
|
|
575
|
+
);
|
|
576
|
+
return buildPartialUserOperation({
|
|
577
|
+
sender: params.userAddress,
|
|
578
|
+
nonce: params.aaNonce,
|
|
579
|
+
operations,
|
|
580
|
+
gasLimits: {
|
|
581
|
+
// +50k headroom for the additional pre-swap ERC-20 transfer when
|
|
582
|
+
// the input-side fee is non-zero. Keeps margin even when fee=0n.
|
|
583
|
+
callGasLimit: params.gasLimits?.callGasLimit ?? 750000n,
|
|
584
|
+
verificationGasLimit: params.gasLimits?.verificationGasLimit ?? 150000n,
|
|
585
|
+
preVerificationGas: params.gasLimits?.preVerificationGas ?? 50000n
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/swap/slippage.ts
|
|
591
|
+
var MAX_SLIPPAGE_BPS = 5e3;
|
|
592
|
+
function isValidSlippageBps(value) {
|
|
593
|
+
if (value === void 0) return true;
|
|
594
|
+
return Number.isInteger(value) && value >= 0 && value <= MAX_SLIPPAGE_BPS;
|
|
595
|
+
}
|
|
361
596
|
|
|
362
597
|
// src/api/handlers.ts
|
|
363
598
|
var TradingHandlers = class {
|
|
@@ -446,10 +681,9 @@ var TradingHandlers = class {
|
|
|
446
681
|
* net. Quote response surfaces both `estimatedOutputAmount` (gross)
|
|
447
682
|
* and `outputNet` so the FE can display reality.
|
|
448
683
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
* `request.userAddress`. See SDK_CORE_TRADING_AUDIT.md C6.
|
|
684
|
+
* `authenticatedAddress` first param: caller (issuer controller / FE
|
|
685
|
+
* proxy) MUST pass the address extracted from the verified
|
|
686
|
+
* session/JWT. Handler asserts it equals `request.userAddress`.
|
|
453
687
|
*/
|
|
454
688
|
async handleSwap(authenticatedAddress, request) {
|
|
455
689
|
if (getAddress(authenticatedAddress) !== getAddress(request.userAddress)) {
|
|
@@ -471,6 +705,13 @@ var TradingHandlers = class {
|
|
|
471
705
|
"handleSwap: amount must be positive"
|
|
472
706
|
);
|
|
473
707
|
}
|
|
708
|
+
if (!isValidSlippageBps(request.slippageBps)) {
|
|
709
|
+
throw new ValidationError(
|
|
710
|
+
"INVALID_SLIPPAGE",
|
|
711
|
+
`handleSwap: slippageBps (${request.slippageBps}) must be an integer in [0, ${MAX_SLIPPAGE_BPS}]`,
|
|
712
|
+
{ received: request.slippageBps, max: MAX_SLIPPAGE_BPS }
|
|
713
|
+
);
|
|
714
|
+
}
|
|
474
715
|
const { pafiFeeRecipient } = getContractAddresses(request.chainId);
|
|
475
716
|
const universalRouter = UNIVERSAL_ROUTER_ADDRESSES[request.chainId];
|
|
476
717
|
if (!universalRouter) {
|
|
@@ -558,6 +799,198 @@ var TradingHandlers = class {
|
|
|
558
799
|
};
|
|
559
800
|
}
|
|
560
801
|
// =========================================================================
|
|
802
|
+
// GET /quote/exact-out — V4 exact-output quote
|
|
803
|
+
// =========================================================================
|
|
804
|
+
/**
|
|
805
|
+
* Quote the input required to receive `request.amount` of the output
|
|
806
|
+
* token via Uniswap V4. Input-side operator fee is auto-quoted, so
|
|
807
|
+
* `inputGross = estimatedInputAmount + feeAmountInput` is the total
|
|
808
|
+
* the user must hold.
|
|
809
|
+
*
|
|
810
|
+
* Returns `quoteError: "QUOTE_UNAVAILABLE"` rather than throwing when
|
|
811
|
+
* no path exists.
|
|
812
|
+
*/
|
|
813
|
+
async handleQuoteExactOut(request) {
|
|
814
|
+
if (request.chainId !== this.chainId) {
|
|
815
|
+
throw new ValidationError(
|
|
816
|
+
"UNSUPPORTED_CHAIN_ID",
|
|
817
|
+
`handleQuoteExactOut: unsupported chainId ${request.chainId}`,
|
|
818
|
+
{ requested: request.chainId, supported: this.chainId }
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
if (request.amount === 0n) {
|
|
822
|
+
return {
|
|
823
|
+
outputAmount: 0n,
|
|
824
|
+
estimatedInputAmount: 0n,
|
|
825
|
+
inputGross: 0n,
|
|
826
|
+
feeAmountInput: 0n,
|
|
827
|
+
gasEstimate: 0n
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
const inputTokenAddress = getAddress(request.inputTokenAddress);
|
|
831
|
+
const outputTokenAddress = getAddress(request.outputTokenAddress);
|
|
832
|
+
const pools = request.pools ?? [];
|
|
833
|
+
try {
|
|
834
|
+
const best = await findBestQuoteExactOut(
|
|
835
|
+
this.provider,
|
|
836
|
+
request.chainId,
|
|
837
|
+
inputTokenAddress,
|
|
838
|
+
outputTokenAddress,
|
|
839
|
+
request.amount,
|
|
840
|
+
pools
|
|
841
|
+
);
|
|
842
|
+
const feeAmountInput = await quoteOperatorFeeInput(
|
|
843
|
+
this.provider,
|
|
844
|
+
request.chainId,
|
|
845
|
+
inputTokenAddress
|
|
846
|
+
).catch(() => 0n);
|
|
847
|
+
const estimatedInputAmount = best.bestRoute.amountIn;
|
|
848
|
+
return {
|
|
849
|
+
outputAmount: request.amount,
|
|
850
|
+
estimatedInputAmount,
|
|
851
|
+
inputGross: estimatedInputAmount + feeAmountInput,
|
|
852
|
+
feeAmountInput,
|
|
853
|
+
gasEstimate: best.bestRoute.gasEstimate
|
|
854
|
+
};
|
|
855
|
+
} catch {
|
|
856
|
+
return {
|
|
857
|
+
outputAmount: request.amount,
|
|
858
|
+
estimatedInputAmount: 0n,
|
|
859
|
+
inputGross: 0n,
|
|
860
|
+
feeAmountInput: 0n,
|
|
861
|
+
gasEstimate: 0n,
|
|
862
|
+
quoteError: "QUOTE_UNAVAILABLE"
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
// =========================================================================
|
|
867
|
+
// POST /swap/exact-out — V4 exact-output swap UserOp
|
|
868
|
+
// =========================================================================
|
|
869
|
+
/**
|
|
870
|
+
* Build a V4 exact-output swap UserOp.
|
|
871
|
+
*
|
|
872
|
+
* Quotes the best exact-output route, applies slippage as a CEILING on
|
|
873
|
+
* `maxAmountIn` (so the cap is never silently tightened by floor
|
|
874
|
+
* division), then encodes a 4-step batch:
|
|
875
|
+
*
|
|
876
|
+
* inputToken.transfer(feeRecipient, gasFeeAmountInput) [if > 0]
|
|
877
|
+
* → input.approve(Permit2, maxAmountIn)
|
|
878
|
+
* → Permit2.approve(router, maxAmountIn)
|
|
879
|
+
* → UniversalRouter.execute (V4 SWAP_EXACT_OUT)
|
|
880
|
+
*
|
|
881
|
+
* Operator fee is INPUT-token-side (charged before swap) so the user
|
|
882
|
+
* receives exactly `request.amount` of output.
|
|
883
|
+
*
|
|
884
|
+
* `authenticatedAddress` first param: caller MUST pass the address
|
|
885
|
+
* extracted from the verified session/JWT. Handler asserts equality
|
|
886
|
+
* with `request.userAddress`.
|
|
887
|
+
*/
|
|
888
|
+
async handleSwapExactOut(authenticatedAddress, request) {
|
|
889
|
+
if (getAddress(authenticatedAddress) !== getAddress(request.userAddress)) {
|
|
890
|
+
throw new ValidationError(
|
|
891
|
+
"USER_ADDRESS_MISMATCH",
|
|
892
|
+
`handleSwapExactOut: authenticatedAddress (${authenticatedAddress}) does not match request.userAddress (${request.userAddress})`
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
if (request.chainId !== this.chainId) {
|
|
896
|
+
throw new ValidationError(
|
|
897
|
+
"UNSUPPORTED_CHAIN_ID",
|
|
898
|
+
`handleSwapExactOut: unsupported chainId ${request.chainId}`,
|
|
899
|
+
{ requested: request.chainId, supported: this.chainId }
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
if (request.amount <= 0n) {
|
|
903
|
+
throw new ValidationError(
|
|
904
|
+
"INVALID_AMOUNT",
|
|
905
|
+
"handleSwapExactOut: amount must be positive"
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
if (!isValidSlippageBps(request.slippageBps)) {
|
|
909
|
+
throw new ValidationError(
|
|
910
|
+
"INVALID_SLIPPAGE",
|
|
911
|
+
`handleSwapExactOut: slippageBps (${request.slippageBps}) must be an integer in [0, ${MAX_SLIPPAGE_BPS}]`,
|
|
912
|
+
{ received: request.slippageBps, max: MAX_SLIPPAGE_BPS }
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
const { pafiFeeRecipient } = getContractAddresses(request.chainId);
|
|
916
|
+
const universalRouter = UNIVERSAL_ROUTER_ADDRESSES[request.chainId];
|
|
917
|
+
if (!universalRouter) {
|
|
918
|
+
throw new ValidationError(
|
|
919
|
+
"ROUTER_NOT_DEPLOYED",
|
|
920
|
+
`handleSwapExactOut: no UniversalRouter for chainId ${request.chainId}`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
const inputTokenAddress = getAddress(request.inputTokenAddress);
|
|
924
|
+
const outputTokenAddress = getAddress(request.outputTokenAddress);
|
|
925
|
+
const userAddress = getAddress(request.userAddress);
|
|
926
|
+
const pools = request.pools ?? [];
|
|
927
|
+
let quoteResult;
|
|
928
|
+
try {
|
|
929
|
+
quoteResult = await findBestQuoteExactOut(
|
|
930
|
+
this.provider,
|
|
931
|
+
request.chainId,
|
|
932
|
+
inputTokenAddress,
|
|
933
|
+
outputTokenAddress,
|
|
934
|
+
request.amount,
|
|
935
|
+
pools
|
|
936
|
+
);
|
|
937
|
+
} catch (err) {
|
|
938
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
939
|
+
throw new ValidationError(
|
|
940
|
+
"NO_SWAP_PATH",
|
|
941
|
+
`handleSwapExactOut: no swap path found from ${inputTokenAddress} to ${outputTokenAddress} (cause: ${cause})`
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
const gasFeeAmountInput = request.gasFeeAmountInput !== void 0 ? request.gasFeeAmountInput : await quoteOperatorFeeInput(
|
|
945
|
+
this.provider,
|
|
946
|
+
request.chainId,
|
|
947
|
+
inputTokenAddress
|
|
948
|
+
).catch(() => 0n);
|
|
949
|
+
const hops = quoteResult.bestRoute.path.length;
|
|
950
|
+
const slippageBps = request.slippageBps ?? (hops > 1 ? 100 : 50);
|
|
951
|
+
const estimatedInputAmount = quoteResult.bestRoute.amountIn;
|
|
952
|
+
const slippageNumerator = estimatedInputAmount * BigInt(1e4 + slippageBps);
|
|
953
|
+
const maxAmountIn = (slippageNumerator + 9999n) / 10000n;
|
|
954
|
+
const deadline = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
955
|
+
const userOp = buildSwapUserOpExactOut({
|
|
956
|
+
userAddress,
|
|
957
|
+
aaNonce: request.aaNonce,
|
|
958
|
+
inputTokenAddress,
|
|
959
|
+
outputTokenAddress,
|
|
960
|
+
universalRouterAddress: universalRouter,
|
|
961
|
+
amountOut: request.amount,
|
|
962
|
+
maxAmountIn,
|
|
963
|
+
swapPath: quoteResult.bestRoute.path,
|
|
964
|
+
deadline,
|
|
965
|
+
gasFeeAmountInput,
|
|
966
|
+
feeRecipient: pafiFeeRecipient
|
|
967
|
+
});
|
|
968
|
+
const userOpFallback = gasFeeAmountInput > 0n ? buildSwapUserOpExactOut({
|
|
969
|
+
userAddress,
|
|
970
|
+
aaNonce: request.aaNonce,
|
|
971
|
+
inputTokenAddress,
|
|
972
|
+
outputTokenAddress,
|
|
973
|
+
universalRouterAddress: universalRouter,
|
|
974
|
+
amountOut: request.amount,
|
|
975
|
+
maxAmountIn,
|
|
976
|
+
swapPath: quoteResult.bestRoute.path,
|
|
977
|
+
deadline,
|
|
978
|
+
gasFeeAmountInput: 0n,
|
|
979
|
+
feeRecipient: pafiFeeRecipient
|
|
980
|
+
}) : void 0;
|
|
981
|
+
return {
|
|
982
|
+
userOp,
|
|
983
|
+
userOpFallback,
|
|
984
|
+
outputAmount: request.amount,
|
|
985
|
+
estimatedInputAmount,
|
|
986
|
+
maxAmountIn,
|
|
987
|
+
hops,
|
|
988
|
+
deadline,
|
|
989
|
+
feeAmountUsed: gasFeeAmountInput,
|
|
990
|
+
feeRecipient: pafiFeeRecipient
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
// =========================================================================
|
|
561
994
|
// POST /perp-deposit
|
|
562
995
|
// =========================================================================
|
|
563
996
|
/**
|
|
@@ -764,6 +1197,17 @@ async function quoteOperatorFeeOutput(provider, chainId, outputTokenAddress) {
|
|
|
764
1197
|
pointTokenAddress: outputTokenAddress
|
|
765
1198
|
});
|
|
766
1199
|
}
|
|
1200
|
+
async function quoteOperatorFeeInput(provider, chainId, inputTokenAddress) {
|
|
1201
|
+
const { usdt } = getContractAddresses(chainId);
|
|
1202
|
+
if (usdt && getAddress(inputTokenAddress) === getAddress(usdt)) {
|
|
1203
|
+
return quoteOperatorFeeUsdt({ provider, chainId });
|
|
1204
|
+
}
|
|
1205
|
+
return quoteOperatorFeePt({
|
|
1206
|
+
provider,
|
|
1207
|
+
chainId,
|
|
1208
|
+
pointTokenAddress: inputTokenAddress
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
767
1211
|
|
|
768
1212
|
// src/pools.ts
|
|
769
1213
|
import { fetchPafiPools, PAFI_SUBGRAPH_URL } from "@pafi-dev/core";
|
|
@@ -778,6 +1222,29 @@ import {
|
|
|
778
1222
|
BATCH_EXECUTOR_7702_IMPL
|
|
779
1223
|
} from "@pafi-dev/core";
|
|
780
1224
|
async function swapDirect(params) {
|
|
1225
|
+
const universalRouter = UNIVERSAL_ROUTER_ADDRESSES2[params.chainId];
|
|
1226
|
+
if (!universalRouter) {
|
|
1227
|
+
throw new Error(`swapDirect: no UniversalRouter for chainId ${params.chainId}`);
|
|
1228
|
+
}
|
|
1229
|
+
if (params.amount <= 0n) {
|
|
1230
|
+
throw new Error("swapDirect: amount must be positive");
|
|
1231
|
+
}
|
|
1232
|
+
if (!isValidSlippageBps(params.slippageBps)) {
|
|
1233
|
+
throw new Error(
|
|
1234
|
+
`swapDirect: slippageBps (${params.slippageBps}) must be an integer in [0, ${MAX_SLIPPAGE_BPS}]`
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
const account = params.walletClient.account;
|
|
1238
|
+
if (!account) {
|
|
1239
|
+
throw new Error(
|
|
1240
|
+
"swapDirect: walletClient has no account attached \u2014 cannot send tx"
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
if (account.address.toLowerCase() !== params.userAddress.toLowerCase()) {
|
|
1244
|
+
throw new Error(
|
|
1245
|
+
`swapDirect: walletClient.account.address (${account.address}) must equal userAddress (${params.userAddress}) \u2014 the native tx must be sent from the same EOA whose 7702-delegated bytecode is being executed`
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
781
1248
|
const code = await params.publicClient.getCode({
|
|
782
1249
|
address: params.userAddress
|
|
783
1250
|
});
|
|
@@ -793,13 +1260,6 @@ async function swapDirect(params) {
|
|
|
793
1260
|
`swapDirect: user delegated to ${delegate} which is not a PAFI-recognised impl (expected ${SIMPLE_7702_IMPL_BASE_MAINNET} or ${BATCH_EXECUTOR_7702_IMPL}). Continuing \u2014 execute will revert if the impl doesn't expose executeBatch.`
|
|
794
1261
|
);
|
|
795
1262
|
}
|
|
796
|
-
const universalRouter = UNIVERSAL_ROUTER_ADDRESSES2[params.chainId];
|
|
797
|
-
if (!universalRouter) {
|
|
798
|
-
throw new Error(`swapDirect: no UniversalRouter for chainId ${params.chainId}`);
|
|
799
|
-
}
|
|
800
|
-
if (params.amount <= 0n) {
|
|
801
|
-
throw new Error("swapDirect: amount must be positive");
|
|
802
|
-
}
|
|
803
1263
|
let quoteResult;
|
|
804
1264
|
try {
|
|
805
1265
|
quoteResult = await findBestQuote(
|
|
@@ -842,12 +1302,124 @@ async function swapDirect(params) {
|
|
|
842
1302
|
gasFeeAmountOutput,
|
|
843
1303
|
feeRecipient: pafiFeeRecipient
|
|
844
1304
|
});
|
|
1305
|
+
const txHash = await params.walletClient.sendTransaction({
|
|
1306
|
+
account,
|
|
1307
|
+
chain: params.walletClient.chain,
|
|
1308
|
+
to: params.userAddress,
|
|
1309
|
+
value: 0n,
|
|
1310
|
+
data: userOp.callData
|
|
1311
|
+
});
|
|
1312
|
+
let receipt;
|
|
1313
|
+
if (params.waitForReceipt !== false) {
|
|
1314
|
+
try {
|
|
1315
|
+
receipt = await params.publicClient.waitForTransactionReceipt({
|
|
1316
|
+
hash: txHash
|
|
1317
|
+
});
|
|
1318
|
+
} catch (err) {
|
|
1319
|
+
params.onWarning?.(
|
|
1320
|
+
`swapDirect: tx ${txHash} sent but receipt fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return {
|
|
1325
|
+
txHash,
|
|
1326
|
+
receipt,
|
|
1327
|
+
estimatedOutputAmount,
|
|
1328
|
+
minAmountOut,
|
|
1329
|
+
hops,
|
|
1330
|
+
deadline,
|
|
1331
|
+
feeAmountUsed: gasFeeAmountOutput
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// src/direct/swapDirectExactOut.ts
|
|
1336
|
+
import {
|
|
1337
|
+
UNIVERSAL_ROUTER_ADDRESSES as UNIVERSAL_ROUTER_ADDRESSES3,
|
|
1338
|
+
getContractAddresses as getContractAddresses3,
|
|
1339
|
+
parseEip7702DelegatedAddress as parseEip7702DelegatedAddress2,
|
|
1340
|
+
detectDelegateImpl as detectDelegateImpl2,
|
|
1341
|
+
SIMPLE_7702_IMPL_BASE_MAINNET as SIMPLE_7702_IMPL_BASE_MAINNET2,
|
|
1342
|
+
BATCH_EXECUTOR_7702_IMPL as BATCH_EXECUTOR_7702_IMPL2
|
|
1343
|
+
} from "@pafi-dev/core";
|
|
1344
|
+
async function swapDirectExactOut(params) {
|
|
1345
|
+
const universalRouter = UNIVERSAL_ROUTER_ADDRESSES3[params.chainId];
|
|
1346
|
+
if (!universalRouter) {
|
|
1347
|
+
throw new Error(
|
|
1348
|
+
`swapDirectExactOut: no UniversalRouter for chainId ${params.chainId}`
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
if (params.amount <= 0n) {
|
|
1352
|
+
throw new Error("swapDirectExactOut: amount must be positive");
|
|
1353
|
+
}
|
|
1354
|
+
if (!isValidSlippageBps(params.slippageBps)) {
|
|
1355
|
+
throw new Error(
|
|
1356
|
+
`swapDirectExactOut: slippageBps (${params.slippageBps}) must be an integer in [0, ${MAX_SLIPPAGE_BPS}]`
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
845
1359
|
const account = params.walletClient.account;
|
|
846
1360
|
if (!account) {
|
|
847
1361
|
throw new Error(
|
|
848
|
-
"
|
|
1362
|
+
"swapDirectExactOut: walletClient has no account attached \u2014 cannot send tx"
|
|
849
1363
|
);
|
|
850
1364
|
}
|
|
1365
|
+
if (account.address.toLowerCase() !== params.userAddress.toLowerCase()) {
|
|
1366
|
+
throw new Error(
|
|
1367
|
+
`swapDirectExactOut: walletClient.account.address (${account.address}) must equal userAddress (${params.userAddress}) \u2014 the native tx must be sent from the same EOA whose 7702-delegated bytecode is being executed`
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
const code = await params.publicClient.getCode({
|
|
1371
|
+
address: params.userAddress
|
|
1372
|
+
});
|
|
1373
|
+
const delegate = parseEip7702DelegatedAddress2(code);
|
|
1374
|
+
if (!delegate) {
|
|
1375
|
+
throw new Error(
|
|
1376
|
+
`swapDirectExactOut: user ${params.userAddress} is not EIP-7702 delegated. Run \`delegateDirect()\` first or use the AA path via \`TradingHandlers.handleSwapExactOut()\` + sponsor-relayer.`
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
const impl = detectDelegateImpl2(delegate);
|
|
1380
|
+
if (impl === "unknown") {
|
|
1381
|
+
params.onWarning?.(
|
|
1382
|
+
`swapDirectExactOut: user delegated to ${delegate} which is not a PAFI-recognised impl (expected ${SIMPLE_7702_IMPL_BASE_MAINNET2} or ${BATCH_EXECUTOR_7702_IMPL2}). Continuing \u2014 execute will revert if the impl doesn't expose executeBatch.`
|
|
1383
|
+
);
|
|
1384
|
+
}
|
|
1385
|
+
let quoteResult;
|
|
1386
|
+
try {
|
|
1387
|
+
quoteResult = await findBestQuoteExactOut(
|
|
1388
|
+
params.publicClient,
|
|
1389
|
+
params.chainId,
|
|
1390
|
+
params.inputTokenAddress,
|
|
1391
|
+
params.outputTokenAddress,
|
|
1392
|
+
params.amount,
|
|
1393
|
+
params.pools ?? []
|
|
1394
|
+
);
|
|
1395
|
+
} catch (err) {
|
|
1396
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
1397
|
+
throw new Error(
|
|
1398
|
+
`swapDirectExactOut: no swap path found from ${params.inputTokenAddress} to ${params.outputTokenAddress} (cause: ${cause})`
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
const hops = quoteResult.bestRoute.path.length;
|
|
1402
|
+
const slippageBps = params.slippageBps ?? (hops > 1 ? 100 : 50);
|
|
1403
|
+
const estimatedInputAmount = quoteResult.bestRoute.amountIn;
|
|
1404
|
+
const slippageNumerator = estimatedInputAmount * BigInt(1e4 + slippageBps);
|
|
1405
|
+
const maxAmountIn = (slippageNumerator + 9999n) / 10000n;
|
|
1406
|
+
const gasFeeAmountInput = params.gasFeeAmountInput ?? 0n;
|
|
1407
|
+
const deadline = params.deadline ?? BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
1408
|
+
const { pafiFeeRecipient } = getContractAddresses3(params.chainId);
|
|
1409
|
+
const userOp = buildSwapUserOpExactOut({
|
|
1410
|
+
userAddress: params.userAddress,
|
|
1411
|
+
aaNonce: 0n,
|
|
1412
|
+
// ignored on the native-tx path; nonce comes from EOA tx count
|
|
1413
|
+
inputTokenAddress: params.inputTokenAddress,
|
|
1414
|
+
outputTokenAddress: params.outputTokenAddress,
|
|
1415
|
+
universalRouterAddress: universalRouter,
|
|
1416
|
+
amountOut: params.amount,
|
|
1417
|
+
maxAmountIn,
|
|
1418
|
+
swapPath: quoteResult.bestRoute.path,
|
|
1419
|
+
deadline,
|
|
1420
|
+
gasFeeAmountInput,
|
|
1421
|
+
feeRecipient: pafiFeeRecipient
|
|
1422
|
+
});
|
|
851
1423
|
const txHash = await params.walletClient.sendTransaction({
|
|
852
1424
|
account,
|
|
853
1425
|
chain: params.walletClient.chain,
|
|
@@ -863,18 +1435,19 @@ async function swapDirect(params) {
|
|
|
863
1435
|
});
|
|
864
1436
|
} catch (err) {
|
|
865
1437
|
params.onWarning?.(
|
|
866
|
-
`
|
|
1438
|
+
`swapDirectExactOut: tx ${txHash} sent but receipt fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
867
1439
|
);
|
|
868
1440
|
}
|
|
869
1441
|
}
|
|
870
1442
|
return {
|
|
871
1443
|
txHash,
|
|
872
1444
|
receipt,
|
|
873
|
-
|
|
874
|
-
|
|
1445
|
+
outputAmount: params.amount,
|
|
1446
|
+
estimatedInputAmount,
|
|
1447
|
+
maxAmountIn,
|
|
875
1448
|
hops,
|
|
876
1449
|
deadline,
|
|
877
|
-
feeAmountUsed:
|
|
1450
|
+
feeAmountUsed: gasFeeAmountInput
|
|
878
1451
|
};
|
|
879
1452
|
}
|
|
880
1453
|
|
|
@@ -887,11 +1460,11 @@ import {
|
|
|
887
1460
|
TOKEN_HASHES as TOKEN_HASHES2,
|
|
888
1461
|
buildPerpDepositViaRelay as buildPerpDepositViaRelay2,
|
|
889
1462
|
computeAccountId as computeAccountId2,
|
|
890
|
-
detectDelegateImpl as
|
|
891
|
-
getContractAddresses as
|
|
892
|
-
parseEip7702DelegatedAddress as
|
|
893
|
-
BATCH_EXECUTOR_7702_IMPL as
|
|
894
|
-
SIMPLE_7702_IMPL_BASE_MAINNET as
|
|
1463
|
+
detectDelegateImpl as detectDelegateImpl3,
|
|
1464
|
+
getContractAddresses as getContractAddresses4,
|
|
1465
|
+
parseEip7702DelegatedAddress as parseEip7702DelegatedAddress3,
|
|
1466
|
+
BATCH_EXECUTOR_7702_IMPL as BATCH_EXECUTOR_7702_IMPL3,
|
|
1467
|
+
SIMPLE_7702_IMPL_BASE_MAINNET as SIMPLE_7702_IMPL_BASE_MAINNET3
|
|
895
1468
|
} from "@pafi-dev/core";
|
|
896
1469
|
async function perpDepositDirect(params) {
|
|
897
1470
|
if (params.amount <= 0n) {
|
|
@@ -900,16 +1473,16 @@ async function perpDepositDirect(params) {
|
|
|
900
1473
|
const code = await params.publicClient.getCode({
|
|
901
1474
|
address: params.userAddress
|
|
902
1475
|
});
|
|
903
|
-
const delegate =
|
|
1476
|
+
const delegate = parseEip7702DelegatedAddress3(code);
|
|
904
1477
|
if (!delegate) {
|
|
905
1478
|
throw new Error(
|
|
906
1479
|
`perpDepositDirect: user ${params.userAddress} is not EIP-7702 delegated. Run \`delegateDirect()\` first or use the AA path via \`TradingHandlers.handlePerpDeposit()\` + sponsor-relayer.`
|
|
907
1480
|
);
|
|
908
1481
|
}
|
|
909
|
-
const impl =
|
|
1482
|
+
const impl = detectDelegateImpl3(delegate);
|
|
910
1483
|
if (impl === "unknown") {
|
|
911
1484
|
params.onWarning?.(
|
|
912
|
-
`perpDepositDirect: user delegated to ${delegate} (not a PAFI-recognised impl ${
|
|
1485
|
+
`perpDepositDirect: user delegated to ${delegate} (not a PAFI-recognised impl ${SIMPLE_7702_IMPL_BASE_MAINNET3} / ${BATCH_EXECUTOR_7702_IMPL3}). Continuing \u2014 execute will revert if the impl doesn't expose executeBatch.`
|
|
913
1486
|
);
|
|
914
1487
|
}
|
|
915
1488
|
const vault = ORDERLY_VAULT_ADDRESSES2[params.chainId];
|
|
@@ -944,7 +1517,7 @@ async function perpDepositDirect(params) {
|
|
|
944
1517
|
`perpDepositDirect: broker "${params.brokerId}" is not whitelisted on Orderly Vault`
|
|
945
1518
|
);
|
|
946
1519
|
}
|
|
947
|
-
const { orderlyRelay: relayAddress, pafiFeeRecipient } =
|
|
1520
|
+
const { orderlyRelay: relayAddress, pafiFeeRecipient } = getContractAddresses4(
|
|
948
1521
|
params.chainId
|
|
949
1522
|
);
|
|
950
1523
|
const RELAY_FEE_FLOOR_USDC = 2000000n;
|
|
@@ -1024,23 +1597,33 @@ async function perpDepositDirect(params) {
|
|
|
1024
1597
|
}
|
|
1025
1598
|
export {
|
|
1026
1599
|
PAFI_SUBGRAPH_URL,
|
|
1600
|
+
SWAP_EXACT_OUT,
|
|
1601
|
+
SWAP_EXACT_OUT_SINGLE,
|
|
1027
1602
|
TradingHandlers,
|
|
1028
1603
|
buildAllPaths,
|
|
1029
1604
|
buildErc20ApprovalCalldata,
|
|
1030
1605
|
buildPermit2ApprovalCalldata,
|
|
1031
1606
|
buildSwapFromQuote,
|
|
1032
1607
|
buildSwapUserOp,
|
|
1608
|
+
buildSwapUserOpExactOut,
|
|
1033
1609
|
buildUniversalRouterExecuteArgs,
|
|
1610
|
+
buildUniversalRouterExecuteArgsExactOut,
|
|
1034
1611
|
buildV4SwapInput,
|
|
1612
|
+
buildV4SwapInputExactOut,
|
|
1035
1613
|
checkAllowance,
|
|
1036
1614
|
combineRoutes,
|
|
1037
1615
|
fetchPafiPools,
|
|
1038
1616
|
findBestQuote,
|
|
1617
|
+
findBestQuoteExactOut,
|
|
1039
1618
|
perpDepositDirect,
|
|
1040
1619
|
quoteBestRoute,
|
|
1620
|
+
quoteBestRouteExactOut,
|
|
1041
1621
|
quoteExactInput,
|
|
1042
1622
|
quoteExactInputSingle,
|
|
1623
|
+
quoteExactOutput,
|
|
1624
|
+
quoteExactOutputSingle,
|
|
1043
1625
|
simulateSwap,
|
|
1044
|
-
swapDirect
|
|
1626
|
+
swapDirect,
|
|
1627
|
+
swapDirectExactOut
|
|
1045
1628
|
};
|
|
1046
1629
|
//# sourceMappingURL=index.js.map
|