@paraswap/dex-lib 2.14.0 → 2.14.1
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/config.js +23 -5
- package/build/config.js.map +1 -1
- package/build/dex/index.js +2 -2
- package/build/dex/index.js.map +1 -1
- package/build/dex/simple-exchange.d.ts +1 -0
- package/build/dex/simple-exchange.js +2 -0
- package/build/dex/simple-exchange.js.map +1 -1
- package/build/lib/decoders.d.ts +2 -1
- package/build/lib/decoders.js +8 -1
- package/build/lib/decoders.js.map +1 -1
- package/build/lib/multi-wrapper.d.ts +2 -1
- package/build/lib/multi-wrapper.js.map +1 -1
- package/build/lib/tokens/balancer-fetcher.js +4 -5
- package/build/lib/tokens/balancer-fetcher.js.map +1 -1
- package/build/lib/tokens/utils.d.ts +2 -2
- package/build/lib/tokens/utils.js +1 -1
- package/build/lib/tokens/utils.js.map +1 -1
- package/build/types.d.ts +3 -0
- package/dex-template/__DexName__-e2e.test.ts.template +1 -0
- package/dex-template/__DexName__-events.test.ts.template +1 -0
- package/dex-template/__DexName__-integration.test.ts.template +1 -0
- package/dex-template/__DexName__-pool.ts.template +8 -5
- package/package.json +1 -1
- package/scripts/generate-bytecode.ts +2 -0
- package/src/abi/woo-fi-v2/IntegrationHelper.abi.json +116 -0
- package/src/abi/{woo-fi/Wooracle.abi.json → woo-fi-v2/WooOracleV2.abi.json} +148 -50
- package/src/abi/{woo-fi/WooPP.abi.json → woo-fi-v2/WooPPV2.abi.json} +201 -169
- package/src/config.ts +27 -5
- package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +0 -1
- package/src/dex/index.ts +2 -2
- package/src/dex/simple-exchange.ts +3 -0
- package/src/dex/woo-fi-v2/config.ts +87 -0
- package/src/dex/woo-fi-v2/constants.ts +22 -0
- package/src/dex/woo-fi-v2/decoders.ts +45 -0
- package/src/dex/woo-fi-v2/types.ts +63 -0
- package/src/dex/woo-fi-v2/utils.ts +19 -0
- package/src/dex/woo-fi-v2/woo-fi-v2-e2e.test.ts +216 -0
- package/src/dex/woo-fi-v2/woo-fi-v2-integration.test.ts +495 -0
- package/src/dex/woo-fi-v2/woo-fi-v2-math.ts +195 -0
- package/src/dex/woo-fi-v2/woo-fi-v2-pool.ts +112 -0
- package/src/dex/woo-fi-v2/woo-fi-v2.ts +487 -0
- package/src/lib/decoders.ts +12 -1
- package/src/lib/log-messages-suppressor.ts +173 -0
- package/src/lib/multi-wrapper.ts +2 -1
- package/src/lib/stateful-rpc-poller/state-polling-manager.ts +320 -0
- package/src/lib/stateful-rpc-poller/stateful-rpc-poller.ts +604 -0
- package/src/lib/stateful-rpc-poller/types.ts +66 -0
- package/src/lib/stateful-rpc-poller/utils.ts +23 -0
- package/src/lib/tokens/balancer-fetcher.ts +5 -6
- package/src/lib/tokens/utils.ts +5 -3
- package/src/types.ts +4 -0
- package/tests/constants-e2e.ts +8 -3
- package/tests/tenderly-simulation.ts +1 -1
- package/tests/utils-e2e.ts +2 -0
- package/tests/utils.ts +2 -0
- package/src/abi/woo-fi/WooFeeManager.abi.json +0 -318
- package/src/dex/woo-fi/config.ts +0 -141
- package/src/dex/woo-fi/constants.ts +0 -21
- package/src/dex/woo-fi/types.ts +0 -55
- package/src/dex/woo-fi/woo-fi-decimal-math.ts +0 -45
- package/src/dex/woo-fi/woo-fi-e2e.test.ts +0 -183
- package/src/dex/woo-fi/woo-fi-integration.test.ts +0 -416
- package/src/dex/woo-fi/woo-fi-math.ts +0 -527
- package/src/dex/woo-fi/woo-fi.ts +0 -693
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
import { Interface, Result } from '@ethersproject/abi';
|
|
6
|
+
import { DummyDexHelper } from '../../dex-helper/index';
|
|
7
|
+
import { Network, SwapSide } from '../../constants';
|
|
8
|
+
import { BI_POWS } from '../../bigint-constants';
|
|
9
|
+
import { WooFiV2 } from './woo-fi-v2';
|
|
10
|
+
import {
|
|
11
|
+
checkPoolPrices,
|
|
12
|
+
checkPoolsLiquidity,
|
|
13
|
+
checkConstantPoolPrices,
|
|
14
|
+
} from '../../../tests/utils';
|
|
15
|
+
import { Tokens } from '../../../tests/constants-e2e';
|
|
16
|
+
import { Address } from '@paraswap/core';
|
|
17
|
+
import { ifaces } from './utils';
|
|
18
|
+
|
|
19
|
+
function getReaderCalldata(
|
|
20
|
+
exchangeAddress: string,
|
|
21
|
+
readerIface: Interface,
|
|
22
|
+
amounts: bigint[],
|
|
23
|
+
funcName: string,
|
|
24
|
+
srcTokenAddress: Address,
|
|
25
|
+
destTokenAddress: Address,
|
|
26
|
+
) {
|
|
27
|
+
return amounts.map(amount => ({
|
|
28
|
+
target: exchangeAddress,
|
|
29
|
+
callData: readerIface.encodeFunctionData(funcName, [
|
|
30
|
+
srcTokenAddress,
|
|
31
|
+
destTokenAddress,
|
|
32
|
+
amount,
|
|
33
|
+
]),
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function decodeReaderResult(
|
|
38
|
+
results: Result,
|
|
39
|
+
readerIface: Interface,
|
|
40
|
+
funcName: string,
|
|
41
|
+
) {
|
|
42
|
+
return results.map(result => {
|
|
43
|
+
const parsed = readerIface.decodeFunctionResult(funcName, result);
|
|
44
|
+
return BigInt(parsed[0]._hex);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function checkOnChainPricing(
|
|
49
|
+
wooFiV2: WooFiV2,
|
|
50
|
+
funcName: string,
|
|
51
|
+
blockNumber: number,
|
|
52
|
+
prices: bigint[],
|
|
53
|
+
amounts: bigint[],
|
|
54
|
+
srcTokenAddress: Address,
|
|
55
|
+
destTokenAddress: Address,
|
|
56
|
+
) {
|
|
57
|
+
const exchangeAddress = wooFiV2.config.wooPPV2Address;
|
|
58
|
+
|
|
59
|
+
const readerIface = ifaces.PPV2;
|
|
60
|
+
|
|
61
|
+
const readerCallData = getReaderCalldata(
|
|
62
|
+
exchangeAddress,
|
|
63
|
+
readerIface,
|
|
64
|
+
amounts.slice(1),
|
|
65
|
+
funcName,
|
|
66
|
+
srcTokenAddress,
|
|
67
|
+
destTokenAddress,
|
|
68
|
+
);
|
|
69
|
+
const readerResult = (
|
|
70
|
+
await wooFiV2.dexHelper.multiContract.methods
|
|
71
|
+
.aggregate(readerCallData)
|
|
72
|
+
.call({}, blockNumber)
|
|
73
|
+
).returnData;
|
|
74
|
+
|
|
75
|
+
const expectedPrices = [0n].concat(
|
|
76
|
+
decodeReaderResult(readerResult, readerIface, funcName),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(prices).toEqual(expectedPrices);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function testPricingForPair(
|
|
83
|
+
wooFiV2: WooFiV2,
|
|
84
|
+
network: Network,
|
|
85
|
+
dexKey: string,
|
|
86
|
+
blockNumber: number,
|
|
87
|
+
srcTokenSymbol: string,
|
|
88
|
+
destTokenSymbol: string,
|
|
89
|
+
side: SwapSide,
|
|
90
|
+
amounts: bigint[],
|
|
91
|
+
funcNameToCheck: string,
|
|
92
|
+
expectNoLiquidity: boolean = false,
|
|
93
|
+
) {
|
|
94
|
+
const networkTokens = Tokens[network];
|
|
95
|
+
|
|
96
|
+
const pools = await wooFiV2.getPoolIdentifiers(
|
|
97
|
+
networkTokens[srcTokenSymbol],
|
|
98
|
+
networkTokens[destTokenSymbol],
|
|
99
|
+
side,
|
|
100
|
+
blockNumber,
|
|
101
|
+
);
|
|
102
|
+
console.log(
|
|
103
|
+
`${srcTokenSymbol} <> ${destTokenSymbol} Pool Identifiers: `,
|
|
104
|
+
pools,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (expectNoLiquidity) {
|
|
108
|
+
expect(pools.length).toEqual(0);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
expect(pools.length).toBeGreaterThan(0);
|
|
113
|
+
|
|
114
|
+
const poolPrices = await wooFiV2.getPricesVolume(
|
|
115
|
+
networkTokens[srcTokenSymbol],
|
|
116
|
+
networkTokens[destTokenSymbol],
|
|
117
|
+
amounts,
|
|
118
|
+
side,
|
|
119
|
+
blockNumber,
|
|
120
|
+
pools,
|
|
121
|
+
);
|
|
122
|
+
console.log(
|
|
123
|
+
`${srcTokenSymbol} <> ${destTokenSymbol} Pool Prices: `,
|
|
124
|
+
poolPrices,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
expect(poolPrices).not.toBeNull();
|
|
128
|
+
if (wooFiV2.hasConstantPriceLargeAmounts) {
|
|
129
|
+
checkConstantPoolPrices(poolPrices!, amounts, dexKey);
|
|
130
|
+
} else {
|
|
131
|
+
checkPoolPrices(poolPrices!, amounts, side, dexKey);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Check if onchain pricing equals to calculated ones
|
|
135
|
+
await checkOnChainPricing(
|
|
136
|
+
wooFiV2,
|
|
137
|
+
funcNameToCheck,
|
|
138
|
+
blockNumber,
|
|
139
|
+
poolPrices![0].prices,
|
|
140
|
+
amounts,
|
|
141
|
+
networkTokens[srcTokenSymbol].address,
|
|
142
|
+
networkTokens[destTokenSymbol].address,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function runTestsForChain(
|
|
147
|
+
dexHelper: DummyDexHelper,
|
|
148
|
+
initProps: { dex: WooFiV2; blockNumber: number },
|
|
149
|
+
baseATokenSymbol: string,
|
|
150
|
+
quoteTokenSymbol: string,
|
|
151
|
+
baseBTokenSymbol: string,
|
|
152
|
+
untradableSymbol: string,
|
|
153
|
+
pricingCheckFuncName: string,
|
|
154
|
+
amountDecimalsDiff: number = 0,
|
|
155
|
+
) {
|
|
156
|
+
const network = dexHelper.config.data.network;
|
|
157
|
+
const tokens = Tokens[network];
|
|
158
|
+
|
|
159
|
+
it(`getPoolIdentifiers and getPricesVolume SELL: ${baseATokenSymbol} -> ${quoteTokenSymbol}`, async function () {
|
|
160
|
+
const { dex: wooFiV2, blockNumber } = initProps;
|
|
161
|
+
const dexKey = wooFiV2.dexKey;
|
|
162
|
+
const srcTokenSymbol = baseATokenSymbol;
|
|
163
|
+
const destTokenSymbol = quoteTokenSymbol;
|
|
164
|
+
|
|
165
|
+
const amountsToTrade = [
|
|
166
|
+
0n,
|
|
167
|
+
1n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
168
|
+
2n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
169
|
+
3n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
170
|
+
4n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
171
|
+
5n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
172
|
+
6n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
173
|
+
7n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
174
|
+
8n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
175
|
+
9n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
176
|
+
10n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
await testPricingForPair(
|
|
180
|
+
wooFiV2,
|
|
181
|
+
network,
|
|
182
|
+
dexKey,
|
|
183
|
+
blockNumber,
|
|
184
|
+
srcTokenSymbol,
|
|
185
|
+
destTokenSymbol,
|
|
186
|
+
SwapSide.SELL,
|
|
187
|
+
amountsToTrade,
|
|
188
|
+
pricingCheckFuncName,
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it(`getPoolIdentifiers and getPricesVolume SELL: ${quoteTokenSymbol} -> ${baseATokenSymbol}`, async function () {
|
|
193
|
+
const { dex: wooFiV2, blockNumber } = initProps;
|
|
194
|
+
const dexKey = wooFiV2.dexKey;
|
|
195
|
+
const srcTokenSymbol = quoteTokenSymbol;
|
|
196
|
+
const destTokenSymbol = baseATokenSymbol;
|
|
197
|
+
|
|
198
|
+
const amountsToTrade = [
|
|
199
|
+
0n,
|
|
200
|
+
1n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
201
|
+
2n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
202
|
+
3n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
203
|
+
4n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
204
|
+
5n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
205
|
+
6n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
206
|
+
7n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
207
|
+
8n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
208
|
+
9n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
209
|
+
10n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
await testPricingForPair(
|
|
213
|
+
wooFiV2,
|
|
214
|
+
network,
|
|
215
|
+
dexKey,
|
|
216
|
+
blockNumber,
|
|
217
|
+
srcTokenSymbol,
|
|
218
|
+
destTokenSymbol,
|
|
219
|
+
SwapSide.SELL,
|
|
220
|
+
amountsToTrade,
|
|
221
|
+
pricingCheckFuncName,
|
|
222
|
+
);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it(`getPoolIdentifiers and getPricesVolume SELL: ${baseATokenSymbol} -> ${baseBTokenSymbol}`, async function () {
|
|
226
|
+
const { dex: wooFiV2, blockNumber } = initProps;
|
|
227
|
+
const dexKey = wooFiV2.dexKey;
|
|
228
|
+
const srcTokenSymbol = baseATokenSymbol;
|
|
229
|
+
const destTokenSymbol = baseBTokenSymbol;
|
|
230
|
+
|
|
231
|
+
const amountsToTrade = [
|
|
232
|
+
0n,
|
|
233
|
+
1n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
234
|
+
2n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
235
|
+
3n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
236
|
+
4n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
237
|
+
5n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
238
|
+
6n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
239
|
+
7n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
240
|
+
8n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
241
|
+
9n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
242
|
+
10n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
await testPricingForPair(
|
|
246
|
+
wooFiV2,
|
|
247
|
+
network,
|
|
248
|
+
dexKey,
|
|
249
|
+
blockNumber,
|
|
250
|
+
srcTokenSymbol,
|
|
251
|
+
destTokenSymbol,
|
|
252
|
+
SwapSide.SELL,
|
|
253
|
+
amountsToTrade,
|
|
254
|
+
pricingCheckFuncName,
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('getPoolIdentifiers and getPricesVolume SELL: No Pool', async function () {
|
|
259
|
+
const { dex: wooFiV2, blockNumber } = initProps;
|
|
260
|
+
const dexKey = wooFiV2.dexKey;
|
|
261
|
+
const srcTokenSymbol = quoteTokenSymbol;
|
|
262
|
+
const destTokenSymbol = untradableSymbol;
|
|
263
|
+
|
|
264
|
+
const amountsToTrade = [
|
|
265
|
+
0n,
|
|
266
|
+
1n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
267
|
+
2n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
268
|
+
3n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
269
|
+
4n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
270
|
+
5n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
271
|
+
6n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
272
|
+
7n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
273
|
+
8n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
274
|
+
9n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
275
|
+
10n * BI_POWS[tokens[srcTokenSymbol].decimals - amountDecimalsDiff],
|
|
276
|
+
];
|
|
277
|
+
await testPricingForPair(
|
|
278
|
+
wooFiV2,
|
|
279
|
+
network,
|
|
280
|
+
dexKey,
|
|
281
|
+
blockNumber,
|
|
282
|
+
srcTokenSymbol,
|
|
283
|
+
destTokenSymbol,
|
|
284
|
+
SwapSide.SELL,
|
|
285
|
+
amountsToTrade,
|
|
286
|
+
pricingCheckFuncName,
|
|
287
|
+
true,
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('getTopPoolsForToken', async function () {
|
|
292
|
+
const { dex: wooFiV2 } = initProps;
|
|
293
|
+
const dexKey = wooFiV2.dexKey;
|
|
294
|
+
// We have to check without calling initializePricing, because
|
|
295
|
+
// pool-tracker is not calling that function
|
|
296
|
+
const newWooFiV2 = new WooFiV2(network, dexKey, dexHelper);
|
|
297
|
+
if (newWooFiV2.updatePoolState) {
|
|
298
|
+
await newWooFiV2.updatePoolState();
|
|
299
|
+
}
|
|
300
|
+
const poolLiquidity = await newWooFiV2.getTopPoolsForToken(
|
|
301
|
+
tokens[quoteTokenSymbol].address,
|
|
302
|
+
10,
|
|
303
|
+
);
|
|
304
|
+
console.log(`${quoteTokenSymbol} Top Pools:`, poolLiquidity);
|
|
305
|
+
|
|
306
|
+
if (!newWooFiV2.hasConstantPriceLargeAmounts) {
|
|
307
|
+
checkPoolsLiquidity(
|
|
308
|
+
poolLiquidity,
|
|
309
|
+
Tokens[network][quoteTokenSymbol].address,
|
|
310
|
+
dexKey,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
describe('WooFiV2', function () {
|
|
317
|
+
const dexKey = 'WooFiV2';
|
|
318
|
+
const pricingCheckFuncName = 'tryQuery';
|
|
319
|
+
|
|
320
|
+
describe('Optimism', () => {
|
|
321
|
+
const network = Network.OPTIMISM;
|
|
322
|
+
const dexHelper = new DummyDexHelper(network);
|
|
323
|
+
const initProps: { dex: WooFiV2; blockNumber: number } = {
|
|
324
|
+
dex: new WooFiV2(network, dexKey, dexHelper),
|
|
325
|
+
blockNumber: 0,
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
beforeAll(async () => {
|
|
329
|
+
initProps.blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
330
|
+
await initProps.dex.initializePricing(initProps.blockNumber);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const baseATokenSymbol = 'WETH';
|
|
334
|
+
const quoteTokenSymbol = 'USDC';
|
|
335
|
+
const baseBTokenSymbol = 'WBTC';
|
|
336
|
+
const untradableSymbol = 'POPS';
|
|
337
|
+
|
|
338
|
+
runTestsForChain(
|
|
339
|
+
dexHelper,
|
|
340
|
+
initProps,
|
|
341
|
+
baseATokenSymbol,
|
|
342
|
+
quoteTokenSymbol,
|
|
343
|
+
baseBTokenSymbol,
|
|
344
|
+
untradableSymbol,
|
|
345
|
+
pricingCheckFuncName,
|
|
346
|
+
);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
describe('BSC', () => {
|
|
350
|
+
const network = Network.BSC;
|
|
351
|
+
const dexHelper = new DummyDexHelper(network);
|
|
352
|
+
const initProps: { dex: WooFiV2; blockNumber: number } = {
|
|
353
|
+
dex: new WooFiV2(network, dexKey, dexHelper),
|
|
354
|
+
blockNumber: 0,
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
beforeAll(async () => {
|
|
358
|
+
initProps.blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
359
|
+
await initProps.dex.initializePricing(initProps.blockNumber);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const baseATokenSymbol = 'WBNB';
|
|
363
|
+
const quoteTokenSymbol = 'BUSD';
|
|
364
|
+
const baseBTokenSymbol = 'bBTC';
|
|
365
|
+
const untradableSymbol = 'POPS';
|
|
366
|
+
|
|
367
|
+
runTestsForChain(
|
|
368
|
+
dexHelper,
|
|
369
|
+
initProps,
|
|
370
|
+
baseATokenSymbol,
|
|
371
|
+
quoteTokenSymbol,
|
|
372
|
+
baseBTokenSymbol,
|
|
373
|
+
untradableSymbol,
|
|
374
|
+
pricingCheckFuncName,
|
|
375
|
+
2,
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe('Polygon', () => {
|
|
380
|
+
const network = Network.POLYGON;
|
|
381
|
+
const dexHelper = new DummyDexHelper(network);
|
|
382
|
+
const initProps: { dex: WooFiV2; blockNumber: number } = {
|
|
383
|
+
dex: new WooFiV2(network, dexKey, dexHelper),
|
|
384
|
+
blockNumber: 0,
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
beforeAll(async () => {
|
|
388
|
+
initProps.blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
389
|
+
await initProps.dex.initializePricing(initProps.blockNumber);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const baseATokenSymbol = 'WMATIC';
|
|
393
|
+
const quoteTokenSymbol = 'USDC';
|
|
394
|
+
const baseBTokenSymbol = 'WETH';
|
|
395
|
+
const untradableSymbol = 'POPS';
|
|
396
|
+
|
|
397
|
+
runTestsForChain(
|
|
398
|
+
dexHelper,
|
|
399
|
+
initProps,
|
|
400
|
+
baseATokenSymbol,
|
|
401
|
+
quoteTokenSymbol,
|
|
402
|
+
baseBTokenSymbol,
|
|
403
|
+
untradableSymbol,
|
|
404
|
+
pricingCheckFuncName,
|
|
405
|
+
);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
describe('Fantom', () => {
|
|
409
|
+
const network = Network.FANTOM;
|
|
410
|
+
const dexHelper = new DummyDexHelper(network);
|
|
411
|
+
const initProps: { dex: WooFiV2; blockNumber: number } = {
|
|
412
|
+
dex: new WooFiV2(network, dexKey, dexHelper),
|
|
413
|
+
blockNumber: 0,
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
beforeAll(async () => {
|
|
417
|
+
initProps.blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
418
|
+
await initProps.dex.initializePricing(initProps.blockNumber);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
const baseATokenSymbol = 'WFTM';
|
|
422
|
+
const quoteTokenSymbol = 'USDC';
|
|
423
|
+
const baseBTokenSymbol = 'ETH';
|
|
424
|
+
const untradableSymbol = 'POPS';
|
|
425
|
+
|
|
426
|
+
runTestsForChain(
|
|
427
|
+
dexHelper,
|
|
428
|
+
initProps,
|
|
429
|
+
baseATokenSymbol,
|
|
430
|
+
quoteTokenSymbol,
|
|
431
|
+
baseBTokenSymbol,
|
|
432
|
+
untradableSymbol,
|
|
433
|
+
pricingCheckFuncName,
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
describe('Arbitrum', () => {
|
|
438
|
+
const network = Network.ARBITRUM;
|
|
439
|
+
const dexHelper = new DummyDexHelper(network);
|
|
440
|
+
const initProps: { dex: WooFiV2; blockNumber: number } = {
|
|
441
|
+
dex: new WooFiV2(network, dexKey, dexHelper),
|
|
442
|
+
blockNumber: 0,
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
beforeAll(async () => {
|
|
446
|
+
initProps.blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
447
|
+
await initProps.dex.initializePricing(initProps.blockNumber);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const baseATokenSymbol = 'WETH';
|
|
451
|
+
const quoteTokenSymbol = 'USDC';
|
|
452
|
+
const baseBTokenSymbol = 'WBTC';
|
|
453
|
+
const untradableSymbol = 'POPS';
|
|
454
|
+
|
|
455
|
+
runTestsForChain(
|
|
456
|
+
dexHelper,
|
|
457
|
+
initProps,
|
|
458
|
+
baseATokenSymbol,
|
|
459
|
+
quoteTokenSymbol,
|
|
460
|
+
baseBTokenSymbol,
|
|
461
|
+
untradableSymbol,
|
|
462
|
+
pricingCheckFuncName,
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
describe('Avalanche', () => {
|
|
467
|
+
const network = Network.AVALANCHE;
|
|
468
|
+
const dexHelper = new DummyDexHelper(network);
|
|
469
|
+
const initProps: { dex: WooFiV2; blockNumber: number } = {
|
|
470
|
+
dex: new WooFiV2(network, dexKey, dexHelper),
|
|
471
|
+
blockNumber: 0,
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
beforeAll(async () => {
|
|
475
|
+
initProps.blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
476
|
+
await initProps.dex.initializePricing(initProps.blockNumber);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const baseATokenSymbol = 'WAVAX';
|
|
480
|
+
const quoteTokenSymbol = 'USDC';
|
|
481
|
+
const baseBTokenSymbol = 'BTCb';
|
|
482
|
+
const untradableSymbol = 'POPS';
|
|
483
|
+
|
|
484
|
+
runTestsForChain(
|
|
485
|
+
dexHelper,
|
|
486
|
+
initProps,
|
|
487
|
+
baseATokenSymbol,
|
|
488
|
+
quoteTokenSymbol,
|
|
489
|
+
baseBTokenSymbol,
|
|
490
|
+
untradableSymbol,
|
|
491
|
+
pricingCheckFuncName,
|
|
492
|
+
3,
|
|
493
|
+
);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { PoolState, TokenState } from './types';
|
|
2
|
+
import { Address, Logger } from '../../types';
|
|
3
|
+
import { BI_POWS } from '../../bigint-constants';
|
|
4
|
+
import { _require } from '../../utils';
|
|
5
|
+
import { NULL_STATE } from './constants';
|
|
6
|
+
|
|
7
|
+
function handleMathError(e: unknown, logger: Logger) {
|
|
8
|
+
if (e instanceof Error && e.message.startsWith('WooGuardian:')) {
|
|
9
|
+
logger.warn('WooFi Guardian check Error:', e);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
logger.error('Unexpected error in WooFi math:', e);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function max(a: bigint, b: bigint): bigint {
|
|
16
|
+
return a > b ? a : b;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class WooFiV2Math {
|
|
20
|
+
state: PoolState = NULL_STATE;
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
private readonly logger: Logger,
|
|
24
|
+
private readonly _quoteToken: Address,
|
|
25
|
+
) {}
|
|
26
|
+
|
|
27
|
+
query(
|
|
28
|
+
fromToken: Address,
|
|
29
|
+
toToken: Address,
|
|
30
|
+
fromAmounts: bigint[],
|
|
31
|
+
): bigint[] | null {
|
|
32
|
+
try {
|
|
33
|
+
if (fromToken === this._quoteToken) {
|
|
34
|
+
return this._querySellQuote(toToken, fromAmounts);
|
|
35
|
+
} else if (toToken === this._quoteToken) {
|
|
36
|
+
return this._querySellBase(fromToken, fromAmounts);
|
|
37
|
+
} else {
|
|
38
|
+
return this._queryBaseToBase(fromToken, toToken, fromAmounts);
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
handleMathError(e, this.logger);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_querySellBase(baseToken: Address, baseAmounts: bigint[]): bigint[] | null {
|
|
47
|
+
try {
|
|
48
|
+
const _state = this.state.tokenStates[baseToken];
|
|
49
|
+
let quoteAmounts = this._calcQuoteAmountSellBase(
|
|
50
|
+
baseToken,
|
|
51
|
+
baseAmounts,
|
|
52
|
+
_state,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
quoteAmounts = quoteAmounts.map(amount => {
|
|
56
|
+
return amount <= this.state.tokenInfos[this._quoteToken].reserve
|
|
57
|
+
? amount
|
|
58
|
+
: 0n;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const feeRate = this.state.tokenInfos[baseToken].feeRate;
|
|
62
|
+
return this._takeFee(quoteAmounts, feeRate);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
handleMathError(e, this.logger);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_querySellQuote(baseToken: Address, quoteAmounts: bigint[]): bigint[] | null {
|
|
70
|
+
try {
|
|
71
|
+
const feeRate = this.state.tokenInfos[baseToken].feeRate;
|
|
72
|
+
quoteAmounts = this._takeFee(quoteAmounts, feeRate);
|
|
73
|
+
|
|
74
|
+
const _state = this.state.tokenStates[baseToken];
|
|
75
|
+
let toAmounts = this._calcBaseAmountSellQuote(
|
|
76
|
+
baseToken,
|
|
77
|
+
quoteAmounts,
|
|
78
|
+
_state,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const baseReserve = this.state.tokenInfos[baseToken].reserve;
|
|
82
|
+
return toAmounts.map(amount => {
|
|
83
|
+
return amount <= baseReserve ? amount : 0n;
|
|
84
|
+
});
|
|
85
|
+
} catch (e) {
|
|
86
|
+
handleMathError(e, this.logger);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
_queryBaseToBase(
|
|
92
|
+
baseToken1: Address,
|
|
93
|
+
baseToken2: Address,
|
|
94
|
+
base1Amounts: bigint[],
|
|
95
|
+
): bigint[] | null {
|
|
96
|
+
try {
|
|
97
|
+
let state1: TokenState = this.state.tokenStates[baseToken1];
|
|
98
|
+
let state2: TokenState = this.state.tokenStates[baseToken2];
|
|
99
|
+
|
|
100
|
+
const feeRate1 = this.state.tokenInfos[baseToken1].feeRate;
|
|
101
|
+
const feeRate2 = this.state.tokenInfos[baseToken2].feeRate;
|
|
102
|
+
|
|
103
|
+
const spread = max(state1.spread, state2.spread) / 2n;
|
|
104
|
+
const feeRate = max(feeRate1, feeRate2);
|
|
105
|
+
|
|
106
|
+
state1.spread = spread;
|
|
107
|
+
state2.spread = spread;
|
|
108
|
+
|
|
109
|
+
let quoteAmounts = this._calcQuoteAmountSellBase(
|
|
110
|
+
baseToken1,
|
|
111
|
+
base1Amounts,
|
|
112
|
+
state1,
|
|
113
|
+
);
|
|
114
|
+
quoteAmounts = this._takeFeeAndCheckReserve(quoteAmounts, feeRate);
|
|
115
|
+
|
|
116
|
+
let toAmounts = this._calcBaseAmountSellQuote(
|
|
117
|
+
baseToken2,
|
|
118
|
+
quoteAmounts,
|
|
119
|
+
state2,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
return toAmounts.map(toAmount => {
|
|
123
|
+
return toAmount <= this.state.tokenInfos[baseToken2].reserve
|
|
124
|
+
? toAmount
|
|
125
|
+
: 0n;
|
|
126
|
+
});
|
|
127
|
+
} catch (e) {
|
|
128
|
+
handleMathError(e, this.logger);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
protected _takeFeeAndCheckReserve(
|
|
134
|
+
amounts: bigint[],
|
|
135
|
+
feeRate: bigint,
|
|
136
|
+
): bigint[] {
|
|
137
|
+
return amounts.map(amount => {
|
|
138
|
+
if (amount === 0n) return 0n;
|
|
139
|
+
const lpFee = (amount * feeRate) / BI_POWS[5];
|
|
140
|
+
const reserve = this.state.tokenInfos[this._quoteToken].reserve;
|
|
141
|
+
return lpFee > reserve ? 0n : amount - lpFee;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
protected _takeFee(amounts: bigint[], feeRate: bigint): bigint[] {
|
|
146
|
+
return amounts.map(amount => {
|
|
147
|
+
const lpFee = (amount * feeRate) / BI_POWS[5];
|
|
148
|
+
return amount === 0n ? 0n : amount - lpFee;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
protected _calcQuoteAmountSellBase(
|
|
153
|
+
baseToken: Address,
|
|
154
|
+
baseAmounts: bigint[],
|
|
155
|
+
state: TokenState,
|
|
156
|
+
): bigint[] {
|
|
157
|
+
let { priceDec, quoteDec, baseDec } = this.state.decimals[baseToken];
|
|
158
|
+
|
|
159
|
+
return baseAmounts.map(baseAmount => {
|
|
160
|
+
if (baseAmount === 0n) return 0n;
|
|
161
|
+
|
|
162
|
+
const coef: bigint =
|
|
163
|
+
BI_POWS[18] -
|
|
164
|
+
(state.coeff * baseAmount * state.price) / baseDec / priceDec -
|
|
165
|
+
state.spread;
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
(((baseAmount * quoteDec * state.price) / priceDec) * coef) /
|
|
169
|
+
BI_POWS[18] /
|
|
170
|
+
baseDec
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
protected _calcBaseAmountSellQuote(
|
|
176
|
+
baseToken: string,
|
|
177
|
+
quoteAmounts: bigint[],
|
|
178
|
+
state: TokenState,
|
|
179
|
+
): bigint[] {
|
|
180
|
+
let { priceDec, quoteDec, baseDec } = this.state.decimals[baseToken];
|
|
181
|
+
|
|
182
|
+
return quoteAmounts.map(quoteAmount => {
|
|
183
|
+
if (quoteAmount === 0n) return 0n;
|
|
184
|
+
|
|
185
|
+
const coef: bigint =
|
|
186
|
+
BI_POWS[18] - (quoteAmount * state.coeff) / quoteDec - state.spread;
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
(((quoteAmount * baseDec * priceDec) / state.price) * coef) /
|
|
190
|
+
BI_POWS[18] /
|
|
191
|
+
quoteDec
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|