@paraswap/dex-lib 4.0.4 → 4.0.5-fluid-dex-pool-discovery-issue.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.
@@ -0,0 +1 @@
1
+ export const MIN_SWAP_LIQUIDITY = 10n ** 4n;
@@ -118,6 +118,23 @@ describe('FluidDex E2E', () => {
118
118
  describe('Mainnet', () => {
119
119
  const network = Network.MAINNET;
120
120
 
121
+ describe('ETH -> INST', () => {
122
+ const tokenASymbol: string = 'ETH';
123
+ const tokenBSymbol: string = 'INST';
124
+
125
+ const tokenAAmount: string = '100000000000000';
126
+ const tokenBAmount: string = '100000000000000';
127
+
128
+ testForNetwork(
129
+ network,
130
+ dexKey,
131
+ tokenASymbol,
132
+ tokenBSymbol,
133
+ tokenAAmount,
134
+ tokenBAmount,
135
+ );
136
+ });
137
+
121
138
  describe('ETH -> wstETH', () => {
122
139
  const tokenASymbol: string = 'wstETH';
123
140
  const tokenBSymbol: string = 'ETH';
@@ -10,14 +10,16 @@ import { FluidDexLiquidityProxyState } from './types';
10
10
  import { FluidDexConfig } from './config';
11
11
  import { FluidDexLiquidityProxy } from './fluid-dex-liquidity-proxy';
12
12
  import { FluidDexFactory } from './fluid-dex-factory';
13
+ import { FluidDexEventPool } from './fluid-dex-pool';
14
+ import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
13
15
 
14
16
  jest.setTimeout(50 * 1000);
15
17
 
16
- async function fetchLiquidityProxyState(
17
- liquidityProxy: FluidDexLiquidityProxy,
18
+ async function fetchState(
19
+ statefulEventSubscriber: StatefulEventSubscriber<any>,
18
20
  blockNumber: number,
19
- ): Promise<FluidDexLiquidityProxyState> {
20
- return liquidityProxy.generateState(blockNumber);
21
+ ): Promise<any> {
22
+ return statefulEventSubscriber.generateState(blockNumber);
21
23
  }
22
24
 
23
25
  // eventName -> blockNumbers
@@ -64,7 +66,7 @@ describe('FluidDex EventPool Mainnet', function () {
64
66
  liquidityProxy,
65
67
  liquidityProxy.addressesSubscribed,
66
68
  (_blockNumber: number) =>
67
- fetchLiquidityProxyState(liquidityProxy, _blockNumber),
69
+ fetchState(liquidityProxy, _blockNumber),
68
70
  blockNumber,
69
71
  `${dexKey}_${poolAddress}`,
70
72
  dexHelper.provider,
@@ -126,4 +128,52 @@ describe('FluidDex EventPool Mainnet', function () {
126
128
  },
127
129
  );
128
130
  });
131
+
132
+ describe('Pool events', () => {
133
+ let dexPool: FluidDexEventPool;
134
+
135
+ const eventsToTest: Record<Address, EventMappings> = {
136
+ '0x8710039D5de6840EdE452A85672B32270a709aE2': {
137
+ LogPauseSwapAndArbitrage: [21337128],
138
+ },
139
+ '0x2886a01a0645390872a9eb99dae1283664b0c524': {
140
+ LogPauseSwapAndArbitrage: [21374547],
141
+ },
142
+ };
143
+
144
+ Object.entries(eventsToTest).forEach(
145
+ ([poolAddress, events]: [string, EventMappings]) => {
146
+ describe(`Events for ${poolAddress}`, () => {
147
+ beforeEach(() => {
148
+ dexPool = new FluidDexEventPool(
149
+ dexKey,
150
+ poolAddress,
151
+ network,
152
+ dexHelper,
153
+ logger,
154
+ );
155
+ });
156
+ Object.entries(events).forEach(
157
+ ([eventName, blockNumbers]: [string, number[]]) => {
158
+ describe(`${eventName}`, () => {
159
+ blockNumbers.forEach((blockNumber: number) => {
160
+ it(`State after ${blockNumber}`, async function () {
161
+ await testEventSubscriber(
162
+ dexPool,
163
+ dexPool.addressesSubscribed,
164
+ (_blockNumber: number) =>
165
+ fetchState(dexPool, _blockNumber),
166
+ blockNumber,
167
+ `${dexKey}_${poolAddress}`,
168
+ dexHelper.provider,
169
+ );
170
+ });
171
+ });
172
+ });
173
+ },
174
+ );
175
+ });
176
+ },
177
+ );
178
+ });
129
179
  });
@@ -5,6 +5,7 @@ import { bigIntify, catchParseLogError } from '../../utils';
5
5
  import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
6
6
  import { IDexHelper } from '../../dex-helper/idex-helper';
7
7
  import ResolverABI from '../../abi/fluid-dex/resolver.abi.json';
8
+ import FluidDexPoolABI from '../../abi/fluid-dex/fluid-dex.abi.json';
8
9
  import LiquidityABI from '../../abi/fluid-dex/liquidityUserModule.abi.json';
9
10
  import {
10
11
  CommonAddresses,
@@ -13,7 +14,7 @@ import {
13
14
  PoolReserveResponse,
14
15
  } from './types';
15
16
  import { Address } from '../../types';
16
- import { Contract } from 'ethers';
17
+ import { Contract, ethers } from 'ethers';
17
18
 
18
19
  export class FluidDexLiquidityProxy extends StatefulEventSubscriber<FluidDexLiquidityProxyState> {
19
20
  handlers: {
@@ -28,7 +29,9 @@ export class FluidDexLiquidityProxy extends StatefulEventSubscriber<FluidDexLiqu
28
29
 
29
30
  addressesSubscribed: Address[];
30
31
 
31
- protected liquidityIface = new Interface(LiquidityABI);
32
+ readonly liquidityIface = new Interface(LiquidityABI);
33
+
34
+ readonly poolIface = new Interface(FluidDexPoolABI);
32
35
 
33
36
  readonly resolverIface = new Interface(ResolverABI);
34
37
 
@@ -123,7 +126,6 @@ export class FluidDexLiquidityProxy extends StatefulEventSubscriber<FluidDexLiqu
123
126
  });
124
127
 
125
128
  const convertedResult = this.convertToFluidDexPoolState(rawResult);
126
-
127
129
  this.logger.info(`${this.parentName}: ${this.name}: generating state...`);
128
130
 
129
131
  return convertedResult;
@@ -0,0 +1,139 @@
1
+ import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
2
+ import { DeepReadonly } from 'ts-essentials';
3
+ import { Address, Log, Logger } from '../../types';
4
+ import { IDexHelper } from '../../dex-helper';
5
+ import { Interface } from '@ethersproject/abi';
6
+ import FluidDexPoolABI from '../../abi/fluid-dex/fluid-dex.abi.json';
7
+ import { catchParseLogError } from '../../utils';
8
+ import { ethers } from 'ethers';
9
+ import { uint256ToBigInt } from '../../lib/decoders';
10
+ import { DecodedStateMultiCallResultWithRelativeBitmaps } from '../uniswap-v3/types';
11
+
12
+ const {
13
+ utils: { hexlify, hexZeroPad },
14
+ } = ethers;
15
+
16
+ type PoolState = {
17
+ isSwapAndArbitragePaused: boolean;
18
+ };
19
+
20
+ export class FluidDexEventPool extends StatefulEventSubscriber<PoolState> {
21
+ handlers: {
22
+ [event: string]: (
23
+ event: any,
24
+ state: DeepReadonly<PoolState>,
25
+ log: Readonly<Log>,
26
+ ) => DeepReadonly<PoolState> | null;
27
+ } = {};
28
+
29
+ logDecoder: (log: Log) => any;
30
+ addressesSubscribed: Address[];
31
+ protected poolIface = new Interface(FluidDexPoolABI);
32
+
33
+ constructor(
34
+ readonly parentName: string,
35
+ readonly poolAddress: string,
36
+ protected network: number,
37
+ protected dexHelper: IDexHelper,
38
+ logger: Logger,
39
+ ) {
40
+ super(parentName, 'pool', dexHelper, logger);
41
+
42
+ this.logDecoder = (log: Log) => this.poolIface.parseLog(log);
43
+ this.addressesSubscribed = [poolAddress];
44
+
45
+ // Add handlers
46
+ this.handlers['LogPauseSwapAndArbitrage'] =
47
+ this.handleLogPauseSwapAndArbitrage.bind(this);
48
+ this.handlers['LogUnpauseSwapAndArbitrage'] =
49
+ this.handleLogUnpauseSwapAndArbitrage.bind(this);
50
+ }
51
+
52
+ /**
53
+ * The function is called every time any of the subscribed
54
+ * addresses release log. The function accepts the current
55
+ * state, updates the state according to the log, and returns
56
+ * the updated state.
57
+ * @param state - Current state of event subscriber
58
+ * @param log - Log released by one of the subscribed addresses
59
+ * @returns Updates state of the event subscriber after the log
60
+ */
61
+ async processLog(
62
+ state: DeepReadonly<PoolState>,
63
+ log: Readonly<Log>,
64
+ ): Promise<DeepReadonly<PoolState> | null> {
65
+ try {
66
+ let event;
67
+ try {
68
+ event = this.logDecoder(log);
69
+ } catch (e) {
70
+ return null;
71
+ }
72
+
73
+ if (event.name in this.handlers) {
74
+ return this.handlers[event.name](event, state, log);
75
+ }
76
+ } catch (e) {
77
+ catchParseLogError(e, this.logger);
78
+ }
79
+
80
+ return null;
81
+ }
82
+
83
+ handleLogPauseSwapAndArbitrage(): PoolState {
84
+ return { isSwapAndArbitragePaused: true };
85
+ }
86
+
87
+ handleLogUnpauseSwapAndArbitrage(): PoolState {
88
+ return { isSwapAndArbitragePaused: false };
89
+ }
90
+
91
+ async getStateOrGenerate(
92
+ blockNumber: number,
93
+ readonly: boolean = false,
94
+ ): Promise<DeepReadonly<PoolState>> {
95
+ let state = this.getState(blockNumber);
96
+ if (!state) {
97
+ state = await this.generateState(blockNumber);
98
+ if (!readonly) this.setState(state, blockNumber);
99
+ }
100
+ return state;
101
+ }
102
+
103
+ /**
104
+ * The function generates state using on-chain calls. This
105
+ * function is called to regenerate state if the event based
106
+ * system fails to fetch events and the local state is no
107
+ * more correct.
108
+ * @param blockNumber - Blocknumber for which the state should
109
+ * should be generated
110
+ * @returns state of the event subscriber at blocknumber
111
+ */
112
+ async generateState(blockNumber: number): Promise<DeepReadonly<PoolState>> {
113
+ const multicallData = [
114
+ {
115
+ target: this.addressesSubscribed[0],
116
+ callData: this.poolIface.encodeFunctionData('readFromStorage', [
117
+ hexZeroPad(hexlify(1), 32),
118
+ ]),
119
+ decodeFunction: uint256ToBigInt,
120
+ },
121
+ ];
122
+
123
+ const storageResults = await this.dexHelper.multiWrapper.tryAggregate<
124
+ bigint | DecodedStateMultiCallResultWithRelativeBitmaps
125
+ >(
126
+ false,
127
+ multicallData,
128
+ blockNumber,
129
+ this.dexHelper.multiWrapper.defaultBatchSize,
130
+ false,
131
+ );
132
+
133
+ const isSwapAndArbitragePaused =
134
+ BigInt(storageResults[0].returnData.toString()) >> BigInt(255) ===
135
+ BigInt(1);
136
+
137
+ return { isSwapAndArbitragePaused };
138
+ }
139
+ }
@@ -34,6 +34,8 @@ import { generalDecoder } from '../../lib/decoders';
34
34
  import { BigNumber } from 'ethers';
35
35
  import { sqrt } from './utils';
36
36
  import { FluidDexLiquidityProxy } from './fluid-dex-liquidity-proxy';
37
+ import { FluidDexEventPool } from './fluid-dex-pool';
38
+ import { MIN_SWAP_LIQUIDITY } from './constants';
37
39
 
38
40
  export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
39
41
  readonly hasConstantPriceLargeAmounts = false;
@@ -46,14 +48,14 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
46
48
 
47
49
  pools: FluidDexPool[] = [];
48
50
 
51
+ eventPools: FluidDexEventPool[] = [];
52
+
49
53
  readonly factory: FluidDexFactory;
50
54
 
51
55
  readonly liquidityProxy: FluidDexLiquidityProxy;
52
56
 
53
57
  readonly fluidDexPoolIface: Interface;
54
58
 
55
- FEE_100_PERCENT = BigInt(1000000);
56
-
57
59
  constructor(
58
60
  readonly network: Network,
59
61
  readonly dexKey: string,
@@ -110,6 +112,20 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
110
112
  await this.factory.initialize(blockNumber);
111
113
  this.pools = await this.fetchFluidDexPools(blockNumber);
112
114
 
115
+ this.eventPools = await Promise.all(
116
+ this.pools.map(async pool => {
117
+ const eventPool = new FluidDexEventPool(
118
+ this.dexKey,
119
+ pool.address,
120
+ this.network,
121
+ this.dexHelper,
122
+ this.logger,
123
+ );
124
+ await eventPool.initialize(blockNumber);
125
+ return eventPool;
126
+ }),
127
+ );
128
+
113
129
  await this.liquidityProxy.initialize(blockNumber);
114
130
  }
115
131
 
@@ -147,29 +163,24 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
147
163
  side: SwapSide,
148
164
  blockNumber: number,
149
165
  ): Promise<string[]> {
150
- const pool = this.getPoolByTokenPair(srcToken.address, destToken.address);
151
- return pool ? [pool.id] : [];
166
+ const pools = this.getPoolsByTokenPair(srcToken.address, destToken.address);
167
+ return pools.map(pool => pool.id);
152
168
  }
153
169
 
154
- getPoolByTokenPair(
155
- srcToken: Address,
156
- destToken: Address,
157
- ): FluidDexPool | null {
170
+ getPoolsByTokenPair(srcToken: Address, destToken: Address): FluidDexPool[] {
158
171
  const srcAddress = srcToken.toLowerCase();
159
172
  const destAddress = destToken.toLowerCase();
160
173
 
161
174
  // A pair must have 2 different tokens.
162
- if (srcAddress === destAddress) return null;
175
+ if (srcAddress === destAddress) return [];
163
176
 
164
- for (const pool of this.pools) {
165
- if (
177
+ const pools = this.pools.filter(
178
+ pool =>
166
179
  (srcAddress === pool.token0 && destAddress === pool.token1) ||
167
- (srcAddress === pool.token1 && destAddress === pool.token0)
168
- ) {
169
- return pool;
170
- }
171
- }
172
- return null;
180
+ (srcAddress === pool.token1 && destAddress === pool.token0),
181
+ );
182
+
183
+ return pools;
173
184
  }
174
185
 
175
186
  // Returns pool prices for amounts.
@@ -187,61 +198,99 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
187
198
  try {
188
199
  if (srcToken.address.toLowerCase() === destToken.address.toLowerCase())
189
200
  return null;
190
- // Get the pool to use.
191
- const pool = this.getPoolByTokenPair(srcToken.address, destToken.address);
192
- if (!pool) return null;
201
+
202
+ // Get the pools to use.
203
+ const pools = this.getPoolsByTokenPair(
204
+ srcToken.address,
205
+ destToken.address,
206
+ );
207
+
208
+ if (!pools.length) return null;
209
+ const poolIds = pools.map(pool => pool.id);
193
210
  // Make sure the pool meets the optional limitPools filter.
194
- if (limitPools && !limitPools.includes(pool.id)) return null;
211
+ if (
212
+ limitPools &&
213
+ !limitPools.every(limitPool => poolIds.includes(limitPool))
214
+ )
215
+ return null;
195
216
 
196
217
  const liquidityProxyState = await this.liquidityProxy.getStateOrGenerate(
197
218
  blockNumber,
198
219
  );
199
220
 
200
- const currentPoolReserves = liquidityProxyState.poolsReserves.find(
201
- poolReserve =>
202
- poolReserve.pool.toLowerCase() === pool.address.toLowerCase(),
203
- );
204
- if (!currentPoolReserves) {
205
- return null;
206
- }
207
- const prices = amounts.map(amount => {
208
- if (side === SwapSide.SELL) {
209
- return this.swapIn(
210
- srcToken.address.toLowerCase() === pool.token0.toLowerCase(),
211
- amount,
212
- currentPoolReserves.collateralReserves,
213
- currentPoolReserves.debtReserves,
214
- srcToken.decimals,
215
- destToken.decimals,
216
- BigInt(currentPoolReserves.fee),
217
- currentPoolReserves.dexLimits,
218
- Math.floor(Date.now() / 1000),
221
+ const poolsPrices = await Promise.all(
222
+ pools.map(async pool => {
223
+ const currentPoolReserves = liquidityProxyState.poolsReserves.find(
224
+ poolReserve =>
225
+ poolReserve.pool.toLowerCase() === pool.address.toLowerCase(),
219
226
  );
220
- } else {
221
- return this.swapOut(
222
- srcToken.address.toLowerCase() === pool.token0.toLowerCase(),
223
- amount,
224
- currentPoolReserves.collateralReserves,
225
- currentPoolReserves.debtReserves,
226
- srcToken.decimals,
227
- destToken.decimals,
228
- BigInt(currentPoolReserves.fee),
229
- currentPoolReserves.dexLimits,
230
- Math.floor(Date.now() / 1000),
227
+
228
+ const eventPool = this.eventPools.find(
229
+ eventPool =>
230
+ eventPool.poolAddress.toLowerCase() ===
231
+ pool.address.toLowerCase(),
231
232
  );
232
- }
233
- });
234
- return [
235
- {
236
- prices: prices,
237
- unit: getBigIntPow(destToken.decimals),
238
- data: {},
239
- exchange: this.dexKey,
240
- poolIdentifier: pool.id,
241
- gasCost: FLUID_DEX_GAS_COST,
242
- poolAddresses: [pool.address],
243
- },
244
- ];
233
+
234
+ if (!eventPool) {
235
+ this.logger.warn(
236
+ `${this.dexKey}-${this.network}: Event pool ${pool.address} was not found...`,
237
+ );
238
+ return null;
239
+ }
240
+
241
+ const state = await eventPool.getStateOrGenerate(blockNumber);
242
+
243
+ if (!currentPoolReserves || state.isSwapAndArbitragePaused === true) {
244
+ return null;
245
+ }
246
+
247
+ const prices = amounts.map(amount => {
248
+ if (side === SwapSide.SELL) {
249
+ return this.swapIn(
250
+ srcToken.address.toLowerCase() === pool.token0.toLowerCase(),
251
+ amount,
252
+ currentPoolReserves.collateralReserves,
253
+ currentPoolReserves.debtReserves,
254
+ srcToken.decimals,
255
+ destToken.decimals,
256
+ BigInt(currentPoolReserves.fee),
257
+ currentPoolReserves.dexLimits,
258
+ Math.floor(Date.now() / 1000),
259
+ );
260
+ } else {
261
+ return this.swapOut(
262
+ srcToken.address.toLowerCase() === pool.token0.toLowerCase(),
263
+ amount,
264
+ currentPoolReserves.collateralReserves,
265
+ currentPoolReserves.debtReserves,
266
+ srcToken.decimals,
267
+ destToken.decimals,
268
+ BigInt(currentPoolReserves.fee),
269
+ currentPoolReserves.dexLimits,
270
+ Math.floor(Date.now() / 1000),
271
+ );
272
+ }
273
+ });
274
+
275
+ return {
276
+ prices: prices,
277
+ unit: getBigIntPow(destToken.decimals),
278
+ data: {
279
+ poolId: pool.id,
280
+ },
281
+ exchange: this.dexKey,
282
+ poolIdentifier: pool.id,
283
+ gasCost: FLUID_DEX_GAS_COST,
284
+ poolAddresses: [pool.address],
285
+ };
286
+ }),
287
+ );
288
+
289
+ const notNullResults = poolsPrices.filter(
290
+ res => res !== null,
291
+ ) as ExchangePrices<FluidDexData>;
292
+
293
+ return notNullResults;
245
294
  } catch (e) {
246
295
  this.logger.error(
247
296
  `Error_getPricesVolume ${srcToken.address || srcToken.symbol}, ${
@@ -272,10 +321,9 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
272
321
  ): AdapterExchangeParam {
273
322
  // Encode here the payload for adapter
274
323
  const payload = '';
275
- const pool = this.getPoolByTokenPair(srcToken, destToken);
276
324
 
277
325
  return {
278
- targetExchange: pool!.address,
326
+ targetExchange: '0x',
279
327
  payload,
280
328
  networkFee: '0',
281
329
  };
@@ -313,7 +361,11 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
313
361
  side === SwapSide.SELL ? 'amountOut_' : 'amountIn_',
314
362
  );
315
363
 
316
- const pool = this.getPoolByTokenPair(srcToken, destToken);
364
+ const pool = this.pools.find(pool => pool.id === data.poolId);
365
+ if (!pool)
366
+ throw new Error(
367
+ `${this.dexKey}-${this.network}: Pool with id: ${data.poolId} was not found`,
368
+ );
317
369
 
318
370
  if (side === SwapSide.SELL) {
319
371
  if (pool!.token0.toLowerCase() !== srcToken.toLowerCase()) {
@@ -584,11 +636,84 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
584
636
  if (priceDiff > maxAllowedDiff) {
585
637
  return 0n;
586
638
  }
639
+
640
+ if (amountInCollateral > 0) {
641
+ let reservesRatioValid = swap0To1
642
+ ? this.verifyToken1Reserves(
643
+ colReserveIn + amountInCollateral,
644
+ colReserveOut - amountOutCollateral,
645
+ oldPrice,
646
+ )
647
+ : this.verifyToken0Reserves(
648
+ colReserveOut - amountOutCollateral,
649
+ colReserveIn + amountInCollateral,
650
+ oldPrice,
651
+ );
652
+ if (!reservesRatioValid) {
653
+ return 0n;
654
+ }
655
+ }
656
+
657
+ if (amountInDebt > 0) {
658
+ let reservesRatioValid = swap0To1
659
+ ? this.verifyToken1Reserves(
660
+ debtReserveIn + amountInDebt,
661
+ debtReserveOut - amountOutDebt,
662
+ oldPrice,
663
+ )
664
+ : this.verifyToken0Reserves(
665
+ debtReserveOut - amountOutDebt,
666
+ debtReserveIn + amountInDebt,
667
+ oldPrice,
668
+ );
669
+ if (!reservesRatioValid) {
670
+ return 0n;
671
+ }
672
+ }
673
+
587
674
  const totalAmountOut = amountOutCollateral + amountOutDebt;
588
675
 
589
676
  return totalAmountOut;
590
677
  }
591
678
 
679
+ /**
680
+ * Checks if token0 reserves are sufficient compared to token1 reserves.
681
+ * This helps prevent edge cases and ensures high precision in calculations.
682
+ * @param {number} token0Reserves - The reserves of token0.
683
+ * @param {number} token1Reserves - The reserves of token1.
684
+ * @param {number} price - The current price used for calculation.
685
+ * @returns {boolean} - Returns false if token0 reserves are too low, true otherwise.
686
+ */
687
+ protected verifyToken0Reserves(
688
+ token0Reserves: bigint,
689
+ token1Reserves: bigint,
690
+ price: bigint,
691
+ ): boolean {
692
+ return (
693
+ token0Reserves >=
694
+ (token1Reserves * 10n ** 27n) / (price * MIN_SWAP_LIQUIDITY)
695
+ );
696
+ }
697
+
698
+ /**
699
+ * Checks if token1 reserves are sufficient compared to token0 reserves.
700
+ * This helps prevent edge cases and ensures high precision in calculations.
701
+ * @param {number} token0Reserves - The reserves of token0.
702
+ * @param {number} token1Reserves - The reserves of token1.
703
+ * @param {number} price - The current price used for calculation.
704
+ * @returns {boolean} - Returns false if token1 reserves are too low, true otherwise.
705
+ */
706
+ protected verifyToken1Reserves(
707
+ token0Reserves: bigint,
708
+ token1Reserves: bigint,
709
+ price: bigint,
710
+ ): boolean {
711
+ return (
712
+ token1Reserves >=
713
+ (token0Reserves * price) / (10n ** 27n * MIN_SWAP_LIQUIDITY)
714
+ );
715
+ }
716
+
592
717
  /**
593
718
  * Calculates the currently available swappable amount for a token limit considering expansion since last syncTime.
594
719
  * @param syncTime - timestamp in seconds when the limits were synced
@@ -765,7 +890,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
765
890
  syncTime,
766
891
  );
767
892
 
768
- if (amountIn == 2n ** 256n - 1n) {
893
+ if (amountIn === 2n ** 256n - 1n) {
769
894
  return amountIn;
770
895
  }
771
896
  const ans = (amountIn * BigInt(10 ** inDecimals)) / BigInt(10 ** 12);
@@ -985,6 +1110,39 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
985
1110
  return 2n ** 256n - 1n;
986
1111
  }
987
1112
 
1113
+ if (amountInCollateral > 0) {
1114
+ let reservesRatioValid = swap0to1
1115
+ ? this.verifyToken1Reserves(
1116
+ colReserveIn + amountInCollateral,
1117
+ colReserveOut - amountOutCollateral,
1118
+ oldPrice,
1119
+ )
1120
+ : this.verifyToken0Reserves(
1121
+ colReserveOut - amountOutCollateral,
1122
+ colReserveIn + amountInCollateral,
1123
+ oldPrice,
1124
+ );
1125
+ if (!reservesRatioValid) {
1126
+ return 0n;
1127
+ }
1128
+ }
1129
+ if (amountInDebt > 0) {
1130
+ let reservesRatioValid = swap0to1
1131
+ ? this.verifyToken1Reserves(
1132
+ debtReserveIn + amountInDebt,
1133
+ debtReserveOut - amountOutDebt,
1134
+ oldPrice,
1135
+ )
1136
+ : this.verifyToken0Reserves(
1137
+ debtReserveOut - amountOutDebt,
1138
+ debtReserveIn + amountInDebt,
1139
+ oldPrice,
1140
+ );
1141
+ if (!reservesRatioValid) {
1142
+ return 0n;
1143
+ }
1144
+ }
1145
+
988
1146
  const totalAmountIn = amountInCollateral + amountInDebt;
989
1147
 
990
1148
  return totalAmountIn;
@@ -72,7 +72,9 @@ export interface PoolWithReserves {
72
72
  debtReserves: DebtReserves;
73
73
  }
74
74
 
75
- export type FluidDexData = {};
75
+ export type FluidDexData = {
76
+ poolId: string;
77
+ };
76
78
 
77
79
  // Each pool has a contract address and token pairs.
78
80
  export type FluidDexPool = {