@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.cjs
CHANGED
|
@@ -21,24 +21,34 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
PAFI_SUBGRAPH_URL: () => import_core10.PAFI_SUBGRAPH_URL,
|
|
24
|
+
SWAP_EXACT_OUT: () => SWAP_EXACT_OUT,
|
|
25
|
+
SWAP_EXACT_OUT_SINGLE: () => SWAP_EXACT_OUT_SINGLE,
|
|
24
26
|
TradingHandlers: () => TradingHandlers,
|
|
25
27
|
buildAllPaths: () => buildAllPaths,
|
|
26
28
|
buildErc20ApprovalCalldata: () => buildErc20ApprovalCalldata,
|
|
27
29
|
buildPermit2ApprovalCalldata: () => buildPermit2ApprovalCalldata,
|
|
28
30
|
buildSwapFromQuote: () => buildSwapFromQuote,
|
|
29
31
|
buildSwapUserOp: () => buildSwapUserOp,
|
|
32
|
+
buildSwapUserOpExactOut: () => buildSwapUserOpExactOut,
|
|
30
33
|
buildUniversalRouterExecuteArgs: () => buildUniversalRouterExecuteArgs,
|
|
34
|
+
buildUniversalRouterExecuteArgsExactOut: () => buildUniversalRouterExecuteArgsExactOut,
|
|
31
35
|
buildV4SwapInput: () => buildV4SwapInput,
|
|
36
|
+
buildV4SwapInputExactOut: () => buildV4SwapInputExactOut,
|
|
32
37
|
checkAllowance: () => checkAllowance,
|
|
33
38
|
combineRoutes: () => combineRoutes,
|
|
34
39
|
fetchPafiPools: () => import_core10.fetchPafiPools,
|
|
35
40
|
findBestQuote: () => findBestQuote,
|
|
41
|
+
findBestQuoteExactOut: () => findBestQuoteExactOut,
|
|
36
42
|
perpDepositDirect: () => perpDepositDirect,
|
|
37
43
|
quoteBestRoute: () => quoteBestRoute,
|
|
44
|
+
quoteBestRouteExactOut: () => quoteBestRouteExactOut,
|
|
38
45
|
quoteExactInput: () => quoteExactInput,
|
|
39
46
|
quoteExactInputSingle: () => quoteExactInputSingle,
|
|
47
|
+
quoteExactOutput: () => quoteExactOutput,
|
|
48
|
+
quoteExactOutputSingle: () => quoteExactOutputSingle,
|
|
40
49
|
simulateSwap: () => simulateSwap,
|
|
41
|
-
swapDirect: () => swapDirect
|
|
50
|
+
swapDirect: () => swapDirect,
|
|
51
|
+
swapDirectExactOut: () => swapDirectExactOut
|
|
42
52
|
});
|
|
43
53
|
module.exports = __toCommonJS(index_exports);
|
|
44
54
|
|
|
@@ -101,7 +111,7 @@ async function quoteExactInput(client, quoterAddress, exactCurrency, path, exact
|
|
|
101
111
|
address: quoterAddress,
|
|
102
112
|
abi: import_core2.v4QuoterAbi,
|
|
103
113
|
functionName: "quoteExactInput",
|
|
104
|
-
args: [{ exactCurrency, path, exactAmount
|
|
114
|
+
args: [{ exactCurrency, path, exactAmount }]
|
|
105
115
|
});
|
|
106
116
|
return { amountOut, gasEstimate, path };
|
|
107
117
|
}
|
|
@@ -114,7 +124,7 @@ async function quoteExactInputSingle(client, quoterAddress, poolKey, zeroForOne,
|
|
|
114
124
|
{
|
|
115
125
|
poolKey,
|
|
116
126
|
zeroForOne,
|
|
117
|
-
exactAmount
|
|
127
|
+
exactAmount,
|
|
118
128
|
hookData
|
|
119
129
|
}
|
|
120
130
|
]
|
|
@@ -131,13 +141,7 @@ async function quoteBestRoute(client, quoterAddress, exactCurrency, routes, exac
|
|
|
131
141
|
{
|
|
132
142
|
exactCurrency,
|
|
133
143
|
path,
|
|
134
|
-
|
|
135
|
-
// signature takes `uint256` (a bigint at the wire level), but
|
|
136
|
-
// our pinned ABI types it as `number`. The runtime is fine
|
|
137
|
-
// (bigint serializes correctly) — this cast tells TS to skip
|
|
138
|
-
// the structural check until we re-generate the ABI with the
|
|
139
|
-
// correct uint256 type. Tracked: SDK_CORE_TRADING_AUDIT.md H11.
|
|
140
|
-
exactAmount: BigInt(exactAmount)
|
|
144
|
+
exactAmount
|
|
141
145
|
}
|
|
142
146
|
]
|
|
143
147
|
})),
|
|
@@ -178,6 +182,84 @@ async function findBestQuote(client, chainId, tokenIn, tokenOut, exactAmount, po
|
|
|
178
182
|
}
|
|
179
183
|
return quoteBestRoute(client, quoter, tokenIn, paths, exactAmount);
|
|
180
184
|
}
|
|
185
|
+
async function quoteExactOutput(client, quoterAddress, exactCurrency, path, exactAmount) {
|
|
186
|
+
const [amountIn, gasEstimate] = await client.readContract({
|
|
187
|
+
address: quoterAddress,
|
|
188
|
+
abi: import_core2.v4QuoterAbi,
|
|
189
|
+
functionName: "quoteExactOutput",
|
|
190
|
+
args: [{ exactCurrency, path, exactAmount }]
|
|
191
|
+
});
|
|
192
|
+
return { amountIn, gasEstimate, path };
|
|
193
|
+
}
|
|
194
|
+
async function quoteExactOutputSingle(client, quoterAddress, poolKey, zeroForOne, exactAmount, hookData) {
|
|
195
|
+
const [amountIn, gasEstimate] = await client.readContract({
|
|
196
|
+
address: quoterAddress,
|
|
197
|
+
abi: import_core2.v4QuoterAbi,
|
|
198
|
+
functionName: "quoteExactOutputSingle",
|
|
199
|
+
args: [
|
|
200
|
+
{
|
|
201
|
+
poolKey,
|
|
202
|
+
zeroForOne,
|
|
203
|
+
exactAmount,
|
|
204
|
+
hookData
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
});
|
|
208
|
+
return { amountIn, gasEstimate };
|
|
209
|
+
}
|
|
210
|
+
async function quoteBestRouteExactOut(client, quoterAddress, exactCurrency, routes, exactAmount) {
|
|
211
|
+
const results = await client.multicall({
|
|
212
|
+
contracts: routes.map((path) => ({
|
|
213
|
+
address: quoterAddress,
|
|
214
|
+
abi: import_core2.v4QuoterAbi,
|
|
215
|
+
functionName: "quoteExactOutput",
|
|
216
|
+
args: [
|
|
217
|
+
{
|
|
218
|
+
exactCurrency,
|
|
219
|
+
path,
|
|
220
|
+
exactAmount
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
})),
|
|
224
|
+
allowFailure: true
|
|
225
|
+
});
|
|
226
|
+
const allRoutes = [];
|
|
227
|
+
let firstFailure;
|
|
228
|
+
for (let i = 0; i < results.length; i++) {
|
|
229
|
+
const r = results[i];
|
|
230
|
+
if (r.status === "success") {
|
|
231
|
+
const [amountIn, gasEstimate] = r.result;
|
|
232
|
+
allRoutes.push({ amountIn, gasEstimate, path: routes[i] });
|
|
233
|
+
} else if (firstFailure === void 0) {
|
|
234
|
+
const errMsg = r.error instanceof Error ? r.error.message : String(r.error ?? "unknown");
|
|
235
|
+
firstFailure = errMsg;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (allRoutes.length === 0) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`No valid exact-output routes found to ${exactCurrency} (${routes.length} candidates probed)` + (firstFailure ? `; first failure: ${firstFailure}` : "")
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
const bestRoute = allRoutes.reduce(
|
|
244
|
+
(best, current) => current.amountIn < best.amountIn ? current : best
|
|
245
|
+
);
|
|
246
|
+
return { bestRoute, allRoutes };
|
|
247
|
+
}
|
|
248
|
+
async function findBestQuoteExactOut(client, chainId, tokenIn, tokenOut, exactAmount, pools = [], quoterAddress, maxHops = 3) {
|
|
249
|
+
const quoter = quoterAddress ?? import_core3.V4_QUOTER_ADDRESSES[chainId];
|
|
250
|
+
if (!quoter) {
|
|
251
|
+
throw new Error(`No V4 Quoter address configured for chain ${chainId}`);
|
|
252
|
+
}
|
|
253
|
+
const commonPools = import_core3.COMMON_POOLS[chainId] ?? [];
|
|
254
|
+
const allPools = [...pools, ...commonPools];
|
|
255
|
+
const paths = buildAllPaths(allPools, tokenOut, tokenIn, maxHops);
|
|
256
|
+
if (paths.length === 0) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
`No exact-output paths found to ${tokenOut} from ${tokenIn}`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
return quoteBestRouteExactOut(client, quoter, tokenOut, paths, exactAmount);
|
|
262
|
+
}
|
|
181
263
|
|
|
182
264
|
// src/swap/approval.ts
|
|
183
265
|
var import_viem = require("viem");
|
|
@@ -210,8 +292,11 @@ function buildPermit2ApprovalCalldata(token, spender, amount, expiration) {
|
|
|
210
292
|
var import_viem2 = require("viem");
|
|
211
293
|
var V4_SWAP = 16;
|
|
212
294
|
var SWAP_EXACT_IN = 7;
|
|
295
|
+
var SWAP_EXACT_OUT_SINGLE = 8;
|
|
296
|
+
var SWAP_EXACT_OUT = 9;
|
|
213
297
|
var SETTLE_ALL = 12;
|
|
214
298
|
var TAKE_ALL = 15;
|
|
299
|
+
var UINT128_MAX = 2n ** 128n - 1n;
|
|
215
300
|
var PATH_KEY_ABI_COMPONENTS = [
|
|
216
301
|
{ name: "intermediateCurrency", type: "address" },
|
|
217
302
|
{ name: "fee", type: "uint256" },
|
|
@@ -229,6 +314,16 @@ var EXACT_INPUT_PARAMS_ABI = [
|
|
|
229
314
|
{ name: "amountIn", type: "uint128" },
|
|
230
315
|
{ name: "amountOutMinimum", type: "uint128" }
|
|
231
316
|
];
|
|
317
|
+
var EXACT_OUTPUT_PARAMS_ABI = [
|
|
318
|
+
{ name: "currencyOut", type: "address" },
|
|
319
|
+
{
|
|
320
|
+
name: "path",
|
|
321
|
+
type: "tuple[]",
|
|
322
|
+
components: PATH_KEY_ABI_COMPONENTS
|
|
323
|
+
},
|
|
324
|
+
{ name: "amountOut", type: "uint128" },
|
|
325
|
+
{ name: "amountInMaximum", type: "uint128" }
|
|
326
|
+
];
|
|
232
327
|
function buildV4SwapInput(currencyIn, path, amountIn, minAmountOut, outputCurrency) {
|
|
233
328
|
const actions = (0, import_viem2.encodePacked)(
|
|
234
329
|
["uint8", "uint8", "uint8"],
|
|
@@ -280,7 +375,81 @@ function buildUniversalRouterExecuteArgs(currencyIn, path, amountIn, minAmountOu
|
|
|
280
375
|
];
|
|
281
376
|
return { commands, inputs };
|
|
282
377
|
}
|
|
378
|
+
function buildV4SwapInputExactOut(currencyOut, path, amountOut, maxAmountIn, inputCurrency) {
|
|
379
|
+
if (amountOut <= 0n || amountOut > UINT128_MAX) {
|
|
380
|
+
throw new Error(
|
|
381
|
+
`buildV4SwapInputExactOut: amountOut (${amountOut}) must be in (0, 2^128-1]`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
if (maxAmountIn <= 0n || maxAmountIn > UINT128_MAX) {
|
|
385
|
+
throw new Error(
|
|
386
|
+
`buildV4SwapInputExactOut: maxAmountIn (${maxAmountIn}) must be in (0, 2^128-1]`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
const actions = (0, import_viem2.encodePacked)(
|
|
390
|
+
["uint8", "uint8", "uint8"],
|
|
391
|
+
[SWAP_EXACT_OUT, SETTLE_ALL, TAKE_ALL]
|
|
392
|
+
);
|
|
393
|
+
const swapParam = (0, import_viem2.encodeAbiParameters)(
|
|
394
|
+
[{ name: "swap", type: "tuple", components: EXACT_OUTPUT_PARAMS_ABI }],
|
|
395
|
+
[
|
|
396
|
+
{
|
|
397
|
+
currencyOut,
|
|
398
|
+
path: path.map((p) => ({
|
|
399
|
+
intermediateCurrency: p.intermediateCurrency,
|
|
400
|
+
fee: BigInt(p.fee),
|
|
401
|
+
tickSpacing: p.tickSpacing,
|
|
402
|
+
hooks: p.hooks,
|
|
403
|
+
hookData: p.hookData
|
|
404
|
+
})),
|
|
405
|
+
amountOut,
|
|
406
|
+
amountInMaximum: maxAmountIn
|
|
407
|
+
}
|
|
408
|
+
]
|
|
409
|
+
);
|
|
410
|
+
const settleParam = (0, import_viem2.encodeAbiParameters)(
|
|
411
|
+
[
|
|
412
|
+
{ name: "currency", type: "address" },
|
|
413
|
+
{ name: "maxAmount", type: "uint256" }
|
|
414
|
+
],
|
|
415
|
+
[inputCurrency, maxAmountIn]
|
|
416
|
+
);
|
|
417
|
+
const takeParam = (0, import_viem2.encodeAbiParameters)(
|
|
418
|
+
[
|
|
419
|
+
{ name: "currency", type: "address" },
|
|
420
|
+
{ name: "minAmount", type: "uint256" }
|
|
421
|
+
],
|
|
422
|
+
[currencyOut, amountOut]
|
|
423
|
+
);
|
|
424
|
+
return (0, import_viem2.encodeAbiParameters)(
|
|
425
|
+
[
|
|
426
|
+
{ name: "actions", type: "bytes" },
|
|
427
|
+
{ name: "params", type: "bytes[]" }
|
|
428
|
+
],
|
|
429
|
+
[actions, [swapParam, settleParam, takeParam]]
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
function buildUniversalRouterExecuteArgsExactOut(currencyOut, path, amountOut, maxAmountIn, inputCurrency) {
|
|
433
|
+
const commands = (0, import_viem2.encodePacked)(["uint8"], [V4_SWAP]);
|
|
434
|
+
const inputs = [
|
|
435
|
+
buildV4SwapInputExactOut(
|
|
436
|
+
currencyOut,
|
|
437
|
+
path,
|
|
438
|
+
amountOut,
|
|
439
|
+
maxAmountIn,
|
|
440
|
+
inputCurrency
|
|
441
|
+
)
|
|
442
|
+
];
|
|
443
|
+
return { commands, inputs };
|
|
444
|
+
}
|
|
445
|
+
var _buildSwapFromQuoteWarned = false;
|
|
283
446
|
function buildSwapFromQuote(params) {
|
|
447
|
+
if (!_buildSwapFromQuoteWarned) {
|
|
448
|
+
_buildSwapFromQuoteWarned = true;
|
|
449
|
+
console.warn(
|
|
450
|
+
"[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`."
|
|
451
|
+
);
|
|
452
|
+
}
|
|
284
453
|
return buildUniversalRouterExecuteArgs(
|
|
285
454
|
params.currencyIn,
|
|
286
455
|
params.quote.path,
|
|
@@ -381,6 +550,82 @@ function buildSwapUserOp(params) {
|
|
|
381
550
|
}
|
|
382
551
|
});
|
|
383
552
|
}
|
|
553
|
+
function buildSwapUserOpExactOut(params) {
|
|
554
|
+
if (params.amountOut <= 0n) {
|
|
555
|
+
throw new Error("buildSwapUserOpExactOut: amountOut must be positive");
|
|
556
|
+
}
|
|
557
|
+
if (params.maxAmountIn <= 0n) {
|
|
558
|
+
throw new Error("buildSwapUserOpExactOut: maxAmountIn must be positive");
|
|
559
|
+
}
|
|
560
|
+
if (params.gasFeeAmountInput < 0n) {
|
|
561
|
+
throw new Error(
|
|
562
|
+
"buildSwapUserOpExactOut: gasFeeAmountInput must be non-negative"
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
if (params.swapPath.length === 0) {
|
|
566
|
+
throw new Error(
|
|
567
|
+
"buildSwapUserOpExactOut: swapPath must contain at least one PathKey"
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const PERMIT2_EXPIRATION_MAX = 2n ** 48n - 1n;
|
|
571
|
+
if (params.deadline <= 0n || params.deadline > PERMIT2_EXPIRATION_MAX) {
|
|
572
|
+
throw new Error(
|
|
573
|
+
`buildSwapUserOpExactOut: deadline (${params.deadline}) must be unix seconds in (0, 2^48-1]. Did you accidentally pass milliseconds?`
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
const { commands, inputs } = buildUniversalRouterExecuteArgsExactOut(
|
|
577
|
+
params.outputTokenAddress,
|
|
578
|
+
params.swapPath,
|
|
579
|
+
params.amountOut,
|
|
580
|
+
params.maxAmountIn,
|
|
581
|
+
params.inputTokenAddress
|
|
582
|
+
);
|
|
583
|
+
const swapCallData = (0, import_viem3.encodeFunctionData)({
|
|
584
|
+
abi: import_core8.universalRouterAbi,
|
|
585
|
+
functionName: "execute",
|
|
586
|
+
args: [commands, inputs, params.deadline]
|
|
587
|
+
});
|
|
588
|
+
const permit2ApproveData = buildPermit2ApprovalCalldata(
|
|
589
|
+
params.inputTokenAddress,
|
|
590
|
+
params.universalRouterAddress,
|
|
591
|
+
params.maxAmountIn,
|
|
592
|
+
Number(params.deadline)
|
|
593
|
+
);
|
|
594
|
+
const operations = [];
|
|
595
|
+
if (params.gasFeeAmountInput > 0n) {
|
|
596
|
+
operations.push(
|
|
597
|
+
(0, import_core8.erc20TransferOp)(
|
|
598
|
+
params.inputTokenAddress,
|
|
599
|
+
params.feeRecipient,
|
|
600
|
+
params.gasFeeAmountInput
|
|
601
|
+
)
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
operations.push(
|
|
605
|
+
(0, import_core8.erc20ApproveOp)(params.inputTokenAddress, import_core8.PERMIT2_ADDRESS, params.maxAmountIn),
|
|
606
|
+
(0, import_core8.rawCallOp)(import_core8.PERMIT2_ADDRESS, permit2ApproveData),
|
|
607
|
+
(0, import_core8.rawCallOp)(params.universalRouterAddress, swapCallData)
|
|
608
|
+
);
|
|
609
|
+
return (0, import_core8.buildPartialUserOperation)({
|
|
610
|
+
sender: params.userAddress,
|
|
611
|
+
nonce: params.aaNonce,
|
|
612
|
+
operations,
|
|
613
|
+
gasLimits: {
|
|
614
|
+
// +50k headroom for the additional pre-swap ERC-20 transfer when
|
|
615
|
+
// the input-side fee is non-zero. Keeps margin even when fee=0n.
|
|
616
|
+
callGasLimit: params.gasLimits?.callGasLimit ?? 750000n,
|
|
617
|
+
verificationGasLimit: params.gasLimits?.verificationGasLimit ?? 150000n,
|
|
618
|
+
preVerificationGas: params.gasLimits?.preVerificationGas ?? 50000n
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/swap/slippage.ts
|
|
624
|
+
var MAX_SLIPPAGE_BPS = 5e3;
|
|
625
|
+
function isValidSlippageBps(value) {
|
|
626
|
+
if (value === void 0) return true;
|
|
627
|
+
return Number.isInteger(value) && value >= 0 && value <= MAX_SLIPPAGE_BPS;
|
|
628
|
+
}
|
|
384
629
|
|
|
385
630
|
// src/api/handlers.ts
|
|
386
631
|
var TradingHandlers = class {
|
|
@@ -469,10 +714,9 @@ var TradingHandlers = class {
|
|
|
469
714
|
* net. Quote response surfaces both `estimatedOutputAmount` (gross)
|
|
470
715
|
* and `outputNet` so the FE can display reality.
|
|
471
716
|
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
* `request.userAddress`. See SDK_CORE_TRADING_AUDIT.md C6.
|
|
717
|
+
* `authenticatedAddress` first param: caller (issuer controller / FE
|
|
718
|
+
* proxy) MUST pass the address extracted from the verified
|
|
719
|
+
* session/JWT. Handler asserts it equals `request.userAddress`.
|
|
476
720
|
*/
|
|
477
721
|
async handleSwap(authenticatedAddress, request) {
|
|
478
722
|
if ((0, import_viem4.getAddress)(authenticatedAddress) !== (0, import_viem4.getAddress)(request.userAddress)) {
|
|
@@ -494,6 +738,13 @@ var TradingHandlers = class {
|
|
|
494
738
|
"handleSwap: amount must be positive"
|
|
495
739
|
);
|
|
496
740
|
}
|
|
741
|
+
if (!isValidSlippageBps(request.slippageBps)) {
|
|
742
|
+
throw new import_core9.ValidationError(
|
|
743
|
+
"INVALID_SLIPPAGE",
|
|
744
|
+
`handleSwap: slippageBps (${request.slippageBps}) must be an integer in [0, ${MAX_SLIPPAGE_BPS}]`,
|
|
745
|
+
{ received: request.slippageBps, max: MAX_SLIPPAGE_BPS }
|
|
746
|
+
);
|
|
747
|
+
}
|
|
497
748
|
const { pafiFeeRecipient } = (0, import_core9.getContractAddresses)(request.chainId);
|
|
498
749
|
const universalRouter = import_core9.UNIVERSAL_ROUTER_ADDRESSES[request.chainId];
|
|
499
750
|
if (!universalRouter) {
|
|
@@ -581,6 +832,198 @@ var TradingHandlers = class {
|
|
|
581
832
|
};
|
|
582
833
|
}
|
|
583
834
|
// =========================================================================
|
|
835
|
+
// GET /quote/exact-out — V4 exact-output quote
|
|
836
|
+
// =========================================================================
|
|
837
|
+
/**
|
|
838
|
+
* Quote the input required to receive `request.amount` of the output
|
|
839
|
+
* token via Uniswap V4. Input-side operator fee is auto-quoted, so
|
|
840
|
+
* `inputGross = estimatedInputAmount + feeAmountInput` is the total
|
|
841
|
+
* the user must hold.
|
|
842
|
+
*
|
|
843
|
+
* Returns `quoteError: "QUOTE_UNAVAILABLE"` rather than throwing when
|
|
844
|
+
* no path exists.
|
|
845
|
+
*/
|
|
846
|
+
async handleQuoteExactOut(request) {
|
|
847
|
+
if (request.chainId !== this.chainId) {
|
|
848
|
+
throw new import_core9.ValidationError(
|
|
849
|
+
"UNSUPPORTED_CHAIN_ID",
|
|
850
|
+
`handleQuoteExactOut: unsupported chainId ${request.chainId}`,
|
|
851
|
+
{ requested: request.chainId, supported: this.chainId }
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
if (request.amount === 0n) {
|
|
855
|
+
return {
|
|
856
|
+
outputAmount: 0n,
|
|
857
|
+
estimatedInputAmount: 0n,
|
|
858
|
+
inputGross: 0n,
|
|
859
|
+
feeAmountInput: 0n,
|
|
860
|
+
gasEstimate: 0n
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
const inputTokenAddress = (0, import_viem4.getAddress)(request.inputTokenAddress);
|
|
864
|
+
const outputTokenAddress = (0, import_viem4.getAddress)(request.outputTokenAddress);
|
|
865
|
+
const pools = request.pools ?? [];
|
|
866
|
+
try {
|
|
867
|
+
const best = await findBestQuoteExactOut(
|
|
868
|
+
this.provider,
|
|
869
|
+
request.chainId,
|
|
870
|
+
inputTokenAddress,
|
|
871
|
+
outputTokenAddress,
|
|
872
|
+
request.amount,
|
|
873
|
+
pools
|
|
874
|
+
);
|
|
875
|
+
const feeAmountInput = await quoteOperatorFeeInput(
|
|
876
|
+
this.provider,
|
|
877
|
+
request.chainId,
|
|
878
|
+
inputTokenAddress
|
|
879
|
+
).catch(() => 0n);
|
|
880
|
+
const estimatedInputAmount = best.bestRoute.amountIn;
|
|
881
|
+
return {
|
|
882
|
+
outputAmount: request.amount,
|
|
883
|
+
estimatedInputAmount,
|
|
884
|
+
inputGross: estimatedInputAmount + feeAmountInput,
|
|
885
|
+
feeAmountInput,
|
|
886
|
+
gasEstimate: best.bestRoute.gasEstimate
|
|
887
|
+
};
|
|
888
|
+
} catch {
|
|
889
|
+
return {
|
|
890
|
+
outputAmount: request.amount,
|
|
891
|
+
estimatedInputAmount: 0n,
|
|
892
|
+
inputGross: 0n,
|
|
893
|
+
feeAmountInput: 0n,
|
|
894
|
+
gasEstimate: 0n,
|
|
895
|
+
quoteError: "QUOTE_UNAVAILABLE"
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
// =========================================================================
|
|
900
|
+
// POST /swap/exact-out — V4 exact-output swap UserOp
|
|
901
|
+
// =========================================================================
|
|
902
|
+
/**
|
|
903
|
+
* Build a V4 exact-output swap UserOp.
|
|
904
|
+
*
|
|
905
|
+
* Quotes the best exact-output route, applies slippage as a CEILING on
|
|
906
|
+
* `maxAmountIn` (so the cap is never silently tightened by floor
|
|
907
|
+
* division), then encodes a 4-step batch:
|
|
908
|
+
*
|
|
909
|
+
* inputToken.transfer(feeRecipient, gasFeeAmountInput) [if > 0]
|
|
910
|
+
* → input.approve(Permit2, maxAmountIn)
|
|
911
|
+
* → Permit2.approve(router, maxAmountIn)
|
|
912
|
+
* → UniversalRouter.execute (V4 SWAP_EXACT_OUT)
|
|
913
|
+
*
|
|
914
|
+
* Operator fee is INPUT-token-side (charged before swap) so the user
|
|
915
|
+
* receives exactly `request.amount` of output.
|
|
916
|
+
*
|
|
917
|
+
* `authenticatedAddress` first param: caller MUST pass the address
|
|
918
|
+
* extracted from the verified session/JWT. Handler asserts equality
|
|
919
|
+
* with `request.userAddress`.
|
|
920
|
+
*/
|
|
921
|
+
async handleSwapExactOut(authenticatedAddress, request) {
|
|
922
|
+
if ((0, import_viem4.getAddress)(authenticatedAddress) !== (0, import_viem4.getAddress)(request.userAddress)) {
|
|
923
|
+
throw new import_core9.ValidationError(
|
|
924
|
+
"USER_ADDRESS_MISMATCH",
|
|
925
|
+
`handleSwapExactOut: authenticatedAddress (${authenticatedAddress}) does not match request.userAddress (${request.userAddress})`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
if (request.chainId !== this.chainId) {
|
|
929
|
+
throw new import_core9.ValidationError(
|
|
930
|
+
"UNSUPPORTED_CHAIN_ID",
|
|
931
|
+
`handleSwapExactOut: unsupported chainId ${request.chainId}`,
|
|
932
|
+
{ requested: request.chainId, supported: this.chainId }
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
if (request.amount <= 0n) {
|
|
936
|
+
throw new import_core9.ValidationError(
|
|
937
|
+
"INVALID_AMOUNT",
|
|
938
|
+
"handleSwapExactOut: amount must be positive"
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
if (!isValidSlippageBps(request.slippageBps)) {
|
|
942
|
+
throw new import_core9.ValidationError(
|
|
943
|
+
"INVALID_SLIPPAGE",
|
|
944
|
+
`handleSwapExactOut: slippageBps (${request.slippageBps}) must be an integer in [0, ${MAX_SLIPPAGE_BPS}]`,
|
|
945
|
+
{ received: request.slippageBps, max: MAX_SLIPPAGE_BPS }
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
const { pafiFeeRecipient } = (0, import_core9.getContractAddresses)(request.chainId);
|
|
949
|
+
const universalRouter = import_core9.UNIVERSAL_ROUTER_ADDRESSES[request.chainId];
|
|
950
|
+
if (!universalRouter) {
|
|
951
|
+
throw new import_core9.ValidationError(
|
|
952
|
+
"ROUTER_NOT_DEPLOYED",
|
|
953
|
+
`handleSwapExactOut: no UniversalRouter for chainId ${request.chainId}`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
const inputTokenAddress = (0, import_viem4.getAddress)(request.inputTokenAddress);
|
|
957
|
+
const outputTokenAddress = (0, import_viem4.getAddress)(request.outputTokenAddress);
|
|
958
|
+
const userAddress = (0, import_viem4.getAddress)(request.userAddress);
|
|
959
|
+
const pools = request.pools ?? [];
|
|
960
|
+
let quoteResult;
|
|
961
|
+
try {
|
|
962
|
+
quoteResult = await findBestQuoteExactOut(
|
|
963
|
+
this.provider,
|
|
964
|
+
request.chainId,
|
|
965
|
+
inputTokenAddress,
|
|
966
|
+
outputTokenAddress,
|
|
967
|
+
request.amount,
|
|
968
|
+
pools
|
|
969
|
+
);
|
|
970
|
+
} catch (err) {
|
|
971
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
972
|
+
throw new import_core9.ValidationError(
|
|
973
|
+
"NO_SWAP_PATH",
|
|
974
|
+
`handleSwapExactOut: no swap path found from ${inputTokenAddress} to ${outputTokenAddress} (cause: ${cause})`
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
const gasFeeAmountInput = request.gasFeeAmountInput !== void 0 ? request.gasFeeAmountInput : await quoteOperatorFeeInput(
|
|
978
|
+
this.provider,
|
|
979
|
+
request.chainId,
|
|
980
|
+
inputTokenAddress
|
|
981
|
+
).catch(() => 0n);
|
|
982
|
+
const hops = quoteResult.bestRoute.path.length;
|
|
983
|
+
const slippageBps = request.slippageBps ?? (hops > 1 ? 100 : 50);
|
|
984
|
+
const estimatedInputAmount = quoteResult.bestRoute.amountIn;
|
|
985
|
+
const slippageNumerator = estimatedInputAmount * BigInt(1e4 + slippageBps);
|
|
986
|
+
const maxAmountIn = (slippageNumerator + 9999n) / 10000n;
|
|
987
|
+
const deadline = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
988
|
+
const userOp = buildSwapUserOpExactOut({
|
|
989
|
+
userAddress,
|
|
990
|
+
aaNonce: request.aaNonce,
|
|
991
|
+
inputTokenAddress,
|
|
992
|
+
outputTokenAddress,
|
|
993
|
+
universalRouterAddress: universalRouter,
|
|
994
|
+
amountOut: request.amount,
|
|
995
|
+
maxAmountIn,
|
|
996
|
+
swapPath: quoteResult.bestRoute.path,
|
|
997
|
+
deadline,
|
|
998
|
+
gasFeeAmountInput,
|
|
999
|
+
feeRecipient: pafiFeeRecipient
|
|
1000
|
+
});
|
|
1001
|
+
const userOpFallback = gasFeeAmountInput > 0n ? buildSwapUserOpExactOut({
|
|
1002
|
+
userAddress,
|
|
1003
|
+
aaNonce: request.aaNonce,
|
|
1004
|
+
inputTokenAddress,
|
|
1005
|
+
outputTokenAddress,
|
|
1006
|
+
universalRouterAddress: universalRouter,
|
|
1007
|
+
amountOut: request.amount,
|
|
1008
|
+
maxAmountIn,
|
|
1009
|
+
swapPath: quoteResult.bestRoute.path,
|
|
1010
|
+
deadline,
|
|
1011
|
+
gasFeeAmountInput: 0n,
|
|
1012
|
+
feeRecipient: pafiFeeRecipient
|
|
1013
|
+
}) : void 0;
|
|
1014
|
+
return {
|
|
1015
|
+
userOp,
|
|
1016
|
+
userOpFallback,
|
|
1017
|
+
outputAmount: request.amount,
|
|
1018
|
+
estimatedInputAmount,
|
|
1019
|
+
maxAmountIn,
|
|
1020
|
+
hops,
|
|
1021
|
+
deadline,
|
|
1022
|
+
feeAmountUsed: gasFeeAmountInput,
|
|
1023
|
+
feeRecipient: pafiFeeRecipient
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
// =========================================================================
|
|
584
1027
|
// POST /perp-deposit
|
|
585
1028
|
// =========================================================================
|
|
586
1029
|
/**
|
|
@@ -787,6 +1230,17 @@ async function quoteOperatorFeeOutput(provider, chainId, outputTokenAddress) {
|
|
|
787
1230
|
pointTokenAddress: outputTokenAddress
|
|
788
1231
|
});
|
|
789
1232
|
}
|
|
1233
|
+
async function quoteOperatorFeeInput(provider, chainId, inputTokenAddress) {
|
|
1234
|
+
const { usdt } = (0, import_core9.getContractAddresses)(chainId);
|
|
1235
|
+
if (usdt && (0, import_viem4.getAddress)(inputTokenAddress) === (0, import_viem4.getAddress)(usdt)) {
|
|
1236
|
+
return (0, import_core9.quoteOperatorFeeUsdt)({ provider, chainId });
|
|
1237
|
+
}
|
|
1238
|
+
return (0, import_core9.quoteOperatorFeePt)({
|
|
1239
|
+
provider,
|
|
1240
|
+
chainId,
|
|
1241
|
+
pointTokenAddress: inputTokenAddress
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
790
1244
|
|
|
791
1245
|
// src/pools.ts
|
|
792
1246
|
var import_core10 = require("@pafi-dev/core");
|
|
@@ -794,6 +1248,29 @@ var import_core10 = require("@pafi-dev/core");
|
|
|
794
1248
|
// src/direct/swapDirect.ts
|
|
795
1249
|
var import_core11 = require("@pafi-dev/core");
|
|
796
1250
|
async function swapDirect(params) {
|
|
1251
|
+
const universalRouter = import_core11.UNIVERSAL_ROUTER_ADDRESSES[params.chainId];
|
|
1252
|
+
if (!universalRouter) {
|
|
1253
|
+
throw new Error(`swapDirect: no UniversalRouter for chainId ${params.chainId}`);
|
|
1254
|
+
}
|
|
1255
|
+
if (params.amount <= 0n) {
|
|
1256
|
+
throw new Error("swapDirect: amount must be positive");
|
|
1257
|
+
}
|
|
1258
|
+
if (!isValidSlippageBps(params.slippageBps)) {
|
|
1259
|
+
throw new Error(
|
|
1260
|
+
`swapDirect: slippageBps (${params.slippageBps}) must be an integer in [0, ${MAX_SLIPPAGE_BPS}]`
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
const account = params.walletClient.account;
|
|
1264
|
+
if (!account) {
|
|
1265
|
+
throw new Error(
|
|
1266
|
+
"swapDirect: walletClient has no account attached \u2014 cannot send tx"
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
if (account.address.toLowerCase() !== params.userAddress.toLowerCase()) {
|
|
1270
|
+
throw new Error(
|
|
1271
|
+
`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`
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
797
1274
|
const code = await params.publicClient.getCode({
|
|
798
1275
|
address: params.userAddress
|
|
799
1276
|
});
|
|
@@ -809,13 +1286,6 @@ async function swapDirect(params) {
|
|
|
809
1286
|
`swapDirect: user delegated to ${delegate} which is not a PAFI-recognised impl (expected ${import_core11.SIMPLE_7702_IMPL_BASE_MAINNET} or ${import_core11.BATCH_EXECUTOR_7702_IMPL}). Continuing \u2014 execute will revert if the impl doesn't expose executeBatch.`
|
|
810
1287
|
);
|
|
811
1288
|
}
|
|
812
|
-
const universalRouter = import_core11.UNIVERSAL_ROUTER_ADDRESSES[params.chainId];
|
|
813
|
-
if (!universalRouter) {
|
|
814
|
-
throw new Error(`swapDirect: no UniversalRouter for chainId ${params.chainId}`);
|
|
815
|
-
}
|
|
816
|
-
if (params.amount <= 0n) {
|
|
817
|
-
throw new Error("swapDirect: amount must be positive");
|
|
818
|
-
}
|
|
819
1289
|
let quoteResult;
|
|
820
1290
|
try {
|
|
821
1291
|
quoteResult = await findBestQuote(
|
|
@@ -858,12 +1328,117 @@ async function swapDirect(params) {
|
|
|
858
1328
|
gasFeeAmountOutput,
|
|
859
1329
|
feeRecipient: pafiFeeRecipient
|
|
860
1330
|
});
|
|
1331
|
+
const txHash = await params.walletClient.sendTransaction({
|
|
1332
|
+
account,
|
|
1333
|
+
chain: params.walletClient.chain,
|
|
1334
|
+
to: params.userAddress,
|
|
1335
|
+
value: 0n,
|
|
1336
|
+
data: userOp.callData
|
|
1337
|
+
});
|
|
1338
|
+
let receipt;
|
|
1339
|
+
if (params.waitForReceipt !== false) {
|
|
1340
|
+
try {
|
|
1341
|
+
receipt = await params.publicClient.waitForTransactionReceipt({
|
|
1342
|
+
hash: txHash
|
|
1343
|
+
});
|
|
1344
|
+
} catch (err) {
|
|
1345
|
+
params.onWarning?.(
|
|
1346
|
+
`swapDirect: tx ${txHash} sent but receipt fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return {
|
|
1351
|
+
txHash,
|
|
1352
|
+
receipt,
|
|
1353
|
+
estimatedOutputAmount,
|
|
1354
|
+
minAmountOut,
|
|
1355
|
+
hops,
|
|
1356
|
+
deadline,
|
|
1357
|
+
feeAmountUsed: gasFeeAmountOutput
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/direct/swapDirectExactOut.ts
|
|
1362
|
+
var import_core12 = require("@pafi-dev/core");
|
|
1363
|
+
async function swapDirectExactOut(params) {
|
|
1364
|
+
const universalRouter = import_core12.UNIVERSAL_ROUTER_ADDRESSES[params.chainId];
|
|
1365
|
+
if (!universalRouter) {
|
|
1366
|
+
throw new Error(
|
|
1367
|
+
`swapDirectExactOut: no UniversalRouter for chainId ${params.chainId}`
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
if (params.amount <= 0n) {
|
|
1371
|
+
throw new Error("swapDirectExactOut: amount must be positive");
|
|
1372
|
+
}
|
|
1373
|
+
if (!isValidSlippageBps(params.slippageBps)) {
|
|
1374
|
+
throw new Error(
|
|
1375
|
+
`swapDirectExactOut: slippageBps (${params.slippageBps}) must be an integer in [0, ${MAX_SLIPPAGE_BPS}]`
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
861
1378
|
const account = params.walletClient.account;
|
|
862
1379
|
if (!account) {
|
|
863
1380
|
throw new Error(
|
|
864
|
-
"
|
|
1381
|
+
"swapDirectExactOut: walletClient has no account attached \u2014 cannot send tx"
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
if (account.address.toLowerCase() !== params.userAddress.toLowerCase()) {
|
|
1385
|
+
throw new Error(
|
|
1386
|
+
`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`
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
const code = await params.publicClient.getCode({
|
|
1390
|
+
address: params.userAddress
|
|
1391
|
+
});
|
|
1392
|
+
const delegate = (0, import_core12.parseEip7702DelegatedAddress)(code);
|
|
1393
|
+
if (!delegate) {
|
|
1394
|
+
throw new Error(
|
|
1395
|
+
`swapDirectExactOut: user ${params.userAddress} is not EIP-7702 delegated. Run \`delegateDirect()\` first or use the AA path via \`TradingHandlers.handleSwapExactOut()\` + sponsor-relayer.`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
const impl = (0, import_core12.detectDelegateImpl)(delegate);
|
|
1399
|
+
if (impl === "unknown") {
|
|
1400
|
+
params.onWarning?.(
|
|
1401
|
+
`swapDirectExactOut: user delegated to ${delegate} which is not a PAFI-recognised impl (expected ${import_core12.SIMPLE_7702_IMPL_BASE_MAINNET} or ${import_core12.BATCH_EXECUTOR_7702_IMPL}). Continuing \u2014 execute will revert if the impl doesn't expose executeBatch.`
|
|
865
1402
|
);
|
|
866
1403
|
}
|
|
1404
|
+
let quoteResult;
|
|
1405
|
+
try {
|
|
1406
|
+
quoteResult = await findBestQuoteExactOut(
|
|
1407
|
+
params.publicClient,
|
|
1408
|
+
params.chainId,
|
|
1409
|
+
params.inputTokenAddress,
|
|
1410
|
+
params.outputTokenAddress,
|
|
1411
|
+
params.amount,
|
|
1412
|
+
params.pools ?? []
|
|
1413
|
+
);
|
|
1414
|
+
} catch (err) {
|
|
1415
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
1416
|
+
throw new Error(
|
|
1417
|
+
`swapDirectExactOut: no swap path found from ${params.inputTokenAddress} to ${params.outputTokenAddress} (cause: ${cause})`
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
const hops = quoteResult.bestRoute.path.length;
|
|
1421
|
+
const slippageBps = params.slippageBps ?? (hops > 1 ? 100 : 50);
|
|
1422
|
+
const estimatedInputAmount = quoteResult.bestRoute.amountIn;
|
|
1423
|
+
const slippageNumerator = estimatedInputAmount * BigInt(1e4 + slippageBps);
|
|
1424
|
+
const maxAmountIn = (slippageNumerator + 9999n) / 10000n;
|
|
1425
|
+
const gasFeeAmountInput = params.gasFeeAmountInput ?? 0n;
|
|
1426
|
+
const deadline = params.deadline ?? BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
1427
|
+
const { pafiFeeRecipient } = (0, import_core12.getContractAddresses)(params.chainId);
|
|
1428
|
+
const userOp = buildSwapUserOpExactOut({
|
|
1429
|
+
userAddress: params.userAddress,
|
|
1430
|
+
aaNonce: 0n,
|
|
1431
|
+
// ignored on the native-tx path; nonce comes from EOA tx count
|
|
1432
|
+
inputTokenAddress: params.inputTokenAddress,
|
|
1433
|
+
outputTokenAddress: params.outputTokenAddress,
|
|
1434
|
+
universalRouterAddress: universalRouter,
|
|
1435
|
+
amountOut: params.amount,
|
|
1436
|
+
maxAmountIn,
|
|
1437
|
+
swapPath: quoteResult.bestRoute.path,
|
|
1438
|
+
deadline,
|
|
1439
|
+
gasFeeAmountInput,
|
|
1440
|
+
feeRecipient: pafiFeeRecipient
|
|
1441
|
+
});
|
|
867
1442
|
const txHash = await params.walletClient.sendTransaction({
|
|
868
1443
|
account,
|
|
869
1444
|
chain: params.walletClient.chain,
|
|
@@ -879,23 +1454,24 @@ async function swapDirect(params) {
|
|
|
879
1454
|
});
|
|
880
1455
|
} catch (err) {
|
|
881
1456
|
params.onWarning?.(
|
|
882
|
-
`
|
|
1457
|
+
`swapDirectExactOut: tx ${txHash} sent but receipt fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
883
1458
|
);
|
|
884
1459
|
}
|
|
885
1460
|
}
|
|
886
1461
|
return {
|
|
887
1462
|
txHash,
|
|
888
1463
|
receipt,
|
|
889
|
-
|
|
890
|
-
|
|
1464
|
+
outputAmount: params.amount,
|
|
1465
|
+
estimatedInputAmount,
|
|
1466
|
+
maxAmountIn,
|
|
891
1467
|
hops,
|
|
892
1468
|
deadline,
|
|
893
|
-
feeAmountUsed:
|
|
1469
|
+
feeAmountUsed: gasFeeAmountInput
|
|
894
1470
|
};
|
|
895
1471
|
}
|
|
896
1472
|
|
|
897
1473
|
// src/direct/perpDepositDirect.ts
|
|
898
|
-
var
|
|
1474
|
+
var import_core13 = require("@pafi-dev/core");
|
|
899
1475
|
async function perpDepositDirect(params) {
|
|
900
1476
|
if (params.amount <= 0n) {
|
|
901
1477
|
throw new Error("perpDepositDirect: amount must be positive");
|
|
@@ -903,41 +1479,41 @@ async function perpDepositDirect(params) {
|
|
|
903
1479
|
const code = await params.publicClient.getCode({
|
|
904
1480
|
address: params.userAddress
|
|
905
1481
|
});
|
|
906
|
-
const delegate = (0,
|
|
1482
|
+
const delegate = (0, import_core13.parseEip7702DelegatedAddress)(code);
|
|
907
1483
|
if (!delegate) {
|
|
908
1484
|
throw new Error(
|
|
909
1485
|
`perpDepositDirect: user ${params.userAddress} is not EIP-7702 delegated. Run \`delegateDirect()\` first or use the AA path via \`TradingHandlers.handlePerpDeposit()\` + sponsor-relayer.`
|
|
910
1486
|
);
|
|
911
1487
|
}
|
|
912
|
-
const impl = (0,
|
|
1488
|
+
const impl = (0, import_core13.detectDelegateImpl)(delegate);
|
|
913
1489
|
if (impl === "unknown") {
|
|
914
1490
|
params.onWarning?.(
|
|
915
|
-
`perpDepositDirect: user delegated to ${delegate} (not a PAFI-recognised impl ${
|
|
1491
|
+
`perpDepositDirect: user delegated to ${delegate} (not a PAFI-recognised impl ${import_core13.SIMPLE_7702_IMPL_BASE_MAINNET} / ${import_core13.BATCH_EXECUTOR_7702_IMPL}). Continuing \u2014 execute will revert if the impl doesn't expose executeBatch.`
|
|
916
1492
|
);
|
|
917
1493
|
}
|
|
918
|
-
const vault =
|
|
1494
|
+
const vault = import_core13.ORDERLY_VAULT_ADDRESSES[params.chainId];
|
|
919
1495
|
if (!vault) {
|
|
920
1496
|
throw new Error(
|
|
921
1497
|
`perpDepositDirect: no Orderly Vault for chainId ${params.chainId}`
|
|
922
1498
|
);
|
|
923
1499
|
}
|
|
924
|
-
const brokerHash =
|
|
1500
|
+
const brokerHash = import_core13.BROKER_HASHES[params.brokerId];
|
|
925
1501
|
if (!brokerHash) {
|
|
926
1502
|
throw new Error(
|
|
927
1503
|
`perpDepositDirect: unknown brokerId "${params.brokerId}"`
|
|
928
1504
|
);
|
|
929
1505
|
}
|
|
930
|
-
const tokenHash =
|
|
1506
|
+
const tokenHash = import_core13.TOKEN_HASHES.USDC;
|
|
931
1507
|
const [usdcAddress, brokerAllowed] = await Promise.all([
|
|
932
1508
|
params.publicClient.readContract({
|
|
933
1509
|
address: vault,
|
|
934
|
-
abi:
|
|
1510
|
+
abi: import_core13.ORDERLY_VAULT_ABI,
|
|
935
1511
|
functionName: "getAllowedToken",
|
|
936
1512
|
args: [tokenHash]
|
|
937
1513
|
}),
|
|
938
1514
|
params.publicClient.readContract({
|
|
939
1515
|
address: vault,
|
|
940
|
-
abi:
|
|
1516
|
+
abi: import_core13.ORDERLY_VAULT_ABI,
|
|
941
1517
|
functionName: "getAllowedBroker",
|
|
942
1518
|
args: [brokerHash]
|
|
943
1519
|
})
|
|
@@ -947,7 +1523,7 @@ async function perpDepositDirect(params) {
|
|
|
947
1523
|
`perpDepositDirect: broker "${params.brokerId}" is not whitelisted on Orderly Vault`
|
|
948
1524
|
);
|
|
949
1525
|
}
|
|
950
|
-
const { orderlyRelay: relayAddress, pafiFeeRecipient } = (0,
|
|
1526
|
+
const { orderlyRelay: relayAddress, pafiFeeRecipient } = (0, import_core13.getContractAddresses)(
|
|
951
1527
|
params.chainId
|
|
952
1528
|
);
|
|
953
1529
|
const RELAY_FEE_FLOOR_USDC = 2000000n;
|
|
@@ -962,7 +1538,7 @@ async function perpDepositDirect(params) {
|
|
|
962
1538
|
};
|
|
963
1539
|
const relayTokenFee = await params.publicClient.readContract({
|
|
964
1540
|
address: relayAddress,
|
|
965
|
-
abi:
|
|
1541
|
+
abi: import_core13.ORDERLY_RELAY_ABI,
|
|
966
1542
|
functionName: "quoteTokenFee",
|
|
967
1543
|
args: [relayRequest]
|
|
968
1544
|
});
|
|
@@ -977,7 +1553,7 @@ async function perpDepositDirect(params) {
|
|
|
977
1553
|
);
|
|
978
1554
|
}
|
|
979
1555
|
const gasFeeUsdc = params.gasFeeUsdc ?? 0n;
|
|
980
|
-
const partial = (0,
|
|
1556
|
+
const partial = (0, import_core13.buildPerpDepositViaRelay)({
|
|
981
1557
|
userAddress: params.userAddress,
|
|
982
1558
|
aaNonce: 0n,
|
|
983
1559
|
// ignored on the native-tx path
|
|
@@ -1011,7 +1587,7 @@ async function perpDepositDirect(params) {
|
|
|
1011
1587
|
);
|
|
1012
1588
|
}
|
|
1013
1589
|
}
|
|
1014
|
-
const accountId = (0,
|
|
1590
|
+
const accountId = (0, import_core13.computeAccountId)(params.userAddress, brokerHash);
|
|
1015
1591
|
return {
|
|
1016
1592
|
txHash,
|
|
1017
1593
|
receipt,
|
|
@@ -1028,23 +1604,33 @@ async function perpDepositDirect(params) {
|
|
|
1028
1604
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1029
1605
|
0 && (module.exports = {
|
|
1030
1606
|
PAFI_SUBGRAPH_URL,
|
|
1607
|
+
SWAP_EXACT_OUT,
|
|
1608
|
+
SWAP_EXACT_OUT_SINGLE,
|
|
1031
1609
|
TradingHandlers,
|
|
1032
1610
|
buildAllPaths,
|
|
1033
1611
|
buildErc20ApprovalCalldata,
|
|
1034
1612
|
buildPermit2ApprovalCalldata,
|
|
1035
1613
|
buildSwapFromQuote,
|
|
1036
1614
|
buildSwapUserOp,
|
|
1615
|
+
buildSwapUserOpExactOut,
|
|
1037
1616
|
buildUniversalRouterExecuteArgs,
|
|
1617
|
+
buildUniversalRouterExecuteArgsExactOut,
|
|
1038
1618
|
buildV4SwapInput,
|
|
1619
|
+
buildV4SwapInputExactOut,
|
|
1039
1620
|
checkAllowance,
|
|
1040
1621
|
combineRoutes,
|
|
1041
1622
|
fetchPafiPools,
|
|
1042
1623
|
findBestQuote,
|
|
1624
|
+
findBestQuoteExactOut,
|
|
1043
1625
|
perpDepositDirect,
|
|
1044
1626
|
quoteBestRoute,
|
|
1627
|
+
quoteBestRouteExactOut,
|
|
1045
1628
|
quoteExactInput,
|
|
1046
1629
|
quoteExactInputSingle,
|
|
1630
|
+
quoteExactOutput,
|
|
1631
|
+
quoteExactOutputSingle,
|
|
1047
1632
|
simulateSwap,
|
|
1048
|
-
swapDirect
|
|
1633
|
+
swapDirect,
|
|
1634
|
+
swapDirectExactOut
|
|
1049
1635
|
});
|
|
1050
1636
|
//# sourceMappingURL=index.cjs.map
|