@panoptic-eng/sdk 1.0.68 → 1.0.70
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/cow/index.js +47 -2
- package/dist/cow/index.js.map +1 -1
- package/dist/{cow-CGgPXhY6.js → cow-sGuN0eas.js} +1 -1
- package/dist/{greeks-CGtgLUyh.js → greeks-DNPP24KL.js} +115 -9
- package/dist/index.d.ts +67 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +161 -62
- package/dist/index.js.map +1 -1
- package/dist/{irm-BDxwCFDm.js → irm-6RzXXa1_.js} +927 -3
- package/dist/panoptic/v2/greeks/index.d.ts +7 -4
- package/dist/panoptic/v2/greeks/index.d.ts.map +1 -1
- package/dist/panoptic/v2/greeks/index.js +74 -7
- package/dist/panoptic/v2/greeks/index.js.map +1 -1
- package/dist/panoptic/v2/index.d.ts +244 -6
- package/dist/panoptic/v2/index.d.ts.map +1 -1
- package/dist/panoptic/v2/index.js +2554 -464
- package/dist/panoptic/v2/index.js.map +1 -1
- package/dist/panoptic/v2/react-public.d.ts +258 -9
- package/dist/panoptic/v2/react-public.d.ts.map +1 -1
- package/dist/panoptic/v2/react-public.js +4655 -2909
- package/dist/panoptic/v2/react-public.js.map +1 -1
- package/dist/{providers-DubVMHWU.js → providers-CbcNt27n.js} +2 -1
- package/dist/{rates-BHS-Qiu7.js → rates-DDK4ZimJ.js} +40 -4
- package/dist/{router-BW6TqwOO.js → router-njWsLvZc.js} +969 -39
- package/dist/rpc/index.d.ts +12 -1
- package/dist/rpc/index.d.ts.map +1 -1
- package/dist/rpc/index.js +65 -5
- package/dist/rpc/index.js.map +1 -1
- package/dist/uniswap/index.js +53 -13
- package/dist/uniswap/index.js.map +1 -1
- package/dist/{v2-Dgh6JxG_.js → v2-BrG4T_Gh.js} +681 -32
- package/dist/{writes-CG612Vuk.js → writes-CI6Haer-.js} +34 -3
- package/package.json +1 -1
- package/dist/router-IDtCENST.js +0 -942
package/dist/router-IDtCENST.js
DELETED
|
@@ -1,942 +0,0 @@
|
|
|
1
|
-
import { PanopticError, getBlockMeta, getPool, submitWrite } from "./rates-BHS-Qiu7.js";
|
|
2
|
-
import { AmountExceedsUint128Error, InvalidSwapTokenError, MissingSweepRecipientError, QuoterUnavailableError, getUniswapV4Addresses, universalRouterAbi } from "./router-BW6TqwOO.js";
|
|
3
|
-
import { BaseError, ContractFunctionRevertedError, encodeAbiParameters, encodeFunctionData, encodePacked, erc20Abi, isAddressEqual, maxUint256, zeroAddress } from "viem";
|
|
4
|
-
|
|
5
|
-
//#region src/uniswap/v4/router/encodeSwap.ts
|
|
6
|
-
/** Universal Router command byte for a v4 swap. */
|
|
7
|
-
const V4_SWAP = 16;
|
|
8
|
-
/** v4 action: exact-in single-hop swap. */
|
|
9
|
-
const SWAP_EXACT_IN_SINGLE = 6;
|
|
10
|
-
/** v4 action: pay all of the input currency owed. */
|
|
11
|
-
const SETTLE_ALL = 12;
|
|
12
|
-
/** v4 action: take all of the output currency owed. */
|
|
13
|
-
const TAKE_ALL = 15;
|
|
14
|
-
/** v4 action: take an output currency to an explicit recipient. */
|
|
15
|
-
const TAKE = 14;
|
|
16
|
-
/** v4 action: exact-out single-hop swap. */
|
|
17
|
-
const SWAP_EXACT_OUT_SINGLE = 8;
|
|
18
|
-
/** Universal Router command: sweep the router's token balance to a recipient. */
|
|
19
|
-
const SWEEP = 4;
|
|
20
|
-
/** v4 sentinel: take the full positive currency delta (used with TAKE). */
|
|
21
|
-
const OPEN_DELTA = 0n;
|
|
22
|
-
const UINT128_MAX$2 = (1n << 128n) - 1n;
|
|
23
|
-
const poolKeyComponents = [
|
|
24
|
-
{
|
|
25
|
-
name: "currency0",
|
|
26
|
-
type: "address"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: "currency1",
|
|
30
|
-
type: "address"
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: "fee",
|
|
34
|
-
type: "uint24"
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
name: "tickSpacing",
|
|
38
|
-
type: "int24"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: "hooks",
|
|
42
|
-
type: "address"
|
|
43
|
-
}
|
|
44
|
-
];
|
|
45
|
-
const exactInputSingleParamsAbi = [{
|
|
46
|
-
type: "tuple",
|
|
47
|
-
components: [
|
|
48
|
-
{
|
|
49
|
-
name: "poolKey",
|
|
50
|
-
type: "tuple",
|
|
51
|
-
components: poolKeyComponents
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: "zeroForOne",
|
|
55
|
-
type: "bool"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: "amountIn",
|
|
59
|
-
type: "uint128"
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: "amountOutMinimum",
|
|
63
|
-
type: "uint128"
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: "hookData",
|
|
67
|
-
type: "bytes"
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
}];
|
|
71
|
-
const exactOutputSingleParamsAbi = [{
|
|
72
|
-
type: "tuple",
|
|
73
|
-
components: [
|
|
74
|
-
{
|
|
75
|
-
name: "poolKey",
|
|
76
|
-
type: "tuple",
|
|
77
|
-
components: poolKeyComponents
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: "zeroForOne",
|
|
81
|
-
type: "bool"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: "amountOut",
|
|
85
|
-
type: "uint128"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: "amountInMaximum",
|
|
89
|
-
type: "uint128"
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: "hookData",
|
|
93
|
-
type: "bytes"
|
|
94
|
-
}
|
|
95
|
-
]
|
|
96
|
-
}];
|
|
97
|
-
const currencyAmountAbi = [{
|
|
98
|
-
name: "currency",
|
|
99
|
-
type: "address"
|
|
100
|
-
}, {
|
|
101
|
-
name: "amount",
|
|
102
|
-
type: "uint256"
|
|
103
|
-
}];
|
|
104
|
-
const takeToRecipientAbi = [
|
|
105
|
-
{
|
|
106
|
-
name: "currency",
|
|
107
|
-
type: "address"
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
name: "recipient",
|
|
111
|
-
type: "address"
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: "amount",
|
|
115
|
-
type: "uint256"
|
|
116
|
-
}
|
|
117
|
-
];
|
|
118
|
-
const sweepParamsAbi = [
|
|
119
|
-
{
|
|
120
|
-
name: "token",
|
|
121
|
-
type: "address"
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
name: "recipient",
|
|
125
|
-
type: "address"
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: "amountMin",
|
|
129
|
-
type: "uint256"
|
|
130
|
-
}
|
|
131
|
-
];
|
|
132
|
-
function assertUint128(amount) {
|
|
133
|
-
if (amount < 0n || amount > UINT128_MAX$2) throw new AmountExceedsUint128Error(amount);
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Encode the input for a Universal Router `SWEEP` command that forwards the
|
|
137
|
-
* router's full native-ETH balance (above `amountMin = 0`) to `recipient`.
|
|
138
|
-
*/
|
|
139
|
-
function encodeEthSweepInput(recipient) {
|
|
140
|
-
return encodeAbiParameters(sweepParamsAbi, [
|
|
141
|
-
zeroAddress,
|
|
142
|
-
recipient,
|
|
143
|
-
0n
|
|
144
|
-
]);
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Build the output-take v4 action byte + param.
|
|
148
|
-
*
|
|
149
|
-
* Native-ETH output is taken with the explicit-recipient `TAKE` action so the
|
|
150
|
-
* router forwards the ETH straight to `recipient` (the implicit-`msgSender`
|
|
151
|
-
* `TAKE_ALL` leaves native ETH held by the router). ERC20 output uses
|
|
152
|
-
* `TAKE_ALL`, which already credits the caller.
|
|
153
|
-
*
|
|
154
|
-
* @param nativeAmount - TAKE amount for native-ETH output: `OPEN_DELTA` (0) to
|
|
155
|
-
* take the full credit (exact-in, protected by the swap's min) or the exact
|
|
156
|
-
* output (exact-out).
|
|
157
|
-
* @param erc20Amount - TAKE_ALL min/amount for ERC20 output: `amountOutMinimum`
|
|
158
|
-
* (exact-in) or the exact output (exact-out).
|
|
159
|
-
*/
|
|
160
|
-
function buildOutputTake(tokenOut, recipient, nativeAmount, erc20Amount) {
|
|
161
|
-
if (tokenOut === zeroAddress) {
|
|
162
|
-
if (recipient === void 0) throw new MissingSweepRecipientError();
|
|
163
|
-
return {
|
|
164
|
-
action: TAKE,
|
|
165
|
-
param: encodeAbiParameters(takeToRecipientAbi, [
|
|
166
|
-
zeroAddress,
|
|
167
|
-
recipient,
|
|
168
|
-
nativeAmount
|
|
169
|
-
])
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
action: TAKE_ALL,
|
|
174
|
-
param: encodeAbiParameters(currencyAmountAbi, [tokenOut, erc20Amount])
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Build the typed `execute(...)` args + msg.value for an exact-in single-hop v4
|
|
179
|
-
* swap. Use this when submitting via viem (`writeContract` / `submitWrite`).
|
|
180
|
-
*
|
|
181
|
-
* When `tokenOut` is native ETH the explicit-recipient `TAKE` action forwards
|
|
182
|
-
* the bought ETH to `recipient` (no trailing SWEEP is needed for exact-in).
|
|
183
|
-
*
|
|
184
|
-
* @returns `args` ready to spread into `execute` and the ETH `value` to send
|
|
185
|
-
* (= `amountIn` for native-ETH input, otherwise `0n`).
|
|
186
|
-
*/
|
|
187
|
-
function buildV4SwapExecuteArgs(args) {
|
|
188
|
-
const { poolKey, zeroForOne, amountIn, amountOutMinimum, tokenIn, tokenOut, deadline, recipient, hookData = "0x" } = args;
|
|
189
|
-
assertUint128(amountIn);
|
|
190
|
-
assertUint128(amountOutMinimum);
|
|
191
|
-
const swapParam = encodeAbiParameters(exactInputSingleParamsAbi, [{
|
|
192
|
-
poolKey: {
|
|
193
|
-
currency0: poolKey.currency0,
|
|
194
|
-
currency1: poolKey.currency1,
|
|
195
|
-
fee: Number(poolKey.fee),
|
|
196
|
-
tickSpacing: Number(poolKey.tickSpacing),
|
|
197
|
-
hooks: poolKey.hooks
|
|
198
|
-
},
|
|
199
|
-
zeroForOne,
|
|
200
|
-
amountIn,
|
|
201
|
-
amountOutMinimum,
|
|
202
|
-
hookData
|
|
203
|
-
}]);
|
|
204
|
-
const settleParam = encodeAbiParameters(currencyAmountAbi, [tokenIn, amountIn]);
|
|
205
|
-
const take = buildOutputTake(tokenOut, recipient, OPEN_DELTA, amountOutMinimum);
|
|
206
|
-
const actions = encodePacked([
|
|
207
|
-
"uint8",
|
|
208
|
-
"uint8",
|
|
209
|
-
"uint8"
|
|
210
|
-
], [
|
|
211
|
-
SWAP_EXACT_IN_SINGLE,
|
|
212
|
-
SETTLE_ALL,
|
|
213
|
-
take.action
|
|
214
|
-
]);
|
|
215
|
-
const v4Input = encodeAbiParameters([{
|
|
216
|
-
name: "actions",
|
|
217
|
-
type: "bytes"
|
|
218
|
-
}, {
|
|
219
|
-
name: "params",
|
|
220
|
-
type: "bytes[]"
|
|
221
|
-
}], [actions, [
|
|
222
|
-
swapParam,
|
|
223
|
-
settleParam,
|
|
224
|
-
take.param
|
|
225
|
-
]]);
|
|
226
|
-
const commands = encodePacked(["uint8"], [V4_SWAP]);
|
|
227
|
-
const value = tokenIn === zeroAddress ? amountIn : 0n;
|
|
228
|
-
return {
|
|
229
|
-
args: [
|
|
230
|
-
commands,
|
|
231
|
-
[v4Input],
|
|
232
|
-
deadline
|
|
233
|
-
],
|
|
234
|
-
value
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Build the `execute(commands, inputs, deadline)` calldata + msg.value for an
|
|
239
|
-
* exact-in single-hop v4 swap.
|
|
240
|
-
*
|
|
241
|
-
* @returns The encoded calldata and the ETH `value` to send.
|
|
242
|
-
*/
|
|
243
|
-
function buildV4SwapExecuteCalldata(args) {
|
|
244
|
-
const { args: executeArgs, value } = buildV4SwapExecuteArgs(args);
|
|
245
|
-
const data = encodeFunctionData({
|
|
246
|
-
abi: universalRouterAbi,
|
|
247
|
-
functionName: "execute",
|
|
248
|
-
args: executeArgs
|
|
249
|
-
});
|
|
250
|
-
return {
|
|
251
|
-
data,
|
|
252
|
-
value
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Build the typed `execute(...)` args + msg.value for an exact-out single-hop v4
|
|
257
|
-
* swap. Use this when submitting via viem (`writeContract` / `submitWrite`).
|
|
258
|
-
*
|
|
259
|
-
* For native-ETH input, `value` is `amountInMaximum` (an overpay) and a trailing
|
|
260
|
-
* `SWEEP` action refunds the unused ETH to `recipient`. For ERC20 input, Permit2
|
|
261
|
-
* pulls only the settled amount, so no SWEEP is appended and `value` is `0n`.
|
|
262
|
-
*
|
|
263
|
-
* @returns `args` ready to spread into `execute` and the ETH `value` to send.
|
|
264
|
-
*/
|
|
265
|
-
function buildV4ExactOutSwapExecuteArgs(args) {
|
|
266
|
-
const { poolKey, zeroForOne, amountOut, amountInMaximum, tokenIn, tokenOut, deadline, recipient, hookData = "0x" } = args;
|
|
267
|
-
assertUint128(amountOut);
|
|
268
|
-
assertUint128(amountInMaximum);
|
|
269
|
-
const swapParam = encodeAbiParameters(exactOutputSingleParamsAbi, [{
|
|
270
|
-
poolKey: {
|
|
271
|
-
currency0: poolKey.currency0,
|
|
272
|
-
currency1: poolKey.currency1,
|
|
273
|
-
fee: Number(poolKey.fee),
|
|
274
|
-
tickSpacing: Number(poolKey.tickSpacing),
|
|
275
|
-
hooks: poolKey.hooks
|
|
276
|
-
},
|
|
277
|
-
zeroForOne,
|
|
278
|
-
amountOut,
|
|
279
|
-
amountInMaximum,
|
|
280
|
-
hookData
|
|
281
|
-
}]);
|
|
282
|
-
const settleParam = encodeAbiParameters(currencyAmountAbi, [tokenIn, amountInMaximum]);
|
|
283
|
-
const take = buildOutputTake(tokenOut, recipient, amountOut, amountOut);
|
|
284
|
-
const actions = encodePacked([
|
|
285
|
-
"uint8",
|
|
286
|
-
"uint8",
|
|
287
|
-
"uint8"
|
|
288
|
-
], [
|
|
289
|
-
SWAP_EXACT_OUT_SINGLE,
|
|
290
|
-
SETTLE_ALL,
|
|
291
|
-
take.action
|
|
292
|
-
]);
|
|
293
|
-
const v4Input = encodeAbiParameters([{
|
|
294
|
-
name: "actions",
|
|
295
|
-
type: "bytes"
|
|
296
|
-
}, {
|
|
297
|
-
name: "params",
|
|
298
|
-
type: "bytes[]"
|
|
299
|
-
}], [actions, [
|
|
300
|
-
swapParam,
|
|
301
|
-
settleParam,
|
|
302
|
-
take.param
|
|
303
|
-
]]);
|
|
304
|
-
const isNativeIn = tokenIn === zeroAddress;
|
|
305
|
-
const commandList = [V4_SWAP];
|
|
306
|
-
const inputs = [v4Input];
|
|
307
|
-
if (isNativeIn) {
|
|
308
|
-
commandList.push(SWEEP);
|
|
309
|
-
inputs.push(encodeEthSweepInput(recipient));
|
|
310
|
-
}
|
|
311
|
-
const commands = encodePacked(commandList.map(() => "uint8"), commandList);
|
|
312
|
-
const value = isNativeIn ? amountInMaximum : 0n;
|
|
313
|
-
return {
|
|
314
|
-
args: [
|
|
315
|
-
commands,
|
|
316
|
-
inputs,
|
|
317
|
-
deadline
|
|
318
|
-
],
|
|
319
|
-
value
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Build the `execute(commands, inputs, deadline)` calldata + msg.value for an
|
|
324
|
-
* exact-out single-hop v4 swap.
|
|
325
|
-
*
|
|
326
|
-
* @returns The encoded calldata and the ETH `value` to send.
|
|
327
|
-
*/
|
|
328
|
-
function buildV4ExactOutSwapExecuteCalldata(args) {
|
|
329
|
-
const { args: executeArgs, value } = buildV4ExactOutSwapExecuteArgs(args);
|
|
330
|
-
const data = encodeFunctionData({
|
|
331
|
-
abi: universalRouterAbi,
|
|
332
|
-
functionName: "execute",
|
|
333
|
-
args: executeArgs
|
|
334
|
-
});
|
|
335
|
-
return {
|
|
336
|
-
data,
|
|
337
|
-
value
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
//#endregion
|
|
342
|
-
//#region src/uniswap/v4/abis/permit2.ts
|
|
343
|
-
/**
|
|
344
|
-
* Minimal Permit2 ABI (`IAllowanceTransfer` subset: `approve` + `allowance`).
|
|
345
|
-
* @module uniswap/v4/abis/permit2
|
|
346
|
-
*/
|
|
347
|
-
const permit2Abi = [{
|
|
348
|
-
type: "function",
|
|
349
|
-
name: "approve",
|
|
350
|
-
stateMutability: "nonpayable",
|
|
351
|
-
inputs: [
|
|
352
|
-
{
|
|
353
|
-
name: "token",
|
|
354
|
-
type: "address"
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
name: "spender",
|
|
358
|
-
type: "address"
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
name: "amount",
|
|
362
|
-
type: "uint160"
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
name: "expiration",
|
|
366
|
-
type: "uint48"
|
|
367
|
-
}
|
|
368
|
-
],
|
|
369
|
-
outputs: []
|
|
370
|
-
}, {
|
|
371
|
-
type: "function",
|
|
372
|
-
name: "allowance",
|
|
373
|
-
stateMutability: "view",
|
|
374
|
-
inputs: [
|
|
375
|
-
{
|
|
376
|
-
name: "user",
|
|
377
|
-
type: "address"
|
|
378
|
-
},
|
|
379
|
-
{
|
|
380
|
-
name: "token",
|
|
381
|
-
type: "address"
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
name: "spender",
|
|
385
|
-
type: "address"
|
|
386
|
-
}
|
|
387
|
-
],
|
|
388
|
-
outputs: [
|
|
389
|
-
{
|
|
390
|
-
name: "amount",
|
|
391
|
-
type: "uint160"
|
|
392
|
-
},
|
|
393
|
-
{
|
|
394
|
-
name: "expiration",
|
|
395
|
-
type: "uint48"
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
name: "nonce",
|
|
399
|
-
type: "uint48"
|
|
400
|
-
}
|
|
401
|
-
]
|
|
402
|
-
}];
|
|
403
|
-
|
|
404
|
-
//#endregion
|
|
405
|
-
//#region src/uniswap/v4/router/permit2.ts
|
|
406
|
-
/** uint160 max — the largest Permit2 allowance amount. */
|
|
407
|
-
const UINT160_MAX = (1n << 160n) - 1n;
|
|
408
|
-
/** Default Permit2 allowance expiration window (30 days). */
|
|
409
|
-
const DEFAULT_EXPIRATION_SECONDS = 2592000n;
|
|
410
|
-
/** uint48 max — the largest Permit2 expiration. */
|
|
411
|
-
const UINT48_MAX = (1n << 48n) - 1n;
|
|
412
|
-
/**
|
|
413
|
-
* Check whether the ERC20 → Permit2 → Universal Router allowance chain is
|
|
414
|
-
* sufficient for an exact-in swap of `amount`.
|
|
415
|
-
*/
|
|
416
|
-
async function checkRouterApproval(params) {
|
|
417
|
-
const { client, chainId, tokenIn, owner, amount, addresses } = params;
|
|
418
|
-
const { permit2, universalRouter } = getUniswapV4Addresses(chainId, addresses);
|
|
419
|
-
const [erc20Allowance, permit2Allowance, blockMeta] = await Promise.all([
|
|
420
|
-
client.readContract({
|
|
421
|
-
address: tokenIn,
|
|
422
|
-
abi: erc20Abi,
|
|
423
|
-
functionName: "allowance",
|
|
424
|
-
args: [owner, permit2]
|
|
425
|
-
}),
|
|
426
|
-
client.readContract({
|
|
427
|
-
address: permit2,
|
|
428
|
-
abi: permit2Abi,
|
|
429
|
-
functionName: "allowance",
|
|
430
|
-
args: [
|
|
431
|
-
owner,
|
|
432
|
-
tokenIn,
|
|
433
|
-
universalRouter
|
|
434
|
-
]
|
|
435
|
-
}),
|
|
436
|
-
getBlockMeta({ client })
|
|
437
|
-
]);
|
|
438
|
-
const [permit2Amount, permit2Expiration] = permit2Allowance;
|
|
439
|
-
const needsErc20Approval = erc20Allowance < amount;
|
|
440
|
-
const needsPermit2Approval = permit2Amount < amount || BigInt(permit2Expiration) <= blockMeta.blockTimestamp;
|
|
441
|
-
return {
|
|
442
|
-
needsErc20Approval,
|
|
443
|
-
needsPermit2Approval,
|
|
444
|
-
erc20Allowance,
|
|
445
|
-
permit2Amount,
|
|
446
|
-
permit2Expiration: BigInt(permit2Expiration)
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* Step 1: approve the ERC20 token to Permit2 (defaults to unlimited).
|
|
451
|
-
*/
|
|
452
|
-
async function approveErc20ForPermit2(params) {
|
|
453
|
-
const { client, walletClient, account, chainId, tokenIn, amount = maxUint256, txOverrides, addresses } = params;
|
|
454
|
-
const { permit2 } = getUniswapV4Addresses(chainId, addresses);
|
|
455
|
-
return submitWrite({
|
|
456
|
-
client,
|
|
457
|
-
walletClient,
|
|
458
|
-
account,
|
|
459
|
-
address: tokenIn,
|
|
460
|
-
abi: erc20Abi,
|
|
461
|
-
functionName: "approve",
|
|
462
|
-
args: [permit2, amount],
|
|
463
|
-
txOverrides
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Step 1 (and wait): approve the ERC20 token to Permit2.
|
|
468
|
-
*/
|
|
469
|
-
async function approveErc20ForPermit2AndWait(params) {
|
|
470
|
-
const result = await approveErc20ForPermit2(params);
|
|
471
|
-
return result.wait();
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Step 2: approve the Universal Router as a Permit2 spender for the token.
|
|
475
|
-
*/
|
|
476
|
-
async function approveRouterViaPermit2(params) {
|
|
477
|
-
const { client, walletClient, account, chainId, tokenIn, amount = UINT160_MAX, expiration, txOverrides, addresses } = params;
|
|
478
|
-
const { permit2, universalRouter } = getUniswapV4Addresses(chainId, addresses);
|
|
479
|
-
const resolvedExpiration = expiration ?? (await getBlockMeta({ client })).blockTimestamp + DEFAULT_EXPIRATION_SECONDS;
|
|
480
|
-
const cappedExpiration = resolvedExpiration > UINT48_MAX ? UINT48_MAX : resolvedExpiration;
|
|
481
|
-
return submitWrite({
|
|
482
|
-
client,
|
|
483
|
-
walletClient,
|
|
484
|
-
account,
|
|
485
|
-
address: permit2,
|
|
486
|
-
abi: permit2Abi,
|
|
487
|
-
functionName: "approve",
|
|
488
|
-
args: [
|
|
489
|
-
tokenIn,
|
|
490
|
-
universalRouter,
|
|
491
|
-
amount,
|
|
492
|
-
cappedExpiration
|
|
493
|
-
],
|
|
494
|
-
txOverrides
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Step 2 (and wait): approve the Universal Router via Permit2.
|
|
499
|
-
*/
|
|
500
|
-
async function approveRouterViaPermit2AndWait(params) {
|
|
501
|
-
const result = await approveRouterViaPermit2(params);
|
|
502
|
-
return result.wait();
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
//#endregion
|
|
506
|
-
//#region src/uniswap/v4/abis/v4Quoter.ts
|
|
507
|
-
/**
|
|
508
|
-
* Minimal V4Quoter ABI (`quoteExactInputSingle`, `quoteExactOutputSingle`).
|
|
509
|
-
*
|
|
510
|
-
* Note: neither function is `view` — they mutate state internally and are
|
|
511
|
-
* intended to be called via `eth_call` / viem `simulateContract`, never
|
|
512
|
-
* `readContract`.
|
|
513
|
-
*
|
|
514
|
-
* @module uniswap/v4/abis/v4Quoter
|
|
515
|
-
*/
|
|
516
|
-
const quoteExactSingleParams = {
|
|
517
|
-
name: "params",
|
|
518
|
-
type: "tuple",
|
|
519
|
-
components: [
|
|
520
|
-
{
|
|
521
|
-
name: "poolKey",
|
|
522
|
-
type: "tuple",
|
|
523
|
-
components: [
|
|
524
|
-
{
|
|
525
|
-
name: "currency0",
|
|
526
|
-
type: "address"
|
|
527
|
-
},
|
|
528
|
-
{
|
|
529
|
-
name: "currency1",
|
|
530
|
-
type: "address"
|
|
531
|
-
},
|
|
532
|
-
{
|
|
533
|
-
name: "fee",
|
|
534
|
-
type: "uint24"
|
|
535
|
-
},
|
|
536
|
-
{
|
|
537
|
-
name: "tickSpacing",
|
|
538
|
-
type: "int24"
|
|
539
|
-
},
|
|
540
|
-
{
|
|
541
|
-
name: "hooks",
|
|
542
|
-
type: "address"
|
|
543
|
-
}
|
|
544
|
-
]
|
|
545
|
-
},
|
|
546
|
-
{
|
|
547
|
-
name: "zeroForOne",
|
|
548
|
-
type: "bool"
|
|
549
|
-
},
|
|
550
|
-
{
|
|
551
|
-
name: "exactAmount",
|
|
552
|
-
type: "uint128"
|
|
553
|
-
},
|
|
554
|
-
{
|
|
555
|
-
name: "hookData",
|
|
556
|
-
type: "bytes"
|
|
557
|
-
}
|
|
558
|
-
]
|
|
559
|
-
};
|
|
560
|
-
const v4QuoterAbi = [{
|
|
561
|
-
type: "function",
|
|
562
|
-
name: "quoteExactInputSingle",
|
|
563
|
-
stateMutability: "nonpayable",
|
|
564
|
-
inputs: [quoteExactSingleParams],
|
|
565
|
-
outputs: [{
|
|
566
|
-
name: "amountOut",
|
|
567
|
-
type: "uint256"
|
|
568
|
-
}, {
|
|
569
|
-
name: "gasEstimate",
|
|
570
|
-
type: "uint256"
|
|
571
|
-
}]
|
|
572
|
-
}, {
|
|
573
|
-
type: "function",
|
|
574
|
-
name: "quoteExactOutputSingle",
|
|
575
|
-
stateMutability: "nonpayable",
|
|
576
|
-
inputs: [quoteExactSingleParams],
|
|
577
|
-
outputs: [{
|
|
578
|
-
name: "amountIn",
|
|
579
|
-
type: "uint256"
|
|
580
|
-
}, {
|
|
581
|
-
name: "gasEstimate",
|
|
582
|
-
type: "uint256"
|
|
583
|
-
}]
|
|
584
|
-
}];
|
|
585
|
-
|
|
586
|
-
//#endregion
|
|
587
|
-
//#region src/uniswap/v4/router/resolvePoolKey.ts
|
|
588
|
-
/**
|
|
589
|
-
* Resolve the PoolKey, swap direction, and output token metadata for a swap.
|
|
590
|
-
*
|
|
591
|
-
* @throws {InvalidSwapTokenError} when `tokenIn` is not part of the pool.
|
|
592
|
-
*/
|
|
593
|
-
async function resolveSwapRoute(params) {
|
|
594
|
-
const { client, poolAddress, chainId, tokenIn, blockNumber } = params;
|
|
595
|
-
const pool = await getPool({
|
|
596
|
-
client,
|
|
597
|
-
poolAddress,
|
|
598
|
-
chainId,
|
|
599
|
-
blockNumber
|
|
600
|
-
});
|
|
601
|
-
const { poolKey } = pool;
|
|
602
|
-
const isCurrency0 = isAddressEqual(tokenIn, poolKey.currency0);
|
|
603
|
-
const isCurrency1 = isAddressEqual(tokenIn, poolKey.currency1);
|
|
604
|
-
if (!isCurrency0 && !isCurrency1) throw new InvalidSwapTokenError(tokenIn, poolKey.currency0, poolKey.currency1);
|
|
605
|
-
const zeroForOne = isCurrency0;
|
|
606
|
-
const tokenOut = zeroForOne ? poolKey.currency1 : poolKey.currency0;
|
|
607
|
-
const outTracker = zeroForOne ? pool.collateralTracker1 : pool.collateralTracker0;
|
|
608
|
-
return {
|
|
609
|
-
poolKey,
|
|
610
|
-
zeroForOne,
|
|
611
|
-
tokenOut,
|
|
612
|
-
tokenOutDecimals: outTracker.decimals,
|
|
613
|
-
tokenOutSymbol: outTracker.symbol
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
//#endregion
|
|
618
|
-
//#region src/uniswap/v4/router/quote.ts
|
|
619
|
-
const BPS_DENOMINATOR$1 = 10000n;
|
|
620
|
-
const UINT128_MAX$1 = (1n << 128n) - 1n;
|
|
621
|
-
/** Reject out-of-range slippage so the min/max amount math can't underflow/overflow. */
|
|
622
|
-
function assertSlippageBps(slippageBps) {
|
|
623
|
-
if (slippageBps < 0n || slippageBps > BPS_DENOMINATOR$1) throw new PanopticError(`invalid slippageBps ${slippageBps}, must be 0..10000`);
|
|
624
|
-
}
|
|
625
|
-
const FALLBACK_META = {
|
|
626
|
-
blockNumber: 0n,
|
|
627
|
-
blockTimestamp: 0n,
|
|
628
|
-
blockHash: "0x0"
|
|
629
|
-
};
|
|
630
|
-
/**
|
|
631
|
-
* Quote an exact-in spot swap against the underlying Uniswap v4 pool.
|
|
632
|
-
*
|
|
633
|
-
* Uses the V4Quoter `quoteExactInputSingle` via `eth_call` (the quoter is
|
|
634
|
-
* state-mutating / revert-based, so it must be simulated, not read). Returns a
|
|
635
|
-
* `SimulationResult` so failures carry a structured error rather than throwing.
|
|
636
|
-
*/
|
|
637
|
-
async function quoteSwapExactInViaRouter(params) {
|
|
638
|
-
const { client, poolAddress, chainId, tokenIn, amountIn, slippageBps, blockNumber, addresses } = params;
|
|
639
|
-
try {
|
|
640
|
-
if (amountIn < 0n || amountIn > UINT128_MAX$1) throw new PanopticError(`amountIn ${amountIn} exceeds uint128 maximum`);
|
|
641
|
-
assertSlippageBps(slippageBps);
|
|
642
|
-
const targetBlockNumber = blockNumber ?? await client.getBlockNumber();
|
|
643
|
-
const metaPromise = getBlockMeta({
|
|
644
|
-
client,
|
|
645
|
-
blockNumber: targetBlockNumber
|
|
646
|
-
});
|
|
647
|
-
const { v4Quoter } = getUniswapV4Addresses(chainId, addresses);
|
|
648
|
-
if (v4Quoter === zeroAddress) throw new QuoterUnavailableError(chainId);
|
|
649
|
-
const route = await resolveSwapRoute({
|
|
650
|
-
client,
|
|
651
|
-
poolAddress,
|
|
652
|
-
chainId,
|
|
653
|
-
tokenIn,
|
|
654
|
-
blockNumber: targetBlockNumber
|
|
655
|
-
});
|
|
656
|
-
const { result } = await client.simulateContract({
|
|
657
|
-
address: v4Quoter,
|
|
658
|
-
abi: v4QuoterAbi,
|
|
659
|
-
functionName: "quoteExactInputSingle",
|
|
660
|
-
blockNumber: targetBlockNumber,
|
|
661
|
-
args: [{
|
|
662
|
-
poolKey: {
|
|
663
|
-
currency0: route.poolKey.currency0,
|
|
664
|
-
currency1: route.poolKey.currency1,
|
|
665
|
-
fee: Number(route.poolKey.fee),
|
|
666
|
-
tickSpacing: Number(route.poolKey.tickSpacing),
|
|
667
|
-
hooks: route.poolKey.hooks
|
|
668
|
-
},
|
|
669
|
-
zeroForOne: route.zeroForOne,
|
|
670
|
-
exactAmount: amountIn,
|
|
671
|
-
hookData: "0x"
|
|
672
|
-
}]
|
|
673
|
-
});
|
|
674
|
-
const [amountOut, gasEstimate] = result;
|
|
675
|
-
const amountOutMinimum = amountOut * (BPS_DENOMINATOR$1 - slippageBps) / BPS_DENOMINATOR$1;
|
|
676
|
-
const _meta = await metaPromise;
|
|
677
|
-
return {
|
|
678
|
-
success: true,
|
|
679
|
-
data: {
|
|
680
|
-
amountOut,
|
|
681
|
-
amountOutMinimum,
|
|
682
|
-
zeroForOne: route.zeroForOne,
|
|
683
|
-
tokenOut: route.tokenOut,
|
|
684
|
-
poolKey: route.poolKey,
|
|
685
|
-
gasEstimate
|
|
686
|
-
},
|
|
687
|
-
gasEstimate,
|
|
688
|
-
_meta
|
|
689
|
-
};
|
|
690
|
-
} catch (error) {
|
|
691
|
-
return {
|
|
692
|
-
success: false,
|
|
693
|
-
error: error instanceof PanopticError ? error : new PanopticError(error instanceof Error ? error.message : "Quote failed", error instanceof Error ? error : void 0),
|
|
694
|
-
_meta: FALLBACK_META
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Quote an exact-out spot swap against the underlying Uniswap v4 pool.
|
|
700
|
-
*
|
|
701
|
-
* Uses the V4Quoter `quoteExactOutputSingle` via `eth_call` (the quoter is
|
|
702
|
-
* state-mutating / revert-based, so it must be simulated, not read). Returns a
|
|
703
|
-
* `SimulationResult` so failures carry a structured error rather than throwing.
|
|
704
|
-
*/
|
|
705
|
-
async function quoteSwapExactOutViaRouter(params) {
|
|
706
|
-
const { client, poolAddress, chainId, tokenIn, amountOut, slippageBps, blockNumber, addresses } = params;
|
|
707
|
-
try {
|
|
708
|
-
if (amountOut < 0n || amountOut > UINT128_MAX$1) throw new PanopticError(`amountOut ${amountOut} exceeds uint128 maximum`);
|
|
709
|
-
assertSlippageBps(slippageBps);
|
|
710
|
-
const targetBlockNumber = blockNumber ?? await client.getBlockNumber();
|
|
711
|
-
const metaPromise = getBlockMeta({
|
|
712
|
-
client,
|
|
713
|
-
blockNumber: targetBlockNumber
|
|
714
|
-
});
|
|
715
|
-
const { v4Quoter } = getUniswapV4Addresses(chainId, addresses);
|
|
716
|
-
if (v4Quoter === zeroAddress) throw new QuoterUnavailableError(chainId);
|
|
717
|
-
const route = await resolveSwapRoute({
|
|
718
|
-
client,
|
|
719
|
-
poolAddress,
|
|
720
|
-
chainId,
|
|
721
|
-
tokenIn,
|
|
722
|
-
blockNumber: targetBlockNumber
|
|
723
|
-
});
|
|
724
|
-
const { result } = await client.simulateContract({
|
|
725
|
-
address: v4Quoter,
|
|
726
|
-
abi: v4QuoterAbi,
|
|
727
|
-
functionName: "quoteExactOutputSingle",
|
|
728
|
-
blockNumber: targetBlockNumber,
|
|
729
|
-
args: [{
|
|
730
|
-
poolKey: {
|
|
731
|
-
currency0: route.poolKey.currency0,
|
|
732
|
-
currency1: route.poolKey.currency1,
|
|
733
|
-
fee: Number(route.poolKey.fee),
|
|
734
|
-
tickSpacing: Number(route.poolKey.tickSpacing),
|
|
735
|
-
hooks: route.poolKey.hooks
|
|
736
|
-
},
|
|
737
|
-
zeroForOne: route.zeroForOne,
|
|
738
|
-
exactAmount: amountOut,
|
|
739
|
-
hookData: "0x"
|
|
740
|
-
}]
|
|
741
|
-
});
|
|
742
|
-
const [amountIn, gasEstimate] = result;
|
|
743
|
-
const amountInMaximum = (amountIn * (BPS_DENOMINATOR$1 + slippageBps) + BPS_DENOMINATOR$1 - 1n) / BPS_DENOMINATOR$1;
|
|
744
|
-
const _meta = await metaPromise;
|
|
745
|
-
return {
|
|
746
|
-
success: true,
|
|
747
|
-
data: {
|
|
748
|
-
amountIn,
|
|
749
|
-
amountInMaximum,
|
|
750
|
-
zeroForOne: route.zeroForOne,
|
|
751
|
-
tokenOut: route.tokenOut,
|
|
752
|
-
poolKey: route.poolKey,
|
|
753
|
-
gasEstimate
|
|
754
|
-
},
|
|
755
|
-
gasEstimate,
|
|
756
|
-
_meta
|
|
757
|
-
};
|
|
758
|
-
} catch (error) {
|
|
759
|
-
return {
|
|
760
|
-
success: false,
|
|
761
|
-
error: error instanceof PanopticError ? error : new PanopticError(error instanceof Error ? error.message : "Quote failed", error instanceof Error ? error : void 0),
|
|
762
|
-
_meta: FALLBACK_META
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
//#endregion
|
|
768
|
-
//#region src/uniswap/v4/router/quoteByPoolKey.ts
|
|
769
|
-
const BPS_DENOMINATOR = 10000n;
|
|
770
|
-
const UINT128_MAX = (1n << 128n) - 1n;
|
|
771
|
-
/**
|
|
772
|
-
* Quote an exact-in v4 swap for a specific PoolKey. Returns `null` when the
|
|
773
|
-
* quote reverts (pool missing / no liquidity) so callers can skip that pool when
|
|
774
|
-
* ranking venues rather than aborting the cycle.
|
|
775
|
-
*/
|
|
776
|
-
async function quoteV4ExactInByPoolKey(params) {
|
|
777
|
-
const { client, chainId, poolKey, zeroForOne, amountIn, slippageBps, blockNumber } = params;
|
|
778
|
-
if (amountIn < 0n || amountIn > UINT128_MAX) throw new PanopticError(`amountIn ${amountIn} exceeds uint128 maximum`);
|
|
779
|
-
if (slippageBps < 0n || slippageBps > BPS_DENOMINATOR) throw new PanopticError(`invalid slippageBps ${slippageBps}, must be 0..10000`);
|
|
780
|
-
const { v4Quoter } = getUniswapV4Addresses(chainId, params.addresses);
|
|
781
|
-
try {
|
|
782
|
-
const { result } = await client.simulateContract({
|
|
783
|
-
address: v4Quoter,
|
|
784
|
-
abi: v4QuoterAbi,
|
|
785
|
-
functionName: "quoteExactInputSingle",
|
|
786
|
-
blockNumber,
|
|
787
|
-
args: [{
|
|
788
|
-
poolKey: {
|
|
789
|
-
currency0: poolKey.currency0,
|
|
790
|
-
currency1: poolKey.currency1,
|
|
791
|
-
fee: Number(poolKey.fee),
|
|
792
|
-
tickSpacing: Number(poolKey.tickSpacing),
|
|
793
|
-
hooks: poolKey.hooks
|
|
794
|
-
},
|
|
795
|
-
zeroForOne,
|
|
796
|
-
exactAmount: amountIn,
|
|
797
|
-
hookData: "0x"
|
|
798
|
-
}]
|
|
799
|
-
});
|
|
800
|
-
const [amountOut, gasEstimate] = result;
|
|
801
|
-
const amountOutMinimum = amountOut * (BPS_DENOMINATOR - slippageBps) / BPS_DENOMINATOR;
|
|
802
|
-
return {
|
|
803
|
-
amountOut,
|
|
804
|
-
amountOutMinimum,
|
|
805
|
-
gasEstimate
|
|
806
|
-
};
|
|
807
|
-
} catch (err) {
|
|
808
|
-
if (isRevert(err)) return null;
|
|
809
|
-
throw err;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
/** True only for genuine contract reverts (missing pool / no liquidity). */
|
|
813
|
-
function isRevert(err) {
|
|
814
|
-
return err instanceof BaseError && err.walk((e) => e instanceof ContractFunctionRevertedError) instanceof ContractFunctionRevertedError;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
//#endregion
|
|
818
|
-
//#region src/uniswap/v4/router/swap.ts
|
|
819
|
-
/** Default swap deadline window (30 minutes) when no deadline is provided. */
|
|
820
|
-
const DEFAULT_DEADLINE_SECONDS = 1800n;
|
|
821
|
-
/**
|
|
822
|
-
* Execute an exact-in spot swap via the Universal Router.
|
|
823
|
-
*
|
|
824
|
-
* @param params - Swap parameters.
|
|
825
|
-
* @returns TxResult with hash + wait().
|
|
826
|
-
*
|
|
827
|
-
* @example
|
|
828
|
-
* ```typescript
|
|
829
|
-
* const result = await swapExactInViaRouter({
|
|
830
|
-
* client, walletClient, account, poolAddress,
|
|
831
|
-
* chainId: 1n,
|
|
832
|
-
* tokenIn: ZERO_ADDRESS, // native ETH
|
|
833
|
-
* amountIn: 10n ** 17n, // 0.1 ETH
|
|
834
|
-
* slippageBps: 50n, // 0.5%
|
|
835
|
-
* })
|
|
836
|
-
* await result.wait()
|
|
837
|
-
* ```
|
|
838
|
-
*/
|
|
839
|
-
async function swapExactInViaRouter(params) {
|
|
840
|
-
const { client, walletClient, account, poolAddress, chainId, tokenIn, amountIn, slippageBps, deadline, recipient, txOverrides, addresses } = params;
|
|
841
|
-
if (recipient !== void 0 && !isAddressEqual(recipient, account)) throw new PanopticError("Custom recipient is not supported yet; output goes to the sender");
|
|
842
|
-
const resolved = getUniswapV4Addresses(chainId, addresses);
|
|
843
|
-
const quote = await quoteSwapExactInViaRouter({
|
|
844
|
-
client,
|
|
845
|
-
poolAddress,
|
|
846
|
-
chainId,
|
|
847
|
-
tokenIn,
|
|
848
|
-
amountIn,
|
|
849
|
-
slippageBps,
|
|
850
|
-
addresses
|
|
851
|
-
});
|
|
852
|
-
if (!quote.success) throw quote.error;
|
|
853
|
-
const resolvedDeadline = deadline ?? (await getBlockMeta({ client })).blockTimestamp + DEFAULT_DEADLINE_SECONDS;
|
|
854
|
-
if (!quote.data.poolKey) throw new PanopticError("v4 swap requires a poolKey from the quote");
|
|
855
|
-
const { args, value } = buildV4SwapExecuteArgs({
|
|
856
|
-
poolKey: quote.data.poolKey,
|
|
857
|
-
zeroForOne: quote.data.zeroForOne,
|
|
858
|
-
amountIn,
|
|
859
|
-
amountOutMinimum: quote.data.amountOutMinimum,
|
|
860
|
-
tokenIn,
|
|
861
|
-
tokenOut: quote.data.tokenOut,
|
|
862
|
-
deadline: resolvedDeadline,
|
|
863
|
-
recipient: account
|
|
864
|
-
});
|
|
865
|
-
return submitWrite({
|
|
866
|
-
client,
|
|
867
|
-
walletClient,
|
|
868
|
-
account,
|
|
869
|
-
address: resolved.universalRouter,
|
|
870
|
-
abi: universalRouterAbi,
|
|
871
|
-
functionName: "execute",
|
|
872
|
-
args,
|
|
873
|
-
value,
|
|
874
|
-
txOverrides
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
|
-
/**
|
|
878
|
-
* Execute an exact-in swap via the Universal Router and wait for confirmation.
|
|
879
|
-
*/
|
|
880
|
-
async function swapExactInViaRouterAndWait(params) {
|
|
881
|
-
const result = await swapExactInViaRouter(params);
|
|
882
|
-
return result.wait();
|
|
883
|
-
}
|
|
884
|
-
/**
|
|
885
|
-
* Execute an exact-out spot swap via the Universal Router.
|
|
886
|
-
*
|
|
887
|
-
* The caller specifies the exact `amountOut` to receive; the input (pay) amount
|
|
888
|
-
* is quoted and capped at `amountInMaximum`. For native-ETH input the router is
|
|
889
|
-
* funded with `amountInMaximum` and the unused surplus is swept back to the
|
|
890
|
-
* sender.
|
|
891
|
-
*
|
|
892
|
-
* @param params - Swap parameters.
|
|
893
|
-
* @returns TxResult with hash + wait().
|
|
894
|
-
*/
|
|
895
|
-
async function swapExactOutViaRouter(params) {
|
|
896
|
-
const { client, walletClient, account, poolAddress, chainId, tokenIn, amountOut, slippageBps, deadline, recipient, txOverrides, addresses } = params;
|
|
897
|
-
if (recipient !== void 0 && !isAddressEqual(recipient, account)) throw new PanopticError("Custom recipient is not supported yet; output goes to the sender");
|
|
898
|
-
const resolved = getUniswapV4Addresses(chainId, addresses);
|
|
899
|
-
const quote = await quoteSwapExactOutViaRouter({
|
|
900
|
-
client,
|
|
901
|
-
poolAddress,
|
|
902
|
-
chainId,
|
|
903
|
-
tokenIn,
|
|
904
|
-
amountOut,
|
|
905
|
-
slippageBps,
|
|
906
|
-
addresses
|
|
907
|
-
});
|
|
908
|
-
if (!quote.success) throw quote.error;
|
|
909
|
-
const resolvedDeadline = deadline ?? (await getBlockMeta({ client })).blockTimestamp + DEFAULT_DEADLINE_SECONDS;
|
|
910
|
-
if (!quote.data.poolKey) throw new PanopticError("v4 swap requires a poolKey from the quote");
|
|
911
|
-
const { args, value } = buildV4ExactOutSwapExecuteArgs({
|
|
912
|
-
poolKey: quote.data.poolKey,
|
|
913
|
-
zeroForOne: quote.data.zeroForOne,
|
|
914
|
-
amountOut,
|
|
915
|
-
amountInMaximum: quote.data.amountInMaximum,
|
|
916
|
-
tokenIn,
|
|
917
|
-
tokenOut: quote.data.tokenOut,
|
|
918
|
-
deadline: resolvedDeadline,
|
|
919
|
-
recipient: account
|
|
920
|
-
});
|
|
921
|
-
return submitWrite({
|
|
922
|
-
client,
|
|
923
|
-
walletClient,
|
|
924
|
-
account,
|
|
925
|
-
address: resolved.universalRouter,
|
|
926
|
-
abi: universalRouterAbi,
|
|
927
|
-
functionName: "execute",
|
|
928
|
-
args,
|
|
929
|
-
value,
|
|
930
|
-
txOverrides
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Execute an exact-out swap via the Universal Router and wait for confirmation.
|
|
935
|
-
*/
|
|
936
|
-
async function swapExactOutViaRouterAndWait(params) {
|
|
937
|
-
const result = await swapExactOutViaRouter(params);
|
|
938
|
-
return result.wait();
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
//#endregion
|
|
942
|
-
export { SETTLE_ALL, SWAP_EXACT_IN_SINGLE, SWAP_EXACT_OUT_SINGLE, SWEEP, TAKE_ALL, V4_SWAP, approveErc20ForPermit2, approveErc20ForPermit2AndWait, approveRouterViaPermit2, approveRouterViaPermit2AndWait, buildV4ExactOutSwapExecuteArgs, buildV4ExactOutSwapExecuteCalldata, buildV4SwapExecuteArgs, buildV4SwapExecuteCalldata, checkRouterApproval, quoteSwapExactInViaRouter, quoteSwapExactOutViaRouter, quoteV4ExactInByPoolKey, resolveSwapRoute, swapExactInViaRouter, swapExactInViaRouterAndWait, swapExactOutViaRouter, swapExactOutViaRouterAndWait };
|