@hawksightco/hawk-sdk 1.3.265 → 1.3.266

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 (181) hide show
  1. package/dist/addresses.d.ts +37 -0
  2. package/dist/addresses.d.ts.map +1 -0
  3. package/dist/addresses.js +84 -0
  4. package/dist/anchor.d.ts +22 -0
  5. package/dist/anchor.d.ts.map +1 -0
  6. package/dist/anchor.js +121 -0
  7. package/dist/classes/AltStorage.d.ts +29 -0
  8. package/dist/classes/AltStorage.d.ts.map +1 -0
  9. package/dist/classes/AltStorage.js +58 -0
  10. package/dist/classes/Atomicity.d.ts +34 -0
  11. package/dist/classes/Atomicity.d.ts.map +1 -0
  12. package/dist/classes/Atomicity.js +110 -0
  13. package/dist/classes/Client.d.ts +35 -0
  14. package/dist/classes/Client.d.ts.map +1 -0
  15. package/dist/classes/Client.js +66 -0
  16. package/dist/classes/CombineTransactions.d.ts +11 -0
  17. package/dist/classes/CombineTransactions.d.ts.map +1 -0
  18. package/dist/classes/CombineTransactions.js +58 -0
  19. package/dist/classes/CreateTxMetadata.d.ts +100 -0
  20. package/dist/classes/CreateTxMetadata.d.ts.map +1 -0
  21. package/dist/classes/CreateTxMetadata.js +415 -0
  22. package/dist/classes/General.d.ts +87 -0
  23. package/dist/classes/General.d.ts.map +1 -0
  24. package/dist/classes/General.js +176 -0
  25. package/dist/classes/GeneralUtility.d.ts +79 -0
  26. package/dist/classes/GeneralUtility.d.ts.map +1 -0
  27. package/dist/classes/GeneralUtility.js +121 -0
  28. package/dist/classes/HawkAPI.d.ts +122 -0
  29. package/dist/classes/HawkAPI.d.ts.map +1 -0
  30. package/dist/classes/HawkAPI.js +169 -0
  31. package/dist/classes/Health.d.ts +29 -0
  32. package/dist/classes/Health.d.ts.map +1 -0
  33. package/dist/classes/Health.js +48 -0
  34. package/dist/classes/JupiterAlts.d.ts +37 -0
  35. package/dist/classes/JupiterAlts.d.ts.map +1 -0
  36. package/dist/classes/JupiterAlts.js +114 -0
  37. package/dist/classes/JupiterSwap.d.ts +21 -0
  38. package/dist/classes/JupiterSwap.d.ts.map +1 -0
  39. package/dist/classes/JupiterSwap.js +40 -0
  40. package/dist/classes/Logging.d.ts +13 -0
  41. package/dist/classes/Logging.d.ts.map +1 -0
  42. package/dist/classes/Logging.js +50 -0
  43. package/dist/classes/MultiTransaction.d.ts +75 -0
  44. package/dist/classes/MultiTransaction.d.ts.map +1 -0
  45. package/dist/classes/MultiTransaction.js +296 -0
  46. package/dist/classes/RaydiumSDK.d.ts +67 -0
  47. package/dist/classes/RaydiumSDK.d.ts.map +1 -0
  48. package/dist/classes/RaydiumSDK.js +206 -0
  49. package/dist/classes/Search.d.ts +100 -0
  50. package/dist/classes/Search.d.ts.map +1 -0
  51. package/dist/classes/Search.js +216 -0
  52. package/dist/classes/SimpleIxGenerator.d.ts +57 -0
  53. package/dist/classes/SimpleIxGenerator.d.ts.map +1 -0
  54. package/dist/classes/SimpleIxGenerator.js +62 -0
  55. package/dist/classes/SimplePdaGenerator.d.ts +28 -0
  56. package/dist/classes/SimplePdaGenerator.d.ts.map +1 -0
  57. package/dist/classes/SimplePdaGenerator.js +33 -0
  58. package/dist/classes/Transaction.d.ts +229 -0
  59. package/dist/classes/Transaction.d.ts.map +1 -0
  60. package/dist/classes/Transaction.js +643 -0
  61. package/dist/classes/Transaction2.d.ts +107 -0
  62. package/dist/classes/Transaction2.d.ts.map +1 -0
  63. package/dist/classes/Transaction2.js +249 -0
  64. package/dist/classes/Transaction3.d.ts +165 -0
  65. package/dist/classes/Transaction3.d.ts.map +1 -0
  66. package/dist/classes/Transaction3.js +416 -0
  67. package/dist/classes/TransactionBatchExecute.d.ts +163 -0
  68. package/dist/classes/TransactionBatchExecute.d.ts.map +1 -0
  69. package/dist/classes/TransactionBatchExecute.js +466 -0
  70. package/dist/classes/TransactionBatchExecute2.d.ts +99 -0
  71. package/dist/classes/TransactionBatchExecute2.d.ts.map +1 -0
  72. package/dist/classes/TransactionBatchExecute2.js +357 -0
  73. package/dist/classes/Transactions.d.ts +586 -0
  74. package/dist/classes/Transactions.d.ts.map +1 -0
  75. package/dist/classes/Transactions.js +7667 -0
  76. package/dist/classes/TxGenerator.d.ts +350 -0
  77. package/dist/classes/TxGenerator.d.ts.map +1 -0
  78. package/dist/classes/TxGenerator.js +1957 -0
  79. package/dist/classes/TxGeneratorAutomations.d.ts +186 -0
  80. package/dist/classes/TxGeneratorAutomations.d.ts.map +1 -0
  81. package/dist/classes/TxGeneratorAutomations.js +897 -0
  82. package/dist/classes/Util.d.ts +87 -0
  83. package/dist/classes/Util.d.ts.map +1 -0
  84. package/dist/classes/Util.js +148 -0
  85. package/dist/classes/account-cache/AccountCache.d.ts +54 -0
  86. package/dist/classes/account-cache/AccountCache.d.ts.map +1 -0
  87. package/dist/classes/account-cache/AccountCache.js +143 -0
  88. package/dist/classes/account-cache/MintCache.d.ts +33 -0
  89. package/dist/classes/account-cache/MintCache.d.ts.map +1 -0
  90. package/dist/classes/account-cache/MintCache.js +47 -0
  91. package/dist/classes/account-cache/TokenCache.d.ts +33 -0
  92. package/dist/classes/account-cache/TokenCache.d.ts.map +1 -0
  93. package/dist/classes/account-cache/TokenCache.js +47 -0
  94. package/dist/errors.d.ts +13 -0
  95. package/dist/errors.d.ts.map +1 -0
  96. package/dist/errors.js +96 -0
  97. package/dist/functions.d.ts +514 -0
  98. package/dist/functions.d.ts.map +1 -0
  99. package/dist/functions.js +1636 -0
  100. package/dist/hawksight.d.ts +53 -0
  101. package/dist/hawksight.d.ts.map +1 -0
  102. package/dist/hawksight.js +328 -0
  103. package/dist/hsToMeteora.d.ts +31 -0
  104. package/dist/hsToMeteora.d.ts.map +1 -0
  105. package/dist/hsToMeteora.js +3175 -0
  106. package/dist/idl/iyf-extension-idl.d.ts +14583 -0
  107. package/dist/idl/iyf-extension-idl.d.ts.map +1 -0
  108. package/dist/idl/iyf-extension-idl.js +14578 -0
  109. package/dist/idl/iyf-extension-wrapper.d.ts +112 -0
  110. package/dist/idl/iyf-extension-wrapper.d.ts.map +1 -0
  111. package/dist/idl/iyf-extension-wrapper.js +2 -0
  112. package/dist/idl/iyf-main-idl.d.ts +12078 -0
  113. package/dist/idl/iyf-main-idl.d.ts.map +1 -0
  114. package/dist/idl/iyf-main-idl.js +12073 -0
  115. package/dist/idl/iyf-main-wrapper.d.ts +112 -0
  116. package/dist/idl/iyf-main-wrapper.d.ts.map +1 -0
  117. package/dist/idl/iyf-main-wrapper.js +2 -0
  118. package/dist/idl/jupiter-idl.d.ts +3063 -0
  119. package/dist/idl/jupiter-idl.d.ts.map +1 -0
  120. package/dist/idl/jupiter-idl.js +3061 -0
  121. package/dist/idl/meteora-idl.d.ts +7371 -0
  122. package/dist/idl/meteora-idl.d.ts.map +1 -0
  123. package/dist/idl/meteora-idl.js +7372 -0
  124. package/dist/idl/orca-idl.d.ts +3833 -0
  125. package/dist/idl/orca-idl.d.ts.map +1 -0
  126. package/dist/idl/orca-idl.js +3834 -0
  127. package/dist/idl/raydium-idl.d.ts +3054 -0
  128. package/dist/idl/raydium-idl.d.ts.map +1 -0
  129. package/dist/idl/raydium-idl.js +3055 -0
  130. package/dist/index.d.ts +9 -0
  131. package/dist/index.d.ts.map +1 -0
  132. package/dist/index.js +31 -0
  133. package/dist/ixGenerator/IyfExtensionIxGenerator.d.ts +126 -0
  134. package/dist/ixGenerator/IyfExtensionIxGenerator.d.ts.map +1 -0
  135. package/dist/ixGenerator/IyfExtensionIxGenerator.js +467 -0
  136. package/dist/ixGenerator/IyfMainIxGenerator.d.ts +330 -0
  137. package/dist/ixGenerator/IyfMainIxGenerator.d.ts.map +1 -0
  138. package/dist/ixGenerator/IyfMainIxGenerator.js +1239 -0
  139. package/dist/ixGenerator/MeteoraDlmmIxGenerator.d.ts +685 -0
  140. package/dist/ixGenerator/MeteoraDlmmIxGenerator.d.ts.map +1 -0
  141. package/dist/ixGenerator/MeteoraDlmmIxGenerator.js +3005 -0
  142. package/dist/ixGenerator/OrcaIxGenerator.d.ts +119 -0
  143. package/dist/ixGenerator/OrcaIxGenerator.d.ts.map +1 -0
  144. package/dist/ixGenerator/OrcaIxGenerator.js +438 -0
  145. package/dist/ixGenerator/RaydiumIxGenerator.d.ts +99 -0
  146. package/dist/ixGenerator/RaydiumIxGenerator.d.ts.map +1 -0
  147. package/dist/ixGenerator/RaydiumIxGenerator.js +558 -0
  148. package/dist/ixGenerator/UtilIxGenerator.d.ts +20 -0
  149. package/dist/ixGenerator/UtilIxGenerator.d.ts.map +1 -0
  150. package/dist/ixGenerator/UtilIxGenerator.js +25 -0
  151. package/dist/meteora/index.d.ts +2 -0
  152. package/dist/meteora/index.d.ts.map +1 -0
  153. package/dist/meteora/index.js +17 -0
  154. package/dist/meteora/liquidityStrategy.d.ts +408 -0
  155. package/dist/meteora/liquidityStrategy.d.ts.map +1 -0
  156. package/dist/meteora/liquidityStrategy.js +1325 -0
  157. package/dist/meteora.d.ts +669 -0
  158. package/dist/meteora.d.ts.map +1 -0
  159. package/dist/meteora.js +1932 -0
  160. package/dist/orca.d.ts +57 -0
  161. package/dist/orca.d.ts.map +1 -0
  162. package/dist/orca.js +123 -0
  163. package/dist/pdaGenerator/HawksightPdaGenerator.d.ts +29 -0
  164. package/dist/pdaGenerator/HawksightPdaGenerator.d.ts.map +1 -0
  165. package/dist/pdaGenerator/HawksightPdaGenerator.js +75 -0
  166. package/dist/pdaGenerator/MeteoraPdaGenerator.d.ts +76 -0
  167. package/dist/pdaGenerator/MeteoraPdaGenerator.d.ts.map +1 -0
  168. package/dist/pdaGenerator/MeteoraPdaGenerator.js +166 -0
  169. package/dist/pdaGenerator/OrcaPdaGenerator.d.ts +23 -0
  170. package/dist/pdaGenerator/OrcaPdaGenerator.d.ts.map +1 -0
  171. package/dist/pdaGenerator/OrcaPdaGenerator.js +68 -0
  172. package/dist/pdaGenerator/RaydiumPdaGenerator.d.ts +49 -0
  173. package/dist/pdaGenerator/RaydiumPdaGenerator.d.ts.map +1 -0
  174. package/dist/pdaGenerator/RaydiumPdaGenerator.js +114 -0
  175. package/dist/pdaGenerator/UtilPdaGenerator.d.ts +8 -0
  176. package/dist/pdaGenerator/UtilPdaGenerator.d.ts.map +1 -0
  177. package/dist/pdaGenerator/UtilPdaGenerator.js +51 -0
  178. package/dist/types.d.ts +1104 -0
  179. package/dist/types.d.ts.map +1 -0
  180. package/dist/types.js +95 -0
  181. package/package.json +6 -5
@@ -0,0 +1,1325 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.StrategyType = void 0;
7
+ exports.chunkHybridDepositParameters = chunkHybridDepositParameters;
8
+ exports.getQPriceFromId = getQPriceFromId;
9
+ exports.buildSpotStrategyParameters = buildSpotStrategyParameters;
10
+ exports.resetUninvolvedLiquidityParams = resetUninvolvedLiquidityParams;
11
+ exports.getAmountXForBin = getAmountXForBin;
12
+ exports.getAmountYForBin = getAmountYForBin;
13
+ exports.calculateChunkAmounts = calculateChunkAmounts;
14
+ exports.buildCurveStrategyParameters = buildCurveStrategyParameters;
15
+ exports.buildBidAskStrategyParameters = buildBidAskStrategyParameters;
16
+ exports.buildStrategyParameters = buildStrategyParameters;
17
+ exports.distributionToStrategyType = distributionToStrategyType;
18
+ exports.distributionToHybridStrategy = distributionToHybridStrategy;
19
+ exports.buildHybridStrategyParameters = buildHybridStrategyParameters;
20
+ exports.chunkBinRange = chunkBinRange;
21
+ exports.buildBitFlagAndNegateStrategyParameters = buildBitFlagAndNegateStrategyParameters;
22
+ exports.toAmountIntoBins = toAmountIntoBins;
23
+ exports.chunkDepositParameters = chunkDepositParameters;
24
+ exports.chunkBinRangeWithInitialSize = chunkBinRangeWithInitialSize;
25
+ exports.chunkDepositParametersWithInitialSize = chunkDepositParametersWithInitialSize;
26
+ const bn_js_1 = __importDefault(require("bn.js"));
27
+ /**
28
+ * Liquidity strategy types matching Meteora DLMM SDK.
29
+ */
30
+ var StrategyType;
31
+ (function (StrategyType) {
32
+ /** Uniform distribution - equal value per bin (adjusted for price) */
33
+ StrategyType[StrategyType["SPOT"] = 0] = "SPOT";
34
+ /** Curve distribution - decreasing amounts away from active bin */
35
+ StrategyType[StrategyType["CURVE"] = 1] = "CURVE";
36
+ /** Bid-Ask distribution - increasing amounts away from active bin */
37
+ StrategyType[StrategyType["BID_ASK"] = 2] = "BID_ASK";
38
+ })(StrategyType || (exports.StrategyType = StrategyType = {}));
39
+ /**
40
+ * Scale offset used for fixed-point arithmetic in price calculations.
41
+ * Matches DLMM SDK's SCALE_OFFSET = 64
42
+ */
43
+ const SCALE_OFFSET = 64;
44
+ /**
45
+ * Constants for Q64.64 fixed-point math (matching Meteora SDK)
46
+ */
47
+ const ONE = new bn_js_1.default(1).shln(SCALE_OFFSET);
48
+ const MAX = new bn_js_1.default(2).pow(new bn_js_1.default(128)).sub(new bn_js_1.default(1));
49
+ const MAX_EXPONENTIAL = new bn_js_1.default(0x80000);
50
+ const BASIS_POINT_MAX = 10000;
51
+ /**
52
+ * Binary exponentiation for Q64.64 fixed-point numbers.
53
+ * Matches Meteora SDK's pow function from u64xu64_math.ts
54
+ *
55
+ * @param base - Base value in Q64.64 format
56
+ * @param exp - Exponent (can be negative)
57
+ * @returns base^exp in Q64.64 format
58
+ */
59
+ function pow(base, exp) {
60
+ let invert = exp.isNeg();
61
+ if (exp.isZero()) {
62
+ return ONE;
63
+ }
64
+ exp = invert ? exp.abs() : exp;
65
+ if (exp.gt(MAX_EXPONENTIAL)) {
66
+ return new bn_js_1.default(0);
67
+ }
68
+ let squaredBase = base;
69
+ let result = ONE;
70
+ // For base >= 1, invert first for better precision
71
+ if (squaredBase.gte(result)) {
72
+ squaredBase = MAX.div(squaredBase);
73
+ invert = !invert;
74
+ }
75
+ if (!exp.and(new bn_js_1.default(0x1)).isZero()) {
76
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
77
+ }
78
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
79
+ if (!exp.and(new bn_js_1.default(0x2)).isZero()) {
80
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
81
+ }
82
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
83
+ if (!exp.and(new bn_js_1.default(0x4)).isZero()) {
84
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
85
+ }
86
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
87
+ if (!exp.and(new bn_js_1.default(0x8)).isZero()) {
88
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
89
+ }
90
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
91
+ if (!exp.and(new bn_js_1.default(0x10)).isZero()) {
92
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
93
+ }
94
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
95
+ if (!exp.and(new bn_js_1.default(0x20)).isZero()) {
96
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
97
+ }
98
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
99
+ if (!exp.and(new bn_js_1.default(0x40)).isZero()) {
100
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
101
+ }
102
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
103
+ if (!exp.and(new bn_js_1.default(0x80)).isZero()) {
104
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
105
+ }
106
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
107
+ if (!exp.and(new bn_js_1.default(0x100)).isZero()) {
108
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
109
+ }
110
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
111
+ if (!exp.and(new bn_js_1.default(0x200)).isZero()) {
112
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
113
+ }
114
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
115
+ if (!exp.and(new bn_js_1.default(0x400)).isZero()) {
116
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
117
+ }
118
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
119
+ if (!exp.and(new bn_js_1.default(0x800)).isZero()) {
120
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
121
+ }
122
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
123
+ if (!exp.and(new bn_js_1.default(0x1000)).isZero()) {
124
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
125
+ }
126
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
127
+ if (!exp.and(new bn_js_1.default(0x2000)).isZero()) {
128
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
129
+ }
130
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
131
+ if (!exp.and(new bn_js_1.default(0x4000)).isZero()) {
132
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
133
+ }
134
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
135
+ if (!exp.and(new bn_js_1.default(0x8000)).isZero()) {
136
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
137
+ }
138
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
139
+ if (!exp.and(new bn_js_1.default(0x10000)).isZero()) {
140
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
141
+ }
142
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
143
+ if (!exp.and(new bn_js_1.default(0x20000)).isZero()) {
144
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
145
+ }
146
+ squaredBase = squaredBase.mul(squaredBase).shrn(SCALE_OFFSET);
147
+ if (!exp.and(new bn_js_1.default(0x40000)).isZero()) {
148
+ result = result.mul(squaredBase).shrn(SCALE_OFFSET);
149
+ }
150
+ if (result.isZero()) {
151
+ return new bn_js_1.default(0);
152
+ }
153
+ if (invert) {
154
+ result = MAX.div(result);
155
+ }
156
+ return result;
157
+ }
158
+ /**
159
+ * Chunks hybrid deposit parameters across a bin range.
160
+ *
161
+ * For each chunk of the bin range, applies resetUninvolvedLiquidityParams to each
162
+ * layer and calculates the per-chunk amounts per layer via toAmountIntoBins.
163
+ *
164
+ * @param layerResults - Array of HybridStrategyResult from distributionToHybridStrategy
165
+ * @param minDeltaId - Minimum delta ID (lowerBinId - activeId) for the full position
166
+ * @param maxDeltaId - Maximum delta ID (upperBinId - activeId) for the full position
167
+ * @param activeId - Current active bin ID
168
+ * @param binStep - Bin step in basis points
169
+ * @param chunkSize - Maximum bins per chunk
170
+ * @param favorXInActiveBin - Whether X is favored in the active bin
171
+ * @returns Array of chunked hybrid deposit parameters, one entry per chunk
172
+ */
173
+ function chunkHybridDepositParameters(layerResults, minDeltaId, maxDeltaId, activeId, binStep, chunkSize, favorXInActiveBin = false) {
174
+ const lowerBinId = activeId.add(minDeltaId).toNumber();
175
+ const upperBinId = activeId.add(maxDeltaId).toNumber();
176
+ const chunks = chunkBinRange(lowerBinId, upperBinId, chunkSize);
177
+ const result = [];
178
+ for (const chunk of chunks) {
179
+ const chunkMinDeltaId = new bn_js_1.default(chunk.lowerBinId).sub(activeId);
180
+ const chunkMaxDeltaId = new bn_js_1.default(chunk.upperBinId).sub(activeId);
181
+ const layers = [];
182
+ for (const layer of layerResults) {
183
+ // Reset uninvolved params for this chunk (zeros out X or Y if chunk is one-sided)
184
+ const chunkParams = resetUninvolvedLiquidityParams(chunkMinDeltaId, chunkMaxDeltaId, favorXInActiveBin, { x0: layer.x0, y0: layer.y0, deltaX: layer.deltaX, deltaY: layer.deltaY });
185
+ // Calculate per-bin amounts for this chunk
186
+ const binsAmounts = toAmountIntoBins(activeId, chunkMinDeltaId, chunkMaxDeltaId, chunkParams.deltaX, chunkParams.deltaY, chunkParams.x0, chunkParams.y0, binStep, favorXInActiveBin);
187
+ const { totalXAmount, totalYAmount } = binsAmounts.reduce((acc, bin) => ({
188
+ totalXAmount: acc.totalXAmount.add(bin.amountX),
189
+ totalYAmount: acc.totalYAmount.add(bin.amountY),
190
+ }), { totalXAmount: new bn_js_1.default(0), totalYAmount: new bn_js_1.default(0) });
191
+ layers.push({
192
+ params: chunkParams,
193
+ maxAmountX: totalXAmount,
194
+ maxAmountY: totalYAmount,
195
+ });
196
+ }
197
+ result.push({
198
+ lowerBinId: chunk.lowerBinId,
199
+ upperBinId: chunk.upperBinId,
200
+ minDeltaId: chunkMinDeltaId,
201
+ maxDeltaId: chunkMaxDeltaId,
202
+ layers,
203
+ });
204
+ }
205
+ return result;
206
+ }
207
+ /**
208
+ * Calculates the Q-price base factor: (1 + binStep/10000) in Q64.64 format.
209
+ * This represents the price multiplier between adjacent bins.
210
+ *
211
+ * @param binStep - The bin step in basis points (e.g., 100 = 1%)
212
+ * @returns Base factor in Q64.64 fixed-point format
213
+ */
214
+ function getQPriceBaseFactor(binStep) {
215
+ const bps = binStep.shln(SCALE_OFFSET).div(new bn_js_1.default(BASIS_POINT_MAX));
216
+ return ONE.add(bps);
217
+ }
218
+ /**
219
+ * Calculates the Q-price from a bin ID.
220
+ * Price = (1 + binStep/10000)^binId in Q64.64 format.
221
+ *
222
+ * Uses binary exponentiation matching Meteora SDK for precision.
223
+ *
224
+ * @param binId - The bin ID (can be negative)
225
+ * @param binStep - The bin step in basis points
226
+ * @returns Price in Q64.64 fixed-point format
227
+ */
228
+ function getQPriceFromId(binId, binStep) {
229
+ return pow(getQPriceBaseFactor(binStep), binId);
230
+ }
231
+ /**
232
+ * Calculates the sum of price weights for bins on the ask side (above active bin).
233
+ *
234
+ * For SPOT strategy, amount in each bin = x0 * price_weight
235
+ * where price_weight = (1 + binStep/10000)^(-(activeId + deltaId))
236
+ *
237
+ * Total amount = x0 * sum(price_weights)
238
+ * Therefore: x0 = totalAmount / sum(price_weights)
239
+ *
240
+ * @param activeId - The active bin ID
241
+ * @param minDeltaId - Minimum delta ID (relative to active bin)
242
+ * @param maxDeltaId - Maximum delta ID (relative to active bin)
243
+ * @param binStep - The bin step in basis points
244
+ * @returns Sum of price weights in Q64.64 format
245
+ */
246
+ function getSumOfPriceWeights(activeId, minDeltaId, maxDeltaId, binStep) {
247
+ let totalWeight = new bn_js_1.default(0);
248
+ const baseFactor = getQPriceBaseFactor(binStep);
249
+ const minBinId = activeId.add(minDeltaId);
250
+ const maxBinId = activeId.add(maxDeltaId);
251
+ // Start from maxBinId and iterate down
252
+ // price = (1 + binStep/10000)^(-binId)
253
+ // We start with the price at -maxBinId and multiply by baseFactor as binId decreases
254
+ let currentPrice = getQPriceFromId(maxBinId.neg(), binStep);
255
+ for (let binId = maxBinId.toNumber(); binId >= minBinId.toNumber(); binId--) {
256
+ totalWeight = totalWeight.add(currentPrice);
257
+ // Moving to lower binId means multiplying price by baseFactor
258
+ currentPrice = currentPrice.mul(baseFactor).shrn(SCALE_OFFSET);
259
+ }
260
+ return totalWeight;
261
+ }
262
+ /**
263
+ * Finds x0 for SPOT strategy on the ask side (bins above active bin).
264
+ *
265
+ * For SPOT strategy with deltaX = 0:
266
+ * - Each bin gets: amountX = x0 * price_weight
267
+ * - Total amount = x0 * sum(price_weights)
268
+ * - Therefore: x0 = totalAmount / sum(price_weights)
269
+ *
270
+ * @param amountX - Total X amount to distribute
271
+ * @param minDeltaId - Minimum delta ID (relative to active bin), must be >= 0 for ask side
272
+ * @param maxDeltaId - Maximum delta ID (relative to active bin)
273
+ * @param binStep - The bin step in basis points
274
+ * @param activeId - The active bin ID
275
+ * @returns x0 value for the strategy
276
+ */
277
+ function findX0ForSpot(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
278
+ if (minDeltaId.gt(maxDeltaId) || amountX.isZero() || amountX.isNeg()) {
279
+ return new bn_js_1.default(0);
280
+ }
281
+ const totalWeight = getSumOfPriceWeights(activeId, minDeltaId, maxDeltaId, binStep);
282
+ if (totalWeight.isZero()) {
283
+ return new bn_js_1.default(0);
284
+ }
285
+ // x0 = amountX / totalWeight (in Q64.64)
286
+ // x0 = (amountX << 64) / totalWeight
287
+ return amountX.shln(SCALE_OFFSET).div(totalWeight);
288
+ }
289
+ /**
290
+ * Finds y0 for SPOT strategy on the bid side (bins below active bin).
291
+ *
292
+ * For SPOT strategy with deltaY = 0:
293
+ * - Each bin gets: amountY = y0 (constant per bin)
294
+ * - Total amount = y0 * num_bins
295
+ * - Therefore: y0 = totalAmount / num_bins
296
+ *
297
+ * @param amountY - Total Y amount to distribute
298
+ * @param minDeltaId - Minimum delta ID (relative to active bin), must be < 0 for bid side
299
+ * @param maxDeltaId - Maximum delta ID (relative to active bin)
300
+ * @returns y0 value for the strategy
301
+ */
302
+ function findY0ForSpot(amountY, minDeltaId, maxDeltaId) {
303
+ if (minDeltaId.gt(maxDeltaId) || amountY.isZero() || amountY.isNeg()) {
304
+ return new bn_js_1.default(0);
305
+ }
306
+ // Number of bins = maxDeltaId - minDeltaId + 1
307
+ const numBins = maxDeltaId.sub(minDeltaId).addn(1);
308
+ if (numBins.isZero()) {
309
+ return new bn_js_1.default(0);
310
+ }
311
+ // y0 = amountY / numBins
312
+ return amountY.div(numBins);
313
+ }
314
+ /**
315
+ * Builds liquidity strategy parameters for SPOT (uniform) distribution.
316
+ *
317
+ * This function calculates x0 and y0 values that will distribute the given amounts
318
+ * uniformly across the specified bin range when used with the rebalanceLiquidity instruction.
319
+ *
320
+ * The function handles three cases:
321
+ * 1. Position entirely below active bin (bid side only) → only Y deposited
322
+ * 2. Position entirely above active bin (ask side only) → only X deposited
323
+ * 3. Position spans active bin → both X and Y deposited
324
+ *
325
+ * @param amountX - Total X amount to deposit
326
+ * @param amountY - Total Y amount to deposit
327
+ * @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
328
+ * @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
329
+ * @param binStep - The bin step in basis points
330
+ * @param activeId - The active bin ID
331
+ * @param favorXInActiveId - Whether X is favored in the active bin (affects bid/ask boundary)
332
+ * @returns Strategy parameters { x0, y0, deltaX, deltaY }
333
+ */
334
+ function buildSpotStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId = false) {
335
+ // Invalid range
336
+ if (minDeltaId.gt(maxDeltaId)) {
337
+ return {
338
+ x0: new bn_js_1.default(0),
339
+ y0: new bn_js_1.default(0),
340
+ deltaX: new bn_js_1.default(0),
341
+ deltaY: new bn_js_1.default(0),
342
+ };
343
+ }
344
+ // Determine bid/ask boundary based on favorXInActiveId
345
+ // - If favorXInActiveId = true: active bin is ask side (X), bid side ends at deltaId = -1
346
+ // - If favorXInActiveId = false: active bin is bid side (Y), ask side starts at deltaId = 1
347
+ const bidSideEndDeltaId = favorXInActiveId ? new bn_js_1.default(-1) : new bn_js_1.default(0);
348
+ const askSideStartDeltaId = favorXInActiveId ? new bn_js_1.default(0) : new bn_js_1.default(1);
349
+ // Case 1: Position entirely on bid side (below active bin) - Y only
350
+ const depositOnlyY = maxDeltaId.lte(bidSideEndDeltaId);
351
+ // Case 2: Position entirely on ask side (above active bin) - X only
352
+ const depositOnlyX = minDeltaId.gte(askSideStartDeltaId);
353
+ if (depositOnlyY) {
354
+ // Only Y token - all bins are on bid side
355
+ const y0 = findY0ForSpot(amountY, minDeltaId, maxDeltaId);
356
+ return {
357
+ x0: new bn_js_1.default(0),
358
+ y0,
359
+ deltaX: new bn_js_1.default(0),
360
+ deltaY: new bn_js_1.default(0),
361
+ };
362
+ }
363
+ if (depositOnlyX) {
364
+ // Only X token - all bins are on ask side
365
+ const x0 = findX0ForSpot(amountX, minDeltaId, maxDeltaId, binStep, activeId);
366
+ return {
367
+ x0,
368
+ y0: new bn_js_1.default(0),
369
+ deltaX: new bn_js_1.default(0),
370
+ deltaY: new bn_js_1.default(0),
371
+ };
372
+ }
373
+ // Case 3: Position spans active bin - both X and Y
374
+ // Y goes to bid side bins (minDeltaId to bidSideEndDeltaId)
375
+ // X goes to ask side bins (askSideStartDeltaId to maxDeltaId)
376
+ const y0 = findY0ForSpot(amountY, minDeltaId, bidSideEndDeltaId);
377
+ const x0 = findX0ForSpot(amountX, askSideStartDeltaId, maxDeltaId, binStep, activeId);
378
+ return {
379
+ x0,
380
+ y0,
381
+ deltaX: new bn_js_1.default(0),
382
+ deltaY: new bn_js_1.default(0),
383
+ };
384
+ }
385
+ /**
386
+ * Resets strategy parameters for a chunk that may not span the full position range.
387
+ *
388
+ * When splitting a position into chunks, each chunk may only cover part of the range:
389
+ * - If chunk is entirely on bid side → zero out X parameters
390
+ * - If chunk is entirely on ask side → zero out Y parameters
391
+ * - If chunk spans active bin → keep both X and Y parameters
392
+ *
393
+ * @param minDeltaId - Chunk's minimum delta ID
394
+ * @param maxDeltaId - Chunk's maximum delta ID
395
+ * @param favorXInActiveId - Whether X is favored in the active bin
396
+ * @param params - Original strategy parameters from buildSpotStrategyParameters
397
+ * @returns Adjusted parameters for the chunk
398
+ */
399
+ function resetUninvolvedLiquidityParams(minDeltaId, maxDeltaId, favorXInActiveId, params) {
400
+ const bidSideEndDeltaId = favorXInActiveId ? new bn_js_1.default(-1) : new bn_js_1.default(0);
401
+ const askSideStartDeltaId = bidSideEndDeltaId.addn(1);
402
+ let { x0, y0, deltaX, deltaY } = params;
403
+ // If chunk is entirely on bid side (maxDeltaId <= bidSideEndDeltaId) → zero out X
404
+ if (maxDeltaId.lte(bidSideEndDeltaId)) {
405
+ x0 = new bn_js_1.default(0);
406
+ deltaX = new bn_js_1.default(0);
407
+ }
408
+ // If chunk is entirely on ask side (minDeltaId >= askSideStartDeltaId) → zero out Y
409
+ if (minDeltaId.gte(askSideStartDeltaId)) {
410
+ y0 = new bn_js_1.default(0);
411
+ deltaY = new bn_js_1.default(0);
412
+ }
413
+ return { x0, y0, deltaX, deltaY };
414
+ }
415
+ /**
416
+ * Calculates the actual X amount that will be deposited into a bin on the ask side.
417
+ *
418
+ * For SPOT strategy: amountX = x0 * price_weight
419
+ * where price_weight = (1 + binStep/10000)^(-(activeId + deltaId))
420
+ *
421
+ * @param x0 - Base X amount from strategy parameters
422
+ * @param deltaId - Delta ID relative to active bin
423
+ * @param binStep - The bin step in basis points
424
+ * @param activeId - The active bin ID
425
+ * @returns Amount of X for this bin
426
+ */
427
+ function getAmountXForBin(x0, deltaId, binStep, activeId) {
428
+ if (x0.isZero()) {
429
+ return new bn_js_1.default(0);
430
+ }
431
+ const binId = activeId.add(deltaId);
432
+ const priceWeight = getQPriceFromId(binId.neg(), binStep);
433
+ // amountX = x0 * priceWeight >> 64
434
+ return x0.mul(priceWeight).shrn(SCALE_OFFSET);
435
+ }
436
+ /**
437
+ * Calculates the actual Y amount that will be deposited into a bin on the bid side.
438
+ *
439
+ * For SPOT strategy: amountY = y0 (constant per bin)
440
+ *
441
+ * @param y0 - Base Y amount from strategy parameters
442
+ * @returns Amount of Y for this bin
443
+ */
444
+ function getAmountYForBin(y0) {
445
+ return y0;
446
+ }
447
+ /**
448
+ * Calculates total X and Y amounts for a chunk based on strategy parameters.
449
+ *
450
+ * This iterates through each bin in the chunk and sums up the amounts.
451
+ *
452
+ * @param activeId - The active bin ID
453
+ * @param minDeltaId - Chunk's minimum delta ID
454
+ * @param maxDeltaId - Chunk's maximum delta ID
455
+ * @param params - Strategy parameters (after resetUninvolvedLiquidityParams)
456
+ * @param binStep - The bin step in basis points
457
+ * @param favorXInActiveId - Whether X is favored in the active bin
458
+ * @returns Total X and Y amounts for the chunk
459
+ */
460
+ function calculateChunkAmounts(activeId, minDeltaId, maxDeltaId, params, binStep, favorXInActiveId) {
461
+ const { x0, y0 } = params;
462
+ const bidSideEndDeltaId = favorXInActiveId ? -1 : 0;
463
+ const askSideStartDeltaId = bidSideEndDeltaId + 1;
464
+ let totalXAmount = new bn_js_1.default(0);
465
+ let totalYAmount = new bn_js_1.default(0);
466
+ for (let deltaId = minDeltaId.toNumber(); deltaId <= maxDeltaId.toNumber(); deltaId++) {
467
+ if (deltaId <= bidSideEndDeltaId) {
468
+ // Bid side - Y token
469
+ totalYAmount = totalYAmount.add(y0);
470
+ }
471
+ else {
472
+ // Ask side - X token
473
+ const amountX = getAmountXForBin(x0, new bn_js_1.default(deltaId), binStep, activeId);
474
+ totalXAmount = totalXAmount.add(amountX);
475
+ }
476
+ }
477
+ return { totalXAmount, totalYAmount };
478
+ }
479
+ // =============================================================================
480
+ // CURVE STRATEGY
481
+ // =============================================================================
482
+ /**
483
+ * Finds base y0 for CURVE strategy.
484
+ *
485
+ * CURVE strategy has liquidity concentrated near active bin, decreasing away from it.
486
+ * Formula: amountY = y0 + deltaY * m where m = distance from active bin
487
+ * Setting deltaY = -y0 / (m1 + 1) gives decreasing amounts.
488
+ *
489
+ * @param amountY - Total Y amount to distribute
490
+ * @param minDeltaId - Minimum delta ID (negative for bid side)
491
+ * @param maxDeltaId - Maximum delta ID (negative for bid side)
492
+ * @returns Base y0 value
493
+ */
494
+ function findBaseY0ForCurve(amountY, minDeltaId, maxDeltaId) {
495
+ if (minDeltaId.gt(maxDeltaId) || amountY.lte(new bn_js_1.default(0))) {
496
+ return new bn_js_1.default(0);
497
+ }
498
+ if (minDeltaId.eq(maxDeltaId)) {
499
+ return amountY;
500
+ }
501
+ // m1 = -minDeltaId, m2 = -maxDeltaId (distances from active bin)
502
+ const m1 = minDeltaId.neg();
503
+ const m2 = maxDeltaId.neg();
504
+ // sum(amounts) = y0 * (m1-m2+1) + deltaY * (m1*(m1+1)/2 - m2*(m2-1)/2)
505
+ // set deltaY = -y0 / (m1 + 1)
506
+ // A = (m1-m2+1) - (m1*(m1+1)/2 - m2*(m2-1)/2) / (m1+1)
507
+ // y0 = amountY / A
508
+ const b = m1.sub(m2).addn(1);
509
+ const c = m1.mul(m1.addn(1)).divn(2);
510
+ const d = m2.mul(m2.subn(1)).divn(2);
511
+ const a = b.sub(c.sub(d).div(m1.addn(1)));
512
+ if (a.isZero()) {
513
+ return amountY;
514
+ }
515
+ return amountY.div(a);
516
+ }
517
+ /**
518
+ * Finds y0 and deltaY for CURVE strategy with iterative refinement.
519
+ *
520
+ * @param amountY - Total Y amount to distribute
521
+ * @param minDeltaId - Minimum delta ID
522
+ * @param maxDeltaId - Maximum delta ID
523
+ * @param activeId - Active bin ID
524
+ * @returns { base: y0, delta: deltaY }
525
+ */
526
+ function findY0AndDeltaYForCurve(amountY, minDeltaId, maxDeltaId, activeId) {
527
+ if (minDeltaId.gt(maxDeltaId) || amountY.isZero()) {
528
+ return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
529
+ }
530
+ let baseY0 = findBaseY0ForCurve(amountY, minDeltaId, maxDeltaId);
531
+ const m1 = minDeltaId.neg();
532
+ // deltaY = -y0 / (m1 + 1)
533
+ const deltaY = baseY0.neg().div(m1.addn(1));
534
+ // Iterative refinement to ensure we don't exceed amountY
535
+ while (true) {
536
+ const totalAmountY = calculateBidSideAmount(activeId, minDeltaId, maxDeltaId, deltaY, baseY0);
537
+ if (totalAmountY.gt(amountY)) {
538
+ baseY0 = baseY0.subn(1);
539
+ }
540
+ else {
541
+ return { base: baseY0, delta: deltaY };
542
+ }
543
+ }
544
+ }
545
+ /**
546
+ * Finds x0 and deltaX for CURVE strategy that produces linear decrease from active bin.
547
+ *
548
+ * For CURVE, we want the ask side to linearly decrease away from the active bin,
549
+ * mirroring the bid side's behavior.
550
+ *
551
+ * The on-chain formula for ask side is:
552
+ * amountX = (x0 + deltaX * delta) * priceWeight
553
+ *
554
+ * For linear decrease in baseAmount (x0 + deltaX * delta):
555
+ * - At minDelta: baseAmount = x0 + deltaX * minDelta (highest)
556
+ * - At maxDelta: baseAmount approaches 0
557
+ *
558
+ * Using deltaX = -x0 / (maxDelta + 1) gives:
559
+ * - At delta=0: baseAmount = x0 (would be highest, but minDelta >= 1)
560
+ * - At delta=maxDelta+1: baseAmount = 0
561
+ *
562
+ * The final amountX will be approximately linear because the decreasing baseAmount
563
+ * partially compensates for the exponentially decaying priceWeight.
564
+ *
565
+ * @param amountX - Total X amount to distribute
566
+ * @param minDeltaId - Minimum delta ID (should be >= 1 for ask side)
567
+ * @param maxDeltaId - Maximum delta ID
568
+ * @param binStep - Bin step in basis points
569
+ * @param activeId - Active bin ID
570
+ * @returns { x0, deltaX } parameters
571
+ */
572
+ function findX0AndDeltaXForCurveMirrored(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
573
+ if (minDeltaId.gt(maxDeltaId) || amountX.lte(new bn_js_1.default(0))) {
574
+ return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
575
+ }
576
+ const numBins = maxDeltaId.sub(minDeltaId).addn(1).toNumber();
577
+ if (numBins === 1) {
578
+ // Single bin: just use x0 directly
579
+ const priceWeight = getQPriceFromId(activeId.add(minDeltaId).neg(), binStep);
580
+ const x0 = amountX.shln(SCALE_OFFSET).div(priceWeight);
581
+ return { base: x0, delta: new bn_js_1.default(0) };
582
+ }
583
+ // Meteora's approach:
584
+ // deltaX = -x0 / maxDelta
585
+ // x0 = amountX * 2^64 / (B - C)
586
+ // where:
587
+ // B = sum of priceWeights: p(m1) + p(m1+1) + ... + p(m2)
588
+ // C = weighted sum / m2: (m1*p(m1) + ... + m2*p(m2)) / m2
589
+ const m1 = minDeltaId.toNumber();
590
+ const m2 = maxDeltaId.toNumber();
591
+ let b = new bn_js_1.default(0);
592
+ let c = new bn_js_1.default(0);
593
+ for (let m = m1; m <= m2; m++) {
594
+ const binId = activeId.addn(m);
595
+ const pm = getQPriceFromId(binId.neg(), binStep);
596
+ b = b.add(pm);
597
+ const cDelta = new bn_js_1.default(m).mul(pm).div(maxDeltaId);
598
+ c = c.add(cDelta);
599
+ }
600
+ const denominator = b.sub(c);
601
+ if (denominator.isZero() || denominator.isNeg()) {
602
+ return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
603
+ }
604
+ let baseX0 = amountX.shln(SCALE_OFFSET).div(denominator);
605
+ const deltaX = baseX0.neg().div(maxDeltaId);
606
+ // Iterative refinement to ensure we don't exceed amountX
607
+ while (true) {
608
+ const totalAmountX = calculateAskSideAmount(activeId, binStep, minDeltaId, maxDeltaId, deltaX, baseX0);
609
+ if (totalAmountX.gt(amountX)) {
610
+ baseX0 = baseX0.subn(1);
611
+ }
612
+ else {
613
+ return { base: baseX0, delta: deltaX };
614
+ }
615
+ }
616
+ }
617
+ /**
618
+ * Finds x0 and deltaX for CURVE strategy.
619
+ *
620
+ * This is a wrapper that delegates to findX0AndDeltaXForCurveMirrored which
621
+ * produces a shape that mirrors the bid side's linear decrease.
622
+ *
623
+ * @param amountX - Total X amount to distribute
624
+ * @param minDeltaId - Minimum delta ID
625
+ * @param maxDeltaId - Maximum delta ID
626
+ * @param binStep - Bin step in basis points
627
+ * @param activeId - Active bin ID
628
+ * @returns { base: x0, delta: deltaX }
629
+ */
630
+ function findX0AndDeltaXForCurve(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
631
+ return findX0AndDeltaXForCurveMirrored(amountX, minDeltaId, maxDeltaId, binStep, activeId);
632
+ }
633
+ /**
634
+ * Builds liquidity strategy parameters for CURVE distribution.
635
+ *
636
+ * CURVE strategy concentrates liquidity near the active bin with amounts
637
+ * decreasing as you move away. This is achieved with negative deltaX/deltaY.
638
+ *
639
+ * @param amountX - Total X amount to deposit
640
+ * @param amountY - Total Y amount to deposit
641
+ * @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
642
+ * @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
643
+ * @param binStep - The bin step in basis points
644
+ * @param activeId - The active bin ID
645
+ * @param favorXInActiveId - Whether X is favored in the active bin
646
+ * @returns Strategy parameters { x0, y0, deltaX, deltaY }
647
+ */
648
+ function buildCurveStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId = false) {
649
+ if (minDeltaId.gt(maxDeltaId)) {
650
+ return { x0: new bn_js_1.default(0), y0: new bn_js_1.default(0), deltaX: new bn_js_1.default(0), deltaY: new bn_js_1.default(0) };
651
+ }
652
+ const bidSideEndDeltaId = favorXInActiveId ? new bn_js_1.default(-1) : new bn_js_1.default(0);
653
+ const askSideStartDeltaId = favorXInActiveId ? new bn_js_1.default(0) : new bn_js_1.default(1);
654
+ const depositOnlyY = maxDeltaId.lte(bidSideEndDeltaId);
655
+ const depositOnlyX = minDeltaId.gte(askSideStartDeltaId);
656
+ if (depositOnlyY) {
657
+ const { base: y0, delta: deltaY } = findY0AndDeltaYForCurve(amountY, minDeltaId, maxDeltaId, activeId);
658
+ return { x0: new bn_js_1.default(0), y0, deltaX: new bn_js_1.default(0), deltaY };
659
+ }
660
+ if (depositOnlyX) {
661
+ const { base: x0, delta: deltaX } = findX0AndDeltaXForCurve(amountX, minDeltaId, maxDeltaId, binStep, activeId);
662
+ return { x0, y0: new bn_js_1.default(0), deltaX, deltaY: new bn_js_1.default(0) };
663
+ }
664
+ // Both sides - calculate independently
665
+ const { base: y0, delta: deltaY } = findY0AndDeltaYForCurve(amountY, minDeltaId, bidSideEndDeltaId, activeId);
666
+ const { base: x0, delta: deltaX } = findX0AndDeltaXForCurve(amountX, askSideStartDeltaId, maxDeltaId, binStep, activeId);
667
+ return { x0, y0, deltaX, deltaY };
668
+ }
669
+ // =============================================================================
670
+ // BID_ASK STRATEGY
671
+ // =============================================================================
672
+ /**
673
+ * Finds base deltaY for BID_ASK strategy.
674
+ *
675
+ * BID_ASK strategy has liquidity increasing away from active bin.
676
+ * Formula: amountY = y0 + deltaY * m
677
+ * Setting y0 = -deltaY * (m2 - 1) gives increasing amounts.
678
+ *
679
+ * @param amountY - Total Y amount to distribute
680
+ * @param minDeltaId - Minimum delta ID
681
+ * @param maxDeltaId - Maximum delta ID
682
+ * @returns Base deltaY value
683
+ */
684
+ function findBaseDeltaYForBidAsk(amountY, minDeltaId, maxDeltaId) {
685
+ if (minDeltaId.gt(maxDeltaId) || amountY.lte(new bn_js_1.default(0))) {
686
+ return new bn_js_1.default(0);
687
+ }
688
+ if (minDeltaId.eq(maxDeltaId)) {
689
+ return amountY;
690
+ }
691
+ const m1 = minDeltaId.neg();
692
+ const m2 = maxDeltaId.neg();
693
+ // sum(amounts) = y0 * (m1-m2+1) + deltaY * (m1*(m1+1)/2 - m2*(m2-1)/2)
694
+ // set y0 = -deltaY * (m2 - 1)
695
+ // A = (-m2+1) * (m1-m2+1) + (m1*(m1+1)/2 - m2*(m2-1)/2)
696
+ // deltaY = amountY / A
697
+ const b = m2.neg().addn(1).mul(m1.sub(m2).addn(1));
698
+ const c = m1.mul(m1.addn(1)).divn(2);
699
+ const d = m2.mul(m2.subn(1)).divn(2);
700
+ const a = b.add(c.sub(d));
701
+ if (a.isZero()) {
702
+ return amountY;
703
+ }
704
+ return amountY.div(a);
705
+ }
706
+ /**
707
+ * Finds y0 and deltaY for BID_ASK strategy with iterative refinement.
708
+ *
709
+ * @param amountY - Total Y amount to distribute
710
+ * @param minDeltaId - Minimum delta ID
711
+ * @param maxDeltaId - Maximum delta ID
712
+ * @param activeId - Active bin ID
713
+ * @returns { base: y0, delta: deltaY }
714
+ */
715
+ function findY0AndDeltaYForBidAsk(amountY, minDeltaId, maxDeltaId, activeId) {
716
+ if (minDeltaId.gt(maxDeltaId) || amountY.isZero()) {
717
+ return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
718
+ }
719
+ const baseDeltaY = findBaseDeltaYForBidAsk(amountY, minDeltaId, maxDeltaId);
720
+ const m2 = maxDeltaId.neg();
721
+ // y0 is calculated once from initial baseDeltaY and kept constant (matches Meteora)
722
+ const y0 = baseDeltaY.neg().mul(m2.subn(1));
723
+ // Binary search for the correct deltaY that doesn't exceed amountY
724
+ let low = new bn_js_1.default(0);
725
+ let high = baseDeltaY.clone();
726
+ let result = new bn_js_1.default(0);
727
+ while (low.lte(high)) {
728
+ const mid = low.add(high).shrn(1);
729
+ const totalAmountY = calculateBidSideAmount(activeId, minDeltaId, maxDeltaId, mid, y0);
730
+ if (totalAmountY.lte(amountY)) {
731
+ result = mid;
732
+ low = mid.addn(1);
733
+ }
734
+ else {
735
+ high = mid.subn(1);
736
+ }
737
+ }
738
+ return { base: y0, delta: result };
739
+ }
740
+ /**
741
+ * Calculates the critical delta beyond which BID_ASK amounts will start decreasing.
742
+ *
743
+ * For BID_ASK, amountX = (x0 + deltaX * delta) * priceWeight
744
+ * where priceWeight decays exponentially as delta increases.
745
+ *
746
+ * The critical point is approximately 1 / (binStep / 10000) = 10000 / binStep.
747
+ * Beyond this point, exponential decay overwhelms linear growth.
748
+ *
749
+ * @param binStep - Bin step in basis points
750
+ * @returns Critical delta value
751
+ */
752
+ function getCriticalDeltaForBidAsk(binStep) {
753
+ // Critical delta ≈ 1 / (binStep / 10000) = 10000 / binStep
754
+ // Using floor to be conservative
755
+ return Math.floor(10000 / binStep.toNumber());
756
+ }
757
+ /**
758
+ * Finds base deltaX for BID_ASK strategy.
759
+ *
760
+ * Formula: amountX = (x0 + deltaX * m) * price_weight
761
+ * Setting x0 = -m1 * deltaX + deltaX gives increasing amounts.
762
+ *
763
+ * IMPORTANT: This formula only produces monotonically increasing amounts
764
+ * when the range is within the critical delta (approximately 10000/binStep bins).
765
+ * For wider ranges, consider using multiple smaller positions.
766
+ *
767
+ * @param amountX - Total X amount to distribute
768
+ * @param minDeltaId - Minimum delta ID
769
+ * @param maxDeltaId - Maximum delta ID
770
+ * @param binStep - Bin step in basis points
771
+ * @param activeId - Active bin ID
772
+ * @returns Base deltaX value
773
+ */
774
+ function findBaseDeltaXForBidAsk(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
775
+ if (minDeltaId.gt(maxDeltaId) || amountX.lte(new bn_js_1.default(0))) {
776
+ return new bn_js_1.default(0);
777
+ }
778
+ // sum(amounts) = x0 * B + deltaX * C
779
+ // where B = sum(price_weights), C = sum(m * price_weight)
780
+ // setting x0 = -m1 * deltaX + deltaX = deltaX * (1 - m1)
781
+ // sum = deltaX * (1 - m1) * B + deltaX * C = deltaX * ((1-m1)*B + C)
782
+ // deltaX = amountX / ((1-m1)*B + C)
783
+ let b = new bn_js_1.default(0);
784
+ let c = new bn_js_1.default(0);
785
+ const m1 = minDeltaId;
786
+ const m2 = maxDeltaId.addn(1); // +1 to ensure no zero amount at active bin
787
+ for (let m = m1.toNumber(); m <= m2.toNumber(); m++) {
788
+ const binId = activeId.addn(m);
789
+ const pm = getQPriceFromId(binId.neg(), binStep);
790
+ const bDelta = m1.mul(pm);
791
+ b = b.add(bDelta);
792
+ const cDelta = new bn_js_1.default(m).mul(pm);
793
+ c = c.add(cDelta);
794
+ }
795
+ const denominator = c.sub(b);
796
+ if (denominator.isZero()) {
797
+ return new bn_js_1.default(0);
798
+ }
799
+ return amountX.shln(SCALE_OFFSET).div(denominator);
800
+ }
801
+ /**
802
+ * Finds x0 and deltaX for BID_ASK strategy with iterative refinement.
803
+ *
804
+ * @param amountX - Total X amount to distribute
805
+ * @param minDeltaId - Minimum delta ID
806
+ * @param maxDeltaId - Maximum delta ID
807
+ * @param binStep - Bin step in basis points
808
+ * @param activeId - Active bin ID
809
+ * @returns { base: x0, delta: deltaX }
810
+ */
811
+ function findX0AndDeltaXForBidAsk(amountX, minDeltaId, maxDeltaId, binStep, activeId) {
812
+ if (minDeltaId.gt(maxDeltaId) || amountX.lte(new bn_js_1.default(0)) || amountX.isZero()) {
813
+ return { base: new bn_js_1.default(0), delta: new bn_js_1.default(0) };
814
+ }
815
+ const baseDeltaX = findBaseDeltaXForBidAsk(amountX, minDeltaId, maxDeltaId, binStep, activeId);
816
+ // x0 is calculated once from initial baseDeltaX and kept constant (matches Meteora)
817
+ const x0 = minDeltaId.neg().mul(baseDeltaX).add(baseDeltaX);
818
+ // Binary search for the correct deltaX that doesn't exceed amountX
819
+ let low = new bn_js_1.default(0);
820
+ let high = baseDeltaX.clone();
821
+ let result = new bn_js_1.default(0);
822
+ while (low.lte(high)) {
823
+ const mid = low.add(high).shrn(1);
824
+ const totalAmountX = calculateAskSideAmount(activeId, binStep, minDeltaId, maxDeltaId, mid, x0);
825
+ if (totalAmountX.lte(amountX)) {
826
+ result = mid;
827
+ low = mid.addn(1);
828
+ }
829
+ else {
830
+ high = mid.subn(1);
831
+ }
832
+ }
833
+ return { base: x0, delta: result };
834
+ }
835
+ /**
836
+ * Builds liquidity strategy parameters for BID_ASK distribution.
837
+ *
838
+ * BID_ASK strategy has liquidity increasing away from the active bin.
839
+ * This is achieved with positive deltaX/deltaY values.
840
+ *
841
+ * @param amountX - Total X amount to deposit
842
+ * @param amountY - Total Y amount to deposit
843
+ * @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
844
+ * @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
845
+ * @param binStep - The bin step in basis points
846
+ * @param activeId - The active bin ID
847
+ * @param favorXInActiveId - Whether X is favored in the active bin
848
+ * @returns Strategy parameters { x0, y0, deltaX, deltaY }
849
+ */
850
+ function buildBidAskStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId = false) {
851
+ if (minDeltaId.gt(maxDeltaId)) {
852
+ return { x0: new bn_js_1.default(0), y0: new bn_js_1.default(0), deltaX: new bn_js_1.default(0), deltaY: new bn_js_1.default(0) };
853
+ }
854
+ const bidSideEndDeltaId = favorXInActiveId ? new bn_js_1.default(-1) : new bn_js_1.default(0);
855
+ const askSideStartDeltaId = favorXInActiveId ? new bn_js_1.default(0) : new bn_js_1.default(1);
856
+ const depositOnlyY = maxDeltaId.lte(bidSideEndDeltaId);
857
+ const depositOnlyX = minDeltaId.gte(askSideStartDeltaId);
858
+ if (depositOnlyY) {
859
+ const { base: y0, delta: deltaY } = findY0AndDeltaYForBidAsk(amountY, minDeltaId, maxDeltaId, activeId);
860
+ return { x0: new bn_js_1.default(0), y0, deltaX: new bn_js_1.default(0), deltaY };
861
+ }
862
+ if (depositOnlyX) {
863
+ const { base: x0, delta: deltaX } = findX0AndDeltaXForBidAsk(amountX, minDeltaId, maxDeltaId, binStep, activeId);
864
+ return { x0, y0: new bn_js_1.default(0), deltaX, deltaY: new bn_js_1.default(0) };
865
+ }
866
+ // Both sides
867
+ const { base: y0, delta: deltaY } = findY0AndDeltaYForBidAsk(amountY, minDeltaId, bidSideEndDeltaId, activeId);
868
+ const { base: x0, delta: deltaX } = findX0AndDeltaXForBidAsk(amountX, askSideStartDeltaId, maxDeltaId, binStep, activeId);
869
+ return { x0, y0, deltaX, deltaY };
870
+ }
871
+ // =============================================================================
872
+ // HELPER FUNCTIONS FOR AMOUNT CALCULATION
873
+ // =============================================================================
874
+ /**
875
+ * Calculates total Y amount for bid side bins using given parameters.
876
+ *
877
+ * @param activeId - Active bin ID
878
+ * @param minDeltaId - Minimum delta ID
879
+ * @param maxDeltaId - Maximum delta ID
880
+ * @param deltaY - Delta Y per bin
881
+ * @param y0 - Base Y amount
882
+ * @returns Total Y amount
883
+ */
884
+ function calculateBidSideAmount(activeId, minDeltaId, maxDeltaId, deltaY, y0) {
885
+ let totalAmount = new bn_js_1.default(0);
886
+ const minBinId = activeId.add(minDeltaId);
887
+ const maxBinId = activeId.add(maxDeltaId);
888
+ for (let binId = minBinId.toNumber(); binId <= maxBinId.toNumber(); binId++) {
889
+ const deltaBin = activeId.toNumber() - binId;
890
+ const totalDeltaY = deltaY.muln(deltaBin);
891
+ const amountY = y0.add(totalDeltaY);
892
+ if (amountY.gtn(0)) {
893
+ totalAmount = totalAmount.add(amountY);
894
+ }
895
+ }
896
+ return totalAmount;
897
+ }
898
+ /**
899
+ * Calculates total X amount for ask side bins using given parameters.
900
+ *
901
+ * @param activeId - Active bin ID
902
+ * @param binStep - Bin step in basis points
903
+ * @param minDeltaId - Minimum delta ID
904
+ * @param maxDeltaId - Maximum delta ID
905
+ * @param deltaX - Delta X per bin
906
+ * @param x0 - Base X amount
907
+ * @returns Total X amount
908
+ */
909
+ function calculateAskSideAmount(activeId, binStep, minDeltaId, maxDeltaId, deltaX, x0) {
910
+ let totalAmount = new bn_js_1.default(0);
911
+ const baseFactor = getQPriceBaseFactor(binStep);
912
+ const minBinId = activeId.add(minDeltaId);
913
+ const maxBinId = activeId.add(maxDeltaId);
914
+ // Start from maxBinId and iterate down
915
+ let inverseBasePrice = getQPriceFromId(maxBinId.neg(), binStep);
916
+ for (let binId = maxBinId.toNumber(); binId >= minBinId.toNumber(); binId--) {
917
+ const delta = binId - activeId.toNumber();
918
+ const totalDeltaX = deltaX.muln(delta);
919
+ const baseAmount = x0.add(totalDeltaX);
920
+ // Only calculate if baseAmount is positive (can't shift right on negative BN)
921
+ if (baseAmount.gtn(0)) {
922
+ const amountX = baseAmount.mul(inverseBasePrice).shrn(SCALE_OFFSET);
923
+ if (amountX.gtn(0)) {
924
+ totalAmount = totalAmount.add(amountX);
925
+ }
926
+ }
927
+ inverseBasePrice = inverseBasePrice.mul(baseFactor).shrn(SCALE_OFFSET);
928
+ }
929
+ return totalAmount;
930
+ }
931
+ // =============================================================================
932
+ // UNIFIED STRATEGY BUILDER
933
+ // =============================================================================
934
+ /**
935
+ * Builds liquidity strategy parameters for the specified strategy type.
936
+ *
937
+ * This is the main entry point that dispatches to the appropriate strategy builder.
938
+ *
939
+ * @param strategyType - The type of strategy (SPOT, CURVE, or BID_ASK)
940
+ * @param amountX - Total X amount to deposit
941
+ * @param amountY - Total Y amount to deposit
942
+ * @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
943
+ * @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
944
+ * @param binStep - The bin step in basis points
945
+ * @param activeId - The active bin ID
946
+ * @param favorXInActiveId - Whether X is favored in the active bin
947
+ * @returns Strategy parameters { x0, y0, deltaX, deltaY }
948
+ */
949
+ function buildStrategyParameters(strategyType, amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId = false) {
950
+ switch (strategyType) {
951
+ case StrategyType.SPOT:
952
+ return buildSpotStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId);
953
+ case StrategyType.CURVE:
954
+ return buildCurveStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId);
955
+ case StrategyType.BID_ASK:
956
+ return buildBidAskStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId);
957
+ default:
958
+ // Default to SPOT if unknown strategy type
959
+ return buildSpotStrategyParameters(amountX, amountY, minDeltaId, maxDeltaId, binStep, activeId, favorXInActiveId);
960
+ }
961
+ }
962
+ /**
963
+ * Maps a Distribution string to a StrategyType enum.
964
+ * All variants of a base distribution (e.g. SPOT, SPOT-IMBALANCED, SPOT-ONE-SIDE)
965
+ * map to the same underlying StrategyType.
966
+ */
967
+ function distributionToStrategyType(distribution) {
968
+ var _a;
969
+ const mapping = {
970
+ 'SPOT': StrategyType.SPOT, 'SPOT-IMBALANCED': StrategyType.SPOT, 'SPOT-ONE-SIDE': StrategyType.SPOT,
971
+ 'CURVE': StrategyType.CURVE, 'CURVE-IMBALANCED': StrategyType.CURVE, 'CURVE-ONE-SIDE': StrategyType.CURVE,
972
+ 'BID-ASK': StrategyType.BID_ASK, 'BID-ASK-IMBALANCED': StrategyType.BID_ASK, 'BID-ASK-ONE-SIDE': StrategyType.BID_ASK,
973
+ };
974
+ return (_a = mapping[distribution]) !== null && _a !== void 0 ? _a : StrategyType.SPOT;
975
+ }
976
+ /**
977
+ * Converts an array of HybridLiquidityDistribution layers into strategy parameters.
978
+ *
979
+ * For each layer, applies the percentage split to the total X/Y amounts and delegates
980
+ * to buildHybridStrategyParameters. The last layer with a non-zero percentage on each
981
+ * side receives the full remaining amount (sweep), ensuring no dust is left behind
982
+ * due to integer division.
983
+ *
984
+ * Layers where both X and Y percentages are 0 are skipped.
985
+ *
986
+ * @param hybridDistribution - Array of layer distribution configs (1-3 layers)
987
+ * @param amountX - Total X token amount to distribute across layers
988
+ * @param amountY - Total Y token amount to distribute across layers
989
+ * @param minDeltaId - Lower bin bound relative to active bin (negative)
990
+ * @param maxDeltaId - Upper bin bound relative to active bin (positive)
991
+ * @param binStep - Bin step of the DLMM pool
992
+ * @param activeId - Current active bin ID
993
+ * @returns One LiquidityStrategyParameters per active layer
994
+ */
995
+ function distributionToHybridStrategy({ amountY, amountX, hybridDistribution, minDeltaId, maxDeltaId, binStep, activeId, }) {
996
+ // Validate: each side must sum to exactly 100% or 0%, and both cannot be 0%
997
+ const sumX = hybridDistribution.reduce((s, l) => s + l.x.percentage, 0);
998
+ const sumY = hybridDistribution.reduce((s, l) => s + l.y.percentage, 0);
999
+ if (sumX !== 100 && sumX !== 0) {
1000
+ throw new Error(`X percentages must sum to 100 or 0 (got ${sumX})`);
1001
+ }
1002
+ if (sumY !== 100 && sumY !== 0) {
1003
+ throw new Error(`Y percentages must sum to 100 or 0 (got ${sumY})`);
1004
+ }
1005
+ if (sumX === 0 && sumY === 0) {
1006
+ throw new Error('Both X and Y percentages cannot be 0');
1007
+ }
1008
+ // Find last layer with non-zero percentage per side (dust goes here)
1009
+ let lastActiveXIndex = -1;
1010
+ let lastActiveYIndex = -1;
1011
+ for (let i = hybridDistribution.length - 1; i >= 0; i--) {
1012
+ if (lastActiveXIndex === -1 && hybridDistribution[i].x.percentage > 0)
1013
+ lastActiveXIndex = i;
1014
+ if (lastActiveYIndex === -1 && hybridDistribution[i].y.percentage > 0)
1015
+ lastActiveYIndex = i;
1016
+ }
1017
+ const results = [];
1018
+ let totalCalculatedX = new bn_js_1.default(0);
1019
+ let totalCalculatedY = new bn_js_1.default(0);
1020
+ for (let i = 0; i < hybridDistribution.length; i++) {
1021
+ const { x, y } = hybridDistribution[i];
1022
+ if (x.percentage === 0 && y.percentage === 0) {
1023
+ throw new Error(`Unexpected error: Both sides (X and Y) cannot have zero deposits on layer ${i + 1}`);
1024
+ }
1025
+ let layerAmountX = amountX.mul(new bn_js_1.default(x.percentage)).div(new bn_js_1.default(100));
1026
+ totalCalculatedX = totalCalculatedX.add(layerAmountX);
1027
+ let layerAmountY = amountY.mul(new bn_js_1.default(y.percentage)).div(new bn_js_1.default(100));
1028
+ totalCalculatedY = totalCalculatedY.add(layerAmountY);
1029
+ // Add dust to the last active layer per side
1030
+ if (i === lastActiveXIndex && amountX.gt(totalCalculatedX)) {
1031
+ layerAmountX = layerAmountX.add(amountX.sub(totalCalculatedX));
1032
+ }
1033
+ if (i === lastActiveYIndex && amountY.gt(totalCalculatedY)) {
1034
+ layerAmountY = layerAmountY.add(amountY.sub(totalCalculatedY));
1035
+ }
1036
+ const strategyParams = buildHybridStrategyParameters({
1037
+ strategyTypeX: distributionToStrategyType(x.distribution),
1038
+ strategyTypeY: distributionToStrategyType(y.distribution),
1039
+ amountX: layerAmountX, // note to AI: amountX should be equal to layerAmountX
1040
+ amountY: layerAmountY,
1041
+ minDeltaId,
1042
+ maxDeltaId,
1043
+ binStep,
1044
+ activeId,
1045
+ });
1046
+ results.push(Object.assign(Object.assign({}, strategyParams), { maxAmountX: layerAmountX, maxAmountY: layerAmountY }));
1047
+ }
1048
+ return results;
1049
+ }
1050
+ /**
1051
+ * Builds liquidity strategy parameters for a hybrid distribution where X (ask)
1052
+ * and Y (bid) sides use different strategy types (e.g. X=BID-ASK, Y=SPOT).
1053
+ *
1054
+ * Splits the bin range at the active bin and computes each side independently:
1055
+ * - Left side (Y/bid): distributes amountY across [minDeltaId, 0] using strategyTypeY
1056
+ * - Right side (X/ask): distributes amountX across [0, maxDeltaId] using strategyTypeX
1057
+ *
1058
+ * The active bin (deltaId=0) is included in both computations. The final result
1059
+ * merges y0/deltaY from the left side with x0/deltaX from the right side, giving
1060
+ * each token its own independent distribution curve.
1061
+ *
1062
+ * @param strategyTypeY - Distribution strategy for the Y (bid) side
1063
+ * @param strategyTypeX - Distribution strategy for the X (ask) side
1064
+ * @param amountX - Total X token amount to distribute
1065
+ * @param amountY - Total Y token amount to distribute
1066
+ * @param minDeltaId - Lower bin bound relative to active bin (negative)
1067
+ * @param maxDeltaId - Upper bin bound relative to active bin (positive)
1068
+ * @param binStep - Bin step of the DLMM pool
1069
+ * @param activeId - Current active bin ID
1070
+ * @returns Merged strategy parameters with independent X/Y distributions
1071
+ */
1072
+ function buildHybridStrategyParameters({ strategyTypeY, strategyTypeX, amountY, amountX, minDeltaId, maxDeltaId, binStep, activeId, }) {
1073
+ const favorXInActiveId = amountY.isZero();
1074
+ // Left side (Y)
1075
+ const leftSide = buildStrategyParameters(strategyTypeY, new bn_js_1.default(0), amountY, minDeltaId, new bn_js_1.default(0), binStep, activeId, favorXInActiveId);
1076
+ // Right side (X)
1077
+ const rightSide = buildStrategyParameters(strategyTypeX, amountX, new bn_js_1.default(0), new bn_js_1.default(0), maxDeltaId, binStep, activeId, favorXInActiveId);
1078
+ return {
1079
+ /** Base amount for X token (ask side) */
1080
+ x0: rightSide.x0,
1081
+ /** Base amount for Y token (bid side) */
1082
+ y0: leftSide.y0,
1083
+ /** Delta change per bin for X (0 for SPOT, negative for CURVE, positive for BID_ASK) */
1084
+ deltaX: rightSide.deltaX,
1085
+ /** Delta change per bin for Y (0 for SPOT, negative for CURVE, positive for BID_ASK) */
1086
+ deltaY: leftSide.deltaY,
1087
+ };
1088
+ }
1089
+ /**
1090
+ * Divides a bin range into chunks of specified size.
1091
+ *
1092
+ * This is similar to Meteora's `chunkBinRange` but with a configurable chunk size
1093
+ * instead of a fixed 70-bin limit.
1094
+ *
1095
+ * @param minBinId - The starting bin ID of the range (absolute bin ID)
1096
+ * @param maxBinId - The ending bin ID of the range (absolute bin ID)
1097
+ * @param chunkSize - Maximum number of bins per chunk
1098
+ * @returns Array of bin range chunks
1099
+ */
1100
+ function chunkBinRange(minBinId, maxBinId, chunkSize) {
1101
+ const chunkedBinRange = [];
1102
+ let startBinId = minBinId;
1103
+ while (startBinId <= maxBinId) {
1104
+ const endBinId = Math.min(startBinId + chunkSize - 1, maxBinId);
1105
+ chunkedBinRange.push({
1106
+ lowerBinId: startBinId,
1107
+ upperBinId: endBinId,
1108
+ });
1109
+ startBinId += chunkSize;
1110
+ }
1111
+ return chunkedBinRange;
1112
+ }
1113
+ /**
1114
+ * Converts strategy parameters to on-chain format with bit flag for negative values.
1115
+ *
1116
+ * The Solana program uses unsigned integers, so negative values are stored as
1117
+ * positive with a bit flag indicating the sign.
1118
+ *
1119
+ * Bit flags:
1120
+ * - Bit 0 (0b0001): x0 is negative
1121
+ * - Bit 1 (0b0010): y0 is negative
1122
+ * - Bit 2 (0b0100): deltaX is negative
1123
+ * - Bit 3 (0b1000): deltaY is negative
1124
+ *
1125
+ * @param x0 - Base X amount (may be negative)
1126
+ * @param y0 - Base Y amount (may be negative)
1127
+ * @param deltaX - Delta X per bin (may be negative)
1128
+ * @param deltaY - Delta Y per bin (may be negative)
1129
+ * @returns Parameters with absolute values and bit flag
1130
+ */
1131
+ function buildBitFlagAndNegateStrategyParameters(x0, y0, deltaX, deltaY) {
1132
+ let bitFlag = 0;
1133
+ if (x0.isNeg()) {
1134
+ bitFlag |= 0b0001;
1135
+ x0 = x0.neg();
1136
+ }
1137
+ if (y0.isNeg()) {
1138
+ bitFlag |= 0b0010;
1139
+ y0 = y0.neg();
1140
+ }
1141
+ if (deltaX.isNeg()) {
1142
+ bitFlag |= 0b0100;
1143
+ deltaX = deltaX.neg();
1144
+ }
1145
+ if (deltaY.isNeg()) {
1146
+ bitFlag |= 0b1000;
1147
+ deltaY = deltaY.neg();
1148
+ }
1149
+ return {
1150
+ bitFlag,
1151
+ x0,
1152
+ y0,
1153
+ deltaX,
1154
+ deltaY,
1155
+ };
1156
+ }
1157
+ /**
1158
+ * Calculates amounts for each bin in a range.
1159
+ *
1160
+ * This function iterates through all bins and calculates the X/Y amount
1161
+ * for each bin based on the strategy parameters.
1162
+ *
1163
+ * @param activeId - The active bin ID
1164
+ * @param minDeltaId - Minimum delta ID relative to active bin
1165
+ * @param maxDeltaId - Maximum delta ID relative to active bin
1166
+ * @param deltaX - Delta X per bin
1167
+ * @param deltaY - Delta Y per bin
1168
+ * @param x0 - Base X amount
1169
+ * @param y0 - Base Y amount
1170
+ * @param binStep - Bin step in basis points
1171
+ * @param favorXInActiveBin - Whether X is favored in active bin
1172
+ * @returns Array of { binId, amountX, amountY } for each bin
1173
+ */
1174
+ function toAmountIntoBins(activeId, minDeltaId, maxDeltaId, deltaX, deltaY, x0, y0, binStep, favorXInActiveBin) {
1175
+ const results = [];
1176
+ const bidSideEndDeltaId = favorXInActiveBin ? -1 : 0;
1177
+ for (let delta = minDeltaId.toNumber(); delta <= maxDeltaId.toNumber(); delta++) {
1178
+ const binId = activeId.toNumber() + delta;
1179
+ if (delta <= bidSideEndDeltaId) {
1180
+ // Bid side - Y token
1181
+ const distance = -delta; // Distance from active bin (positive)
1182
+ const amountY = y0.add(deltaY.muln(distance));
1183
+ results.push({
1184
+ binId,
1185
+ amountX: new bn_js_1.default(0),
1186
+ amountY: amountY.gtn(0) ? amountY : new bn_js_1.default(0),
1187
+ });
1188
+ }
1189
+ else {
1190
+ // Ask side - X token
1191
+ const priceWeight = getQPriceFromId(new bn_js_1.default(binId).neg(), binStep);
1192
+ const baseAmount = x0.add(deltaX.muln(delta));
1193
+ const amountX = baseAmount.mul(priceWeight).shrn(SCALE_OFFSET);
1194
+ results.push({
1195
+ binId,
1196
+ amountX: amountX.gtn(0) ? amountX : new bn_js_1.default(0),
1197
+ amountY: new bn_js_1.default(0),
1198
+ });
1199
+ }
1200
+ }
1201
+ return results;
1202
+ }
1203
+ /**
1204
+ * Chunks a deposit into multiple transactions based on bin range.
1205
+ *
1206
+ * This function:
1207
+ * 1. Takes pre-built strategy parameters for the entire position
1208
+ * 2. Chunks the bin range into smaller ranges
1209
+ * 3. For each chunk, adjusts params and calculates amounts
1210
+ *
1211
+ * @param params - Strategy parameters from buildStrategyParameters (for entire position)
1212
+ * @param minDeltaId - Minimum delta ID (lowerBinId - activeId)
1213
+ * @param maxDeltaId - Maximum delta ID (upperBinId - activeId)
1214
+ * @param activeId - Current active bin ID
1215
+ * @param binStep - Bin step in basis points
1216
+ * @param chunkSize - Maximum bins per chunk
1217
+ * @param favorXInActiveBin - Whether X is favored in active bin
1218
+ * @returns Array of chunked deposit parameters
1219
+ */
1220
+ function chunkDepositParameters(params, minDeltaId, maxDeltaId, activeId, binStep, chunkSize, favorXInActiveBin = false) {
1221
+ // Convert to absolute bin IDs for chunking
1222
+ const lowerBinId = activeId.add(minDeltaId).toNumber();
1223
+ const upperBinId = activeId.add(maxDeltaId).toNumber();
1224
+ // Chunk the bin range
1225
+ const chunks = chunkBinRange(lowerBinId, upperBinId, chunkSize);
1226
+ // For each chunk, calculate the deposit parameters
1227
+ const result = [];
1228
+ for (const chunk of chunks) {
1229
+ const chunkMinDeltaId = new bn_js_1.default(chunk.lowerBinId).sub(activeId);
1230
+ const chunkMaxDeltaId = new bn_js_1.default(chunk.upperBinId).sub(activeId);
1231
+ // Reset uninvolved params for this chunk
1232
+ const chunkParams = resetUninvolvedLiquidityParams(chunkMinDeltaId, chunkMaxDeltaId, favorXInActiveBin, params);
1233
+ // Calculate amounts for this chunk using toAmountIntoBins
1234
+ const binsAmounts = toAmountIntoBins(activeId, chunkMinDeltaId, chunkMaxDeltaId, chunkParams.deltaX, chunkParams.deltaY, chunkParams.x0, chunkParams.y0, binStep, favorXInActiveBin);
1235
+ // Sum up amounts
1236
+ const { totalXAmount, totalYAmount } = binsAmounts.reduce((acc, bin) => ({
1237
+ totalXAmount: acc.totalXAmount.add(bin.amountX),
1238
+ totalYAmount: acc.totalYAmount.add(bin.amountY),
1239
+ }), { totalXAmount: new bn_js_1.default(0), totalYAmount: new bn_js_1.default(0) });
1240
+ result.push({
1241
+ lowerBinId: chunk.lowerBinId,
1242
+ upperBinId: chunk.upperBinId,
1243
+ minDeltaId: chunkMinDeltaId,
1244
+ maxDeltaId: chunkMaxDeltaId,
1245
+ params: chunkParams,
1246
+ maxAmountX: totalXAmount,
1247
+ maxAmountY: totalYAmount,
1248
+ });
1249
+ }
1250
+ return result;
1251
+ }
1252
+ /**
1253
+ * Chunks a bin range with a custom initial chunk size and uniform subsequent chunk size.
1254
+ *
1255
+ * @param minBinId - The minimum bin ID of the range
1256
+ * @param maxBinId - The maximum bin ID of the range
1257
+ * @param initialChunkSize - Size of the first chunk (e.g., 150 bins)
1258
+ * @param subsequentChunkSize - Size of subsequent chunks (e.g., 69 bins)
1259
+ * @returns Array of chunk ranges with lowerBinId and upperBinId
1260
+ */
1261
+ function chunkBinRangeWithInitialSize(minBinId, maxBinId, initialChunkSize, subsequentChunkSize) {
1262
+ const chunkedBinRange = [];
1263
+ let startBinId = minBinId;
1264
+ let isFirstChunk = true;
1265
+ while (startBinId <= maxBinId) {
1266
+ const currentChunkSize = isFirstChunk ? initialChunkSize : subsequentChunkSize;
1267
+ const endBinId = Math.min(startBinId + currentChunkSize - 1, maxBinId);
1268
+ chunkedBinRange.push({
1269
+ lowerBinId: startBinId,
1270
+ upperBinId: endBinId,
1271
+ });
1272
+ startBinId += currentChunkSize;
1273
+ isFirstChunk = false;
1274
+ }
1275
+ return chunkedBinRange;
1276
+ }
1277
+ /**
1278
+ * Chunks deposit parameters with a custom initial chunk size.
1279
+ *
1280
+ * This is useful when the first deposit can accommodate a larger bin range
1281
+ * (e.g., 150 bins for a fresh position) but subsequent deposits need smaller
1282
+ * chunks due to compute limits.
1283
+ *
1284
+ * @param params - The liquidity strategy parameters
1285
+ * @param minDeltaId - Minimum delta from active bin
1286
+ * @param maxDeltaId - Maximum delta from active bin
1287
+ * @param activeId - The current active bin ID
1288
+ * @param binStep - The bin step of the pool
1289
+ * @param initialChunkSize - Size of the first chunk (e.g., 150 bins)
1290
+ * @param subsequentChunkSize - Size of subsequent chunks (e.g., 69 bins)
1291
+ * @param favorXInActiveBin - Whether to favor token X in the active bin
1292
+ * @returns Array of chunked deposit parameters
1293
+ */
1294
+ function chunkDepositParametersWithInitialSize(params, minDeltaId, maxDeltaId, activeId, binStep, initialChunkSize, subsequentChunkSize, favorXInActiveBin = false) {
1295
+ // Convert to absolute bin IDs for chunking
1296
+ const lowerBinId = activeId.add(minDeltaId).toNumber();
1297
+ const upperBinId = activeId.add(maxDeltaId).toNumber();
1298
+ // Chunk the bin range with initial size
1299
+ const chunks = chunkBinRangeWithInitialSize(lowerBinId, upperBinId, initialChunkSize, subsequentChunkSize);
1300
+ // For each chunk, calculate the deposit parameters
1301
+ const result = [];
1302
+ for (const chunk of chunks) {
1303
+ const chunkMinDeltaId = new bn_js_1.default(chunk.lowerBinId).sub(activeId);
1304
+ const chunkMaxDeltaId = new bn_js_1.default(chunk.upperBinId).sub(activeId);
1305
+ // Reset uninvolved params for this chunk
1306
+ const chunkParams = resetUninvolvedLiquidityParams(chunkMinDeltaId, chunkMaxDeltaId, favorXInActiveBin, params);
1307
+ // Calculate amounts for this chunk using toAmountIntoBins
1308
+ const binsAmounts = toAmountIntoBins(activeId, chunkMinDeltaId, chunkMaxDeltaId, chunkParams.deltaX, chunkParams.deltaY, chunkParams.x0, chunkParams.y0, binStep, favorXInActiveBin);
1309
+ // Sum up amounts
1310
+ const { totalXAmount, totalYAmount } = binsAmounts.reduce((acc, bin) => ({
1311
+ totalXAmount: acc.totalXAmount.add(bin.amountX),
1312
+ totalYAmount: acc.totalYAmount.add(bin.amountY),
1313
+ }), { totalXAmount: new bn_js_1.default(0), totalYAmount: new bn_js_1.default(0) });
1314
+ result.push({
1315
+ lowerBinId: chunk.lowerBinId,
1316
+ upperBinId: chunk.upperBinId,
1317
+ minDeltaId: chunkMinDeltaId,
1318
+ maxDeltaId: chunkMaxDeltaId,
1319
+ params: chunkParams,
1320
+ maxAmountX: totalXAmount,
1321
+ maxAmountY: totalYAmount,
1322
+ });
1323
+ }
1324
+ return result;
1325
+ }