@paraswap/dex-lib 2.42.3 → 2.42.4-baseswap

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 (99) hide show
  1. package/build/dex/balancer-v1/balancer-v1.js +21 -10
  2. package/build/dex/balancer-v1/balancer-v1.js.map +1 -1
  3. package/build/dex/balancer-v1/config.d.ts +2 -1
  4. package/build/dex/balancer-v1/config.js +3 -2
  5. package/build/dex/balancer-v1/config.js.map +1 -1
  6. package/build/dex/baseswap-v3/baseswap-v3-factory.d.ts +26 -0
  7. package/build/dex/baseswap-v3/baseswap-v3-factory.js +44 -0
  8. package/build/dex/baseswap-v3/baseswap-v3-factory.js.map +1 -0
  9. package/build/dex/baseswap-v3/baseswap-v3-pool.d.ts +46 -0
  10. package/build/dex/baseswap-v3/baseswap-v3-pool.js +312 -0
  11. package/build/dex/baseswap-v3/baseswap-v3-pool.js.map +1 -0
  12. package/build/dex/baseswap-v3/baseswap-v3.d.ts +71 -0
  13. package/build/dex/baseswap-v3/baseswap-v3.js +752 -0
  14. package/build/dex/baseswap-v3/baseswap-v3.js.map +1 -0
  15. package/build/dex/baseswap-v3/config.d.ts +9 -0
  16. package/build/dex/baseswap-v3/config.js +48 -0
  17. package/build/dex/baseswap-v3/config.js.map +1 -0
  18. package/build/dex/baseswap-v3/constants.d.ts +28 -0
  19. package/build/dex/baseswap-v3/constants.js +35 -0
  20. package/build/dex/baseswap-v3/constants.js.map +1 -0
  21. package/build/dex/baseswap-v3/contract-math/BitMath.d.ts +4 -0
  22. package/build/dex/baseswap-v3/contract-math/BitMath.js +93 -0
  23. package/build/dex/baseswap-v3/contract-math/BitMath.js.map +1 -0
  24. package/build/dex/baseswap-v3/contract-math/FixedPoint128.d.ts +3 -0
  25. package/build/dex/baseswap-v3/contract-math/FixedPoint128.js +8 -0
  26. package/build/dex/baseswap-v3/contract-math/FixedPoint128.js.map +1 -0
  27. package/build/dex/baseswap-v3/contract-math/FixedPoint96.d.ts +4 -0
  28. package/build/dex/baseswap-v3/contract-math/FixedPoint96.js +9 -0
  29. package/build/dex/baseswap-v3/contract-math/FixedPoint96.js.map +1 -0
  30. package/build/dex/baseswap-v3/contract-math/FullMath.d.ts +4 -0
  31. package/build/dex/baseswap-v3/contract-math/FullMath.js +19 -0
  32. package/build/dex/baseswap-v3/contract-math/FullMath.js.map +1 -0
  33. package/build/dex/baseswap-v3/contract-math/LiquidityMath.d.ts +3 -0
  34. package/build/dex/baseswap-v3/contract-math/LiquidityMath.js +22 -0
  35. package/build/dex/baseswap-v3/contract-math/LiquidityMath.js.map +1 -0
  36. package/build/dex/baseswap-v3/contract-math/Oracle.d.ts +10 -0
  37. package/build/dex/baseswap-v3/contract-math/Oracle.js +133 -0
  38. package/build/dex/baseswap-v3/contract-math/Oracle.js.map +1 -0
  39. package/build/dex/baseswap-v3/contract-math/SqrtPriceMath.d.ts +10 -0
  40. package/build/dex/baseswap-v3/contract-math/SqrtPriceMath.js +91 -0
  41. package/build/dex/baseswap-v3/contract-math/SqrtPriceMath.js.map +1 -0
  42. package/build/dex/baseswap-v3/contract-math/SwapMath.d.ts +8 -0
  43. package/build/dex/baseswap-v3/contract-math/SwapMath.js +70 -0
  44. package/build/dex/baseswap-v3/contract-math/SwapMath.js.map +1 -0
  45. package/build/dex/baseswap-v3/contract-math/Tick.d.ts +7 -0
  46. package/build/dex/baseswap-v3/contract-math/Tick.js +45 -0
  47. package/build/dex/baseswap-v3/contract-math/Tick.js.map +1 -0
  48. package/build/dex/baseswap-v3/contract-math/TickBitMap.d.ts +7 -0
  49. package/build/dex/baseswap-v3/contract-math/TickBitMap.js +84 -0
  50. package/build/dex/baseswap-v3/contract-math/TickBitMap.js.map +1 -0
  51. package/build/dex/baseswap-v3/contract-math/TickMath.d.ts +8 -0
  52. package/build/dex/baseswap-v3/contract-math/TickMath.js +162 -0
  53. package/build/dex/baseswap-v3/contract-math/TickMath.js.map +1 -0
  54. package/build/dex/baseswap-v3/contract-math/UnsafeMath.d.ts +3 -0
  55. package/build/dex/baseswap-v3/contract-math/UnsafeMath.js +10 -0
  56. package/build/dex/baseswap-v3/contract-math/UnsafeMath.js.map +1 -0
  57. package/build/dex/baseswap-v3/contract-math/uniswap-v3-math.d.ts +37 -0
  58. package/build/dex/baseswap-v3/contract-math/uniswap-v3-math.js +393 -0
  59. package/build/dex/baseswap-v3/contract-math/uniswap-v3-math.js.map +1 -0
  60. package/build/dex/baseswap-v3/contract-math/utils.d.ts +7 -0
  61. package/build/dex/baseswap-v3/contract-math/utils.js +42 -0
  62. package/build/dex/baseswap-v3/contract-math/utils.js.map +1 -0
  63. package/build/dex/baseswap-v3/types.d.ts +164 -0
  64. package/build/dex/baseswap-v3/types.js +9 -0
  65. package/build/dex/baseswap-v3/types.js.map +1 -0
  66. package/build/dex/baseswap-v3/utils.d.ts +6 -0
  67. package/build/dex/baseswap-v3/utils.js +65 -0
  68. package/build/dex/baseswap-v3/utils.js.map +1 -0
  69. package/build/dex/index.js +2 -0
  70. package/build/dex/index.js.map +1 -1
  71. package/package.json +1 -1
  72. package/src/dex/balancer-v1/balancer-v1.ts +32 -13
  73. package/src/dex/balancer-v1/config.ts +2 -1
  74. package/src/dex/baseswap-v3/baseswap-v3-e2e.test.ts +129 -0
  75. package/src/dex/baseswap-v3/baseswap-v3-events.test.ts +141 -0
  76. package/src/dex/baseswap-v3/baseswap-v3-factory.ts +73 -0
  77. package/src/dex/baseswap-v3/baseswap-v3-integration.test.ts +472 -0
  78. package/src/dex/baseswap-v3/baseswap-v3-pool.ts +519 -0
  79. package/src/dex/baseswap-v3/baseswap-v3.ts +1243 -0
  80. package/src/dex/baseswap-v3/config.ts +53 -0
  81. package/src/dex/baseswap-v3/constants.ts +41 -0
  82. package/src/dex/baseswap-v3/contract-math/BitMath.ts +90 -0
  83. package/src/dex/baseswap-v3/contract-math/FixedPoint128.ts +3 -0
  84. package/src/dex/baseswap-v3/contract-math/FixedPoint96.ts +4 -0
  85. package/src/dex/baseswap-v3/contract-math/FullMath.ts +30 -0
  86. package/src/dex/baseswap-v3/contract-math/LiquidityMath.ts +17 -0
  87. package/src/dex/baseswap-v3/contract-math/Oracle.ts +226 -0
  88. package/src/dex/baseswap-v3/contract-math/SqrtPriceMath.ts +226 -0
  89. package/src/dex/baseswap-v3/contract-math/SwapMath.ts +139 -0
  90. package/src/dex/baseswap-v3/contract-math/Tick.ts +82 -0
  91. package/src/dex/baseswap-v3/contract-math/TickBitMap.ts +123 -0
  92. package/src/dex/baseswap-v3/contract-math/TickMath.ts +211 -0
  93. package/src/dex/baseswap-v3/contract-math/UnsafeMath.ts +5 -0
  94. package/src/dex/baseswap-v3/contract-math/uniswap-v3-math.ts +666 -0
  95. package/src/dex/baseswap-v3/contract-math/utils.ts +55 -0
  96. package/src/dex/baseswap-v3/types.ts +191 -0
  97. package/src/dex/baseswap-v3/utils.ts +83 -0
  98. package/src/dex/index.ts +2 -0
  99. package/tests/constants-e2e.ts +1 -1
@@ -0,0 +1,666 @@
1
+ import _ from 'lodash';
2
+ import { OutputResult, PoolState, Slot0, TickInfo } from '../types';
3
+ import { LiquidityMath } from './LiquidityMath';
4
+ import { Oracle } from './Oracle';
5
+ import { SqrtPriceMath } from './SqrtPriceMath';
6
+ import { SwapMath } from './SwapMath';
7
+ import { Tick } from './Tick';
8
+ import { TickBitMap } from './TickBitMap';
9
+ import { TickMath } from './TickMath';
10
+ import { _require } from '../../../utils';
11
+ import { DeepReadonly } from 'ts-essentials';
12
+ import { NumberAsString, SwapSide } from '@paraswap/core';
13
+ import { BI_MAX_INT } from '../../../bigint-constants';
14
+ import {
15
+ MAX_PRICING_COMPUTATION_STEPS_ALLOWED,
16
+ OUT_OF_RANGE_ERROR_POSTFIX,
17
+ } from '../constants';
18
+
19
+ type ModifyPositionParams = {
20
+ tickLower: bigint;
21
+ tickUpper: bigint;
22
+ liquidityDelta: bigint;
23
+ };
24
+
25
+ export type PriceComputationState = {
26
+ amountSpecifiedRemaining: bigint;
27
+ amountCalculated: bigint;
28
+ sqrtPriceX96: bigint;
29
+ tick: bigint;
30
+ protocolFee: bigint;
31
+ liquidity: bigint;
32
+ isFirstCycleState: boolean;
33
+ };
34
+
35
+ export type PriceComputationCache = {
36
+ liquidityStart: bigint;
37
+ blockTimestamp: bigint;
38
+ feeProtocol: bigint;
39
+ secondsPerLiquidityCumulativeX128: bigint;
40
+ tickCumulative: bigint;
41
+ computedLatestObservation: boolean;
42
+ tickCount: number;
43
+ };
44
+
45
+ export function _updatePriceComputationObjects<
46
+ T extends PriceComputationState | PriceComputationCache,
47
+ >(toUpdate: T, updateBy: T) {
48
+ for (const k of Object.keys(updateBy) as (keyof T)[]) {
49
+ toUpdate[k] = updateBy[k];
50
+ }
51
+ }
52
+
53
+ function _priceComputationCycles(
54
+ poolState: DeepReadonly<PoolState>,
55
+ ticksCopy: Record<NumberAsString, TickInfo>,
56
+ slot0Start: Slot0,
57
+ state: PriceComputationState,
58
+ cache: PriceComputationCache,
59
+ sqrtPriceLimitX96: bigint,
60
+ zeroForOne: boolean,
61
+ exactInput: boolean,
62
+ ): [
63
+ // result
64
+ PriceComputationState,
65
+ // Latest calculated full cycle state we can use for bigger amounts
66
+ {
67
+ latestFullCycleState: PriceComputationState;
68
+ latestFullCycleCache: PriceComputationCache;
69
+ },
70
+ ] {
71
+ const latestFullCycleState: PriceComputationState = { ...state };
72
+
73
+ if (cache.tickCount == 0) {
74
+ cache.tickCount = 1;
75
+ }
76
+ const latestFullCycleCache: PriceComputationCache = { ...cache };
77
+
78
+ // We save tick before any change. Later we use this to restore
79
+ // state before last step
80
+ let lastTicksCopy: { index: number; tick: TickInfo } | undefined;
81
+
82
+ let i = 0;
83
+ for (
84
+ ;
85
+ state.amountSpecifiedRemaining !== 0n &&
86
+ state.sqrtPriceX96 !== sqrtPriceLimitX96;
87
+ ++i
88
+ ) {
89
+ if (
90
+ latestFullCycleCache.tickCount + i >
91
+ MAX_PRICING_COMPUTATION_STEPS_ALLOWED
92
+ ) {
93
+ state.amountSpecifiedRemaining = 0n;
94
+ state.amountCalculated = 0n;
95
+ break;
96
+ }
97
+
98
+ const step = {
99
+ sqrtPriceStartX96: 0n,
100
+ tickNext: 0n,
101
+ initialized: false,
102
+ sqrtPriceNextX96: 0n,
103
+ amountIn: 0n,
104
+ amountOut: 0n,
105
+ feeAmount: 0n,
106
+ };
107
+
108
+ step.sqrtPriceStartX96 = state.sqrtPriceX96;
109
+
110
+ try {
111
+ [step.tickNext, step.initialized] =
112
+ TickBitMap.nextInitializedTickWithinOneWord(
113
+ poolState,
114
+ state.tick,
115
+ poolState.tickSpacing,
116
+ zeroForOne,
117
+ true,
118
+ );
119
+ } catch (e) {
120
+ if (
121
+ e instanceof Error &&
122
+ e.message.endsWith(OUT_OF_RANGE_ERROR_POSTFIX)
123
+ ) {
124
+ state.amountSpecifiedRemaining = 0n;
125
+ state.amountCalculated = 0n;
126
+ break;
127
+ }
128
+ throw e;
129
+ }
130
+
131
+ if (step.tickNext < TickMath.MIN_TICK) {
132
+ step.tickNext = TickMath.MIN_TICK;
133
+ } else if (step.tickNext > TickMath.MAX_TICK) {
134
+ step.tickNext = TickMath.MAX_TICK;
135
+ }
136
+
137
+ step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
138
+
139
+ const swapStepResult = SwapMath.computeSwapStep(
140
+ state.sqrtPriceX96,
141
+ (
142
+ zeroForOne
143
+ ? step.sqrtPriceNextX96 < sqrtPriceLimitX96
144
+ : step.sqrtPriceNextX96 > sqrtPriceLimitX96
145
+ )
146
+ ? sqrtPriceLimitX96
147
+ : step.sqrtPriceNextX96,
148
+ state.liquidity,
149
+ state.amountSpecifiedRemaining,
150
+ poolState.fee,
151
+ );
152
+
153
+ state.sqrtPriceX96 = swapStepResult.sqrtRatioNextX96;
154
+ step.amountIn = swapStepResult.amountIn;
155
+ step.amountOut = swapStepResult.amountOut;
156
+ step.feeAmount = swapStepResult.feeAmount;
157
+
158
+ if (exactInput) {
159
+ state.amountSpecifiedRemaining -= step.amountIn + step.feeAmount;
160
+ state.amountCalculated = state.amountCalculated - step.amountOut;
161
+ } else {
162
+ state.amountSpecifiedRemaining += step.amountOut;
163
+ state.amountCalculated =
164
+ state.amountCalculated + step.amountIn + step.feeAmount;
165
+ }
166
+
167
+ if (cache.feeProtocol > 0n) {
168
+ const delta = step.feeAmount / cache.feeProtocol;
169
+ step.feeAmount -= delta;
170
+ state.protocolFee += delta;
171
+ }
172
+
173
+ if (state.sqrtPriceX96 === step.sqrtPriceNextX96) {
174
+ if (step.initialized) {
175
+ if (!cache.computedLatestObservation) {
176
+ [cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128] =
177
+ Oracle.observeSingle(
178
+ poolState,
179
+ cache.blockTimestamp,
180
+ 0n,
181
+ slot0Start.tick,
182
+ slot0Start.observationIndex,
183
+ cache.liquidityStart,
184
+ slot0Start.observationCardinality,
185
+ );
186
+ cache.computedLatestObservation = true;
187
+ }
188
+
189
+ if (state.amountSpecifiedRemaining === 0n) {
190
+ const castTickNext = Number(step.tickNext);
191
+ lastTicksCopy = {
192
+ index: castTickNext,
193
+ tick: { ...ticksCopy[castTickNext] },
194
+ };
195
+ }
196
+
197
+ let liquidityNet = Tick.cross(
198
+ ticksCopy,
199
+ step.tickNext,
200
+ cache.secondsPerLiquidityCumulativeX128,
201
+ cache.tickCumulative,
202
+ cache.blockTimestamp,
203
+ );
204
+ if (zeroForOne) liquidityNet = -liquidityNet;
205
+
206
+ state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet);
207
+ }
208
+
209
+ state.tick = zeroForOne ? step.tickNext - 1n : step.tickNext;
210
+ } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) {
211
+ state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);
212
+ }
213
+
214
+ if (state.amountSpecifiedRemaining !== 0n) {
215
+ _updatePriceComputationObjects(latestFullCycleState, state);
216
+ _updatePriceComputationObjects(latestFullCycleCache, cache);
217
+ // If it last cycle, check if ticks were changed and then restore previous state
218
+ // for next calculations
219
+ } else if (lastTicksCopy !== undefined) {
220
+ ticksCopy[lastTicksCopy.index] = lastTicksCopy.tick;
221
+ }
222
+ }
223
+
224
+ if (i > 1) {
225
+ latestFullCycleCache.tickCount += i - 1;
226
+ }
227
+
228
+ if (state.amountSpecifiedRemaining !== 0n) {
229
+ state.amountSpecifiedRemaining = 0n;
230
+ state.amountCalculated = 0n;
231
+ }
232
+
233
+ return [state, { latestFullCycleState, latestFullCycleCache }];
234
+ }
235
+
236
+ class UniswapV3Math {
237
+ queryOutputs(
238
+ poolState: DeepReadonly<PoolState>,
239
+ // Amounts must increase
240
+ amounts: bigint[],
241
+ zeroForOne: boolean,
242
+ side: SwapSide,
243
+ ): OutputResult {
244
+ const slot0Start = poolState.slot0;
245
+
246
+ const isSell = side === SwapSide.SELL;
247
+
248
+ // While calculating, ticks are changing, so to not change the actual state,
249
+ // we use copy
250
+ const ticksCopy = _.cloneDeep(poolState.ticks);
251
+
252
+ const sqrtPriceLimitX96 = zeroForOne
253
+ ? TickMath.MIN_SQRT_RATIO + 1n
254
+ : TickMath.MAX_SQRT_RATIO - 1n;
255
+
256
+ const cache: PriceComputationCache = {
257
+ liquidityStart: poolState.liquidity,
258
+ blockTimestamp: this._blockTimestamp(poolState),
259
+ feeProtocol: zeroForOne
260
+ ? slot0Start.feeProtocol % 16n
261
+ : slot0Start.feeProtocol >> 4n,
262
+ secondsPerLiquidityCumulativeX128: 0n,
263
+ tickCumulative: 0n,
264
+ computedLatestObservation: false,
265
+ tickCount: 0,
266
+ };
267
+
268
+ const state: PriceComputationState = {
269
+ // Will be overwritten later
270
+ amountSpecifiedRemaining: 0n,
271
+ amountCalculated: 0n,
272
+ sqrtPriceX96: slot0Start.sqrtPriceX96,
273
+ tick: slot0Start.tick,
274
+ protocolFee: 0n,
275
+ liquidity: cache.liquidityStart,
276
+ isFirstCycleState: true,
277
+ };
278
+
279
+ let isOutOfRange = false;
280
+ let previousAmount = 0n;
281
+
282
+ const outputs = new Array(amounts.length);
283
+ const tickCounts = new Array(amounts.length);
284
+ for (const [i, amount] of amounts.entries()) {
285
+ if (amount === 0n) {
286
+ outputs[i] = 0n;
287
+ tickCounts[i] = 0;
288
+ continue;
289
+ }
290
+
291
+ const amountSpecified = isSell
292
+ ? BigInt.asIntN(256, amount)
293
+ : -BigInt.asIntN(256, amount);
294
+
295
+ if (state.isFirstCycleState) {
296
+ // Set first non zero amount
297
+ state.amountSpecifiedRemaining = amountSpecified;
298
+ state.isFirstCycleState = false;
299
+ } else {
300
+ state.amountSpecifiedRemaining =
301
+ amountSpecified - (previousAmount - state.amountSpecifiedRemaining);
302
+ }
303
+
304
+ const exactInput = amountSpecified > 0n;
305
+
306
+ _require(
307
+ zeroForOne
308
+ ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 &&
309
+ sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO
310
+ : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 &&
311
+ sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,
312
+ 'SPL',
313
+ { zeroForOne, sqrtPriceLimitX96, slot0Start },
314
+ 'zeroForOne ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO',
315
+ );
316
+
317
+ if (!isOutOfRange) {
318
+ const [finalState, { latestFullCycleState, latestFullCycleCache }] =
319
+ _priceComputationCycles(
320
+ poolState,
321
+ ticksCopy,
322
+ slot0Start,
323
+ state,
324
+ cache,
325
+ sqrtPriceLimitX96,
326
+ zeroForOne,
327
+ exactInput,
328
+ );
329
+ if (
330
+ finalState.amountSpecifiedRemaining === 0n &&
331
+ finalState.amountCalculated === 0n
332
+ ) {
333
+ isOutOfRange = true;
334
+ outputs[i] = 0n;
335
+ tickCounts[i] = 0;
336
+ continue;
337
+ }
338
+
339
+ // We use it on next step to correct state.amountSpecifiedRemaining
340
+ previousAmount = amountSpecified;
341
+
342
+ // First extract calculated values
343
+ const [amount0, amount1] =
344
+ zeroForOne === exactInput
345
+ ? [
346
+ amountSpecified - finalState.amountSpecifiedRemaining,
347
+ finalState.amountCalculated,
348
+ ]
349
+ : [
350
+ finalState.amountCalculated,
351
+ amountSpecified - finalState.amountSpecifiedRemaining,
352
+ ];
353
+
354
+ // Update for next amount
355
+ _updatePriceComputationObjects(state, latestFullCycleState);
356
+ _updatePriceComputationObjects(cache, latestFullCycleCache);
357
+
358
+ if (isSell) {
359
+ outputs[i] = BigInt.asUintN(256, -(zeroForOne ? amount1 : amount0));
360
+ tickCounts[i] = latestFullCycleCache.tickCount;
361
+ continue;
362
+ } else {
363
+ outputs[i] = zeroForOne
364
+ ? BigInt.asUintN(256, amount0)
365
+ : BigInt.asUintN(256, amount1);
366
+ tickCounts[i] = latestFullCycleCache.tickCount;
367
+ continue;
368
+ }
369
+ } else {
370
+ outputs[i] = 0n;
371
+ tickCounts[i] = 0;
372
+ }
373
+ }
374
+
375
+ return {
376
+ outputs,
377
+ tickCounts,
378
+ };
379
+ }
380
+
381
+ swapFromEvent(
382
+ poolState: PoolState,
383
+ newSqrtPriceX96: bigint,
384
+ newTick: bigint,
385
+ newLiquidity: bigint,
386
+ zeroForOne: boolean,
387
+ ): void {
388
+ const slot0Start = poolState.slot0;
389
+
390
+ const cache = {
391
+ liquidityStart: poolState.liquidity,
392
+ blockTimestamp: this._blockTimestamp(poolState),
393
+ feeProtocol: 0n,
394
+ secondsPerLiquidityCumulativeX128: 0n,
395
+ tickCumulative: 0n,
396
+ computedLatestObservation: false,
397
+ };
398
+
399
+ const state = {
400
+ // Because I don't have the exact amount user used, set this number to MAX_NUMBER to proceed
401
+ // with calculations. I think it is not a problem since in loop I don't rely on this value
402
+ amountSpecifiedRemaining: BI_MAX_INT,
403
+ amountCalculated: 0n,
404
+ sqrtPriceX96: slot0Start.sqrtPriceX96,
405
+ tick: slot0Start.tick,
406
+ protocolFee: 0n,
407
+ liquidity: cache.liquidityStart,
408
+ };
409
+
410
+ // Because I didn't have all variables, adapted loop stop with state.tick !== newTick
411
+ // condition. This cycle need only to calculate Tick.cross() function values
412
+ // It means that we are interested in cycling only if state.tick !== newTick
413
+ // When they become equivalent, we proceed with state updating part as normal
414
+ // And if assumptions regarding this cycle are correct, we don't need to process
415
+ // the last cycle when state.tick === newTick
416
+ while (state.tick !== newTick && state.sqrtPriceX96 !== newSqrtPriceX96) {
417
+ const step = {
418
+ sqrtPriceStartX96: 0n,
419
+ tickNext: 0n,
420
+ initialized: false,
421
+ sqrtPriceNextX96: 0n,
422
+ amountIn: 0n,
423
+ amountOut: 0n,
424
+ feeAmount: 0n,
425
+ };
426
+
427
+ step.sqrtPriceStartX96 = state.sqrtPriceX96;
428
+
429
+ [step.tickNext, step.initialized] =
430
+ TickBitMap.nextInitializedTickWithinOneWord(
431
+ poolState,
432
+ state.tick,
433
+ poolState.tickSpacing,
434
+ zeroForOne,
435
+ false,
436
+ );
437
+
438
+ if (step.tickNext < TickMath.MIN_TICK) {
439
+ step.tickNext = TickMath.MIN_TICK;
440
+ } else if (step.tickNext > TickMath.MAX_TICK) {
441
+ step.tickNext = TickMath.MAX_TICK;
442
+ }
443
+
444
+ step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
445
+
446
+ const swapStepResult = SwapMath.computeSwapStep(
447
+ state.sqrtPriceX96,
448
+ (
449
+ zeroForOne
450
+ ? step.sqrtPriceNextX96 < newSqrtPriceX96
451
+ : step.sqrtPriceNextX96 > newSqrtPriceX96
452
+ )
453
+ ? newSqrtPriceX96
454
+ : step.sqrtPriceNextX96,
455
+ state.liquidity,
456
+ state.amountSpecifiedRemaining,
457
+ poolState.fee,
458
+ );
459
+
460
+ state.sqrtPriceX96 = swapStepResult.sqrtRatioNextX96;
461
+
462
+ if (state.sqrtPriceX96 == step.sqrtPriceNextX96) {
463
+ if (step.initialized) {
464
+ if (!cache.computedLatestObservation) {
465
+ [cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128] =
466
+ Oracle.observeSingle(
467
+ poolState,
468
+ cache.blockTimestamp,
469
+ 0n,
470
+ slot0Start.tick,
471
+ slot0Start.observationIndex,
472
+ cache.liquidityStart,
473
+ slot0Start.observationCardinality,
474
+ );
475
+ cache.computedLatestObservation = true;
476
+ }
477
+
478
+ let liquidityNet = Tick.cross(
479
+ poolState.ticks,
480
+ step.tickNext,
481
+ cache.secondsPerLiquidityCumulativeX128,
482
+ cache.tickCumulative,
483
+ cache.blockTimestamp,
484
+ );
485
+
486
+ if (zeroForOne) liquidityNet = -liquidityNet;
487
+
488
+ state.liquidity = LiquidityMath.addDelta(
489
+ state.liquidity,
490
+ liquidityNet,
491
+ );
492
+ }
493
+
494
+ state.tick = zeroForOne ? step.tickNext - 1n : step.tickNext;
495
+ } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) {
496
+ state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);
497
+ }
498
+ }
499
+
500
+ if (slot0Start.tick !== newTick) {
501
+ const [observationIndex, observationCardinality] = Oracle.write(
502
+ poolState,
503
+ slot0Start.observationIndex,
504
+ this._blockTimestamp(poolState),
505
+ slot0Start.tick,
506
+ poolState.liquidity,
507
+ slot0Start.observationCardinality,
508
+ slot0Start.observationCardinalityNext,
509
+ );
510
+
511
+ [
512
+ poolState.slot0.sqrtPriceX96,
513
+ poolState.slot0.tick,
514
+ poolState.slot0.observationIndex,
515
+ poolState.slot0.observationCardinality,
516
+ ] = [newSqrtPriceX96, newTick, observationIndex, observationCardinality];
517
+ } else {
518
+ poolState.slot0.sqrtPriceX96 = newSqrtPriceX96;
519
+ }
520
+
521
+ if (poolState.liquidity !== newLiquidity)
522
+ poolState.liquidity = newLiquidity;
523
+ }
524
+
525
+ _modifyPosition(
526
+ state: PoolState,
527
+ params: ModifyPositionParams,
528
+ ): [bigint, bigint] {
529
+ const _slot0 = state.slot0;
530
+
531
+ this._updatePosition(
532
+ state,
533
+ params.tickLower,
534
+ params.tickUpper,
535
+ params.liquidityDelta,
536
+ _slot0.tick,
537
+ );
538
+
539
+ let amount0 = 0n;
540
+ let amount1 = 0n;
541
+ if (params.liquidityDelta !== 0n) {
542
+ if (_slot0.tick < params.tickLower) {
543
+ amount0 = SqrtPriceMath._getAmount0DeltaO(
544
+ TickMath.getSqrtRatioAtTick(params.tickLower),
545
+ TickMath.getSqrtRatioAtTick(params.tickUpper),
546
+ params.liquidityDelta,
547
+ );
548
+ } else if (_slot0.tick < params.tickUpper) {
549
+ const liquidityBefore = state.liquidity;
550
+
551
+ [state.slot0.observationIndex, state.slot0.observationCardinality] =
552
+ Oracle.write(
553
+ state,
554
+ _slot0.observationIndex,
555
+ this._blockTimestamp(state),
556
+ _slot0.tick,
557
+ liquidityBefore,
558
+ _slot0.observationCardinality,
559
+ _slot0.observationCardinalityNext,
560
+ );
561
+
562
+ amount0 = SqrtPriceMath._getAmount0DeltaO(
563
+ _slot0.sqrtPriceX96,
564
+ TickMath.getSqrtRatioAtTick(params.tickUpper),
565
+ params.liquidityDelta,
566
+ );
567
+ amount1 = SqrtPriceMath._getAmount1DeltaO(
568
+ TickMath.getSqrtRatioAtTick(params.tickLower),
569
+ _slot0.sqrtPriceX96,
570
+ params.liquidityDelta,
571
+ );
572
+
573
+ state.liquidity = LiquidityMath.addDelta(
574
+ liquidityBefore,
575
+ params.liquidityDelta,
576
+ );
577
+ } else {
578
+ amount1 = SqrtPriceMath._getAmount1DeltaO(
579
+ TickMath.getSqrtRatioAtTick(params.tickLower),
580
+ TickMath.getSqrtRatioAtTick(params.tickUpper),
581
+ params.liquidityDelta,
582
+ );
583
+ }
584
+ }
585
+ return [amount0, amount1];
586
+ }
587
+
588
+ private _isTickToProcess(state: PoolState, tick: bigint): boolean {
589
+ return tick >= state.lowestKnownTick && tick <= state.highestKnownTick;
590
+ }
591
+
592
+ private _updatePosition(
593
+ state: PoolState,
594
+ tickLower: bigint,
595
+ tickUpper: bigint,
596
+ liquidityDelta: bigint,
597
+ tick: bigint,
598
+ ): void {
599
+ // if we need to update the ticks, do it
600
+ let flippedLower = false;
601
+ let flippedUpper = false;
602
+ if (liquidityDelta !== 0n) {
603
+ const time = this._blockTimestamp(state);
604
+ const [tickCumulative, secondsPerLiquidityCumulativeX128] =
605
+ Oracle.observeSingle(
606
+ state,
607
+ time,
608
+ 0n,
609
+ state.slot0.tick,
610
+ state.slot0.observationIndex,
611
+ state.liquidity,
612
+ state.slot0.observationCardinality,
613
+ );
614
+
615
+ if (this._isTickToProcess(state, tickLower)) {
616
+ flippedLower = Tick.update(
617
+ state,
618
+ tickLower,
619
+ tick,
620
+ liquidityDelta,
621
+ secondsPerLiquidityCumulativeX128,
622
+ tickCumulative,
623
+ time,
624
+ false,
625
+ state.maxLiquidityPerTick,
626
+ );
627
+ }
628
+ if (this._isTickToProcess(state, tickUpper)) {
629
+ flippedUpper = Tick.update(
630
+ state,
631
+ tickUpper,
632
+ tick,
633
+ liquidityDelta,
634
+ secondsPerLiquidityCumulativeX128,
635
+ tickCumulative,
636
+ time,
637
+ true,
638
+ state.maxLiquidityPerTick,
639
+ );
640
+ }
641
+
642
+ if (flippedLower) {
643
+ TickBitMap.flipTick(state, tickLower, state.tickSpacing);
644
+ }
645
+ if (flippedUpper) {
646
+ TickBitMap.flipTick(state, tickUpper, state.tickSpacing);
647
+ }
648
+ }
649
+
650
+ // clear any tick data that is no longer needed
651
+ if (liquidityDelta < 0n) {
652
+ if (flippedLower) {
653
+ Tick.clear(state, tickLower);
654
+ }
655
+ if (flippedUpper) {
656
+ Tick.clear(state, tickUpper);
657
+ }
658
+ }
659
+ }
660
+
661
+ private _blockTimestamp(state: DeepReadonly<PoolState>) {
662
+ return BigInt.asUintN(32, state.blockTimestamp);
663
+ }
664
+ }
665
+
666
+ export const uniswapV3Math = new UniswapV3Math();
@@ -0,0 +1,55 @@
1
+ import { NumberAsString } from '@paraswap/core';
2
+ import {
3
+ TickBitMapMappingsWithBigNumber,
4
+ TickInfo,
5
+ TickInfoMappingsWithBigNumber,
6
+ } from '../types';
7
+ import { bigIntify } from '../../../utils';
8
+
9
+ export function _mulmod(x: bigint, y: bigint, m: bigint): bigint {
10
+ return m === 0n ? 0n : (x * y) % m;
11
+ }
12
+
13
+ export function _lt(x: bigint, y: bigint) {
14
+ return x < y ? 1n : 0n;
15
+ }
16
+
17
+ export function _gt(x: bigint, y: bigint) {
18
+ return x > y ? 1n : 0n;
19
+ }
20
+
21
+ export function _reduceTickBitmap(
22
+ tickBitmap: Record<NumberAsString, bigint>,
23
+ tickBitmapToReduce: TickBitMapMappingsWithBigNumber[],
24
+ ) {
25
+ return tickBitmapToReduce.reduce<Record<NumberAsString, bigint>>(
26
+ (acc, curr) => {
27
+ const { index, value } = curr;
28
+ acc[index] = bigIntify(value);
29
+ return acc;
30
+ },
31
+ tickBitmap,
32
+ );
33
+ }
34
+
35
+ export function _reduceTicks(
36
+ ticks: Record<NumberAsString, TickInfo>,
37
+ ticksToReduce: TickInfoMappingsWithBigNumber[],
38
+ ) {
39
+ return ticksToReduce.reduce<Record<string, TickInfo>>((acc, curr) => {
40
+ const { index, value } = curr;
41
+ if (value.initialized) {
42
+ acc[index] = {
43
+ liquidityGross: bigIntify(value.liquidityGross),
44
+ liquidityNet: bigIntify(value.liquidityNet),
45
+ tickCumulativeOutside: bigIntify(value.tickCumulativeOutside),
46
+ secondsPerLiquidityOutsideX128: bigIntify(
47
+ value.secondsPerLiquidityOutsideX128,
48
+ ),
49
+ secondsOutside: bigIntify(value.secondsOutside),
50
+ initialized: value.initialized,
51
+ };
52
+ }
53
+ return acc;
54
+ }, ticks);
55
+ }