Skip to content

Commit 7dffeaf

Browse files
authored
Merge pull request #1154 from VeloraDEX/fix/fluid-dex-state-generation
fix(BACK-2167): reduce number of state generations
2 parents 9ed1597 + a03cdf5 commit 7dffeaf

File tree

6 files changed

+80
-111
lines changed

6 files changed

+80
-111
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@paraswap/dex-lib",
3-
"version": "4.8.20",
3+
"version": "4.8.21",
44
"main": "build/index.js",
55
"types": "build/index.d.ts",
66
"repository": "https://github.com/paraswap/paraswap-dex-lib",

src/dex/balancer-v3/balancer-v3.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import { removeCircularStepPairs } from './utils';
3232

3333
const MAX_UINT256 =
3434
'115792089237316195423570985008687907853269984665640564039457584007913129639935';
35-
const POOL_UPDATE_TTL = 1 * 60; // 1min
36-
const RATE_UPDATE_TTL = 1 * 60; // 1min
35+
const POOL_UPDATE_TTL = 2 * 60; // 2min
36+
const RATE_UPDATE_TTL = 2 * 60; // 2min
3737
const HOOK_UPDATE_TTL = 5 * 60; // 5min
3838

3939
type DeepMutable<T> = {

src/dex/fluid-dex/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
// on-chain we use 1e4 but use extra buffer to avoid reverts
22
export const MIN_SWAP_LIQUIDITY = 8500n;
3+
4+
export const RESERVE_REFRESH_INTERVAL_MS = 10 * 1000;

src/dex/fluid-dex/fluid-dex-events.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('FluidDex EventPool Mainnet', function () {
4040
const eventsToTest: Record<Address, EventMappings> = {
4141
'0x52aa899454998be5b000ad077a46bbe360f4e497': {
4242
LogOperate: [
43-
21190399, 21190405, 21190420, 21190452, 21190454, 21190465, 21190506,
43+
24619596, 24619595, 24619593, 24619592, 24619591, 24619590, 24619589,
4444
],
4545
},
4646
};
@@ -86,7 +86,7 @@ describe('FluidDex EventPool Mainnet', function () {
8686

8787
const eventsToTest: Record<Address, EventMappings> = {
8888
'0x91716C4EDA1Fb55e84Bf8b4c7085f84285c19085': {
89-
LogDexDeployed: [21199929],
89+
LogDexDeployed: [24611892],
9090
},
9191
};
9292

src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts

Lines changed: 34 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,66 @@
1-
import { Interface } from '@ethersproject/abi';
2-
import { DeepReadonly } from 'ts-essentials';
3-
import { Log, Logger } from '../../types';
4-
import { bigIntify, catchParseLogError } from '../../utils';
5-
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
1+
import { Logger } from '../../types';
2+
import { bigIntify, Utils } from '../../utils';
63
import { IDexHelper } from '../../dex-helper/idex-helper';
7-
import ResolverABI from '../../abi/fluid-dex/resolver.abi.json';
8-
import LiquidityABI from '../../abi/fluid-dex/liquidityUserModule.abi.json';
94
import {
105
CommonAddresses,
116
FluidDexLiquidityProxyState,
127
PoolReserve,
138
PoolReserveResponse,
149
} from './types';
15-
import { Address } from '../../types';
1610
import { Contract } from 'ethers';
11+
import ResolverABI from '../../abi/fluid-dex/resolver.abi.json';
1712

18-
export class FluidDexLiquidityProxy extends StatefulEventSubscriber<FluidDexLiquidityProxyState> {
19-
handlers: {
20-
[event: string]: (
21-
event: any,
22-
state: DeepReadonly<FluidDexLiquidityProxyState>,
23-
log: Readonly<Log>,
24-
) => Promise<DeepReadonly<FluidDexLiquidityProxyState> | null>;
25-
} = {};
26-
27-
logDecoder: (log: Log) => any;
28-
29-
addressesSubscribed: Address[];
30-
31-
readonly liquidityIface = new Interface(LiquidityABI);
32-
33-
readonly resolverIface = new Interface(ResolverABI);
13+
const STATE_CACHE_KEY = 'liquidity_proxy_state';
14+
const STATE_TTL_SECONDS = 600; // 10 minutes
15+
const LOCAL_CACHE_TTL_SECONDS = 5; // 5 seconds
3416

35-
resolverContract: Contract;
17+
export class FluidDexLiquidityProxy {
18+
readonly resolverContract: Contract;
3619

3720
constructor(
38-
readonly parentName: string,
21+
readonly dexKey: string,
3922
readonly commonAddresses: CommonAddresses,
4023
protected network: number,
4124
readonly dexHelper: IDexHelper,
42-
logger: Logger,
25+
readonly logger: Logger,
4326
) {
44-
super(parentName, 'liquidity proxy', dexHelper, logger);
45-
46-
this.logDecoder = (log: Log) => this.liquidityIface.parseLog(log);
47-
4827
this.resolverContract = new Contract(
4928
this.commonAddresses.resolver,
5029
ResolverABI,
5130
this.dexHelper.provider,
5231
);
53-
54-
this.addressesSubscribed = [commonAddresses.liquidityProxy];
55-
56-
// Add handlers
57-
this.handlers['LogOperate'] = this.handleOperate.bind(this);
58-
}
59-
60-
/**
61-
* Handle a trade rate change on the pool.
62-
*/
63-
async handleOperate(
64-
event: any,
65-
state: DeepReadonly<FluidDexLiquidityProxyState>,
66-
log: Readonly<Log>,
67-
): Promise<DeepReadonly<FluidDexLiquidityProxyState> | null> {
68-
return this.generateState(log.blockNumber);
69-
}
70-
71-
/**
72-
* The function is called every time any of the subscribed
73-
* addresses release log. The function accepts the current
74-
* state, updates the state according to the log, and returns
75-
* the updated state.
76-
* @param state - Current state of event subscriber
77-
* @param log - Log released by one of the subscribed addresses
78-
* @returns Updates state of the event subscriber after the log
79-
*/
80-
async processLog(
81-
state: DeepReadonly<FluidDexLiquidityProxyState>,
82-
log: Readonly<Log>,
83-
): Promise<DeepReadonly<FluidDexLiquidityProxyState> | null> {
84-
try {
85-
const event = this.logDecoder(log);
86-
if (event.name in this.handlers) {
87-
return await this.handlers[event.name](event, state, log);
88-
}
89-
} catch (e) {
90-
catchParseLogError(e, this.logger);
91-
}
92-
93-
return null;
9432
}
9533

96-
async getStateOrGenerate(
97-
blockNumber: number,
98-
): Promise<FluidDexLiquidityProxyState> {
99-
let state = this.getState(blockNumber);
100-
if (!state) {
101-
state = await this.generateState(blockNumber);
102-
this.setState(state, blockNumber);
103-
}
104-
return state;
105-
}
106-
107-
/**
108-
* The function generates state using on-chain calls. This
109-
* function is called to regenerate state if the event based
110-
* system fails to fetch events and the local state is no
111-
* more correct.
112-
* @param blockNumber - Blocknumber for which the state should
113-
* should be generated
114-
* @returns state of the event subscriber at blocknumber
115-
*/
116-
async generateState(
117-
blockNumber: number,
118-
): Promise<DeepReadonly<FluidDexLiquidityProxyState>> {
34+
async fetchAndSetState(blockNumber: number): Promise<void> {
11935
const rawResult =
12036
await this.resolverContract.callStatic.getAllPoolsReservesAdjusted({
12137
blockTag: blockNumber,
12238
});
12339

124-
const convertedResult = this.convertToFluidDexPoolState(rawResult);
125-
this.logger.info(`${this.parentName}: ${this.name}: generating state...`);
40+
const state = this.convertToFluidDexPoolState(rawResult);
41+
42+
await this.dexHelper.cache.setex(
43+
this.dexKey,
44+
this.network,
45+
STATE_CACHE_KEY,
46+
STATE_TTL_SECONDS,
47+
Utils.Serialize(state),
48+
);
49+
}
50+
51+
async getState(): Promise<FluidDexLiquidityProxyState | null> {
52+
const cached = await this.dexHelper.cache.getAndCacheLocally(
53+
this.dexKey,
54+
this.network,
55+
STATE_CACHE_KEY,
56+
LOCAL_CACHE_TTL_SECONDS,
57+
);
12658

127-
return convertedResult;
59+
if (cached) {
60+
return Utils.Parse(cached) as FluidDexLiquidityProxyState;
61+
}
62+
63+
return null;
12864
}
12965

13066
private convertToFluidDexPoolState(

src/dex/fluid-dex/fluid-dex.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ import { BigNumber } from 'ethers';
3232
import { sqrt } from './utils';
3333
import { FluidDexLiquidityProxy } from './fluid-dex-liquidity-proxy';
3434
import { FluidDexEventPool } from './fluid-dex-pool';
35-
import { MIN_SWAP_LIQUIDITY } from './constants';
35+
import { MIN_SWAP_LIQUIDITY, RESERVE_REFRESH_INTERVAL_MS } from './constants';
3636

3737
export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
3838
readonly hasConstantPriceLargeAmounts = false;
3939
readonly needWrapNative = false;
4040
readonly isFeeOnTransferSupported = false;
41+
readonly isStatePollingDex = true;
4142
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
4243
getDexKeysWithNetwork(FluidDexConfig);
4344

@@ -53,6 +54,8 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
5354

5455
readonly fluidDexPoolIface: Interface;
5556

57+
private reserveUpdateIntervalTask?: NodeJS.Timeout;
58+
5659
constructor(
5760
readonly network: Network,
5861
readonly dexKey: string,
@@ -124,7 +127,25 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
124127
}),
125128
);
126129

127-
await this.liquidityProxy.initialize(blockNumber);
130+
if (!this.dexHelper.config.isSlave) {
131+
void this.updateReserves();
132+
133+
if (!this.reserveUpdateIntervalTask) {
134+
this.reserveUpdateIntervalTask = setInterval(
135+
this.updateReserves.bind(this),
136+
RESERVE_REFRESH_INTERVAL_MS,
137+
);
138+
}
139+
}
140+
}
141+
142+
private async updateReserves(): Promise<void> {
143+
try {
144+
const blockNumber = await this.dexHelper.provider.getBlockNumber();
145+
await this.liquidityProxy.fetchAndSetState(blockNumber);
146+
} catch (error) {
147+
this.logger.error(`${this.dexKey}: Error updating reserves:`, error);
148+
}
128149
}
129150

130151
getAdapters(side: SwapSide) {
@@ -191,9 +212,14 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
191212

192213
if (!pools.length) return null;
193214

194-
const liquidityProxyState = await this.liquidityProxy.getStateOrGenerate(
195-
blockNumber,
196-
);
215+
const liquidityProxyState = await this.liquidityProxy.getState();
216+
217+
if (!liquidityProxyState) {
218+
this.logger.warn(
219+
`${this.dexKey}-${this.network}: Liquidity proxy state is not available`,
220+
);
221+
return null;
222+
}
197223

198224
const poolsPrices = await Promise.all(
199225
pools.map(async pool => {
@@ -324,8 +350,6 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
324350
return eventPool;
325351
}),
326352
);
327-
328-
await this.liquidityProxy.updatePoolState(blockNumber);
329353
}
330354

331355
// Returns list of top pools based on liquidity. Max
@@ -347,7 +371,7 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
347371
return [];
348372
}
349373

350-
const liquidityProxyState = this.liquidityProxy.getStaleState();
374+
const liquidityProxyState = await this.liquidityProxy.getState();
351375

352376
if (!liquidityProxyState) {
353377
return [];
@@ -1271,4 +1295,11 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
12711295
private abs(value: bigint): bigint {
12721296
return value < 0 ? -value : value;
12731297
}
1298+
1299+
releaseResources(): void {
1300+
if (this.reserveUpdateIntervalTask) {
1301+
clearInterval(this.reserveUpdateIntervalTask);
1302+
this.reserveUpdateIntervalTask = undefined;
1303+
}
1304+
}
12741305
}

0 commit comments

Comments
 (0)