@nradko/metric-omm-sdk-v1 0.4.2 → 0.4.4

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 (117) hide show
  1. package/README.md +134 -19
  2. package/dist/abis/DepositAllowlistExtension.d.ts +43 -0
  3. package/dist/abis/DepositAllowlistExtension.d.ts.map +1 -1
  4. package/dist/abis/DepositAllowlistExtension.js +56 -0
  5. package/dist/abis/DepositAllowlistExtension.js.map +1 -1
  6. package/dist/abis/MetricOmmPoolFactory.d.ts +12 -0
  7. package/dist/abis/MetricOmmPoolFactory.d.ts.map +1 -1
  8. package/dist/abis/MetricOmmPoolFactory.js +16 -0
  9. package/dist/abis/MetricOmmPoolFactory.js.map +1 -1
  10. package/dist/abis/MetricOmmPoolLiquidityAdder.d.ts +116 -5
  11. package/dist/abis/MetricOmmPoolLiquidityAdder.d.ts.map +1 -1
  12. package/dist/abis/MetricOmmPoolLiquidityAdder.js +150 -5
  13. package/dist/abis/MetricOmmPoolLiquidityAdder.js.map +1 -1
  14. package/dist/abis/MetricOmmSimpleRouter.d.ts +32 -0
  15. package/dist/abis/MetricOmmSimpleRouter.d.ts.map +1 -1
  16. package/dist/abis/MetricOmmSimpleRouter.js +42 -0
  17. package/dist/abis/MetricOmmSimpleRouter.js.map +1 -1
  18. package/dist/abis/MetricOmmSwapQuoter.d.ts +14 -2
  19. package/dist/abis/MetricOmmSwapQuoter.d.ts.map +1 -1
  20. package/dist/abis/MetricOmmSwapQuoter.js +18 -2
  21. package/dist/abis/MetricOmmSwapQuoter.js.map +1 -1
  22. package/dist/abis/SwapAllowlistExtension.d.ts +43 -0
  23. package/dist/abis/SwapAllowlistExtension.d.ts.map +1 -1
  24. package/dist/abis/SwapAllowlistExtension.js +56 -0
  25. package/dist/abis/SwapAllowlistExtension.js.map +1 -1
  26. package/dist/abis/index.d.ts +2 -3
  27. package/dist/abis/index.d.ts.map +1 -1
  28. package/dist/abis/index.js +2 -3
  29. package/dist/abis/index.js.map +1 -1
  30. package/dist/addresses.d.ts +2 -4
  31. package/dist/addresses.d.ts.map +1 -1
  32. package/dist/addresses.js +11 -12
  33. package/dist/addresses.js.map +1 -1
  34. package/dist/dataProvider/index.d.ts +1 -1
  35. package/dist/dataProvider/index.d.ts.map +1 -1
  36. package/dist/dataProvider/index.js +1 -1
  37. package/dist/dataProvider/index.js.map +1 -1
  38. package/dist/dataProvider/read.d.ts +1 -3
  39. package/dist/dataProvider/read.d.ts.map +1 -1
  40. package/dist/dataProvider/read.js +1 -3
  41. package/dist/dataProvider/read.js.map +1 -1
  42. package/dist/index.d.ts +5 -3
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +6 -3
  45. package/dist/index.js.map +1 -1
  46. package/dist/peripheryPayments/index.d.ts +2 -0
  47. package/dist/peripheryPayments/index.d.ts.map +1 -0
  48. package/dist/peripheryPayments/index.js +2 -0
  49. package/dist/peripheryPayments/index.js.map +1 -0
  50. package/dist/peripheryPayments/simpleRouter.d.ts +9 -0
  51. package/dist/peripheryPayments/simpleRouter.d.ts.map +1 -0
  52. package/dist/peripheryPayments/simpleRouter.js +27 -0
  53. package/dist/peripheryPayments/simpleRouter.js.map +1 -0
  54. package/dist/pool/cursorBounds.d.ts +29 -0
  55. package/dist/pool/cursorBounds.d.ts.map +1 -0
  56. package/dist/pool/cursorBounds.js +76 -0
  57. package/dist/pool/cursorBounds.js.map +1 -0
  58. package/dist/pool/index.d.ts +3 -1
  59. package/dist/pool/index.d.ts.map +1 -1
  60. package/dist/pool/index.js +3 -1
  61. package/dist/pool/index.js.map +1 -1
  62. package/dist/pool/liquidity.d.ts +114 -7
  63. package/dist/pool/liquidity.d.ts.map +1 -1
  64. package/dist/pool/liquidity.js +96 -11
  65. package/dist/pool/liquidity.js.map +1 -1
  66. package/dist/pool/nativeLiquidity.d.ts +32 -0
  67. package/dist/pool/nativeLiquidity.d.ts.map +1 -0
  68. package/dist/pool/nativeLiquidity.js +47 -0
  69. package/dist/pool/nativeLiquidity.js.map +1 -0
  70. package/dist/router/fromRoute.d.ts +93 -0
  71. package/dist/router/fromRoute.d.ts.map +1 -0
  72. package/dist/router/fromRoute.js +187 -0
  73. package/dist/router/fromRoute.js.map +1 -0
  74. package/dist/router/index.d.ts +8 -1
  75. package/dist/router/index.d.ts.map +1 -1
  76. package/dist/router/index.js +8 -1
  77. package/dist/router/index.js.map +1 -1
  78. package/dist/router/nativeSwap.d.ts +47 -0
  79. package/dist/router/nativeSwap.d.ts.map +1 -0
  80. package/dist/router/nativeSwap.js +89 -0
  81. package/dist/router/nativeSwap.js.map +1 -0
  82. package/dist/router/swap.d.ts +82 -1
  83. package/dist/router/swap.d.ts.map +1 -1
  84. package/dist/router/swap.js +51 -1
  85. package/dist/router/swap.js.map +1 -1
  86. package/dist/router/swapPath.d.ts +46 -1
  87. package/dist/router/swapPath.d.ts.map +1 -1
  88. package/dist/router/swapPath.js +34 -0
  89. package/dist/router/swapPath.js.map +1 -1
  90. package/package.json +2 -2
  91. package/src/abis/DepositAllowlistExtension.ts +56 -0
  92. package/src/abis/MetricOmmPoolFactory.ts +16 -0
  93. package/src/abis/MetricOmmPoolLiquidityAdder.ts +150 -5
  94. package/src/abis/MetricOmmSimpleRouter.ts +42 -0
  95. package/src/abis/MetricOmmSwapQuoter.ts +18 -2
  96. package/src/abis/SwapAllowlistExtension.ts +56 -0
  97. package/src/abis/index.ts +2 -3
  98. package/src/addresses.ts +12 -15
  99. package/src/dataProvider/index.ts +0 -1
  100. package/src/dataProvider/read.ts +1 -4
  101. package/src/index.ts +38 -2
  102. package/src/peripheryPayments/index.ts +5 -0
  103. package/src/peripheryPayments/simpleRouter.ts +31 -0
  104. package/src/pool/cursorBounds.ts +112 -0
  105. package/src/pool/index.ts +15 -0
  106. package/src/pool/liquidity.ts +124 -15
  107. package/src/pool/nativeLiquidity.ts +71 -0
  108. package/src/router/fromRoute.ts +318 -0
  109. package/src/router/index.ts +36 -1
  110. package/src/router/nativeSwap.ts +123 -0
  111. package/src/router/swap.ts +82 -1
  112. package/src/router/swapPath.ts +47 -1
  113. package/dist/abis/MetricOmmPoolStateView.d.ts +0 -238
  114. package/dist/abis/MetricOmmPoolStateView.d.ts.map +0 -1
  115. package/dist/abis/MetricOmmPoolStateView.js +0 -315
  116. package/dist/abis/MetricOmmPoolStateView.js.map +0 -1
  117. package/src/abis/MetricOmmPoolStateView.ts +0 -314
@@ -1,6 +1,22 @@
1
1
  /**
2
- * MetricAMM SDK - Liquidity
3
- * Unified liquidity calculations and position operations.
2
+ * Liquidity position builders and calldata encoders.
3
+ *
4
+ * **Add liquidity (recommended path):**
5
+ * 1. `buildModifyLiquidityArgsForAddition` / `ForUniformAddition` / `ForUniformAdditionWithTotalTokenAmount`
6
+ * 2. `buildAddLiquidityExactSharesParamsFromBinTargets` (optional convenience)
7
+ * 3. `encodeAddLiquidityExactSharesSelfCalldata` or `encodeAddLiquidityExactSharesWithOwnerCalldata`
8
+ * — target `addresses.liquidityAdder`
9
+ * 4. Approve token0 and token1 on the LiquidityAdder for `maxAmountToken0` / `maxAmountToken1`
10
+ *
11
+ * **Self vs WithOwner encoders:**
12
+ * - `*SelfCalldata` — credits liquidity to **`msg.sender`** (the transaction sender owns the position).
13
+ * - `*WithOwnerCalldata` — credits liquidity to **`owner`** (e.g. router, vault, or relayer paying on behalf of a user).
14
+ * The caller still supplies tokens and must approve the LiquidityAdder; only the position owner differs.
15
+ *
16
+ * **Remove liquidity:** `buildModifyLiquidityArgsForRemoval` → `buildRemoveLiquidityPoolDelta`
17
+ * → `encodeRemoveLiquidityCalldata` — call the **pool** directly (no adder approval).
18
+ *
19
+ * Position identity is `(owner, salt)`; use the same `salt` for adds and removes.
4
20
  */
5
21
 
6
22
  import type { Address, PublicClient, Hex } from "viem";
@@ -40,19 +56,27 @@ function getLiquidityAdderFunction(name: string, inputCount: number) {
40
56
  return item;
41
57
  }
42
58
 
59
+ /** High-level removal options (used by UI helpers). Prefer {@link BuildModifyLiquidityArgsForRemovalParams} in SDK code. */
43
60
  export interface RemoveLiquidityParams {
61
+ /** Percent of each selected bin's shares to burn (0–100). */
44
62
  percentageToRemove: number;
63
+ /** Explicit bin list; when omitted, scans `lowerBin`–`upperBin` for position shares. */
45
64
  bins?: number[];
46
65
  lowerBin?: number;
47
66
  upperBin?: number;
48
67
  }
49
68
 
69
+ /** Per-bin removal: specify exactly one of `percentageToRemove` or `sharesToRemove`. */
50
70
  export interface BinRemovalSpec {
51
71
  bin: number;
52
72
  percentageToRemove?: number;
53
73
  sharesToRemove?: bigint;
54
74
  }
55
75
 
76
+ /**
77
+ * Sentinel passed as `specAmount0` / `specAmount1` on removals — no minimum output enforced.
78
+ * Same value the pool uses for "accept any amount" on `modifyLiquidity` removes.
79
+ */
56
80
  export const NO_SLIPPAGE_LIMIT = MAX_INT128;
57
81
 
58
82
  type ModifyLiquidityParams = {
@@ -62,30 +86,39 @@ type ModifyLiquidityParams = {
62
86
  specAmount1: bigint;
63
87
  };
64
88
 
89
+ /**
90
+ * Tuple for low-level `modifyLiquidity` on the pool:
91
+ * `[salt, deltas, specAmount0, specAmount1]`.
92
+ * Prefer LiquidityAdder encode helpers for additions.
93
+ */
65
94
  export type ModifyLiquidityArgs = readonly [bigint, LiquidityDelta[], bigint, bigint];
66
95
 
67
96
  export interface BuildModifyLiquidityArgsBase {
68
97
  publicClient: PublicClient;
69
- /** MetricOmmPoolDataProvider address (`addresses.dataProvider` on mainnet). */
98
+ /** MetricOmmPoolDataProvider (`addresses.dataProvider`). */
70
99
  dataProviderAddress: Address;
71
100
  poolAddress: Address;
101
+ /** Extra headroom on spec amounts (percent); `0` = exact computed caps. */
72
102
  specAmountBufferPercent?: number;
73
103
  }
74
104
 
75
- /** Addition builders read pool immutables via `getImmutables()` removal only needs the data provider lens. */
105
+ /** Addition builders need the factory to read pool immutables (decimals, bin range). */
76
106
  export interface BuildModifyLiquidityArgsWithFactory extends BuildModifyLiquidityArgsBase {
77
107
  factoryAddress: Address;
78
108
  }
79
109
 
110
+ /** Per-bin target amounts for {@link buildModifyLiquidityArgsForAddition}. */
80
111
  export interface BuildModifyLiquidityArgsForAdditionParams extends BuildModifyLiquidityArgsWithFactory {
81
112
  bins: LiquidityBinValueInput[];
82
- /** Human-readable price (token1 per token0). Omit when using currentPriceX64. */
113
+ /** Human price token1/token0; omit when `currentPriceX64` is set. */
83
114
  currentPrice?: number;
84
- /** On-chain Q64 price. Preferred over currentPrice when available from slot0 / oracle. */
115
+ /** Q64.64 price from slot0; preferred over `currentPrice`. */
85
116
  currentPriceX64?: bigint;
117
+ /** Position salt; default `0n`. Reuse across adds to the same position. */
86
118
  salt?: bigint;
87
119
  }
88
120
 
121
+ /** Same value in every bin from `lowerBin` through `upperBin` inclusive. */
89
122
  export interface BuildModifyLiquidityArgsForUniformAdditionParams extends BuildModifyLiquidityArgsWithFactory {
90
123
  amountInTokensPerBin: bigint;
91
124
  amountIsInToken0: boolean;
@@ -95,6 +128,7 @@ export interface BuildModifyLiquidityArgsForUniformAdditionParams extends BuildM
95
128
  salt?: bigint;
96
129
  }
97
130
 
131
+ /** Split a total token budget across `bins` (optional `weights`); calibrates share scaling on-chain. */
98
132
  export interface BuildModifyLiquidityArgsForUniformAdditionWithTotalTokenAmountParams extends BuildModifyLiquidityArgsWithFactory {
99
133
  bins: number[];
100
134
  weights?: number[];
@@ -104,6 +138,7 @@ export interface BuildModifyLiquidityArgsForUniformAdditionWithTotalTokenAmountP
104
138
  salt?: bigint;
105
139
  }
106
140
 
141
+ /** Remove the same percentage from all bins with liquidity (or from explicit `bins`). */
107
142
  export interface BuildModifyLiquidityArgsForPercentageRemovalParams extends BuildModifyLiquidityArgsBase {
108
143
  owner: Address;
109
144
  salt: bigint;
@@ -113,6 +148,7 @@ export interface BuildModifyLiquidityArgsForPercentageRemovalParams extends Buil
113
148
  upperBin?: number;
114
149
  }
115
150
 
151
+ /** Granular per-bin removal specs. */
116
152
  export interface BuildModifyLiquidityArgsForRemovalParams extends BuildModifyLiquidityArgsBase {
117
153
  owner: Address;
118
154
  salt: bigint;
@@ -130,7 +166,10 @@ function resolvePriceE18(params: { currentPrice?: number; currentPriceX64?: bigi
130
166
  }
131
167
 
132
168
  /**
133
- * Prepare uniform liquidity distribution across a bin range.
169
+ * Uniform liquidity: same token amount in every bin from `lowerBin` to `upperBin`.
170
+ * Delegates to {@link buildModifyLiquidityArgsForAddition}.
171
+ *
172
+ * @returns `[salt, deltas, specAmount0, specAmount1]` — max token caps for the adder.
134
173
  */
135
174
  export async function buildModifyLiquidityArgsForUniformAddition({
136
175
  publicClient,
@@ -517,6 +556,11 @@ export async function buildModifyLiquidityArgsForAddition({
517
556
  return toModifyLiquidityArgs(computed);
518
557
  }
519
558
 
559
+ /**
560
+ * Remove liquidity by explicit per-bin specs (percentage or absolute shares).
561
+ *
562
+ * `specAmount0` / `specAmount1` are set to {@link NO_SLIPPAGE_LIMIT} (accept any output).
563
+ */
520
564
  export async function buildModifyLiquidityArgsForRemoval(
521
565
  params: BuildModifyLiquidityArgsForRemovalParams,
522
566
  ): Promise<ModifyLiquidityArgs> {
@@ -598,6 +642,10 @@ export async function buildModifyLiquidityArgsForRemoval(
598
642
  return toModifyLiquidityArgs(computed);
599
643
  }
600
644
 
645
+ /**
646
+ * Remove the same `percentageToRemove` from every bin that has position shares.
647
+ * When `bins` is omitted, discovers bins via `getPositionBinSharesRange(lowerBin, upperBin)`.
648
+ */
601
649
  export async function buildModifyLiquidityArgsForPercentageRemoval({
602
650
  publicClient,
603
651
  dataProviderAddress,
@@ -650,6 +698,7 @@ export async function buildModifyLiquidityArgsForPercentageRemoval({
650
698
  });
651
699
  }
652
700
 
701
+ /** Convert positive `deltaShares` from a modify plan into `PoolLiquidityDelta` for the adder. */
653
702
  export function additionPlanToPoolLiquidityDelta(
654
703
  deltas: readonly LiquidityDelta[],
655
704
  ): PoolLiquidityDelta {
@@ -660,6 +709,7 @@ export function additionPlanToPoolLiquidityDelta(
660
709
  };
661
710
  }
662
711
 
712
+ /** Convert negative `deltaShares` from a modify plan into burn `PoolLiquidityDelta` for `removeLiquidity`. */
663
713
  export function removalPlanToPoolLiquidityDelta(
664
714
  deltas: readonly LiquidityDelta[],
665
715
  ): PoolLiquidityDelta {
@@ -698,6 +748,19 @@ export function scaleWeightedPoolDeltasToFitCaps(params: {
698
748
  return { binIdxs: weights.binIdxs, shares };
699
749
  }
700
750
 
751
+ /**
752
+ * Convenience wrapper: bin targets → adder-ready `{ salt, poolLiquidityDelta, maxAmountToken0, maxAmountToken1 }`.
753
+ *
754
+ * @example
755
+ * ```ts
756
+ * const add = await buildAddLiquidityExactSharesParamsFromBinTargets({
757
+ * publicClient, factoryAddress, dataProviderAddress, poolAddress,
758
+ * bins: [{ bin: 0, targetValueInToken: parseUnits("100", 6), targetValueInToken0: false }],
759
+ * currentPriceX64: slot0.priceX64,
760
+ * });
761
+ * const data = encodeAddLiquidityExactSharesSelfCalldata({ pool: poolAddress, ...add });
762
+ * ```
763
+ */
701
764
  export async function buildAddLiquidityExactSharesParamsFromBinTargets(
702
765
  params: BuildModifyLiquidityArgsForAdditionParams,
703
766
  ): Promise<{
@@ -715,6 +778,27 @@ export async function buildAddLiquidityExactSharesParamsFromBinTargets(
715
778
  };
716
779
  }
717
780
 
781
+ /** Cursor bounds that disable weighted-add cursor checks (matches on-chain test helpers). */
782
+ export const UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS = {
783
+ minimalCurBin: -128,
784
+ minimalPosition: 0n,
785
+ maximalCurBin: 127,
786
+ maximalPosition: MAX_UINT104,
787
+ } as const;
788
+
789
+ export type LiquidityCursorBounds = {
790
+ minimalCurBin: number;
791
+ minimalPosition: bigint;
792
+ maximalCurBin: number;
793
+ maximalPosition: bigint;
794
+ };
795
+
796
+ /**
797
+ * Encode `LiquidityAdder.addLiquidityExactShares` for the transaction sender.
798
+ *
799
+ * Creates or increases the position `(msg.sender, salt)` — liquidity is credited to whoever
800
+ * sends the transaction. Approve both pool tokens on the LiquidityAdder before sending.
801
+ */
718
802
  export function encodeAddLiquidityExactSharesSelfCalldata(params: {
719
803
  pool: Address;
720
804
  salt: bigint;
@@ -737,6 +821,13 @@ export function encodeAddLiquidityExactSharesSelfCalldata(params: {
737
821
  });
738
822
  }
739
823
 
824
+ /**
825
+ * Encode `LiquidityAdder.addLiquidityExactShares` for an explicit position owner.
826
+ *
827
+ * Creates or increases the position `(owner, salt)` while `msg.sender` pays for the deposit
828
+ * (e.g. router, vault, or meta-transaction relayer acting on behalf of `owner`).
829
+ * Approve both pool tokens on the LiquidityAdder before sending.
830
+ */
740
831
  export function encodeAddLiquidityExactSharesWithOwnerCalldata(params: {
741
832
  pool: Address;
742
833
  owner: Address;
@@ -761,6 +852,13 @@ export function encodeAddLiquidityExactSharesWithOwnerCalldata(params: {
761
852
  });
762
853
  }
763
854
 
855
+ /**
856
+ * Encode weighted add for the transaction sender.
857
+ *
858
+ * `weightDeltas` are relative share weights; the adder scales to fit
859
+ * `maxAmountToken0` / `maxAmountToken1`. Use {@link scaleWeightedPoolDeltasToFitCaps} to pre-scale.
860
+ * Creates or increases the position `(msg.sender, salt)`.
861
+ */
764
862
  export function encodeAddLiquidityWeightedSelfCalldata(params: {
765
863
  pool: Address;
766
864
  salt: bigint;
@@ -782,15 +880,21 @@ export function encodeAddLiquidityWeightedSelfCalldata(params: {
782
880
  { binIdxs: [...params.weightDeltas.binIdxs], shares: [...params.weightDeltas.shares] },
783
881
  params.maxAmountToken0,
784
882
  params.maxAmountToken1,
785
- params.minimalCurBin ?? -128,
786
- params.minimalPosition ?? 0n,
787
- params.maximalCurBin ?? 127,
788
- params.maximalPosition ?? MAX_UINT104,
883
+ params.minimalCurBin ?? UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS.minimalCurBin,
884
+ params.minimalPosition ?? UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS.minimalPosition,
885
+ params.maximalCurBin ?? UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS.maximalCurBin,
886
+ params.maximalPosition ?? UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS.maximalPosition,
789
887
  params.extensionData ?? EMPTY_EXTENSION_DATA,
790
888
  ],
791
889
  });
792
890
  }
793
891
 
892
+ /**
893
+ * Encode weighted add for an explicit position owner.
894
+ *
895
+ * Same as {@link encodeAddLiquidityWeightedSelfCalldata} but credits liquidity to `(owner, salt)`
896
+ * while `msg.sender` pays for the deposit.
897
+ */
794
898
  export function encodeAddLiquidityWeightedWithOwnerCalldata(params: {
795
899
  pool: Address;
796
900
  owner: Address;
@@ -814,15 +918,19 @@ export function encodeAddLiquidityWeightedWithOwnerCalldata(params: {
814
918
  { binIdxs: [...params.weightDeltas.binIdxs], shares: [...params.weightDeltas.shares] },
815
919
  params.maxAmountToken0,
816
920
  params.maxAmountToken1,
817
- params.minimalCurBin ?? -128,
818
- params.minimalPosition ?? 0n,
819
- params.maximalCurBin ?? 127,
820
- params.maximalPosition ?? MAX_UINT104,
921
+ params.minimalCurBin ?? UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS.minimalCurBin,
922
+ params.minimalPosition ?? UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS.minimalPosition,
923
+ params.maximalCurBin ?? UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS.maximalCurBin,
924
+ params.maximalPosition ?? UNCONSTRAINED_LIQUIDITY_CURSOR_BOUNDS.maximalPosition,
821
925
  params.extensionData ?? EMPTY_EXTENSION_DATA,
822
926
  ],
823
927
  });
824
928
  }
825
929
 
930
+ /**
931
+ * Encode `Pool.removeLiquidity(owner, salt, deltas, extensionData)`.
932
+ * Send to the **pool** contract; no token approval required (tokens are sent out).
933
+ */
826
934
  export function encodeRemoveLiquidityCalldata(params: {
827
935
  owner: Address;
828
936
  salt: bigint;
@@ -841,6 +949,7 @@ export function encodeRemoveLiquidityCalldata(params: {
841
949
  });
842
950
  }
843
951
 
952
+ /** Build `{ salt, poolLiquidityDelta }` for {@link encodeRemoveLiquidityCalldata}. */
844
953
  export async function buildRemoveLiquidityPoolDelta(
845
954
  params: BuildModifyLiquidityArgsForRemovalParams,
846
955
  ): Promise<{ salt: bigint; poolLiquidityDelta: PoolLiquidityDelta }> {
@@ -0,0 +1,71 @@
1
+ import type { Hex } from "viem";
2
+ import {
3
+ encodeAddLiquidityExactSharesSelfCalldata,
4
+ encodeAddLiquidityExactSharesWithOwnerCalldata,
5
+ encodeAddLiquidityWeightedSelfCalldata,
6
+ encodeAddLiquidityWeightedWithOwnerCalldata,
7
+ } from "./liquidity.js";
8
+ import { encodeMulticallCalldata, encodeRefundETHCalldata } from "../peripheryPayments/index.js";
9
+
10
+ type ExactSharesSelfParams = Parameters<typeof encodeAddLiquidityExactSharesSelfCalldata>[0];
11
+ type ExactSharesWithOwnerParams = Parameters<
12
+ typeof encodeAddLiquidityExactSharesWithOwnerCalldata
13
+ >[0];
14
+ type WeightedSelfParams = Parameters<typeof encodeAddLiquidityWeightedSelfCalldata>[0];
15
+ type WeightedWithOwnerParams = Parameters<typeof encodeAddLiquidityWeightedWithOwnerCalldata>[0];
16
+
17
+ /**
18
+ * Exact-shares add with native ETH for the pool WETH leg.
19
+ * Credits liquidity to `(msg.sender, salt)`. Set `msg.value` on the transaction;
20
+ * unused ETH is refunded to the caller.
21
+ */
22
+ export function prepareAddLiquidityExactSharesSelfSupportingNativePaymentCalldata(
23
+ params: ExactSharesSelfParams,
24
+ ): Hex {
25
+ return encodeMulticallCalldata([
26
+ encodeAddLiquidityExactSharesSelfCalldata(params),
27
+ encodeRefundETHCalldata(),
28
+ ]);
29
+ }
30
+
31
+ /**
32
+ * Exact-shares add with native ETH for the pool WETH leg.
33
+ * Credits liquidity to `(owner, salt)` while `msg.sender` pays. Set `msg.value` on the
34
+ * transaction; unused ETH is refunded to the caller.
35
+ */
36
+ export function prepareAddLiquidityExactSharesWithOwnerSupportingNativePaymentCalldata(
37
+ params: ExactSharesWithOwnerParams,
38
+ ): Hex {
39
+ return encodeMulticallCalldata([
40
+ encodeAddLiquidityExactSharesWithOwnerCalldata(params),
41
+ encodeRefundETHCalldata(),
42
+ ]);
43
+ }
44
+
45
+ /**
46
+ * Weighted add with native ETH for the pool WETH leg.
47
+ * Credits liquidity to `(msg.sender, salt)`. Set `msg.value` on the transaction;
48
+ * unused ETH is refunded to the caller.
49
+ */
50
+ export function prepareAddLiquidityWeightedSelfSupportingNativePaymentCalldata(
51
+ params: WeightedSelfParams,
52
+ ): Hex {
53
+ return encodeMulticallCalldata([
54
+ encodeAddLiquidityWeightedSelfCalldata(params),
55
+ encodeRefundETHCalldata(),
56
+ ]);
57
+ }
58
+
59
+ /**
60
+ * Weighted add with native ETH for the pool WETH leg.
61
+ * Credits liquidity to `(owner, salt)` while `msg.sender` pays. Set `msg.value` on the
62
+ * transaction; unused ETH is refunded to the caller.
63
+ */
64
+ export function prepareAddLiquidityWeightedWithOwnerSupportingNativePaymentCalldata(
65
+ params: WeightedWithOwnerParams,
66
+ ): Hex {
67
+ return encodeMulticallCalldata([
68
+ encodeAddLiquidityWeightedWithOwnerCalldata(params),
69
+ encodeRefundETHCalldata(),
70
+ ]);
71
+ }
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Map route API POST /public/v1/evm/:chain_id/route responses to SimpleRouter swap params.
3
+ */
4
+
5
+ import type { Address, Hex, PublicClient } from "viem";
6
+ import { getAddress } from "viem";
7
+ import { resolveSwapPath } from "../swap/path.js";
8
+ import { ceilDiv, toSlippageBps } from "./swap.js";
9
+ import type { ExactInputParams, ExactOutputParams } from "./swapPath.js";
10
+ import type { ExactInputSingleParams, ExactOutputSingleParams } from "./swap.js";
11
+
12
+ const BPS_BASE = 10_000n;
13
+
14
+ /** Wire tradeType from the route API. */
15
+ export type TradeType = "exact_in" | "exact_out";
16
+
17
+ /** Wire routeType from the route API. */
18
+ export type RouteType = "direct" | "one_hop";
19
+
20
+ /** One hop in a route API response (RouteLegResponse). */
21
+ export type RouteLegResponse = {
22
+ tradeType: TradeType;
23
+ poolAddress: string;
24
+ pair: string;
25
+ zeroForOne: boolean;
26
+ tokenIn: string;
27
+ tokenOut: string;
28
+ amountIn: string;
29
+ amountOut: string;
30
+ effectivePriceX64: string;
31
+ priceImpactE6: string;
32
+ priceLimitX64: string;
33
+ };
34
+
35
+ /** One execution path in a route API response (RouteOptionResponse). */
36
+ export type RouteOptionResponse = {
37
+ tradeType: TradeType;
38
+ routeType: RouteType;
39
+ amountIn: string;
40
+ amountOut: string;
41
+ effectivePriceX64: string;
42
+ priceImpactE6: string;
43
+ legs: RouteLegResponse[];
44
+ };
45
+
46
+ /** Route API response (RouteResponse). */
47
+ export type RouteResponse = {
48
+ tokenIn: string;
49
+ tokenOut: string;
50
+ tradeType: TradeType;
51
+ amountIn: string;
52
+ amountOut: string;
53
+ effectivePriceX64: string;
54
+ priceImpactBestRoute: string;
55
+ candidatesConsidered: number;
56
+ routes: RouteOptionResponse[];
57
+ };
58
+
59
+ export type PrepareExactInputFromRouteParams = {
60
+ publicClient: PublicClient;
61
+ route: RouteResponse;
62
+ recipient: Address;
63
+ deadline: bigint;
64
+ /** Human-readable slippage tolerance applied to the selected route's `amountOut`, e.g. `0.5` = 0.5%. */
65
+ slippagePercent: number;
66
+ extensionDatas?: readonly Hex[];
67
+ };
68
+
69
+ export type PrepareExactOutputFromRouteParams = {
70
+ publicClient: PublicClient;
71
+ route: RouteResponse;
72
+ recipient: Address;
73
+ deadline: bigint;
74
+ /** Human-readable slippage tolerance applied to the selected route's `amountIn`, e.g. `0.5` = 0.5%. */
75
+ slippagePercent: number;
76
+ extensionDatas?: readonly Hex[];
77
+ };
78
+
79
+ export type PreparedExactInputFromRoute =
80
+ | { kind: "single"; params: ExactInputSingleParams }
81
+ | { kind: "multihop"; params: ExactInputParams };
82
+
83
+ export type PreparedExactOutputFromRoute =
84
+ | { kind: "single"; params: ExactOutputSingleParams }
85
+ | { kind: "multihop"; params: ExactOutputParams };
86
+
87
+ function parseAddress(value: string, field: string): Address {
88
+ try {
89
+ return getAddress(value);
90
+ } catch {
91
+ throw new Error(`invalid ${field}: ${value}`);
92
+ }
93
+ }
94
+
95
+ function parseAmount(value: string, field: string): bigint {
96
+ try {
97
+ const amount = BigInt(value);
98
+ if (amount < 0n) {
99
+ throw new Error(`${field} must be non-negative`);
100
+ }
101
+ return amount;
102
+ } catch (error) {
103
+ if (error instanceof Error && error.message.includes("must be non-negative")) {
104
+ throw error;
105
+ }
106
+ throw new Error(`invalid ${field}: ${value}`, { cause: error });
107
+ }
108
+ }
109
+
110
+ function selectRouteOption(route: RouteResponse): RouteOptionResponse {
111
+ if (route.routes.length === 0) {
112
+ throw new Error("route.routes must not be empty");
113
+ }
114
+ if (route.routes.length > 1) {
115
+ throw new Error("route was split across multiple routes; re-request with maxRoutes=1");
116
+ }
117
+ return route.routes[0]!;
118
+ }
119
+
120
+ function assertTradeType(
121
+ route: RouteResponse,
122
+ option: RouteOptionResponse,
123
+ tradeType: TradeType,
124
+ ): void {
125
+ if (route.tradeType !== tradeType) {
126
+ throw new Error(
127
+ `route tradeType must be ${tradeType}; use ${
128
+ tradeType === "exact_in" ? "prepareExactOutputFromRoute" : "prepareExactInputFromRoute"
129
+ } for ${tradeType === "exact_in" ? "exact_out" : "exact_in"}`,
130
+ );
131
+ }
132
+ if (option.tradeType !== tradeType) {
133
+ throw new Error(`routes[0].tradeType must be ${tradeType}`);
134
+ }
135
+ }
136
+
137
+ function validateRouteLegs(
138
+ legs: RouteLegResponse[],
139
+ tokenIn: Address,
140
+ tokenOut: Address,
141
+ ): Address[] {
142
+ if (legs.length === 0) {
143
+ throw new Error("routes[0].legs must not be empty");
144
+ }
145
+
146
+ const pools: Address[] = [];
147
+ for (let i = 0; i < legs.length; i++) {
148
+ const leg = legs[i]!;
149
+ const legTokenIn = parseAddress(leg.tokenIn, `legs[${i}].tokenIn`);
150
+ const legTokenOut = parseAddress(leg.tokenOut, `legs[${i}].tokenOut`);
151
+ pools.push(parseAddress(leg.poolAddress, `legs[${i}].poolAddress`));
152
+
153
+ if (i === 0 && legTokenIn !== tokenIn) {
154
+ throw new Error(`legs[0].tokenIn (${legTokenIn}) does not match route.tokenIn (${tokenIn})`);
155
+ }
156
+ if (i === legs.length - 1 && legTokenOut !== tokenOut) {
157
+ throw new Error(
158
+ `legs[${i}].tokenOut (${legTokenOut}) does not match route.tokenOut (${tokenOut})`,
159
+ );
160
+ }
161
+ if (i > 0) {
162
+ const prevLegTokenOut = parseAddress(legs[i - 1]!.tokenOut, `legs[${i - 1}].tokenOut`);
163
+ if (legTokenIn !== prevLegTokenOut) {
164
+ throw new Error(
165
+ `legs[${i}].tokenIn (${legTokenIn}) does not connect to legs[${i - 1}].tokenOut (${prevLegTokenOut})`,
166
+ );
167
+ }
168
+ }
169
+ }
170
+
171
+ return pools;
172
+ }
173
+
174
+ function amountOutMinimumFromRoute(amountOut: bigint, slippagePercent: number): bigint {
175
+ const slippageBps = toSlippageBps(slippagePercent);
176
+ return (amountOut * (BPS_BASE - slippageBps)) / BPS_BASE;
177
+ }
178
+
179
+ function amountInMaximumFromRoute(amountIn: bigint, slippagePercent: number): bigint {
180
+ const slippageBps = toSlippageBps(slippagePercent);
181
+ return ceilDiv(amountIn * (BPS_BASE + slippageBps), BPS_BASE);
182
+ }
183
+
184
+ /**
185
+ * Convert a route API exact-in response into SimpleRouter exact-input swap params.
186
+ *
187
+ * Resolves swap direction on-chain, applies slippage to the quoted `amountOut`, and
188
+ * returns params ready for `encodeExactInputSingleCalldata` or `encodeExactInputCalldata`.
189
+ */
190
+ export async function routeToExactInputParams(
191
+ params: PrepareExactInputFromRouteParams,
192
+ ): Promise<PreparedExactInputFromRoute> {
193
+ const { route, publicClient, recipient, deadline, slippagePercent, extensionDatas } = params;
194
+
195
+ const tokenIn = parseAddress(route.tokenIn, "tokenIn");
196
+ const tokenOut = parseAddress(route.tokenOut, "tokenOut");
197
+ const option = selectRouteOption(route);
198
+ assertTradeType(route, option, "exact_in");
199
+
200
+ const requestedAmountIn = parseAmount(route.amountIn, "amountIn");
201
+ const amountIn = parseAmount(option.amountIn, "routes[0].amountIn");
202
+ if (amountIn !== requestedAmountIn) {
203
+ throw new Error(
204
+ "routes[0].amountIn does not match request amountIn; partial fill is not supported",
205
+ );
206
+ }
207
+ const quotedAmountOut = parseAmount(option.amountOut, "routes[0].amountOut");
208
+ const amountOutMinimum = amountOutMinimumFromRoute(quotedAmountOut, slippagePercent);
209
+
210
+ const pools = validateRouteLegs(option.legs, tokenIn, tokenOut);
211
+ const {
212
+ tokens,
213
+ zeroForOnes,
214
+ extensionDatas: resolvedExtensionDatas,
215
+ zeroForOneBitMap,
216
+ } = await resolveSwapPath(publicClient, tokenIn, tokenOut, pools, extensionDatas);
217
+
218
+ if (pools.length === 1) {
219
+ const leg = option.legs[0]!;
220
+ return {
221
+ kind: "single",
222
+ params: {
223
+ pool: pools[0]!,
224
+ tokenIn,
225
+ tokenOut,
226
+ zeroForOne: zeroForOnes[0]!,
227
+ amountIn,
228
+ amountOutMinimum,
229
+ recipient,
230
+ deadline,
231
+ priceLimitX64: parseAmount(leg.priceLimitX64, "legs[0].priceLimitX64"),
232
+ extensionData: resolvedExtensionDatas[0],
233
+ },
234
+ };
235
+ }
236
+
237
+ return {
238
+ kind: "multihop",
239
+ params: {
240
+ tokens,
241
+ pools,
242
+ extensionDatas: resolvedExtensionDatas,
243
+ zeroForOneBitMap,
244
+ amountIn,
245
+ amountOutMinimum,
246
+ recipient,
247
+ deadline,
248
+ },
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Convert a route API exact-out response into SimpleRouter exact-output swap params.
254
+ *
255
+ * Resolves swap direction on-chain, applies slippage to the quoted `amountIn`, and
256
+ * returns params ready for `encodeExactOutputSingleCalldata` or `encodeExactOutputCalldata`.
257
+ */
258
+ export async function routeToExactOutputParams(
259
+ params: PrepareExactOutputFromRouteParams,
260
+ ): Promise<PreparedExactOutputFromRoute> {
261
+ const { route, publicClient, recipient, deadline, slippagePercent, extensionDatas } = params;
262
+
263
+ const tokenIn = parseAddress(route.tokenIn, "tokenIn");
264
+ const tokenOut = parseAddress(route.tokenOut, "tokenOut");
265
+ const option = selectRouteOption(route);
266
+ assertTradeType(route, option, "exact_out");
267
+
268
+ const requestedAmountOut = parseAmount(route.amountOut, "amountOut");
269
+ const amountOut = parseAmount(option.amountOut, "routes[0].amountOut");
270
+ if (amountOut !== requestedAmountOut) {
271
+ throw new Error(
272
+ "routes[0].amountOut does not match request amountOut; partial fill is not supported",
273
+ );
274
+ }
275
+ const quotedAmountIn = parseAmount(option.amountIn, "routes[0].amountIn");
276
+ const amountInMaximum = amountInMaximumFromRoute(quotedAmountIn, slippagePercent);
277
+
278
+ const pools = validateRouteLegs(option.legs, tokenIn, tokenOut);
279
+ const {
280
+ tokens,
281
+ zeroForOnes,
282
+ extensionDatas: resolvedExtensionDatas,
283
+ zeroForOneBitMap,
284
+ } = await resolveSwapPath(publicClient, tokenIn, tokenOut, pools, extensionDatas);
285
+
286
+ if (pools.length === 1) {
287
+ const leg = option.legs[0]!;
288
+ return {
289
+ kind: "single",
290
+ params: {
291
+ pool: pools[0]!,
292
+ tokenIn,
293
+ tokenOut,
294
+ zeroForOne: zeroForOnes[0]!,
295
+ amountOut,
296
+ amountInMaximum,
297
+ recipient,
298
+ deadline,
299
+ priceLimitX64: parseAmount(leg.priceLimitX64, "legs[0].priceLimitX64"),
300
+ extensionData: resolvedExtensionDatas[0],
301
+ },
302
+ };
303
+ }
304
+
305
+ return {
306
+ kind: "multihop",
307
+ params: {
308
+ tokens,
309
+ pools,
310
+ extensionDatas: resolvedExtensionDatas,
311
+ zeroForOneBitMap,
312
+ amountOut,
313
+ amountInMaximum,
314
+ recipient,
315
+ deadline,
316
+ },
317
+ };
318
+ }