From 7d01f5a5b1a423db184decd2773465a46e8038ce Mon Sep 17 00:00:00 2001 From: Alexander Burkut Date: Fri, 1 Nov 2024 18:20:56 +0300 Subject: [PATCH] update FluidDex to use single instance of stateful event subscriber --- src/abi/fluid-dex/resolver.abi.json | 428 ++++++++++++++---- src/dex/fluid-dex/fluid-dex-events.test.ts | 270 ++--------- src/dex/fluid-dex/fluid-dex-factory.ts | 2 +- .../fluid-dex/fluid-dex-integration.test.ts | 1 + ...x-pool.ts => fluid-dex-liquidity-proxy.ts} | 131 +++--- src/dex/fluid-dex/fluid-dex.ts | 63 ++- src/dex/fluid-dex/types.ts | 19 +- 7 files changed, 496 insertions(+), 418 deletions(-) rename src/dex/fluid-dex/{fluid-dex-pool.ts => fluid-dex-liquidity-proxy.ts} (59%) diff --git a/src/abi/fluid-dex/resolver.abi.json b/src/abi/fluid-dex/resolver.abi.json index 44a0251af..9fd418e5c 100644 --- a/src/abi/fluid-dex/resolver.abi.json +++ b/src/abi/fluid-dex/resolver.abi.json @@ -1,7 +1,11 @@ [ { "inputs": [ - { "internalType": "address", "name": "factory_", "type": "address" } + { + "internalType": "address", + "name": "factory_", + "type": "address" + } ], "stateMutability": "nonpayable", "type": "constructor" @@ -21,28 +25,68 @@ }, { "inputs": [ - { "internalType": "address", "name": "dex_", "type": "address" }, - { "internalType": "bool", "name": "swap0to1_", "type": "bool" }, - { "internalType": "uint256", "name": "amountIn_", "type": "uint256" }, - { "internalType": "uint256", "name": "amountOutMin_", "type": "uint256" } + { + "internalType": "address", + "name": "dex_", + "type": "address" + }, + { + "internalType": "bool", + "name": "swap0to1_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "amountIn_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin_", + "type": "uint256" + } ], "name": "estimateSwapIn", "outputs": [ - { "internalType": "uint256", "name": "amountOut_", "type": "uint256" } + { + "internalType": "uint256", + "name": "amountOut_", + "type": "uint256" + } ], "stateMutability": "payable", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "dex_", "type": "address" }, - { "internalType": "bool", "name": "swap0to1_", "type": "bool" }, - { "internalType": "uint256", "name": "amountOut_", "type": "uint256" }, - { "internalType": "uint256", "name": "amountInMax_", "type": "uint256" } + { + "internalType": "address", + "name": "dex_", + "type": "address" + }, + { + "internalType": "bool", + "name": "swap0to1_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "amountOut_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax_", + "type": "uint256" + } ], "name": "estimateSwapOut", "outputs": [ - { "internalType": "uint256", "name": "amountIn_", "type": "uint256" } + { + "internalType": "uint256", + "name": "amountIn_", + "type": "uint256" + } ], "stateMutability": "payable", "type": "function" @@ -51,7 +95,11 @@ "inputs": [], "name": "getAllPoolAddresses", "outputs": [ - { "internalType": "address[]", "name": "pools_", "type": "address[]" } + { + "internalType": "address[]", + "name": "pools_", + "type": "address[]" + } ], "stateMutability": "view", "type": "function" @@ -62,10 +110,26 @@ "outputs": [ { "components": [ - { "internalType": "address", "name": "pool", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, - { "internalType": "uint256", "name": "fee", "type": "uint256" } + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } ], "internalType": "struct Structs.Pool[]", "name": "pools_", @@ -81,10 +145,26 @@ "outputs": [ { "components": [ - { "internalType": "address", "name": "pool", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, - { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, { "components": [ { @@ -164,10 +244,26 @@ "outputs": [ { "components": [ - { "internalType": "address", "name": "pool", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, - { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, { "components": [ { @@ -243,7 +339,11 @@ }, { "inputs": [ - { "internalType": "address", "name": "dex_", "type": "address" } + { + "internalType": "address", + "name": "dex_", + "type": "address" + } ], "name": "getDexCollateralReserves", "outputs": [ @@ -280,7 +380,11 @@ }, { "inputs": [ - { "internalType": "address", "name": "dex_", "type": "address" } + { + "internalType": "address", + "name": "dex_", + "type": "address" + } ], "name": "getDexCollateralReservesAdjusted", "outputs": [ @@ -317,7 +421,11 @@ }, { "inputs": [ - { "internalType": "address", "name": "dex_", "type": "address" } + { + "internalType": "address", + "name": "dex_", + "type": "address" + } ], "name": "getDexDebtReserves", "outputs": [ @@ -364,7 +472,11 @@ }, { "inputs": [ - { "internalType": "address", "name": "dex_", "type": "address" } + { + "internalType": "address", + "name": "dex_", + "type": "address" + } ], "name": "getDexDebtReservesAdjusted", "outputs": [ @@ -411,7 +523,11 @@ }, { "inputs": [ - { "internalType": "address", "name": "dex_", "type": "address" } + { + "internalType": "address", + "name": "dex_", + "type": "address" + } ], "name": "getDexPricesAndExchangePrices", "outputs": [ @@ -473,16 +589,36 @@ }, { "inputs": [ - { "internalType": "uint256", "name": "poolId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + } ], "name": "getPool", "outputs": [ { "components": [ - { "internalType": "address", "name": "pool", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, - { "internalType": "uint256", "name": "fee", "type": "uint256" } + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } ], "internalType": "struct Structs.Pool", "name": "pool_", @@ -494,30 +630,62 @@ }, { "inputs": [ - { "internalType": "uint256", "name": "poolId_", "type": "uint256" } + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + } ], "name": "getPoolAddress", "outputs": [ - { "internalType": "address", "name": "pool_", "type": "address" } + { + "internalType": "address", + "name": "pool_", + "type": "address" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "pool_", "type": "address" } + { + "internalType": "address", + "name": "pool_", + "type": "address" + } ], "name": "getPoolConstantsView", "outputs": [ { "components": [ - { "internalType": "uint256", "name": "dexId", "type": "uint256" }, - { "internalType": "address", "name": "liquidity", "type": "address" }, - { "internalType": "address", "name": "factory", "type": "address" }, + { + "internalType": "uint256", + "name": "dexId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "liquidity", + "type": "address" + }, + { + "internalType": "address", + "name": "factory", + "type": "address" + }, { "components": [ - { "internalType": "address", "name": "shift", "type": "address" }, - { "internalType": "address", "name": "admin", "type": "address" }, + { + "internalType": "address", + "name": "shift", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, { "internalType": "address", "name": "colOperations", @@ -543,8 +711,16 @@ "name": "deployerContract", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, { "internalType": "bytes32", "name": "supplyToken0Slot", @@ -591,7 +767,11 @@ }, { "inputs": [ - { "internalType": "address", "name": "pool_", "type": "address" } + { + "internalType": "address", + "name": "pool_", + "type": "address" + } ], "name": "getPoolConstantsView2", "outputs": [ @@ -628,27 +808,55 @@ }, { "inputs": [ - { "internalType": "address", "name": "pool_", "type": "address" } + { + "internalType": "address", + "name": "pool_", + "type": "address" + } ], "name": "getPoolFee", "outputs": [ - { "internalType": "uint256", "name": "fee_", "type": "uint256" } + { + "internalType": "uint256", + "name": "fee_", + "type": "uint256" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address", "name": "pool_", "type": "address" } + { + "internalType": "address", + "name": "pool_", + "type": "address" + } ], "name": "getPoolReserves", "outputs": [ { "components": [ - { "internalType": "address", "name": "pool", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, - { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, { "components": [ { @@ -723,17 +931,31 @@ "type": "function" }, { - "inputs": [ - { "internalType": "address", "name": "pool_", "type": "address" } - ], + "inputs": [], "name": "getPoolReservesAdjusted", "outputs": [ { "components": [ - { "internalType": "address", "name": "pool", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, - { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, { "components": [ { @@ -799,9 +1021,9 @@ "type": "tuple" } ], - "internalType": "struct Structs.PoolWithReserves", + "internalType": "struct Structs.PoolWithReserves[]", "name": "poolReserves_", - "type": "tuple" + "type": "tuple[]" } ], "stateMutability": "nonpayable", @@ -809,28 +1031,60 @@ }, { "inputs": [ - { "internalType": "address", "name": "pool_", "type": "address" } + { + "internalType": "address", + "name": "pool_", + "type": "address" + } ], "name": "getPoolTokens", "outputs": [ - { "internalType": "address", "name": "token0_", "type": "address" }, - { "internalType": "address", "name": "token1_", "type": "address" } + { + "internalType": "address", + "name": "token0_", + "type": "address" + }, + { + "internalType": "address", + "name": "token1_", + "type": "address" + } ], "stateMutability": "view", "type": "function" }, { "inputs": [ - { "internalType": "address[]", "name": "pools_", "type": "address[]" } + { + "internalType": "address[]", + "name": "pools_", + "type": "address[]" + } ], "name": "getPoolsReserves", "outputs": [ { "components": [ - { "internalType": "address", "name": "pool", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, - { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, { "components": [ { @@ -905,17 +1159,31 @@ "type": "function" }, { - "inputs": [ - { "internalType": "address[]", "name": "pools_", "type": "address[]" } - ], + "inputs": [], "name": "getPoolsReservesAdjusted", "outputs": [ { "components": [ - { "internalType": "address", "name": "pool", "type": "address" }, - { "internalType": "address", "name": "token0", "type": "address" }, - { "internalType": "address", "name": "token1", "type": "address" }, - { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, { "components": [ { @@ -992,7 +1260,13 @@ { "inputs": [], "name": "getTotalPools", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" } diff --git a/src/dex/fluid-dex/fluid-dex-events.test.ts b/src/dex/fluid-dex/fluid-dex-events.test.ts index 5a3de2ec5..13f85dbe5 100644 --- a/src/dex/fluid-dex/fluid-dex-events.test.ts +++ b/src/dex/fluid-dex/fluid-dex-events.test.ts @@ -2,97 +2,21 @@ import dotenv from 'dotenv'; dotenv.config(); -import { FluidDexEventPool } from './fluid-dex-pool'; -import { FluidDexFactory } from './fluid-dex-factory'; -import { FluidDex } from './fluid-dex'; import { Network } from '../../constants'; import { Address } from '../../types'; import { DummyDexHelper } from '../../dex-helper/index'; import { testEventSubscriber } from '../../../tests/utils-events'; -import { CommonAddresses, FluidDexPoolState, Pool } from './types'; +import { FluidDexLiquidityProxyState } from './types'; import { FluidDexConfig } from './config'; -import { DeepReadonly } from 'ts-essentials'; -import { TenderlySimulation } from '../../../tests/tenderly-simulation'; - -/* - README - ====== - - This test script adds unit tests for FluidDex event based - system. This is done by fetching the state on-chain before the - event block, manually pushing the block logs to the event-subscriber, - comparing the local state with on-chain state. - - Most of the logic for testing is abstracted by `testEventSubscriber`. - You need to do two things to make the tests work: - - 1. Fetch the block numbers where certain events were released. You - can modify the `./scripts/fetch-event-blocknumber.ts` to get the - block numbers for different events. Make sure to get sufficient - number of blockNumbers to cover all possible cases for the event - mutations. - - 2. Complete the implementation for fetchPoolState function. The - function should fetch the on-chain state of the event subscriber - using just the blocknumber. - - The template tests only include the test for a single event - subscriber. There can be cases where multiple event subscribers - exist for a single DEX. In such cases additional tests should be - added. - - You can run this individual test script by running: - `npx jest src/dex//-events.test.ts` - - (This comment should be removed from the final implementation) -*/ +import { FluidDexLiquidityProxy } from './fluid-dex-liquidity-proxy'; jest.setTimeout(50 * 1000); -async function fetchPoolState( - fluidDexPool: FluidDexEventPool, - blockNumber: number, -): Promise { - return await fluidDexPool.generateState(blockNumber); -} - -async function fetchTotalPools( - factory: FluidDexFactory, +async function fetchLiquidityProxyState( + liquidityProxy: FluidDexLiquidityProxy, blockNumber: number, -): Promise> { - return await factory.generateState(blockNumber); -} - -async function delay(seconds: number): Promise { - for (let i = seconds; i > 0; i--) { - process.stdout.write(`\r${i} second${i !== 1 ? 's' : ''} left`); - await new Promise(resolve => setTimeout(resolve, 1000)); - } - process.stdout.write("\rTime's up! \n"); -} - -function stringifyCircular(obj: any, space?: number): string { - const seen = new WeakSet(); - return JSON.stringify( - obj, - (key, value) => { - if (typeof value === 'object' && value !== null) { - if (seen.has(value)) { - return '[Circular]'; - } - seen.add(value); - } - return value; - }, - space, - ); -} - -function replacer(key: string, value: any) { - if (typeof value === 'bigint') { - return value.toString(); - } - return value; +): Promise { + return liquidityProxy.generateState(blockNumber); } // eventName -> blockNumbers @@ -103,181 +27,43 @@ describe('FluidDex EventPool Mainnet', function () { const network = Network.MAINNET; const dexHelper = new DummyDexHelper(network); const logger = dexHelper.getLogger(dexKey); - const fluidDexCommonAddressStruct: CommonAddresses = - FluidDexConfig[dexKey][network].commonAddresses; - const liquidityProxy: Address = '0x52aa899454998be5b000ad077a46bbe360f4e497'; - const dexFactory: Address = '0x91716C4EDA1Fb55e84Bf8b4c7085f84285c19085'; + let liquidityProxy: FluidDexLiquidityProxy; - const poolFetchEventsToTest: Record = { - [dexFactory]: { - DexDeployed: [21063272], - }, - }; + const commonAddresses = FluidDexConfig.FluidDex[network].commonAddresses; // poolAddress -> EventMappings - const poolUpdateEventsToTest: Record = { - [dexFactory]: { - LogOperate: [21063272], + const eventsToTest: Record = { + '0x52aa899454998be5b000ad077a46bbe360f4e497': { + LogOperate: [ + 21091850, 21091882, 21091897, 21091915, 21092008, 21092022, 21092039, + 21092142, 21092176, 21092187, 21092230, 21092286, 21092289, 21092295, + 21092319, 21092352, 21092360, 21092368, 21092378, 21092383, + ], }, }; - let fluidDexEventPool: FluidDexEventPool; - let factory: FluidDexFactory; - - Object.entries(poolUpdateEventsToTest).forEach( + Object.entries(eventsToTest).forEach( ([poolAddress, events]: [string, EventMappings]) => { describe(`Events for ${poolAddress}`, () => { + beforeEach(() => { + liquidityProxy = new FluidDexLiquidityProxy( + dexKey, + commonAddresses, + network, + dexHelper, + logger, + ); + }); Object.entries(events).forEach( ([eventName, blockNumbers]: [string, number[]]) => { describe(`${eventName}`, () => { blockNumbers.forEach((blockNumber: number) => { it(`State after ${blockNumber}`, async function () { - const fluidDex = new FluidDex(network, dexKey, dexHelper); - - await fluidDex.initializePricing(blockNumber); - console.log('hello'); - - const ts: TenderlySimulation = new TenderlySimulation( - network, - ); - - await ts.setup(); - - const forkId = ts.forkId; - dexHelper.replaceProviderWithRPC( - `https://rpc.tenderly.co/fork/${forkId}`, - ); - - console.log(forkId); - - const pools = fluidDex.factory.getState(blockNumber); - let pool: string | undefined; - - if (pools) { - for (let i = 0; i < pools.length; i++) { - if ( - pools[i].token0.toLowerCase() === - '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0' && - pools[i].token1.toLowerCase() === - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' - ) { - pool = pools[i].address.toLowerCase(); - break; - } - } - } else { - console.error('Pools data is null or undefined'); - } - - fluidDexEventPool = fluidDex.eventPools[`FluidDex_${pool}`]; - console.log( - `this is the dex key that i fetched from writing the for loop FluidDex_${pool}`, - ); - - console.log( - 'eth balance before : ' + - (await dexHelper.provider.getBalance( - '0x3c22ec75ea5d745c78fc84762f7f1e6d82a2c5bf', - )), - ); - - console.log( - 'state 1 block before : ' + - JSON.stringify( - await fluidDexEventPool.generateState( - await dexHelper.provider.getBlockNumber(), - ), - replacer, - 2, - ), - ); - - const allowanceTxn = await ts.simulate({ - from: '0x3c22ec75ea5d745c78fc84762f7f1e6d82a2c5bf', - to: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0', // undefined in case of contract deployment - value: '0', - data: '0x095ea7b30000000000000000000000006a000f20005980200259b80c51020030400010680000000000000000000000000000000000000000000000056bc75e2d63100000', - }); - - console.log( - 'allowance txn (isSuccess?) : ' + allowanceTxn.success, - ); - - const swapTxn = await ts.simulate({ - from: '0x3c22ec75ea5d745c78fc84762f7f1e6d82a2c5bf', - to: '0x6a000f20005980200259b80c5102003040001068', // undefined in case of contract deployment - value: '0', - data: '0xe3ead59e000000000000000000000000000010036c0190e009a000d0fc3541100a07380a0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000006a6b745e1dd000000000000000000000000000000000000000000000000000006b7ea416a9c0a47d24fdc9fd475bb1cc320a468c050100000000000000000000000001405b6c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001807f39c581f595b53c5cb19bd0b3f8da6c935e2ca00000006000000044ff00000000000000000000000000000000000000000000000000000000000000095ea7b30000000000000000000000000b1a513ee24972daef112bc777a5610d4325c9e7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b1a513ee24972daef112bc777a5610d4325c9e7000000a00024000000000007000000000000000000000000000000000000000000000000000000002668dfaa000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006a000f20005980200259b80c5102003040001068', - }); - - console.log('swap txn (isSuccess?) : ' + swapTxn.success); - - console.log( - 'eth balance after : ' + - (await dexHelper.provider.getBalance( - '0x3c22ec75ea5d745c78fc84762f7f1e6d82a2c5bf', - )), - ); - - // console.log(JSON.stringify(swapTxn)); - - const txnBlockNumber = swapTxn.transaction.block_number; - - console.log(txnBlockNumber); - console.log(await dexHelper.provider.getBlockNumber()); - - await delay(30); - - console.log( - 'state 1 block before : ' + - JSON.stringify( - await fluidDexEventPool.generateState( - txnBlockNumber - 2, - ), - replacer, - 2, - ), - ); - console.log( - 'state 1 block after : ' + - JSON.stringify( - await fluidDexEventPool.generateState( - await dexHelper.provider.getBlockNumber(), - ), - replacer, - 2, - ), - ); - }); - }); - }); - }, - ); - }); - }, - ); - - Object.entries(poolFetchEventsToTest).forEach( - ([poolAddress, events]: [string, EventMappings]) => { - describe(`Events for ${poolAddress}`, () => { - Object.entries(events).forEach( - ([eventName, blockNumbers]: [string, number[]]) => { - describe(`${eventName}`, () => { - blockNumbers.forEach((blockNumber: number) => { - it(`State after ${blockNumber}`, async function () { - factory = new FluidDexFactory( - 'FluidDex', - fluidDexCommonAddressStruct, - network, - dexHelper, - logger, - ); - await testEventSubscriber( - factory, - factory.addressesSubscribed, + liquidityProxy, + liquidityProxy.addressesSubscribed, (_blockNumber: number) => - fetchTotalPools(factory, _blockNumber), + fetchLiquidityProxyState(liquidityProxy, _blockNumber), blockNumber, `${dexKey}_${poolAddress}`, dexHelper.provider, diff --git a/src/dex/fluid-dex/fluid-dex-factory.ts b/src/dex/fluid-dex/fluid-dex-factory.ts index cdfbce849..821a7e670 100644 --- a/src/dex/fluid-dex/fluid-dex-factory.ts +++ b/src/dex/fluid-dex/fluid-dex-factory.ts @@ -35,7 +35,7 @@ export class FluidDexFactory extends StatefulEventSubscriber { protected dexHelper: IDexHelper, logger: Logger, ) { - super(parentName, 'getAllPools', dexHelper, logger); + super(parentName, 'factory', dexHelper, logger); this.logDecoder = (log: Log) => this.dexFactoryIface.parseLog(log); this.addressesSubscribed = [commonAddresses.dexFactory]; diff --git a/src/dex/fluid-dex/fluid-dex-integration.test.ts b/src/dex/fluid-dex/fluid-dex-integration.test.ts index 1002281db..8b5ad762d 100644 --- a/src/dex/fluid-dex/fluid-dex-integration.test.ts +++ b/src/dex/fluid-dex/fluid-dex-integration.test.ts @@ -263,6 +263,7 @@ describe('FluidDex', function () { 80n * BI_POWS[6], 90n * BI_POWS[6], 100n * BI_POWS[6], + 1000000n * BI_POWS[6], ]; it('USDC -> USDT getPoolIdentifiers and getPricesVolume SELL', async function () { diff --git a/src/dex/fluid-dex/fluid-dex-pool.ts b/src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts similarity index 59% rename from src/dex/fluid-dex/fluid-dex-pool.ts rename to src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts index a00740307..f83879d6d 100644 --- a/src/dex/fluid-dex/fluid-dex-pool.ts +++ b/src/dex/fluid-dex/fluid-dex-liquidity-proxy.ts @@ -1,45 +1,48 @@ import { Interface } from '@ethersproject/abi'; import { DeepReadonly } from 'ts-essentials'; import { Log, Logger } from '../../types'; -import { catchParseLogError } from '../../utils'; +import { bigIntify, catchParseLogError } from '../../utils'; import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; import { IDexHelper } from '../../dex-helper/idex-helper'; import ResolverABI from '../../abi/fluid-dex/resolver.abi.json'; import LiquidityABI from '../../abi/fluid-dex/liquidityUserModule.abi.json'; import { CommonAddresses, - FluidDexPoolState, - CollateralReserves, - DebtReserves, + FluidDexLiquidityProxyState, + PoolReserve, + PoolReserveResponse, } from './types'; import { Address } from '../../types'; import { Contract } from 'ethers'; -export class FluidDexEventPool extends StatefulEventSubscriber { +export class FluidDexLiquidityProxy extends StatefulEventSubscriber { handlers: { [event: string]: ( event: any, - state: DeepReadonly, + state: DeepReadonly, log: Readonly, - ) => Promise | null>; + ) => Promise | null>; } = {}; logDecoder: (log: Log) => any; addressesSubscribed: Address[]; + protected liquidityIface = new Interface(LiquidityABI); + readonly resolverIface = new Interface(ResolverABI); + constructor( readonly parentName: string, - readonly pool: Address, readonly commonAddresses: CommonAddresses, protected network: number, readonly dexHelper: IDexHelper, logger: Logger, ) { - super(parentName, 'FluidDex_' + pool, dexHelper, logger); + super(parentName, 'liquidity proxy', dexHelper, logger); this.logDecoder = (log: Log) => this.liquidityIface.parseLog(log); + this.addressesSubscribed = [commonAddresses.liquidityProxy]; // Add handlers @@ -51,30 +54,21 @@ export class FluidDexEventPool extends StatefulEventSubscriber, + state: DeepReadonly, log: Readonly, - ): Promise | null> { - if (!(event.args.user in [this.pool])) { - return null; - } + ): Promise | null> { const resolverContract = new Contract( this.commonAddresses.resolver, ResolverABI, this.dexHelper.provider, ); - const rawResult = await resolverContract.callStatic.getPoolReservesAdjusted( - this.pool, - { - blockTag: this.dexHelper.provider, - }, - ); - const generatedState = this.convertToFluidDexPoolState(rawResult); + const rawResult = + await resolverContract.callStatic.getAllPoolsReservesAdjusted({ + blockTag: log.blockNumber, + }); - this.setState( - generatedState, - await this.dexHelper.provider.getBlockNumber(), - ); + const generatedState = this.convertToFluidDexPoolState(rawResult); return generatedState; } @@ -89,9 +83,9 @@ export class FluidDexEventPool extends StatefulEventSubscriber, + state: DeepReadonly, log: Readonly, - ): Promise | null> { + ): Promise | null> { try { const event = this.logDecoder(log); if (event.name in this.handlers) { @@ -107,7 +101,7 @@ export class FluidDexEventPool extends StatefulEventSubscriber { + ): Promise { let state = this.getState(blockNumber); if (!state) { state = await this.generateState(blockNumber); @@ -127,52 +121,63 @@ export class FluidDexEventPool extends StatefulEventSubscriber> { + ): Promise> { const resolverContract = new Contract( this.commonAddresses.resolver, ResolverABI, this.dexHelper.provider, ); - const rawResult = await resolverContract.callStatic.getPoolReservesAdjusted( - this.pool, - { + const rawResult = + await resolverContract.callStatic.getAllPoolsReservesAdjusted({ blockTag: blockNumber, - }, - ); + }); const convertedResult = this.convertToFluidDexPoolState(rawResult); return convertedResult; } - private convertToFluidDexPoolState(input: any[]): FluidDexPoolState { - // Ignore the first three addresses - const [, , , feeHex, collateralReservesHex, debtReservesHex] = input; - // Convert fee from hex to number - const fee = Number(feeHex.toString()); - - // Convert collateral reserves - const collateralReserves: CollateralReserves = { - token0RealReserves: BigInt(collateralReservesHex[0].toString()), - token1RealReserves: BigInt(collateralReservesHex[1].toString()), - token0ImaginaryReserves: BigInt(collateralReservesHex[2].toString()), - token1ImaginaryReserves: BigInt(collateralReservesHex[3].toString()), - }; - - // Convert debt reserves - const debtReserves: DebtReserves = { - token0Debt: BigInt(debtReservesHex[0].toString()), - token1Debt: BigInt(debtReservesHex[1].toString()), - token0RealReserves: BigInt(debtReservesHex[2].toString()), - token1RealReserves: BigInt(debtReservesHex[3].toString()), - token0ImaginaryReserves: BigInt(debtReservesHex[4].toString()), - token1ImaginaryReserves: BigInt(debtReservesHex[5].toString()), - }; - - return { - collateralReserves, - debtReserves, - fee, - }; + private convertToFluidDexPoolState( + poolReserves: PoolReserveResponse[], + ): FluidDexLiquidityProxyState { + const result: PoolReserve[] = poolReserves.map(poolReserve => { + const [ + pool, + token0, + token1, + feeHex, + collateralReservesHex, + debtReservesHex, + ] = poolReserve; + + const fee = Number(feeHex.toString()); + + const collateralReserves = { + token0RealReserves: bigIntify(collateralReservesHex[0]), + token1RealReserves: bigIntify(collateralReservesHex[1]), + token0ImaginaryReserves: bigIntify(collateralReservesHex[2]), + token1ImaginaryReserves: bigIntify(collateralReservesHex[3]), + }; + + const debtReserves = { + token0Debt: bigIntify(debtReservesHex[0]), + token1Debt: bigIntify(debtReservesHex[1]), + token0RealReserves: bigIntify(debtReservesHex[2]), + token1RealReserves: bigIntify(debtReservesHex[3]), + token0ImaginaryReserves: bigIntify(debtReservesHex[4]), + token1ImaginaryReserves: bigIntify(debtReservesHex[5]), + }; + + return { + pool, + token0, + token1, + fee, + collateralReserves, + debtReserves, + }; + }); + + return { poolsReserves: result }; } } diff --git a/src/dex/fluid-dex/fluid-dex.ts b/src/dex/fluid-dex/fluid-dex.ts index be13c52fa..7785524bf 100644 --- a/src/dex/fluid-dex/fluid-dex.ts +++ b/src/dex/fluid-dex/fluid-dex.ts @@ -19,13 +19,12 @@ import { DebtReserves, FluidDexData, FluidDexPool, - FluidDexPoolState, + // FluidDexPoolState, Pool, } from './types'; import { SimpleExchange } from '../simple-exchange'; import FluidDexPoolABI from '../../abi/fluid-dex/fluid-dex.abi.json'; import { FluidDexConfig, FLUID_DEX_GAS_COST } from './config'; -import { FluidDexEventPool } from './fluid-dex-pool'; import { FluidDexFactory } from './fluid-dex-factory'; import { getDexKeysWithNetwork, getBigIntPow } from '../../utils'; import { extractReturnAmountPosition } from '../../executor/utils'; @@ -33,9 +32,9 @@ import { MultiResult } from '../../lib/multi-wrapper'; import { generalDecoder } from '../../lib/decoders'; import { BigNumber } from 'ethers'; import { sqrt } from './utils'; +import { FluidDexLiquidityProxy } from './fluid-dex-liquidity-proxy'; export class FluidDex extends SimpleExchange implements IDex { - eventPools: { [id: string]: FluidDexEventPool } = {}; readonly hasConstantPriceLargeAmounts = false; readonly needWrapNative = false; readonly isFeeOnTransferSupported = false; @@ -48,6 +47,8 @@ export class FluidDex extends SimpleExchange implements IDex { readonly factory: FluidDexFactory; + readonly liquidityProxy: FluidDexLiquidityProxy; + readonly fluidDexPoolIface: Interface; FEE_100_PERCENT = BigInt(1000000); @@ -60,12 +61,21 @@ export class FluidDex extends SimpleExchange implements IDex { super(dexHelper, dexKey); this.logger = dexHelper.getLogger(dexKey); this.factory = new FluidDexFactory( - 'FluidDex', - FluidDexConfig['FluidDex'][network].commonAddresses, + dexKey, + FluidDexConfig[dexKey][network].commonAddresses, network, dexHelper, this.logger, ); + + this.liquidityProxy = new FluidDexLiquidityProxy( + dexKey, + this.factory.commonAddresses, + this.network, + this.dexHelper, + this.logger, + ); + this.fluidDexPoolIface = new Interface(FluidDexPoolABI); } @@ -92,24 +102,7 @@ export class FluidDex extends SimpleExchange implements IDex { await this.factory.initialize(blockNumber); this.pools = await this.fetchFluidDexPools(blockNumber); - for (const pool of this.pools) { - if (!this.eventPools[pool.id]) { - this.eventPools[pool.id] = new FluidDexEventPool( - 'FluidDex', - pool.address, - this.factory.commonAddresses, - this.network, - this.dexHelper, - this.logger, - ); - } - } - - await Promise.all( - Object.values(this.eventPools).map(async eventPool => { - return eventPool.initialize(blockNumber); - }), - ); + await this.liquidityProxy.initialize(blockNumber); } getAdapters(side: SwapSide) { @@ -198,26 +191,28 @@ export class FluidDex extends SimpleExchange implements IDex { // Make sure the pool meets the optional limitPools filter. if (limitPools && !limitPools.includes(pool.id)) return null; - const eventPool = this.eventPools[pool.id]; - - if (!eventPool) { - this.logger.error(`fluid-dex pool ${pool.id}: No EventPool found.`); + const liquidityProxyState = this.liquidityProxy.getState(blockNumber); + if (!liquidityProxyState) { + this.logger.error(`${this.dexKey} liquidity proxy is missing state`); return null; } - const state = await eventPool.getState(blockNumber); - if (!state) return null; + const currentPoolReserves = liquidityProxyState.poolsReserves.find( + poolReserve => + poolReserve.pool.toLowerCase() === pool.address.toLowerCase(), + ); + if (!currentPoolReserves) return null; const prices = amounts.map(amount => { return this.swapIn( srcToken.address.toLowerCase() === pool.token0.toLowerCase(), amount, - state.collateralReserves, - state.debtReserves, + currentPoolReserves.collateralReserves, + currentPoolReserves.debtReserves, srcToken.decimals, destToken.decimals, - BigInt(state.fee), + BigInt(currentPoolReserves.fee), ); }); return [ @@ -227,8 +222,8 @@ export class FluidDex extends SimpleExchange implements IDex { (side === SwapSide.SELL ? destToken : srcToken).decimals, ), data: { - colReserves: state.collateralReserves, - debtReserves: state.debtReserves, + colReserves: currentPoolReserves.collateralReserves, + debtReserves: currentPoolReserves.debtReserves, exchange: this.dexKey, }, exchange: this.dexKey, diff --git a/src/dex/fluid-dex/types.ts b/src/dex/fluid-dex/types.ts index 5efbe4dcd..37a6994d1 100644 --- a/src/dex/fluid-dex/types.ts +++ b/src/dex/fluid-dex/types.ts @@ -1,11 +1,28 @@ import { Address } from '../../types'; +import { BigNumber } from 'ethers'; -export type FluidDexPoolState = { +export type PoolReserve = { + pool: string; + token0: string; + token1: string; collateralReserves: CollateralReserves; debtReserves: DebtReserves; fee: number; }; +export type PoolReserveResponse = [ + string, + string, + string, + BigNumber, + BigNumber[], + BigNumber[], +]; + +export type FluidDexLiquidityProxyState = { + poolsReserves: readonly PoolReserve[]; +}; + export type CollateralReserves = { token0RealReserves: bigint; token1RealReserves: bigint;