@panoptic-eng/sdk 1.0.5 → 1.0.6

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