@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.
- package/build/abi/fluid-dex/fluid-dex.abi.json +579 -25
- package/build/dex/fluid-dex/constants.d.ts +1 -0
- package/build/dex/fluid-dex/constants.js +5 -0
- package/build/dex/fluid-dex/constants.js.map +1 -0
- package/build/dex/fluid-dex/fluid-dex-liquidity-proxy.d.ts +2 -1
- package/build/dex/fluid-dex/fluid-dex-liquidity-proxy.js +2 -0
- package/build/dex/fluid-dex/fluid-dex-liquidity-proxy.js.map +1 -1
- package/build/dex/fluid-dex/fluid-dex-pool.d.ts +19 -21
- package/build/dex/fluid-dex/fluid-dex-pool.js +48 -62
- package/build/dex/fluid-dex/fluid-dex-pool.js.map +1 -1
- package/build/dex/fluid-dex/fluid-dex.d.ts +21 -2
- package/build/dex/fluid-dex/fluid-dex.js +108 -35
- package/build/dex/fluid-dex/fluid-dex.js.map +1 -1
- package/build/dex/fluid-dex/types.d.ts +3 -1
- package/package.json +1 -1
- package/src/abi/fluid-dex/fluid-dex.abi.json +581 -25
- package/src/dex/fluid-dex/constants.ts +1 -0
- package/src/dex/fluid-dex/fluid-dex-e2e.test.ts +17 -0
- package/src/dex/fluid-dex/fluid-dex-events.test.ts +55 -5
- package/src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts +5 -3
- package/src/dex/fluid-dex/fluid-dex-pool.ts +139 -0
- package/src/dex/fluid-dex/fluid-dex.ts +226 -68
- package/src/dex/fluid-dex/types.ts +3 -1
- package/tests/constants-e2e.ts +5 -0
|
@@ -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
|
|
17
|
-
|
|
18
|
+
async function fetchState(
|
|
19
|
+
statefulEventSubscriber: StatefulEventSubscriber<any>,
|
|
18
20
|
blockNumber: number,
|
|
19
|
-
): Promise<
|
|
20
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
151
|
-
return pool
|
|
166
|
+
const pools = this.getPoolsByTokenPair(srcToken.address, destToken.address);
|
|
167
|
+
return pools.map(pool => pool.id);
|
|
152
168
|
}
|
|
153
169
|
|
|
154
|
-
|
|
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
|
|
175
|
+
if (srcAddress === destAddress) return [];
|
|
163
176
|
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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 (
|
|
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
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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:
|
|
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.
|
|
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
|
|
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 = {
|