@paraswap/dex-lib 3.11.5-cables.0 → 3.11.5-fluid-dex-changes.0

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 (41) hide show
  1. package/build/abi/fluid-dex/resolver.abi.json +731 -4
  2. package/build/dex/balancer-v2/balancer-v2.js +0 -1
  3. package/build/dex/balancer-v2/balancer-v2.js.map +1 -1
  4. package/build/dex/bebop/bebop.js +1 -1
  5. package/build/dex/bebop/bebop.js.map +1 -1
  6. package/build/dex/cables/cables.js +10 -2
  7. package/build/dex/cables/cables.js.map +1 -1
  8. package/build/dex/fluid-dex/config.js +1 -1
  9. package/build/dex/fluid-dex/fluid-dex-liquidity-proxy.js +28 -1
  10. package/build/dex/fluid-dex/fluid-dex-liquidity-proxy.js.map +1 -1
  11. package/build/dex/fluid-dex/fluid-dex.d.ts +17 -10
  12. package/build/dex/fluid-dex/fluid-dex.js +218 -67
  13. package/build/dex/fluid-dex/fluid-dex.js.map +1 -1
  14. package/build/dex/fluid-dex/types.d.ts +22 -2
  15. package/build/dex/index.js +0 -2
  16. package/build/dex/index.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/abi/fluid-dex/resolver.abi.json +731 -4
  19. package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +0 -78
  20. package/src/dex/balancer-v2/balancer-v2.ts +0 -2
  21. package/src/dex/bebop/bebop.ts +1 -1
  22. package/src/dex/fluid-dex/config.ts +1 -1
  23. package/src/dex/fluid-dex/fluid-dex-e2e.test.ts +151 -3
  24. package/src/dex/fluid-dex/fluid-dex-events.test.ts +2 -4
  25. package/src/dex/fluid-dex/fluid-dex-integration.test.ts +79 -11
  26. package/src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts +33 -0
  27. package/src/dex/fluid-dex/fluid-dex.ts +302 -104
  28. package/src/dex/fluid-dex/types.ts +25 -1
  29. package/src/dex/index.ts +0 -2
  30. package/tests/constants-e2e.ts +2 -2
  31. package/.env.example +0 -7
  32. package/src/abi/cables/CablesMainnetRFQ.json +0 -1083
  33. package/src/dex/cables/cables-e2e.test.ts +0 -171
  34. package/src/dex/cables/cables-integration.test.ts +0 -319
  35. package/src/dex/cables/cables.ts +0 -911
  36. package/src/dex/cables/config.ts +0 -13
  37. package/src/dex/cables/constants.ts +0 -35
  38. package/src/dex/cables/rate-fetcher.ts +0 -212
  39. package/src/dex/cables/types.ts +0 -128
  40. package/src/dex/cables/validators.test.ts +0 -151
  41. package/src/dex/cables/validators.ts +0 -61
@@ -20,6 +20,8 @@ import {
20
20
  FluidDexData,
21
21
  FluidDexPool,
22
22
  Pool,
23
+ DexLimits,
24
+ TokenLimit,
23
25
  } from './types';
24
26
  import { SimpleExchange } from '../simple-exchange';
25
27
  import FluidDexPoolABI from '../../abi/fluid-dex/fluid-dex.abi.json';
@@ -145,8 +147,6 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
145
147
  side: SwapSide,
146
148
  blockNumber: number,
147
149
  ): Promise<string[]> {
148
- if (side === SwapSide.BUY) return [];
149
-
150
150
  const pool = this.getPoolByTokenPair(srcToken.address, destToken.address);
151
151
  return pool ? [pool.id] : [];
152
152
  }
@@ -187,42 +187,53 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
187
187
  try {
188
188
  if (srcToken.address.toLowerCase() === destToken.address.toLowerCase())
189
189
  return null;
190
-
191
- if (side === SwapSide.BUY) return null;
192
190
  // Get the pool to use.
193
191
  const pool = this.getPoolByTokenPair(srcToken.address, destToken.address);
194
192
  if (!pool) return null;
195
-
196
193
  // Make sure the pool meets the optional limitPools filter.
197
194
  if (limitPools && !limitPools.includes(pool.id)) return null;
198
195
 
199
196
  const liquidityProxyState = await this.liquidityProxy.getStateOrGenerate(
200
197
  blockNumber,
201
198
  );
202
-
203
199
  const currentPoolReserves = liquidityProxyState.poolsReserves.find(
204
200
  poolReserve =>
205
201
  poolReserve.pool.toLowerCase() === pool.address.toLowerCase(),
206
202
  );
207
- if (!currentPoolReserves) return null;
208
-
203
+ if (!currentPoolReserves) {
204
+ return null;
205
+ }
209
206
  const prices = amounts.map(amount => {
210
- return this.swapIn(
211
- srcToken.address.toLowerCase() === pool.token0.toLowerCase(),
212
- amount,
213
- currentPoolReserves.collateralReserves,
214
- currentPoolReserves.debtReserves,
215
- srcToken.decimals,
216
- destToken.decimals,
217
- BigInt(currentPoolReserves.fee),
218
- );
207
+ if (side == SwapSide.SELL) {
208
+ return this.swapIn(
209
+ srcToken.address.toLowerCase() === pool.token0.toLowerCase(),
210
+ amount,
211
+ currentPoolReserves.collateralReserves,
212
+ currentPoolReserves.debtReserves,
213
+ srcToken.decimals,
214
+ destToken.decimals,
215
+ BigInt(currentPoolReserves.fee),
216
+ currentPoolReserves.dexLimits,
217
+ Math.floor(Date.now() / 1000),
218
+ );
219
+ } else {
220
+ return this.swapOut(
221
+ srcToken.address.toLowerCase() === pool.token0.toLowerCase(),
222
+ amount,
223
+ currentPoolReserves.collateralReserves,
224
+ currentPoolReserves.debtReserves,
225
+ srcToken.decimals,
226
+ destToken.decimals,
227
+ BigInt(currentPoolReserves.fee),
228
+ currentPoolReserves.dexLimits,
229
+ Math.floor(Date.now() / 1000),
230
+ );
231
+ }
219
232
  });
220
233
  return [
221
234
  {
222
235
  prices: prices,
223
- unit: getBigIntPow(
224
- (side === SwapSide.SELL ? destToken : srcToken).decimals,
225
- ),
236
+ unit: getBigIntPow(destToken.decimals),
226
237
  data: {},
227
238
  exchange: this.dexKey,
228
239
  poolIdentifier: pool.id,
@@ -237,7 +248,6 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
237
248
  }, ${side}:`,
238
249
  e,
239
250
  );
240
-
241
251
  return null;
242
252
  }
243
253
  }
@@ -259,8 +269,6 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
259
269
  data: FluidDexData,
260
270
  side: SwapSide,
261
271
  ): AdapterExchangeParam {
262
- if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
263
-
264
272
  // Encode here the payload for adapter
265
273
  const payload = '';
266
274
  const pool = this.getPoolByTokenPair(srcToken, destToken);
@@ -293,29 +301,33 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
293
301
  context: Context,
294
302
  executorAddress: Address,
295
303
  ): Promise<DexExchangeParam> {
296
- if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
297
-
298
304
  let args: any;
299
305
  let returnAmountPos: number | undefined;
300
306
 
301
- const method = 'swapIn';
307
+ const method = side == SwapSide.SELL ? 'swapIn' : 'swapOut';
302
308
 
303
309
  returnAmountPos = extractReturnAmountPosition(
304
310
  this.fluidDexPoolIface,
305
311
  method,
306
- 'amountOut_',
312
+ side == SwapSide.SELL ? 'amountOut_' : 'amountIn_',
307
313
  );
308
314
 
309
315
  const pool = this.getPoolByTokenPair(srcToken, destToken);
310
316
 
311
- if (pool!.token0.toLowerCase() !== srcToken.toLowerCase()) {
312
- args = [false, BigInt(srcAmount), BigInt(destAmount), recipient];
317
+ if (side == SwapSide.SELL) {
318
+ if (pool!.token0.toLowerCase() !== srcToken.toLowerCase()) {
319
+ args = [false, BigInt(srcAmount), BigInt(destAmount), recipient];
320
+ } else {
321
+ args = [true, BigInt(srcAmount), BigInt(destAmount), recipient];
322
+ }
313
323
  } else {
314
- args = [true, BigInt(srcAmount), BigInt(destAmount), recipient];
324
+ if (pool!.token0.toLowerCase() !== srcToken.toLowerCase()) {
325
+ args = [false, BigInt(destAmount), BigInt(srcAmount), recipient];
326
+ } else {
327
+ args = [true, BigInt(destAmount), BigInt(srcAmount), recipient];
328
+ }
315
329
  }
316
-
317
330
  const swapData = this.fluidDexPoolIface.encodeFunctionData(method, args);
318
-
319
331
  return {
320
332
  needWrapNative: this.needWrapNative,
321
333
  dexFuncHasRecipient: true,
@@ -335,7 +347,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
335
347
  * @param outDecimals - The number of decimals for the output token.
336
348
  * @returns The calculated output amount (as a BigInt).
337
349
  */
338
- private swapIn(
350
+ public swapIn(
339
351
  swap0To1: boolean,
340
352
  amountIn: bigint,
341
353
  colReserves: CollateralReserves,
@@ -343,6 +355,8 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
343
355
  inDecimals: number,
344
356
  outDecimals: number,
345
357
  fee: bigint,
358
+ currentLimits: DexLimits,
359
+ syncTime: number,
346
360
  ): bigint {
347
361
  if (amountIn === 0n) {
348
362
  return 0n; // Return 0 if input amount is 0
@@ -354,17 +368,19 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
354
368
  return 0n;
355
369
  }
356
370
  const amountInAdjusted =
357
- (amountIn * BigInt(10 ** 12)) / BigInt(10 ** inDecimals);
358
-
371
+ (((amountIn * (this.FEE_100_PERCENT - fee)) / this.FEE_100_PERCENT) *
372
+ BigInt(10 ** 12)) /
373
+ BigInt(10 ** inDecimals);
359
374
  const amountOut = this.swapInAdjusted(
360
375
  swap0To1,
361
- amountInAdjusted, // Convert back to number for internal calculations
376
+ amountInAdjusted,
362
377
  colReserves,
363
378
  debtReserves,
364
- fee,
379
+ outDecimals,
380
+ currentLimits,
381
+ syncTime,
365
382
  );
366
- const result = (amountOut * BigInt(10 ** outDecimals)) / BigInt(10 ** 12);
367
- return result;
383
+ return (amountOut * BigInt(10 ** outDecimals)) / BigInt(10 ** 12);
368
384
  }
369
385
 
370
386
  /**
@@ -375,12 +391,14 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
375
391
  * @param debtReserves - The reserves of the debt pool.
376
392
  * @returns The calculated output amount.
377
393
  */
378
- private swapInAdjusted(
394
+ public swapInAdjusted(
379
395
  swap0To1: boolean,
380
396
  amountToSwap: bigint,
381
397
  colReserves: CollateralReserves,
382
398
  debtReserves: DebtReserves,
383
- fee: bigint,
399
+ outDecimals: number,
400
+ currentLimits: DexLimits,
401
+ syncTime: number,
384
402
  ): bigint {
385
403
  const {
386
404
  token0RealReserves,
@@ -388,7 +406,6 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
388
406
  token0ImaginaryReserves,
389
407
  token1ImaginaryReserves,
390
408
  } = colReserves;
391
-
392
409
  const {
393
410
  token0RealReserves: debtToken0RealReserves,
394
411
  token1RealReserves: debtToken1RealReserves,
@@ -398,26 +415,21 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
398
415
 
399
416
  // Check if all reserves of collateral pool are greater than 0
400
417
  const colPoolEnabled =
401
- token0RealReserves > BigInt(0) &&
402
- token1RealReserves > BigInt(0) &&
403
- token0ImaginaryReserves > BigInt(0) &&
404
- token1ImaginaryReserves > BigInt(0);
418
+ token0RealReserves > 0 &&
419
+ token1RealReserves > 0 &&
420
+ token0ImaginaryReserves > 0 &&
421
+ token1ImaginaryReserves > 0;
405
422
 
406
423
  // Check if all reserves of debt pool are greater than 0
407
424
  const debtPoolEnabled =
408
- debtToken0RealReserves > BigInt(0) &&
409
- debtToken1RealReserves > BigInt(0) &&
410
- debtToken0ImaginaryReserves > BigInt(0) &&
411
- debtToken1ImaginaryReserves > BigInt(0);
425
+ debtToken0RealReserves > 0 &&
426
+ debtToken1RealReserves > 0 &&
427
+ debtToken0ImaginaryReserves > 0 &&
428
+ debtToken1ImaginaryReserves > 0;
412
429
 
413
- let colReserveIn: bigint,
414
- colReserveOut: bigint,
415
- debtReserveIn: bigint,
416
- debtReserveOut: bigint;
417
- let colIReserveIn: bigint,
418
- colIReserveOut: bigint,
419
- debtIReserveIn: bigint,
420
- debtIReserveOut: bigint;
430
+ let colReserveIn, colReserveOut, debtReserveIn, debtReserveOut;
431
+ let colIReserveIn, colIReserveOut, debtIReserveIn, debtIReserveOut;
432
+ let borrowable, withdrawable;
421
433
 
422
434
  if (swap0To1) {
423
435
  colReserveIn = token0RealReserves;
@@ -428,6 +440,14 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
428
440
  debtReserveOut = debtToken1RealReserves;
429
441
  debtIReserveIn = debtToken0ImaginaryReserves;
430
442
  debtIReserveOut = debtToken1ImaginaryReserves;
443
+ borrowable = this.getExpandedLimit(
444
+ syncTime,
445
+ currentLimits.borrowableToken1,
446
+ );
447
+ withdrawable = this.getExpandedLimit(
448
+ syncTime,
449
+ currentLimits.withdrawableToken1,
450
+ );
431
451
  } else {
432
452
  colReserveIn = token1RealReserves;
433
453
  colReserveOut = token0RealReserves;
@@ -437,8 +457,21 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
437
457
  debtReserveOut = debtToken0RealReserves;
438
458
  debtIReserveIn = debtToken1ImaginaryReserves;
439
459
  debtIReserveOut = debtToken0ImaginaryReserves;
460
+ borrowable = this.getExpandedLimit(
461
+ syncTime,
462
+ currentLimits.borrowableToken0,
463
+ );
464
+ withdrawable = this.getExpandedLimit(
465
+ syncTime,
466
+ currentLimits.withdrawableToken0,
467
+ );
440
468
  }
441
469
 
470
+ // bring borrowable and withdrawable from token decimals to 1e12 decimals, same as amounts
471
+ borrowable = (borrowable * BigInt(10 ** 12)) / BigInt(10 ** outDecimals);
472
+ withdrawable =
473
+ (withdrawable * BigInt(10 ** 12)) / BigInt(10 ** outDecimals);
474
+
442
475
  let a: bigint;
443
476
  if (colPoolEnabled && debtPoolEnabled) {
444
477
  a = this.swapRoutingIn(
@@ -449,39 +482,41 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
449
482
  debtIReserveIn,
450
483
  );
451
484
  } else if (debtPoolEnabled) {
452
- a = BigInt(-1); // Route from debt pool
485
+ a = -1n; // Route from debt pool
453
486
  } else if (colPoolEnabled) {
454
- a = amountToSwap + BigInt(1); // Route from collateral pool
487
+ a = amountToSwap + 1n; // Route from collateral pool
455
488
  } else {
456
489
  throw new Error('No pools are enabled');
457
490
  }
458
491
 
459
- let amountOutCollateral = BigInt(0);
460
- let amountOutDebt = BigInt(0);
492
+ let amountOutCollateral = 0n;
493
+ let amountOutDebt = 0n;
494
+ let amountInCollateral = 0n;
495
+ let amountInDebt = 0n;
461
496
 
462
- if (a <= BigInt(0)) {
497
+ if (a <= 0n) {
463
498
  // Entire trade routes through debt pool
499
+ amountInDebt = amountToSwap;
464
500
  amountOutDebt = this.getAmountOut(
465
- this.applyFee(amountToSwap, fee),
501
+ amountToSwap,
466
502
  debtIReserveIn,
467
503
  debtIReserveOut,
468
504
  );
469
505
  } else if (a >= amountToSwap) {
470
506
  // Entire trade routes through collateral pool
507
+ amountInCollateral = amountToSwap;
471
508
  amountOutCollateral = this.getAmountOut(
472
- this.applyFee(amountToSwap, fee),
509
+ amountToSwap,
473
510
  colIReserveIn,
474
511
  colIReserveOut,
475
512
  );
476
513
  } else {
477
514
  // Trade routes through both pools
478
- amountOutCollateral = this.getAmountOut(
479
- this.applyFee(a, fee),
480
- colIReserveIn,
481
- colIReserveOut,
482
- );
515
+ amountInCollateral = a;
516
+ amountOutCollateral = this.getAmountOut(a, colIReserveIn, colIReserveOut);
517
+ amountInDebt = amountToSwap - a;
483
518
  amountOutDebt = this.getAmountOut(
484
- this.applyFee(amountToSwap - a, fee),
519
+ amountInDebt,
485
520
  debtIReserveIn,
486
521
  debtIReserveOut,
487
522
  );
@@ -490,17 +525,91 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
490
525
  if (amountOutDebt > debtReserveOut) {
491
526
  return 0n;
492
527
  }
528
+ if (amountOutDebt > borrowable) {
529
+ return 0n;
530
+ }
493
531
 
494
532
  if (amountOutCollateral > colReserveOut) {
495
533
  return 0n;
496
534
  }
535
+
536
+ if (amountOutCollateral > withdrawable) {
537
+ return 0n;
538
+ }
539
+
540
+ // For price calculations, we'll use a precision factor for bigint division
541
+ const PRECISION = 1000000000000000000000000000n; // 1e27
542
+
543
+ let oldPrice: bigint;
544
+ let newPrice: bigint;
545
+
546
+ if (amountInCollateral > amountInDebt) {
547
+ // new pool price from col pool
548
+ oldPrice = swap0To1
549
+ ? (colIReserveOut * PRECISION) / colIReserveIn
550
+ : (colIReserveIn * PRECISION) / colIReserveOut;
551
+
552
+ newPrice = swap0To1
553
+ ? ((colIReserveOut - amountOutCollateral) * PRECISION) /
554
+ (colIReserveIn + amountInCollateral)
555
+ : ((colIReserveIn + amountInCollateral) * PRECISION) /
556
+ (colIReserveOut - amountOutCollateral);
557
+ } else {
558
+ // new pool price from debt pool
559
+ oldPrice = swap0To1
560
+ ? (debtIReserveOut * PRECISION) / debtIReserveIn
561
+ : (debtIReserveIn * PRECISION) / debtIReserveOut;
562
+
563
+ newPrice = swap0To1
564
+ ? ((debtIReserveOut - amountOutDebt) * PRECISION) /
565
+ (debtIReserveIn + amountInDebt)
566
+ : ((debtIReserveIn + amountInDebt) * PRECISION) /
567
+ (debtIReserveOut - amountOutDebt);
568
+ }
569
+ // Calculate price difference using bigint arithmetic
570
+ const MAX_PRICE_DIFF = 5n; // 5%
571
+ const priceDiff =
572
+ oldPrice > newPrice ? oldPrice - newPrice : newPrice - oldPrice;
573
+ const maxAllowedDiff = (oldPrice * MAX_PRICE_DIFF) / 100n;
574
+
575
+ if (priceDiff > maxAllowedDiff) {
576
+ return 0n;
577
+ }
497
578
  const totalAmountOut = amountOutCollateral + amountOutDebt;
498
579
 
499
580
  return totalAmountOut;
500
581
  }
501
582
 
502
- applyFee(amount: bigint, fee: bigint): bigint {
503
- return (amount * (this.FEE_100_PERCENT - fee)) / this.FEE_100_PERCENT;
583
+ /**
584
+ * Calculates the currently available swappable amount for a token limit considering expansion since last syncTime.
585
+ * @param syncTime - timestamp in seconds when the limits were synced
586
+ * @param limit - token limit object containing available amount, expandsTo amount, and expandDuration
587
+ * @returns The calculated available swappable amount (borrowable or withdrawable)
588
+ */
589
+ public getExpandedLimit(syncTime: number, limit: TokenLimit): bigint {
590
+ const currentTime = Math.floor(Date.now() / 1000); // convert milliseconds to seconds
591
+ const elapsedTime = currentTime - syncTime;
592
+ limit.expandsDuration = limit.expandsDuration || 0n;
593
+ if (elapsedTime < 10n) {
594
+ // if almost no time has elapsed, return available amount
595
+ return limit.available;
596
+ }
597
+
598
+ if (elapsedTime >= limit.expandsDuration) {
599
+ // if duration has passed, return max amount
600
+ return limit.expandsTo;
601
+ }
602
+
603
+ // Calculate expansion ratio using bigint arithmetic
604
+ // Multiply by a large factor for precision in integer arithmetic
605
+ const PRECISION = 1000000;
606
+ const ratio = BigInt(elapsedTime * PRECISION) / limit.expandsDuration;
607
+
608
+ // Calculate expanded amount with precision factor
609
+ const expansion =
610
+ ((limit.expandsTo - limit.available) * ratio) / BigInt(PRECISION);
611
+ const expandedAmount = limit.available + expansion;
612
+ return expandedAmount;
504
613
  }
505
614
 
506
615
  /**
@@ -510,7 +619,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
510
619
  * @param iReserveOut - Imaginary token reserve of output amount.
511
620
  * @returns The maximum output amount of the other asset.
512
621
  */
513
- private getAmountOut(
622
+ public getAmountOut(
514
623
  amountIn: bigint,
515
624
  iReserveIn: bigint,
516
625
  iReserveOut: bigint,
@@ -532,7 +641,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
532
641
  * @param iReserveOut - Imaginary token reserve of output amount.
533
642
  * @returns The input amount of the other asset.
534
643
  */
535
- private getAmountIn(
644
+ public getAmountIn(
536
645
  amountOut: bigint,
537
646
  iReserveIn: bigint,
538
647
  iReserveOut: bigint,
@@ -557,7 +666,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
557
666
  * @note If a > t then entire trade route through col pool and col pool arbitrage with debt pool.
558
667
  * @note If a > 0 & a < t then swap will route through both pools.
559
668
  */
560
- private swapRoutingOut(
669
+ public swapRoutingOut(
561
670
  t: bigint,
562
671
  x: bigint,
563
672
  y: bigint,
@@ -594,7 +703,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
594
703
  * @note If a > t then entire trade route through col pool and col pool arbitrage with debt pool.
595
704
  * @note If a > 0 & a < t then swap will route through both pools.
596
705
  */
597
- private swapRoutingIn(
706
+ public swapRoutingIn(
598
707
  t: bigint,
599
708
  x: bigint,
600
709
  y: bigint,
@@ -624,7 +733,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
624
733
  * @param {number} fee - The fee for the swap. 1e4 = 1%
625
734
  * @returns {bigint} amountIn - The calculated input amount required for the swap.
626
735
  */
627
- private swapOut(
736
+ public swapOut(
628
737
  swap0to1: boolean,
629
738
  amountOut: bigint,
630
739
  colReserves: CollateralReserves,
@@ -632,6 +741,8 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
632
741
  inDecimals: number,
633
742
  outDecimals: number,
634
743
  fee: bigint,
744
+ currentLimits: DexLimits,
745
+ syncTime: number,
635
746
  ): bigint {
636
747
  const amountOutAdjusted =
637
748
  (amountOut * BigInt(10 ** 12)) / BigInt(10 ** outDecimals);
@@ -640,15 +751,19 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
640
751
  amountOutAdjusted,
641
752
  colReserves,
642
753
  debtReserves,
754
+ outDecimals,
755
+ currentLimits,
756
+ syncTime,
643
757
  );
644
758
 
645
- const FEE_100_PERCENT = BigInt(1e6); // Assuming this constant is defined elsewhere
646
-
647
- const result =
648
- ((amountIn * FEE_100_PERCENT) / (FEE_100_PERCENT - fee)) *
649
- BigInt(10 ** (inDecimals - 12));
650
-
651
- return result;
759
+ if (amountIn == 2n ** 256n - 1n) {
760
+ return amountIn;
761
+ }
762
+ const ans =
763
+ (amountIn * this.FEE_100_PERCENT * BigInt(10 ** inDecimals)) /
764
+ BigInt(10 ** 12) /
765
+ (this.FEE_100_PERCENT - fee);
766
+ return ans;
652
767
  }
653
768
 
654
769
  /**
@@ -659,12 +774,18 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
659
774
  * @param {DebtReserves} debtReserves - The reserves of the debt pool.
660
775
  * @returns {bigint} The calculated input amount required for the swap.
661
776
  */
662
- private swapOutAdjusted(
777
+ public swapOutAdjusted(
663
778
  swap0to1: boolean,
664
779
  amountOut: bigint,
665
780
  colReserves: CollateralReserves,
666
781
  debtReserves: DebtReserves,
782
+ outDecimals: number,
783
+ currentLimits: DexLimits,
784
+ syncTime: number,
667
785
  ): bigint {
786
+ if (amountOut === 0n) {
787
+ return 0n; // Return 0 if output amount is 0
788
+ }
668
789
  const {
669
790
  token0RealReserves,
670
791
  token1RealReserves,
@@ -681,17 +802,17 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
681
802
 
682
803
  // Check if all reserves of collateral pool are greater than 0
683
804
  const colPoolEnabled =
684
- token0RealReserves > 0n &&
685
- token1RealReserves > 0n &&
686
- token0ImaginaryReserves > 0n &&
687
- token1ImaginaryReserves > 0n;
805
+ token0RealReserves > BigInt(0) &&
806
+ token1RealReserves > BigInt(0) &&
807
+ token0ImaginaryReserves > BigInt(0) &&
808
+ token1ImaginaryReserves > BigInt(0);
688
809
 
689
810
  // Check if all reserves of debt pool are greater than 0
690
811
  const debtPoolEnabled =
691
- debtToken0RealReserves > 0n &&
692
- debtToken1RealReserves > 0n &&
693
- debtToken0ImaginaryReserves > 0n &&
694
- debtToken1ImaginaryReserves > 0n;
812
+ debtToken0RealReserves > BigInt(0) &&
813
+ debtToken1RealReserves > BigInt(0) &&
814
+ debtToken0ImaginaryReserves > BigInt(0) &&
815
+ debtToken1ImaginaryReserves > BigInt(0);
695
816
 
696
817
  let colReserveIn: bigint,
697
818
  colReserveOut: bigint,
@@ -701,6 +822,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
701
822
  colIReserveOut: bigint,
702
823
  debtIReserveIn: bigint,
703
824
  debtIReserveOut: bigint;
825
+ let borrowable: bigint, withdrawable: bigint;
704
826
 
705
827
  if (swap0to1) {
706
828
  colReserveIn = token0RealReserves;
@@ -711,6 +833,14 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
711
833
  debtReserveOut = debtToken1RealReserves;
712
834
  debtIReserveIn = debtToken0ImaginaryReserves;
713
835
  debtIReserveOut = debtToken1ImaginaryReserves;
836
+ borrowable = this.getExpandedLimit(
837
+ syncTime,
838
+ currentLimits.borrowableToken1,
839
+ );
840
+ withdrawable = this.getExpandedLimit(
841
+ syncTime,
842
+ currentLimits.withdrawableToken1,
843
+ );
714
844
  } else {
715
845
  colReserveIn = token1RealReserves;
716
846
  colReserveOut = token0RealReserves;
@@ -720,8 +850,21 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
720
850
  debtReserveOut = debtToken0RealReserves;
721
851
  debtIReserveIn = debtToken1ImaginaryReserves;
722
852
  debtIReserveOut = debtToken0ImaginaryReserves;
853
+ borrowable = this.getExpandedLimit(
854
+ syncTime,
855
+ currentLimits.borrowableToken0,
856
+ );
857
+ withdrawable = this.getExpandedLimit(
858
+ syncTime,
859
+ currentLimits.withdrawableToken0,
860
+ );
723
861
  }
724
862
 
863
+ // bring borrowable and withdrawable from token decimals to 1e12 decimals, same as amounts
864
+ borrowable = (borrowable * BigInt(10 ** 12)) / BigInt(10 ** outDecimals);
865
+ withdrawable =
866
+ (withdrawable * BigInt(10 ** 12)) / BigInt(10 ** outDecimals);
867
+
725
868
  let a: bigint;
726
869
  if (colPoolEnabled && debtPoolEnabled) {
727
870
  a = this.swapRoutingOut(
@@ -732,51 +875,106 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
732
875
  debtIReserveOut,
733
876
  );
734
877
  } else if (debtPoolEnabled) {
735
- a = -1n; // Route from debt pool
878
+ a = BigInt(-1); // Route from debt pool
736
879
  } else if (colPoolEnabled) {
737
- a = amountOut + 1n; // Route from collateral pool
880
+ a = amountOut + BigInt(1); // Route from collateral pool
738
881
  } else {
739
882
  throw new Error('No pools are enabled');
740
883
  }
741
884
 
742
- let amountInCollateral = 0n;
743
- let amountInDebt = 0n;
885
+ let amountInCollateral: bigint = BigInt(0);
886
+ let amountInDebt: bigint = BigInt(0);
887
+ let amountOutCollateral: bigint = BigInt(0);
888
+ let amountOutDebt: bigint = BigInt(0);
744
889
 
745
- if (a <= 0n) {
890
+ if (a <= BigInt(0)) {
746
891
  // Entire trade routes through debt pool
892
+ amountOutDebt = amountOut;
747
893
  amountInDebt = this.getAmountIn(
748
894
  amountOut,
749
895
  debtIReserveIn,
750
896
  debtIReserveOut,
751
897
  );
752
898
  if (amountOut > debtReserveOut) {
753
- return 2n ** 64n - 1n; // BigInt max value
899
+ return 2n ** 256n - 1n;
900
+ }
901
+ if (amountOut > borrowable) {
902
+ return 2n ** 256n - 1n;
754
903
  }
755
904
  } else if (a >= amountOut) {
756
905
  // Entire trade routes through collateral pool
906
+ amountOutCollateral = amountOut;
757
907
  amountInCollateral = this.getAmountIn(
758
908
  amountOut,
759
909
  colIReserveIn,
760
910
  colIReserveOut,
761
911
  );
762
912
  if (amountOut > colReserveOut) {
763
- return 2n ** 64n - 1n; // BigInt max value
913
+ return 2n ** 256n - 1n;
914
+ }
915
+ if (amountOut > withdrawable) {
916
+ return 2n ** 256n - 1n;
764
917
  }
765
918
  } else {
766
919
  // Trade routes through both pools
920
+ amountOutCollateral = a;
767
921
  amountInCollateral = this.getAmountIn(a, colIReserveIn, colIReserveOut);
922
+ amountOutDebt = amountOut - a;
768
923
  amountInDebt = this.getAmountIn(
769
- amountOut - a,
924
+ amountOutDebt,
770
925
  debtIReserveIn,
771
926
  debtIReserveOut,
772
927
  );
773
- if (amountOut - a > debtReserveOut || a > debtReserveOut) {
774
- return 2n ** 64n - 1n; // BigInt max value
928
+ if (amountOutDebt > debtReserveOut || a > colReserveOut) {
929
+ return 2n ** 256n - 1n;
930
+ }
931
+ if (amountOutDebt > borrowable || a > withdrawable) {
932
+ return 2n ** 256n - 1n;
775
933
  }
776
934
  }
777
935
 
936
+ let oldPrice: bigint;
937
+ let newPrice: bigint;
938
+ const SCALE = BigInt(1e27);
939
+
940
+ // from whatever pool higher amount of swap is routing we are taking that as final price, does not matter much because both pools final price should be same
941
+ if (amountOutCollateral > amountOutDebt) {
942
+ // new pool price from col pool
943
+ oldPrice = swap0to1
944
+ ? (colIReserveOut * SCALE) / colIReserveIn
945
+ : (colIReserveIn * SCALE) / colIReserveOut;
946
+ newPrice = swap0to1
947
+ ? ((colIReserveOut - amountOutCollateral) * SCALE) /
948
+ (colIReserveIn + amountInCollateral)
949
+ : ((colIReserveIn + amountInCollateral) * SCALE) /
950
+ (colIReserveOut - amountOutCollateral);
951
+ } else {
952
+ // new pool price from debt pool
953
+ oldPrice = swap0to1
954
+ ? (debtIReserveOut * SCALE) / debtIReserveIn
955
+ : (debtIReserveIn * SCALE) / debtIReserveOut;
956
+ newPrice = swap0to1
957
+ ? ((debtIReserveOut - amountOutDebt) * SCALE) /
958
+ (debtIReserveIn + amountInDebt)
959
+ : ((debtIReserveIn + amountInDebt) * SCALE) /
960
+ (debtIReserveOut - amountOutDebt);
961
+ }
962
+
963
+ const MAX_PRICE_DIFF = BigInt(5); // 5%
964
+ if (
965
+ this.abs(oldPrice - newPrice) >
966
+ (oldPrice / BigInt(100)) * MAX_PRICE_DIFF
967
+ ) {
968
+ // if price diff is > 5% then swap would revert.
969
+ return 2n ** 256n - 1n;
970
+ }
971
+
778
972
  const totalAmountIn = amountInCollateral + amountInDebt;
779
973
 
780
974
  return totalAmountIn;
781
975
  }
976
+
977
+ private abs(value: bigint): bigint {
978
+ return value < 0 ? -value : value;
979
+ }
782
980
  }