@paraswap/dex-lib 3.10.0 → 3.10.2
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/dexFactory.abi.json +506 -0
- package/build/abi/fluid-dex/fluid-dex.abi.json +922 -0
- package/build/abi/fluid-dex/liquidityUserModule.abi.json +145 -0
- package/build/abi/fluid-dex/resolver.abi.json +999 -0
- package/build/dex/fluid-dex/config.d.ts +4 -0
- package/build/dex/fluid-dex/config.js +20 -0
- package/build/dex/fluid-dex/config.js.map +1 -0
- package/build/dex/fluid-dex/fluid-dex-factory.d.ts +49 -0
- package/build/dex/fluid-dex/fluid-dex-factory.js +104 -0
- package/build/dex/fluid-dex/fluid-dex-factory.js.map +1 -0
- package/build/dex/fluid-dex/fluid-dex-pool.d.ts +47 -0
- package/build/dex/fluid-dex/fluid-dex-pool.js +119 -0
- package/build/dex/fluid-dex/fluid-dex-pool.js.map +1 -0
- package/build/dex/fluid-dex/fluid-dex.d.ts +127 -0
- package/build/dex/fluid-dex/fluid-dex.js +502 -0
- package/build/dex/fluid-dex/fluid-dex.js.map +1 -0
- package/build/dex/fluid-dex/types.d.ts +52 -0
- package/build/dex/fluid-dex/types.js +3 -0
- package/build/dex/fluid-dex/types.js.map +1 -0
- package/build/dex/fluid-dex/utils.d.ts +2 -0
- package/build/dex/fluid-dex/utils.js +18 -0
- package/build/dex/fluid-dex/utils.js.map +1 -0
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/paraswap-limit-orders/paraswap-limit-orders.js +1 -1
- package/build/dex/paraswap-limit-orders/paraswap-limit-orders.js.map +1 -1
- package/package.json +1 -1
- package/src/abi/fluid-dex/dexFactory.abi.json +506 -0
- package/src/abi/fluid-dex/fluid-dex.abi.json +922 -0
- package/src/abi/fluid-dex/liquidityUserModule.abi.json +145 -0
- package/src/abi/fluid-dex/resolver.abi.json +999 -0
- package/src/dex/fluid-dex/config.ts +20 -0
- package/src/dex/fluid-dex/fluid-dex-e2e.test.ts +153 -0
- package/src/dex/fluid-dex/fluid-dex-events.test.ts +293 -0
- package/src/dex/fluid-dex/fluid-dex-factory.ts +143 -0
- package/src/dex/fluid-dex/fluid-dex-integration.test.ts +299 -0
- package/src/dex/fluid-dex/fluid-dex-pool.ts +178 -0
- package/src/dex/fluid-dex/fluid-dex.ts +790 -0
- package/src/dex/fluid-dex/types.ts +62 -0
- package/src/dex/fluid-dex/utils.ts +15 -0
- package/src/dex/index.ts +2 -0
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders.ts +1 -1
- package/tests/constants-e2e.ts +2 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DexParams } from './types';
|
|
2
|
+
import { DexConfigMap } from '../../types';
|
|
3
|
+
import { Network } from '../../constants';
|
|
4
|
+
|
|
5
|
+
export const FluidDexConfig: DexConfigMap<DexParams> = {
|
|
6
|
+
FluidDex: {
|
|
7
|
+
[Network.MAINNET]: {
|
|
8
|
+
commonAddresses: {
|
|
9
|
+
liquidityProxy: '0x52aa899454998be5b000ad077a46bbe360f4e497',
|
|
10
|
+
resolver: '0xE8a07a32489BD9d5a00f01A55749Cf5cB854Fd13',
|
|
11
|
+
dexFactory: '0x91716C4EDA1Fb55e84Bf8b4c7085f84285c19085',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Uniswap takes total gas of 125k = 21k base gas & 104k swap (this is when user has token balance)
|
|
18
|
+
// Fluid takes total gas of 175k = 21k base gas & 154k swap (this is when user has token balance),
|
|
19
|
+
// with ETH swaps costing less (because no WETH conversion)
|
|
20
|
+
export const FLUID_DEX_GAS_COST = 154_000;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
import { StaticJsonRpcProvider } from '@ethersproject/providers';
|
|
6
|
+
|
|
7
|
+
import { testE2E } from '../../../tests/utils-e2e';
|
|
8
|
+
import {
|
|
9
|
+
Tokens,
|
|
10
|
+
Holders,
|
|
11
|
+
NativeTokenSymbols,
|
|
12
|
+
} from '../../../tests/constants-e2e';
|
|
13
|
+
import { Network, ContractMethod, SwapSide } from '../../constants';
|
|
14
|
+
import { generateConfig } from '../../config';
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
README
|
|
18
|
+
======
|
|
19
|
+
|
|
20
|
+
This test script should add e2e tests for FluidDex. The tests
|
|
21
|
+
should cover as many cases as possible. Most of the DEXes follow
|
|
22
|
+
the following test structure:
|
|
23
|
+
- DexName
|
|
24
|
+
- ForkName + Network
|
|
25
|
+
- ContractMethod
|
|
26
|
+
- ETH -> Token swap
|
|
27
|
+
- Token -> ETH swap
|
|
28
|
+
- Token -> Token swap
|
|
29
|
+
|
|
30
|
+
The template already enumerates the basic structure which involves
|
|
31
|
+
testing simpleSwap, multiSwap, megaSwap contract methods for
|
|
32
|
+
ETH <> TOKEN and TOKEN <> TOKEN swaps. You should replace tokenA and
|
|
33
|
+
tokenB with any two highly liquid tokens on FluidDex for the tests
|
|
34
|
+
to work. If the tokens that you would like to use are not defined in
|
|
35
|
+
Tokens or Holders map, you can update the './tests/constants-e2e'
|
|
36
|
+
|
|
37
|
+
Other than the standard cases that are already added by the template
|
|
38
|
+
it is highly recommended to add test cases which could be specific
|
|
39
|
+
to testing FluidDex (Eg. Tests based on poolType, special tokens,
|
|
40
|
+
etc).
|
|
41
|
+
|
|
42
|
+
You can run this individual test script by running:
|
|
43
|
+
`npx jest src/dex/<dex-name>/<dex-name>-e2e.test.ts`
|
|
44
|
+
|
|
45
|
+
e2e tests use the Tenderly fork api. Please add the following to your
|
|
46
|
+
.env file:
|
|
47
|
+
TENDERLY_TOKEN=Find this under Account>Settings>Authorization.
|
|
48
|
+
TENDERLY_ACCOUNT_ID=Your Tenderly account name.
|
|
49
|
+
TENDERLY_PROJECT=Name of a Tenderly project you have created in your
|
|
50
|
+
dashboard.
|
|
51
|
+
|
|
52
|
+
(This comment should be removed from the final implementation)
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
function testForNetwork(
|
|
56
|
+
network: Network,
|
|
57
|
+
dexKey: string,
|
|
58
|
+
tokenASymbol: string,
|
|
59
|
+
tokenBSymbol: string,
|
|
60
|
+
tokenAAmount: string,
|
|
61
|
+
tokenBAmount: string,
|
|
62
|
+
) {
|
|
63
|
+
const provider = new StaticJsonRpcProvider(
|
|
64
|
+
generateConfig(network).privateHttpProvider,
|
|
65
|
+
network,
|
|
66
|
+
);
|
|
67
|
+
const tokens = Tokens[network];
|
|
68
|
+
const holders = Holders[network];
|
|
69
|
+
const nativeTokenSymbol = NativeTokenSymbols[network];
|
|
70
|
+
|
|
71
|
+
const sideToContractMethods = new Map([
|
|
72
|
+
[SwapSide.SELL, [ContractMethod.swapExactAmountIn]],
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
describe(`${network}`, () => {
|
|
76
|
+
sideToContractMethods.forEach((contractMethods, side) =>
|
|
77
|
+
describe(`${side}`, () => {
|
|
78
|
+
contractMethods.forEach((contractMethod: string) => {
|
|
79
|
+
describe(`${contractMethod}`, () => {
|
|
80
|
+
it(`${tokenASymbol} -> ${tokenBSymbol}`, async () => {
|
|
81
|
+
await testE2E(
|
|
82
|
+
tokens[tokenASymbol],
|
|
83
|
+
tokens[tokenBSymbol],
|
|
84
|
+
holders[tokenASymbol],
|
|
85
|
+
tokenBAmount,
|
|
86
|
+
SwapSide.SELL,
|
|
87
|
+
dexKey,
|
|
88
|
+
contractMethod as ContractMethod,
|
|
89
|
+
network,
|
|
90
|
+
provider,
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
it(`${tokenBSymbol} -> ${tokenASymbol}`, async () => {
|
|
94
|
+
await testE2E(
|
|
95
|
+
tokens[tokenBSymbol],
|
|
96
|
+
tokens[tokenASymbol],
|
|
97
|
+
holders[tokenBSymbol],
|
|
98
|
+
tokenBAmount,
|
|
99
|
+
SwapSide.SELL,
|
|
100
|
+
dexKey,
|
|
101
|
+
contractMethod as ContractMethod,
|
|
102
|
+
network,
|
|
103
|
+
provider,
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
describe('FluidDex E2E', () => {
|
|
114
|
+
const dexKey = 'FluidDex';
|
|
115
|
+
|
|
116
|
+
describe('Mainnet', () => {
|
|
117
|
+
const network = Network.MAINNET;
|
|
118
|
+
|
|
119
|
+
describe('ETH -> wstETH', () => {
|
|
120
|
+
const tokenASymbol: string = 'wstETH';
|
|
121
|
+
const tokenBSymbol: string = 'ETH';
|
|
122
|
+
|
|
123
|
+
const tokenAAmount: string = '100000000000000';
|
|
124
|
+
const tokenBAmount: string = '100000000000000';
|
|
125
|
+
|
|
126
|
+
testForNetwork(
|
|
127
|
+
network,
|
|
128
|
+
dexKey,
|
|
129
|
+
tokenASymbol,
|
|
130
|
+
tokenBSymbol,
|
|
131
|
+
tokenAAmount,
|
|
132
|
+
tokenBAmount,
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('USDC -> USDT', () => {
|
|
137
|
+
const tokenASymbol: string = 'USDC';
|
|
138
|
+
const tokenBSymbol: string = 'USDT';
|
|
139
|
+
|
|
140
|
+
const tokenAAmount: string = '1000000';
|
|
141
|
+
const tokenBAmount: string = '100000000';
|
|
142
|
+
|
|
143
|
+
testForNetwork(
|
|
144
|
+
network,
|
|
145
|
+
dexKey,
|
|
146
|
+
tokenASymbol,
|
|
147
|
+
tokenBSymbol,
|
|
148
|
+
tokenAAmount,
|
|
149
|
+
tokenBAmount,
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
import { FluidDexEventPool } from './fluid-dex-pool';
|
|
6
|
+
import { FluidDexFactory } from './fluid-dex-factory';
|
|
7
|
+
import { FluidDex } from './fluid-dex';
|
|
8
|
+
import { Network } from '../../constants';
|
|
9
|
+
import { Address } from '../../types';
|
|
10
|
+
import { DummyDexHelper } from '../../dex-helper/index';
|
|
11
|
+
import { testEventSubscriber } from '../../../tests/utils-events';
|
|
12
|
+
import { CommonAddresses, FluidDexPoolState, Pool } from './types';
|
|
13
|
+
import { FluidDexConfig } from './config';
|
|
14
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
15
|
+
import { TenderlySimulation } from '../../../tests/tenderly-simulation';
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
README
|
|
19
|
+
======
|
|
20
|
+
|
|
21
|
+
This test script adds unit tests for FluidDex event based
|
|
22
|
+
system. This is done by fetching the state on-chain before the
|
|
23
|
+
event block, manually pushing the block logs to the event-subscriber,
|
|
24
|
+
comparing the local state with on-chain state.
|
|
25
|
+
|
|
26
|
+
Most of the logic for testing is abstracted by `testEventSubscriber`.
|
|
27
|
+
You need to do two things to make the tests work:
|
|
28
|
+
|
|
29
|
+
1. Fetch the block numbers where certain events were released. You
|
|
30
|
+
can modify the `./scripts/fetch-event-blocknumber.ts` to get the
|
|
31
|
+
block numbers for different events. Make sure to get sufficient
|
|
32
|
+
number of blockNumbers to cover all possible cases for the event
|
|
33
|
+
mutations.
|
|
34
|
+
|
|
35
|
+
2. Complete the implementation for fetchPoolState function. The
|
|
36
|
+
function should fetch the on-chain state of the event subscriber
|
|
37
|
+
using just the blocknumber.
|
|
38
|
+
|
|
39
|
+
The template tests only include the test for a single event
|
|
40
|
+
subscriber. There can be cases where multiple event subscribers
|
|
41
|
+
exist for a single DEX. In such cases additional tests should be
|
|
42
|
+
added.
|
|
43
|
+
|
|
44
|
+
You can run this individual test script by running:
|
|
45
|
+
`npx jest src/dex/<dex-name>/<dex-name>-events.test.ts`
|
|
46
|
+
|
|
47
|
+
(This comment should be removed from the final implementation)
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
jest.setTimeout(50 * 1000);
|
|
51
|
+
|
|
52
|
+
async function fetchPoolState(
|
|
53
|
+
fluidDexPool: FluidDexEventPool,
|
|
54
|
+
blockNumber: number,
|
|
55
|
+
): Promise<FluidDexPoolState> {
|
|
56
|
+
return await fluidDexPool.generateState(blockNumber);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function fetchTotalPools(
|
|
60
|
+
factory: FluidDexFactory,
|
|
61
|
+
blockNumber: number,
|
|
62
|
+
): Promise<DeepReadonly<Pool[]>> {
|
|
63
|
+
return await factory.generateState(blockNumber);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function delay(seconds: number): Promise<void> {
|
|
67
|
+
for (let i = seconds; i > 0; i--) {
|
|
68
|
+
process.stdout.write(`\r${i} second${i !== 1 ? 's' : ''} left`);
|
|
69
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
70
|
+
}
|
|
71
|
+
process.stdout.write("\rTime's up! \n");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function stringifyCircular(obj: any, space?: number): string {
|
|
75
|
+
const seen = new WeakSet();
|
|
76
|
+
return JSON.stringify(
|
|
77
|
+
obj,
|
|
78
|
+
(key, value) => {
|
|
79
|
+
if (typeof value === 'object' && value !== null) {
|
|
80
|
+
if (seen.has(value)) {
|
|
81
|
+
return '[Circular]';
|
|
82
|
+
}
|
|
83
|
+
seen.add(value);
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
},
|
|
87
|
+
space,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function replacer(key: string, value: any) {
|
|
92
|
+
if (typeof value === 'bigint') {
|
|
93
|
+
return value.toString();
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// eventName -> blockNumbers
|
|
99
|
+
type EventMappings = Record<string, number[]>;
|
|
100
|
+
|
|
101
|
+
describe('FluidDex EventPool Mainnet', function () {
|
|
102
|
+
const dexKey = 'FluidDex';
|
|
103
|
+
const network = Network.MAINNET;
|
|
104
|
+
const dexHelper = new DummyDexHelper(network);
|
|
105
|
+
const logger = dexHelper.getLogger(dexKey);
|
|
106
|
+
const fluidDexCommonAddressStruct: CommonAddresses =
|
|
107
|
+
FluidDexConfig[dexKey][network].commonAddresses;
|
|
108
|
+
const liquidityProxy: Address = '0x52aa899454998be5b000ad077a46bbe360f4e497';
|
|
109
|
+
const dexFactory: Address = '0x91716C4EDA1Fb55e84Bf8b4c7085f84285c19085';
|
|
110
|
+
|
|
111
|
+
const poolFetchEventsToTest: Record<Address, EventMappings> = {
|
|
112
|
+
[dexFactory]: {
|
|
113
|
+
DexDeployed: [21063272],
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// poolAddress -> EventMappings
|
|
118
|
+
const poolUpdateEventsToTest: Record<Address, EventMappings> = {
|
|
119
|
+
[dexFactory]: {
|
|
120
|
+
LogOperate: [21063272],
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
let fluidDexEventPool: FluidDexEventPool;
|
|
125
|
+
let factory: FluidDexFactory;
|
|
126
|
+
|
|
127
|
+
Object.entries(poolUpdateEventsToTest).forEach(
|
|
128
|
+
([poolAddress, events]: [string, EventMappings]) => {
|
|
129
|
+
describe(`Events for ${poolAddress}`, () => {
|
|
130
|
+
Object.entries(events).forEach(
|
|
131
|
+
([eventName, blockNumbers]: [string, number[]]) => {
|
|
132
|
+
describe(`${eventName}`, () => {
|
|
133
|
+
blockNumbers.forEach((blockNumber: number) => {
|
|
134
|
+
it(`State after ${blockNumber}`, async function () {
|
|
135
|
+
const fluidDex = new FluidDex(network, dexKey, dexHelper);
|
|
136
|
+
|
|
137
|
+
await fluidDex.initializePricing(blockNumber);
|
|
138
|
+
console.log('hello');
|
|
139
|
+
|
|
140
|
+
const ts: TenderlySimulation = new TenderlySimulation(
|
|
141
|
+
network,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
await ts.setup();
|
|
145
|
+
|
|
146
|
+
const forkId = ts.forkId;
|
|
147
|
+
dexHelper.replaceProviderWithRPC(
|
|
148
|
+
`https://rpc.tenderly.co/fork/${forkId}`,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
console.log(forkId);
|
|
152
|
+
|
|
153
|
+
const pools = fluidDex.factory.getState(blockNumber);
|
|
154
|
+
let pool: string | undefined;
|
|
155
|
+
|
|
156
|
+
if (pools) {
|
|
157
|
+
for (let i = 0; i < pools.length; i++) {
|
|
158
|
+
if (
|
|
159
|
+
pools[i].token0.toLowerCase() ===
|
|
160
|
+
'0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0' &&
|
|
161
|
+
pools[i].token1.toLowerCase() ===
|
|
162
|
+
'0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
|
|
163
|
+
) {
|
|
164
|
+
pool = pools[i].address.toLowerCase();
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
console.error('Pools data is null or undefined');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fluidDexEventPool = fluidDex.eventPools[`FluidDex_${pool}`];
|
|
173
|
+
console.log(
|
|
174
|
+
`this is the dex key that i fetched from writing the for loop FluidDex_${pool}`,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
console.log(
|
|
178
|
+
'eth balance before : ' +
|
|
179
|
+
(await dexHelper.provider.getBalance(
|
|
180
|
+
'0x3c22ec75ea5d745c78fc84762f7f1e6d82a2c5bf',
|
|
181
|
+
)),
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
console.log(
|
|
185
|
+
'state 1 block before : ' +
|
|
186
|
+
JSON.stringify(
|
|
187
|
+
await fluidDexEventPool.generateState(
|
|
188
|
+
await dexHelper.provider.getBlockNumber(),
|
|
189
|
+
),
|
|
190
|
+
replacer,
|
|
191
|
+
2,
|
|
192
|
+
),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const allowanceTxn = await ts.simulate({
|
|
196
|
+
from: '0x3c22ec75ea5d745c78fc84762f7f1e6d82a2c5bf',
|
|
197
|
+
to: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0', // undefined in case of contract deployment
|
|
198
|
+
value: '0',
|
|
199
|
+
data: '0x095ea7b30000000000000000000000006a000f20005980200259b80c51020030400010680000000000000000000000000000000000000000000000056bc75e2d63100000',
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
console.log(
|
|
203
|
+
'allowance txn (isSuccess?) : ' + allowanceTxn.success,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const swapTxn = await ts.simulate({
|
|
207
|
+
from: '0x3c22ec75ea5d745c78fc84762f7f1e6d82a2c5bf',
|
|
208
|
+
to: '0x6a000f20005980200259b80c5102003040001068', // undefined in case of contract deployment
|
|
209
|
+
value: '0',
|
|
210
|
+
data: '0xe3ead59e000000000000000000000000000010036c0190e009a000d0fc3541100a07380a0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000006a6b745e1dd000000000000000000000000000000000000000000000000000006b7ea416a9c0a47d24fdc9fd475bb1cc320a468c050100000000000000000000000001405b6c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001807f39c581f595b53c5cb19bd0b3f8da6c935e2ca00000006000000044ff00000000000000000000000000000000000000000000000000000000000000095ea7b30000000000000000000000000b1a513ee24972daef112bc777a5610d4325c9e7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b1a513ee24972daef112bc777a5610d4325c9e7000000a00024000000000007000000000000000000000000000000000000000000000000000000002668dfaa000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006a000f20005980200259b80c5102003040001068',
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
console.log('swap txn (isSuccess?) : ' + swapTxn.success);
|
|
214
|
+
|
|
215
|
+
console.log(
|
|
216
|
+
'eth balance after : ' +
|
|
217
|
+
(await dexHelper.provider.getBalance(
|
|
218
|
+
'0x3c22ec75ea5d745c78fc84762f7f1e6d82a2c5bf',
|
|
219
|
+
)),
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// console.log(JSON.stringify(swapTxn));
|
|
223
|
+
|
|
224
|
+
const txnBlockNumber = swapTxn.transaction.block_number;
|
|
225
|
+
|
|
226
|
+
console.log(txnBlockNumber);
|
|
227
|
+
console.log(await dexHelper.provider.getBlockNumber());
|
|
228
|
+
|
|
229
|
+
await delay(30);
|
|
230
|
+
|
|
231
|
+
console.log(
|
|
232
|
+
'state 1 block before : ' +
|
|
233
|
+
JSON.stringify(
|
|
234
|
+
await fluidDexEventPool.generateState(
|
|
235
|
+
txnBlockNumber - 2,
|
|
236
|
+
),
|
|
237
|
+
replacer,
|
|
238
|
+
2,
|
|
239
|
+
),
|
|
240
|
+
);
|
|
241
|
+
console.log(
|
|
242
|
+
'state 1 block after : ' +
|
|
243
|
+
JSON.stringify(
|
|
244
|
+
await fluidDexEventPool.generateState(
|
|
245
|
+
await dexHelper.provider.getBlockNumber(),
|
|
246
|
+
),
|
|
247
|
+
replacer,
|
|
248
|
+
2,
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
Object.entries(poolFetchEventsToTest).forEach(
|
|
261
|
+
([poolAddress, events]: [string, EventMappings]) => {
|
|
262
|
+
describe(`Events for ${poolAddress}`, () => {
|
|
263
|
+
Object.entries(events).forEach(
|
|
264
|
+
([eventName, blockNumbers]: [string, number[]]) => {
|
|
265
|
+
describe(`${eventName}`, () => {
|
|
266
|
+
blockNumbers.forEach((blockNumber: number) => {
|
|
267
|
+
it(`State after ${blockNumber}`, async function () {
|
|
268
|
+
factory = new FluidDexFactory(
|
|
269
|
+
'FluidDex',
|
|
270
|
+
fluidDexCommonAddressStruct,
|
|
271
|
+
network,
|
|
272
|
+
dexHelper,
|
|
273
|
+
logger,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
await testEventSubscriber(
|
|
277
|
+
factory,
|
|
278
|
+
factory.addressesSubscribed,
|
|
279
|
+
(_blockNumber: number) =>
|
|
280
|
+
fetchTotalPools(factory, _blockNumber),
|
|
281
|
+
blockNumber,
|
|
282
|
+
`${dexKey}_${poolAddress}`,
|
|
283
|
+
dexHelper.provider,
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
);
|
|
293
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Interface } from '@ethersproject/abi';
|
|
2
|
+
import { BytesLike } from 'ethers/lib/utils';
|
|
3
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
4
|
+
import { Log, Logger } from '../../types';
|
|
5
|
+
import { catchParseLogError } from '../../utils';
|
|
6
|
+
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
|
|
7
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
8
|
+
import ResolverABI from '../../abi/fluid-dex/resolver.abi.json';
|
|
9
|
+
import DexFactoryABI from '../../abi/fluid-dex/dexFactory.abi.json';
|
|
10
|
+
import { CommonAddresses, Pool } from './types';
|
|
11
|
+
import { MultiResult } from '../../lib/multi-wrapper';
|
|
12
|
+
import { Address } from '../../types';
|
|
13
|
+
import { generalDecoder } from '../../lib/decoders';
|
|
14
|
+
import { Contract } from 'ethers';
|
|
15
|
+
|
|
16
|
+
export class FluidDexFactory extends StatefulEventSubscriber<Pool[]> {
|
|
17
|
+
handlers: {
|
|
18
|
+
[event: string]: (
|
|
19
|
+
event: any,
|
|
20
|
+
state: DeepReadonly<Pool[]>,
|
|
21
|
+
log: Readonly<Log>,
|
|
22
|
+
) => Promise<DeepReadonly<Pool[]> | null>;
|
|
23
|
+
} = {};
|
|
24
|
+
|
|
25
|
+
logDecoder: (log: Log) => any;
|
|
26
|
+
|
|
27
|
+
addressesSubscribed: Address[];
|
|
28
|
+
protected dexFactoryIface = new Interface(DexFactoryABI);
|
|
29
|
+
protected resolverIface = new Interface(ResolverABI);
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
readonly parentName: string,
|
|
33
|
+
readonly commonAddresses: CommonAddresses,
|
|
34
|
+
protected network: number,
|
|
35
|
+
protected dexHelper: IDexHelper,
|
|
36
|
+
logger: Logger,
|
|
37
|
+
) {
|
|
38
|
+
super(parentName, 'getAllPools', dexHelper, logger);
|
|
39
|
+
|
|
40
|
+
this.logDecoder = (log: Log) => this.dexFactoryIface.parseLog(log);
|
|
41
|
+
this.addressesSubscribed = [commonAddresses.dexFactory];
|
|
42
|
+
|
|
43
|
+
// Add handlers
|
|
44
|
+
this.handlers['DexDeployed'] = this.handleDexDeployed.bind(this);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Handle a trade rate change on the pool.
|
|
49
|
+
*/
|
|
50
|
+
async handleDexDeployed(
|
|
51
|
+
event: any,
|
|
52
|
+
state: DeepReadonly<Pool[]>,
|
|
53
|
+
log: Readonly<Log>,
|
|
54
|
+
): Promise<DeepReadonly<Pool[]> | null> {
|
|
55
|
+
const blockNumber_ = await this.dexHelper.provider.getBlockNumber();
|
|
56
|
+
return this.getStateOrGenerate(blockNumber_, false);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
decodePool = (result: MultiResult<BytesLike> | BytesLike): Pool => {
|
|
60
|
+
return generalDecoder(
|
|
61
|
+
result,
|
|
62
|
+
['tuple(address pool, address token0, address token1, uint256 fee)'],
|
|
63
|
+
undefined,
|
|
64
|
+
decoded => {
|
|
65
|
+
return {
|
|
66
|
+
address: decoded[0].toLowerCase(),
|
|
67
|
+
token0: decoded[1].toLowerCase(),
|
|
68
|
+
token1: decoded[2].toLowerCase(),
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The function is called every time any of the subscribed
|
|
76
|
+
* addresses release log. The function accepts the current
|
|
77
|
+
* state, updates the state according to the log, and returns
|
|
78
|
+
* the updated state.
|
|
79
|
+
* @param state - Current state of event subscriber
|
|
80
|
+
* @param log - Log released by one of the subscribed addresses
|
|
81
|
+
* @returns Updates state of the event subscriber after the log
|
|
82
|
+
*/
|
|
83
|
+
async processLog(
|
|
84
|
+
state: DeepReadonly<Pool[]>,
|
|
85
|
+
log: Readonly<Log>,
|
|
86
|
+
): Promise<DeepReadonly<Pool[]> | null> {
|
|
87
|
+
try {
|
|
88
|
+
let event;
|
|
89
|
+
try {
|
|
90
|
+
event = this.logDecoder(log);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (event.name in this.handlers) {
|
|
95
|
+
return await this.handlers[event.name](event, state, log);
|
|
96
|
+
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
catchParseLogError(e, this.logger);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async getStateOrGenerate(
|
|
105
|
+
blockNumber: number,
|
|
106
|
+
readonly: boolean = false,
|
|
107
|
+
): Promise<DeepReadonly<Pool[]>> {
|
|
108
|
+
let state = this.getState(blockNumber);
|
|
109
|
+
if (!state) {
|
|
110
|
+
state = await this.generateState(blockNumber);
|
|
111
|
+
if (!readonly) this.setState(state, blockNumber);
|
|
112
|
+
}
|
|
113
|
+
return state;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The function generates state using on-chain calls. This
|
|
118
|
+
* function is called to regenerate state if the event based
|
|
119
|
+
* system fails to fetch events and the local state is no
|
|
120
|
+
* more correct.
|
|
121
|
+
* @param blockNumber - Blocknumber for which the state should
|
|
122
|
+
* should be generated
|
|
123
|
+
* @returns state of the event subscriber at blocknumber
|
|
124
|
+
*/
|
|
125
|
+
async generateState(blockNumber: number): Promise<DeepReadonly<Pool[]>> {
|
|
126
|
+
const resolverContract = new Contract(
|
|
127
|
+
this.commonAddresses.resolver,
|
|
128
|
+
ResolverABI,
|
|
129
|
+
this.dexHelper.provider,
|
|
130
|
+
);
|
|
131
|
+
const rawResult = await resolverContract.callStatic.getAllPools({
|
|
132
|
+
blockTag: blockNumber,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const pools: Pool[] = rawResult.map((result: any) => ({
|
|
136
|
+
address: result[0],
|
|
137
|
+
token0: result[1],
|
|
138
|
+
token1: result[2],
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
return pools;
|
|
142
|
+
}
|
|
143
|
+
}
|