@paraswap/dex-lib 3.4.2-balancer-v2-single-swap.0 → 3.4.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/README.md +377 -377
- package/build/config.js +9 -5
- package/build/config.js.map +1 -1
- package/build/dex/algebra/algebra.js +25 -25
- package/build/dex/balancer-v1/balancer-v1.js +34 -34
- package/build/dex/balancer-v2/balancer-v2.d.ts +1 -1
- package/build/dex/balancer-v2/balancer-v2.js +65 -78
- package/build/dex/balancer-v2/balancer-v2.js.map +1 -1
- package/build/dex/camelot/camelot.js +27 -27
- package/build/dex/kyberdmm/kyberdmm.js +27 -27
- package/build/dex/pancakeswap-v3/pancakeswap-v3.js +25 -25
- package/build/dex/solidly/solidly.js +27 -27
- package/build/dex/solidly-v3/solidly-v3.js +25 -25
- package/build/dex/swaap-v2/config.js +15 -0
- package/build/dex/swaap-v2/config.js.map +1 -1
- package/build/dex/uniswap-v2/uniswap-v2.js +26 -26
- package/build/dex/uniswap-v3/uniswap-v3.js +25 -25
- package/funding.json +5 -5
- package/package.json +1 -1
- package/src/config.ts +500 -496
- package/src/constants.ts +67 -67
- package/src/dex/aave-v2/aave-v2.ts +409 -409
- package/src/dex/aave-v3/aave-v3.ts +320 -320
- package/src/dex/algebra/algebra.ts +1180 -1180
- package/src/dex/angle-staked-stable/angle-staked-stable.ts +347 -347
- package/src/dex/augustus-rfq.ts +180 -180
- package/src/dex/bProtocol/bProtocol.ts +108 -108
- package/src/dex/balancer-v1/balancer-v1.ts +572 -572
- package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +1699 -1699
- package/src/dex/balancer-v2/balancer-v2.ts +1669 -1701
- package/src/dex/bancor/bancor.ts +128 -128
- package/src/dex/camelot/camelot.ts +873 -873
- package/src/dex/compound/compound.ts +107 -107
- package/src/dex/curve-v1/config.ts +1179 -1179
- package/src/dex/curve-v1/curve-v1-e2e.test.ts +507 -507
- package/src/dex/curve-v1/curve-v1.ts +1315 -1315
- package/src/dex/curve-v1-factory/config.ts +1018 -1018
- package/src/dex/curve-v1-factory/curve-v1-factory.ts +1288 -1288
- package/src/dex/curve-v2/curve-v2.ts +440 -440
- package/src/dex/dexalot/dexalot.ts +1100 -1100
- package/src/dex/dodo-v1/dodo-v1.ts +132 -132
- package/src/dex/dodo-v2.ts +266 -266
- package/src/dex/etherfi.ts +239 -239
- package/src/dex/generic-rfq/generic-rfq.ts +431 -431
- package/src/dex/gmx/gmx.ts +321 -321
- package/src/dex/hashflow/hashflow.ts +1135 -1135
- package/src/dex/idex.ts +254 -254
- package/src/dex/index.ts +357 -357
- package/src/dex/jarvis-v6/jarvis-v6.ts +509 -509
- package/src/dex/jarvis.ts +262 -262
- package/src/dex/kyberdmm/kyberdmm.ts +649 -649
- package/src/dex/lido/lido.ts +110 -110
- package/src/dex/maker-psm/maker-psm.ts +592 -592
- package/src/dex/maverick-v1/maverick-v1.ts +537 -537
- package/src/dex/nerve/config.ts +635 -635
- package/src/dex/nerve/nerve-e2e.test.ts +731 -731
- package/src/dex/nerve/nerve-events.test.ts +911 -911
- package/src/dex/nerve/nerve.ts +467 -467
- package/src/dex/onebit/onebit.ts +113 -113
- package/src/dex/pancakeswap-v3/pancakeswap-v3.ts +1150 -1150
- package/src/dex/paraswap-limit-orders/config.ts +60 -60
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders-e2e.test.ts +137 -137
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders-integration.test.ts +328 -328
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders.ts +631 -631
- package/src/dex/polygon-migrator/polygon-migrator.ts +194 -194
- package/src/dex/quick-perps/quick-perps.ts +337 -337
- package/src/dex/quickswap/config.ts +16 -16
- package/src/dex/quickswap/quickswap-e2e.test.ts +384 -384
- package/src/dex/smoothy/smoothy.ts +110 -110
- package/src/dex/solidly/config.ts +225 -225
- package/src/dex/solidly/solidly-e2e.test.ts +1421 -1421
- package/src/dex/solidly/solidly-integration.test.ts +1753 -1753
- package/src/dex/solidly/solidly.ts +687 -687
- package/src/dex/solidly-v3/solidly-v3.ts +968 -968
- package/src/dex/spark/spark.ts +284 -284
- package/src/dex/stable-pool/stable-pool.ts +129 -129
- package/src/dex/swaap-v2/config.ts +15 -0
- package/src/dex/swaap-v2/swaap-v2-e2e.test.ts +233 -17
- package/src/dex/swaap-v2/swaap-v2-integration.test.ts +1 -1
- package/src/dex/swaap-v2/swaap-v2.ts +1056 -1056
- package/src/dex/swell/swell.ts +312 -312
- package/src/dex/synthetix/synthetix.ts +420 -420
- package/src/dex/trader-joe-v2.1/trader-joe-v2.1.ts +180 -180
- package/src/dex/uniswap-v2/config.ts +637 -637
- package/src/dex/uniswap-v2/constants.ts +58 -58
- package/src/dex/uniswap-v2/uniswap-v2-e2e-avalanche.test.ts +1257 -1257
- package/src/dex/uniswap-v2/uniswap-v2-e2e-base.test.ts +42 -42
- package/src/dex/uniswap-v2/uniswap-v2-e2e-bsc.test.ts +1295 -1295
- package/src/dex/uniswap-v2/uniswap-v2-e2e-mainnet.test.ts +1834 -1834
- package/src/dex/uniswap-v2/uniswap-v2-e2e-polygon.test.ts +871 -871
- package/src/dex/uniswap-v2/uniswap-v2.ts +1117 -1117
- package/src/dex/uniswap-v3/uniswap-v3.ts +1501 -1501
- package/src/dex/uniswap-v3.ts +242 -242
- package/src/dex/weth/config.ts +73 -73
- package/src/dex/weth/weth.ts +230 -230
- package/src/dex/wombat/wombat.ts +467 -467
- package/src/dex/woo-fi-v2/woo-fi-v2.ts +529 -529
- package/src/dex/wsteth/wsteth.ts +304 -304
- package/src/dex/zerox/index.ts +328 -328
- package/src/executor/Executor01BytecodeBuilder.ts +474 -474
- package/src/executor/Executor02BytecodeBuilder.ts +1392 -1392
- package/src/executor/ExecutorBytecodeBuilder.ts +390 -390
- package/src/executor/ExecutorDetector.ts +131 -131
- package/src/executor/constants.ts +66 -66
- package/src/executor/executor01-bytecode-builder-e2e.test.ts +1478 -1478
- package/src/executor/utils.ts +62 -62
- package/src/router/simpleswap.ts +366 -366
- package/src/types.ts +342 -342
- package/tests/constants-e2e.ts +1632 -1622
- package/tests/generate-new-limit-order.ts +107 -107
package/src/dex/nerve/nerve.ts
CHANGED
|
@@ -1,467 +1,467 @@
|
|
|
1
|
-
import { Interface } from '@ethersproject/abi';
|
|
2
|
-
import { DeepReadonly } from 'ts-essentials';
|
|
3
|
-
import {
|
|
4
|
-
Token,
|
|
5
|
-
Address,
|
|
6
|
-
ExchangePrices,
|
|
7
|
-
PoolPrices,
|
|
8
|
-
AdapterExchangeParam,
|
|
9
|
-
SimpleExchangeParam,
|
|
10
|
-
PoolLiquidity,
|
|
11
|
-
Logger,
|
|
12
|
-
NumberAsString,
|
|
13
|
-
DexExchangeParam,
|
|
14
|
-
} from '../../types';
|
|
15
|
-
import nervePoolABIDefault from '../../abi/nerve/nerve-pool.json';
|
|
16
|
-
import { SwapSide, Network } from '../../constants';
|
|
17
|
-
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
18
|
-
import { getDexKeysWithNetwork, getBigIntPow } from '../../utils';
|
|
19
|
-
import { IDex } from '../../dex/idex';
|
|
20
|
-
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
21
|
-
import {
|
|
22
|
-
NerveData,
|
|
23
|
-
PoolState,
|
|
24
|
-
DexParams,
|
|
25
|
-
EventPoolMappings,
|
|
26
|
-
OptimizedNerveData,
|
|
27
|
-
NervePoolFunctions,
|
|
28
|
-
NervePoolSwapParams,
|
|
29
|
-
NervePoolConfig,
|
|
30
|
-
} from './types';
|
|
31
|
-
import { SimpleExchange } from '../simple-exchange';
|
|
32
|
-
import { NerveConfig, Adapters, NERVE_GAS_COST } from './config';
|
|
33
|
-
import { NerveEventPool } from './nerve-pool';
|
|
34
|
-
import _ from 'lodash';
|
|
35
|
-
import { bigIntify } from './utils';
|
|
36
|
-
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
37
|
-
|
|
38
|
-
export class Nerve
|
|
39
|
-
extends SimpleExchange
|
|
40
|
-
implements IDex<NerveData, DexParams, OptimizedNerveData>
|
|
41
|
-
{
|
|
42
|
-
protected eventPools: EventPoolMappings = {};
|
|
43
|
-
|
|
44
|
-
readonly hasConstantPriceLargeAmounts = false;
|
|
45
|
-
readonly isFeeOnTransferSupported = false;
|
|
46
|
-
|
|
47
|
-
readonly minConversionRate = '1';
|
|
48
|
-
|
|
49
|
-
public static dexKeysWithNetwork: {
|
|
50
|
-
key: string;
|
|
51
|
-
networks: Network[];
|
|
52
|
-
}[] = getDexKeysWithNetwork(NerveConfig);
|
|
53
|
-
|
|
54
|
-
logger: Logger;
|
|
55
|
-
|
|
56
|
-
static getIdentifier(dexKey: string, poolAddress: string) {
|
|
57
|
-
return `${dexKey.toLowerCase()}_${poolAddress.toLowerCase()}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
constructor(
|
|
61
|
-
protected network: Network,
|
|
62
|
-
dexKey: string,
|
|
63
|
-
protected dexHelper: IDexHelper,
|
|
64
|
-
protected adapters = Adapters[network] || {},
|
|
65
|
-
protected poolConfigs = NerveConfig[dexKey][network].poolConfigs,
|
|
66
|
-
protected nervePoolIface = new Interface(nervePoolABIDefault),
|
|
67
|
-
) {
|
|
68
|
-
super(dexHelper, dexKey);
|
|
69
|
-
this.logger = dexHelper.getLogger(dexKey);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
get allPools() {
|
|
73
|
-
return Object.values(this.eventPools);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async setupEventPool(poolConfig: NervePoolConfig, blockNumber: number) {
|
|
77
|
-
const poolIdentifier = Nerve.getIdentifier(this.dexKey, poolConfig.address);
|
|
78
|
-
|
|
79
|
-
if (!poolConfig.isMetapool) {
|
|
80
|
-
const newPool = new NerveEventPool(
|
|
81
|
-
this.dexKey,
|
|
82
|
-
this.network,
|
|
83
|
-
this.dexHelper,
|
|
84
|
-
this.logger,
|
|
85
|
-
poolConfig.name,
|
|
86
|
-
);
|
|
87
|
-
this.eventPools[poolIdentifier] = newPool;
|
|
88
|
-
|
|
89
|
-
// Generate first state for the blockNumber and subscribe to logs
|
|
90
|
-
await newPool.initialize(blockNumber);
|
|
91
|
-
} else {
|
|
92
|
-
this.logger.warn(
|
|
93
|
-
`We don't support metapools for Nerve. Check config: ${poolConfig.name}`,
|
|
94
|
-
);
|
|
95
|
-
// No need to throw because we want to initialize other pools
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async initializePricing(blockNumber: number) {
|
|
100
|
-
await Promise.all(
|
|
101
|
-
Object.values(this.poolConfigs).map(
|
|
102
|
-
async poolConfig => await this.setupEventPool(poolConfig, blockNumber),
|
|
103
|
-
),
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
108
|
-
return this.adapters[side] || null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async getStates(
|
|
112
|
-
pools?: NerveEventPool[],
|
|
113
|
-
blockNumber?: number,
|
|
114
|
-
): Promise<DeepReadonly<{ state: PoolState; pool: NerveEventPool }[]>> {
|
|
115
|
-
const _pools = pools === undefined ? this.allPools : pools;
|
|
116
|
-
|
|
117
|
-
const _blockNumber =
|
|
118
|
-
blockNumber === undefined
|
|
119
|
-
? await this.dexHelper.web3Provider.eth.getBlockNumber()
|
|
120
|
-
: blockNumber;
|
|
121
|
-
|
|
122
|
-
// TODO: Need to batch this RPC calls in one multicall
|
|
123
|
-
return Promise.all(
|
|
124
|
-
_pools.map(async eventPool => {
|
|
125
|
-
let state = eventPool.getState(_blockNumber);
|
|
126
|
-
if (!state || !state.isValid) {
|
|
127
|
-
this.logger.info(
|
|
128
|
-
`State for ${this.dexKey} pool ${eventPool.name} on ${this.network} is stale or invalid on block ${_blockNumber}. Generating new one`,
|
|
129
|
-
);
|
|
130
|
-
const newState = await eventPool.generateState(_blockNumber);
|
|
131
|
-
eventPool.setState(newState, _blockNumber);
|
|
132
|
-
return { state: newState, pool: eventPool };
|
|
133
|
-
} else {
|
|
134
|
-
return { state, pool: eventPool };
|
|
135
|
-
}
|
|
136
|
-
}),
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async getPoolIdentifiers(
|
|
141
|
-
srcToken: Token,
|
|
142
|
-
destToken: Token,
|
|
143
|
-
side: SwapSide,
|
|
144
|
-
blockNumber: number,
|
|
145
|
-
): Promise<string[]> {
|
|
146
|
-
if (side === SwapSide.BUY) return [];
|
|
147
|
-
|
|
148
|
-
const _srcToken = this.dexHelper.config.wrapETH(srcToken);
|
|
149
|
-
const _destToken = this.dexHelper.config.wrapETH(destToken);
|
|
150
|
-
|
|
151
|
-
return this.allPools
|
|
152
|
-
.filter(pool => {
|
|
153
|
-
return (
|
|
154
|
-
pool.tokenAddresses.includes(_srcToken.address.toLowerCase()) &&
|
|
155
|
-
pool.tokenAddresses.includes(_destToken.address.toLowerCase())
|
|
156
|
-
);
|
|
157
|
-
})
|
|
158
|
-
.map(pool => Nerve.getIdentifier(this.dexKey, pool.address));
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async getPricesVolume(
|
|
162
|
-
srcToken: Token,
|
|
163
|
-
destToken: Token,
|
|
164
|
-
amounts: bigint[],
|
|
165
|
-
side: SwapSide,
|
|
166
|
-
blockNumber: number,
|
|
167
|
-
limitPools?: string[],
|
|
168
|
-
): Promise<null | ExchangePrices<NerveData>> {
|
|
169
|
-
try {
|
|
170
|
-
if (side === SwapSide.BUY) return null;
|
|
171
|
-
|
|
172
|
-
const _srcToken = this.dexHelper.config.wrapETH(srcToken);
|
|
173
|
-
const _destToken = this.dexHelper.config.wrapETH(destToken);
|
|
174
|
-
|
|
175
|
-
if (
|
|
176
|
-
_srcToken.address.toLowerCase() === _destToken.address.toLowerCase()
|
|
177
|
-
) {
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const filterPoolsByIdentifiers = (
|
|
182
|
-
identifiers: string[],
|
|
183
|
-
pools: NerveEventPool[],
|
|
184
|
-
) => {
|
|
185
|
-
return pools.filter(pool =>
|
|
186
|
-
identifiers.includes(Nerve.getIdentifier(this.dexKey, pool.address)),
|
|
187
|
-
);
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
const selectedPools = !limitPools
|
|
191
|
-
? filterPoolsByIdentifiers(
|
|
192
|
-
await this.getPoolIdentifiers(
|
|
193
|
-
_srcToken,
|
|
194
|
-
_destToken,
|
|
195
|
-
side,
|
|
196
|
-
blockNumber,
|
|
197
|
-
),
|
|
198
|
-
this.allPools,
|
|
199
|
-
)
|
|
200
|
-
: filterPoolsByIdentifiers(limitPools, this.allPools);
|
|
201
|
-
|
|
202
|
-
const statePoolPair = await this.getStates(selectedPools, blockNumber);
|
|
203
|
-
|
|
204
|
-
// here side === SwapSide.SELL
|
|
205
|
-
const unitVolume = getBigIntPow(_srcToken.decimals);
|
|
206
|
-
|
|
207
|
-
const _amounts = [unitVolume, ...amounts.slice(1)];
|
|
208
|
-
|
|
209
|
-
const result: ExchangePrices<NerveData> = [];
|
|
210
|
-
for (const { pool, state } of statePoolPair) {
|
|
211
|
-
if (state.paused) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const srcIndex = pool.tokens.findIndex(
|
|
216
|
-
token =>
|
|
217
|
-
token.address.toLowerCase() === _srcToken.address.toLowerCase(),
|
|
218
|
-
);
|
|
219
|
-
const destIndex = pool.tokens.findIndex(
|
|
220
|
-
token =>
|
|
221
|
-
token.address.toLowerCase() === _destToken.address.toLowerCase(),
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
if (srcIndex === -1 || destIndex === -1) {
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const _prices: bigint[] = [];
|
|
229
|
-
for (const _amount of _amounts) {
|
|
230
|
-
try {
|
|
231
|
-
const out = pool.math.calculateSwap(
|
|
232
|
-
state,
|
|
233
|
-
srcIndex,
|
|
234
|
-
destIndex,
|
|
235
|
-
_amount,
|
|
236
|
-
// Actually we need here block timestamp, but +- 15 seconds shouldn't
|
|
237
|
-
// affect the calculations
|
|
238
|
-
bigIntify((Date.now() / 1000).toFixed(0)),
|
|
239
|
-
);
|
|
240
|
-
_prices.push(out.dy);
|
|
241
|
-
} catch (e) {
|
|
242
|
-
// Something unexpected happen, so set invalidated state.
|
|
243
|
-
// Later it will be regenerated
|
|
244
|
-
pool.setState({ ...state, isValid: false }, blockNumber);
|
|
245
|
-
this.logger.error(
|
|
246
|
-
`${this.dexKey} protocol ${pool.name} (${pool.address}) pool can not calculate out swap for amount ${_amount}`,
|
|
247
|
-
e,
|
|
248
|
-
);
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const unit = _prices[0];
|
|
254
|
-
|
|
255
|
-
result.push({
|
|
256
|
-
unit,
|
|
257
|
-
prices: [0n, ..._prices.slice(1)],
|
|
258
|
-
data: {
|
|
259
|
-
i: srcIndex.toString(),
|
|
260
|
-
j: destIndex.toString(),
|
|
261
|
-
exchange: pool.address,
|
|
262
|
-
deadline: (Math.floor(Date.now() / 1000) + 10 * 60).toString(),
|
|
263
|
-
},
|
|
264
|
-
poolIdentifier: Nerve.getIdentifier(this.dexKey, pool.address),
|
|
265
|
-
exchange: this.dexKey,
|
|
266
|
-
gasCost: NERVE_GAS_COST,
|
|
267
|
-
poolAddresses: [pool.address],
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
return result;
|
|
271
|
-
} catch (e) {
|
|
272
|
-
if (blockNumber === 0)
|
|
273
|
-
this.logger.error(
|
|
274
|
-
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
275
|
-
);
|
|
276
|
-
this.logger.error(`Error_getPrices:`, e);
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
282
|
-
getCalldataGasCost(poolPrices: PoolPrices<NerveData>): number | number[] {
|
|
283
|
-
return (
|
|
284
|
-
CALLDATA_GAS_COST.DEX_OVERHEAD +
|
|
285
|
-
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
286
|
-
CALLDATA_GAS_COST.INDEX +
|
|
287
|
-
CALLDATA_GAS_COST.INDEX +
|
|
288
|
-
CALLDATA_GAS_COST.TIMESTAMP
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
getAdapterParam(
|
|
293
|
-
srcToken: string,
|
|
294
|
-
destToken: string,
|
|
295
|
-
srcAmount: string,
|
|
296
|
-
destAmount: string,
|
|
297
|
-
data: OptimizedNerveData,
|
|
298
|
-
side: SwapSide,
|
|
299
|
-
): AdapterExchangeParam {
|
|
300
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
301
|
-
|
|
302
|
-
const { i, j, deadline } = data;
|
|
303
|
-
const payload = this.abiCoder.encodeParameter(
|
|
304
|
-
{
|
|
305
|
-
ParentStruct: {
|
|
306
|
-
i: 'int128',
|
|
307
|
-
j: 'int128',
|
|
308
|
-
deadline: 'uint256',
|
|
309
|
-
},
|
|
310
|
-
},
|
|
311
|
-
{ i, j, deadline },
|
|
312
|
-
);
|
|
313
|
-
return {
|
|
314
|
-
targetExchange: data.exchange,
|
|
315
|
-
payload,
|
|
316
|
-
networkFee: '0',
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
async getSimpleParam(
|
|
321
|
-
srcToken: string,
|
|
322
|
-
destToken: string,
|
|
323
|
-
srcAmount: string,
|
|
324
|
-
destAmount: string,
|
|
325
|
-
data: OptimizedNerveData,
|
|
326
|
-
side: SwapSide,
|
|
327
|
-
): Promise<SimpleExchangeParam> {
|
|
328
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
329
|
-
|
|
330
|
-
const { exchange, i, j, deadline } = data;
|
|
331
|
-
|
|
332
|
-
const swapFunctionParam: NervePoolSwapParams = [
|
|
333
|
-
i,
|
|
334
|
-
j,
|
|
335
|
-
srcAmount,
|
|
336
|
-
this.minConversionRate,
|
|
337
|
-
deadline,
|
|
338
|
-
];
|
|
339
|
-
|
|
340
|
-
const swapData = this.nervePoolIface.encodeFunctionData(
|
|
341
|
-
NervePoolFunctions.swap,
|
|
342
|
-
swapFunctionParam,
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
return this.buildSimpleParamWithoutWETHConversion(
|
|
346
|
-
srcToken,
|
|
347
|
-
srcAmount,
|
|
348
|
-
destToken,
|
|
349
|
-
destAmount,
|
|
350
|
-
swapData,
|
|
351
|
-
exchange,
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
getDexParam(
|
|
356
|
-
srcToken: Address,
|
|
357
|
-
destToken: Address,
|
|
358
|
-
srcAmount: NumberAsString,
|
|
359
|
-
destAmount: NumberAsString,
|
|
360
|
-
recipient: Address,
|
|
361
|
-
data: OptimizedNerveData,
|
|
362
|
-
side: SwapSide,
|
|
363
|
-
): DexExchangeParam {
|
|
364
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
365
|
-
|
|
366
|
-
const { exchange, i, j, deadline } = data;
|
|
367
|
-
|
|
368
|
-
const swapFunctionParam: NervePoolSwapParams = [
|
|
369
|
-
i,
|
|
370
|
-
j,
|
|
371
|
-
srcAmount,
|
|
372
|
-
this.minConversionRate,
|
|
373
|
-
deadline,
|
|
374
|
-
];
|
|
375
|
-
|
|
376
|
-
const swapData = this.nervePoolIface.encodeFunctionData(
|
|
377
|
-
NervePoolFunctions.swap,
|
|
378
|
-
swapFunctionParam,
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
return {
|
|
382
|
-
needWrapNative: this.needWrapNative,
|
|
383
|
-
dexFuncHasRecipient: false,
|
|
384
|
-
exchangeData: swapData,
|
|
385
|
-
targetExchange: exchange,
|
|
386
|
-
returnAmountPos:
|
|
387
|
-
side === SwapSide.SELL
|
|
388
|
-
? extractReturnAmountPosition(
|
|
389
|
-
this.nervePoolIface,
|
|
390
|
-
NervePoolFunctions.swap,
|
|
391
|
-
)
|
|
392
|
-
: undefined,
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
async getTopPoolsForToken(
|
|
397
|
-
tokenAddress: Address,
|
|
398
|
-
limit: number,
|
|
399
|
-
): Promise<PoolLiquidity[]> {
|
|
400
|
-
// We set decimals to default as we don't really care of actual number.
|
|
401
|
-
// We use here only address
|
|
402
|
-
const wrappedTokenAddress = this.dexHelper.config.wrapETH({
|
|
403
|
-
address: tokenAddress,
|
|
404
|
-
decimals: 18,
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
const selectedPools = this.allPools.filter(pool =>
|
|
408
|
-
pool.tokenAddresses.includes(wrappedTokenAddress.address),
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
return Promise.all(
|
|
412
|
-
// As the state is readonly we spread it to receive a copy,
|
|
413
|
-
// It is not a deep copy, so we shouldn't alter the nested objects
|
|
414
|
-
[...(await this.getStates(selectedPools))]
|
|
415
|
-
.sort((a, b) => {
|
|
416
|
-
const diff = b.state.lpToken_supply - a.state.lpToken_supply;
|
|
417
|
-
if (diff === 0n) {
|
|
418
|
-
return 0;
|
|
419
|
-
} else if (diff.toString().startsWith('-')) {
|
|
420
|
-
return -1;
|
|
421
|
-
} else {
|
|
422
|
-
return 1;
|
|
423
|
-
}
|
|
424
|
-
})
|
|
425
|
-
.slice(0, limit)
|
|
426
|
-
.map(async ({ state, pool }) => {
|
|
427
|
-
const normalisedBalances = pool.math._xp(state);
|
|
428
|
-
const totalLiquidity = normalisedBalances.reduce(
|
|
429
|
-
(prev, acc) => prev + acc,
|
|
430
|
-
0n,
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
let priceInUSD = 0;
|
|
434
|
-
if (pool.poolConfig.isUSDPool) {
|
|
435
|
-
// We force here precision to be 0
|
|
436
|
-
priceInUSD = Number(
|
|
437
|
-
totalLiquidity /
|
|
438
|
-
bigIntify(10) ** pool.math.POOL_PRECISION_DECIMALS,
|
|
439
|
-
);
|
|
440
|
-
} else {
|
|
441
|
-
priceInUSD = await this.dexHelper.getTokenUSDPrice(
|
|
442
|
-
{
|
|
443
|
-
// I assume that the first indexed token is the most popular
|
|
444
|
-
address: pool.tokens[0].address,
|
|
445
|
-
decimals: Number(pool.math.POOL_PRECISION_DECIMALS),
|
|
446
|
-
},
|
|
447
|
-
totalLiquidity,
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return {
|
|
452
|
-
exchange: pool.name,
|
|
453
|
-
address: pool.address,
|
|
454
|
-
connectorTokens: _(pool.tokens)
|
|
455
|
-
.uniqBy('address')
|
|
456
|
-
.filter(
|
|
457
|
-
token =>
|
|
458
|
-
token.address.toLowerCase() !==
|
|
459
|
-
wrappedTokenAddress.address.toLowerCase(),
|
|
460
|
-
)
|
|
461
|
-
.value(),
|
|
462
|
-
liquidityUSD: priceInUSD,
|
|
463
|
-
};
|
|
464
|
-
}),
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
1
|
+
import { Interface } from '@ethersproject/abi';
|
|
2
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
3
|
+
import {
|
|
4
|
+
Token,
|
|
5
|
+
Address,
|
|
6
|
+
ExchangePrices,
|
|
7
|
+
PoolPrices,
|
|
8
|
+
AdapterExchangeParam,
|
|
9
|
+
SimpleExchangeParam,
|
|
10
|
+
PoolLiquidity,
|
|
11
|
+
Logger,
|
|
12
|
+
NumberAsString,
|
|
13
|
+
DexExchangeParam,
|
|
14
|
+
} from '../../types';
|
|
15
|
+
import nervePoolABIDefault from '../../abi/nerve/nerve-pool.json';
|
|
16
|
+
import { SwapSide, Network } from '../../constants';
|
|
17
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
18
|
+
import { getDexKeysWithNetwork, getBigIntPow } from '../../utils';
|
|
19
|
+
import { IDex } from '../../dex/idex';
|
|
20
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
21
|
+
import {
|
|
22
|
+
NerveData,
|
|
23
|
+
PoolState,
|
|
24
|
+
DexParams,
|
|
25
|
+
EventPoolMappings,
|
|
26
|
+
OptimizedNerveData,
|
|
27
|
+
NervePoolFunctions,
|
|
28
|
+
NervePoolSwapParams,
|
|
29
|
+
NervePoolConfig,
|
|
30
|
+
} from './types';
|
|
31
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
32
|
+
import { NerveConfig, Adapters, NERVE_GAS_COST } from './config';
|
|
33
|
+
import { NerveEventPool } from './nerve-pool';
|
|
34
|
+
import _ from 'lodash';
|
|
35
|
+
import { bigIntify } from './utils';
|
|
36
|
+
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
37
|
+
|
|
38
|
+
export class Nerve
|
|
39
|
+
extends SimpleExchange
|
|
40
|
+
implements IDex<NerveData, DexParams, OptimizedNerveData>
|
|
41
|
+
{
|
|
42
|
+
protected eventPools: EventPoolMappings = {};
|
|
43
|
+
|
|
44
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
45
|
+
readonly isFeeOnTransferSupported = false;
|
|
46
|
+
|
|
47
|
+
readonly minConversionRate = '1';
|
|
48
|
+
|
|
49
|
+
public static dexKeysWithNetwork: {
|
|
50
|
+
key: string;
|
|
51
|
+
networks: Network[];
|
|
52
|
+
}[] = getDexKeysWithNetwork(NerveConfig);
|
|
53
|
+
|
|
54
|
+
logger: Logger;
|
|
55
|
+
|
|
56
|
+
static getIdentifier(dexKey: string, poolAddress: string) {
|
|
57
|
+
return `${dexKey.toLowerCase()}_${poolAddress.toLowerCase()}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
protected network: Network,
|
|
62
|
+
dexKey: string,
|
|
63
|
+
protected dexHelper: IDexHelper,
|
|
64
|
+
protected adapters = Adapters[network] || {},
|
|
65
|
+
protected poolConfigs = NerveConfig[dexKey][network].poolConfigs,
|
|
66
|
+
protected nervePoolIface = new Interface(nervePoolABIDefault),
|
|
67
|
+
) {
|
|
68
|
+
super(dexHelper, dexKey);
|
|
69
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get allPools() {
|
|
73
|
+
return Object.values(this.eventPools);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async setupEventPool(poolConfig: NervePoolConfig, blockNumber: number) {
|
|
77
|
+
const poolIdentifier = Nerve.getIdentifier(this.dexKey, poolConfig.address);
|
|
78
|
+
|
|
79
|
+
if (!poolConfig.isMetapool) {
|
|
80
|
+
const newPool = new NerveEventPool(
|
|
81
|
+
this.dexKey,
|
|
82
|
+
this.network,
|
|
83
|
+
this.dexHelper,
|
|
84
|
+
this.logger,
|
|
85
|
+
poolConfig.name,
|
|
86
|
+
);
|
|
87
|
+
this.eventPools[poolIdentifier] = newPool;
|
|
88
|
+
|
|
89
|
+
// Generate first state for the blockNumber and subscribe to logs
|
|
90
|
+
await newPool.initialize(blockNumber);
|
|
91
|
+
} else {
|
|
92
|
+
this.logger.warn(
|
|
93
|
+
`We don't support metapools for Nerve. Check config: ${poolConfig.name}`,
|
|
94
|
+
);
|
|
95
|
+
// No need to throw because we want to initialize other pools
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async initializePricing(blockNumber: number) {
|
|
100
|
+
await Promise.all(
|
|
101
|
+
Object.values(this.poolConfigs).map(
|
|
102
|
+
async poolConfig => await this.setupEventPool(poolConfig, blockNumber),
|
|
103
|
+
),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
108
|
+
return this.adapters[side] || null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async getStates(
|
|
112
|
+
pools?: NerveEventPool[],
|
|
113
|
+
blockNumber?: number,
|
|
114
|
+
): Promise<DeepReadonly<{ state: PoolState; pool: NerveEventPool }[]>> {
|
|
115
|
+
const _pools = pools === undefined ? this.allPools : pools;
|
|
116
|
+
|
|
117
|
+
const _blockNumber =
|
|
118
|
+
blockNumber === undefined
|
|
119
|
+
? await this.dexHelper.web3Provider.eth.getBlockNumber()
|
|
120
|
+
: blockNumber;
|
|
121
|
+
|
|
122
|
+
// TODO: Need to batch this RPC calls in one multicall
|
|
123
|
+
return Promise.all(
|
|
124
|
+
_pools.map(async eventPool => {
|
|
125
|
+
let state = eventPool.getState(_blockNumber);
|
|
126
|
+
if (!state || !state.isValid) {
|
|
127
|
+
this.logger.info(
|
|
128
|
+
`State for ${this.dexKey} pool ${eventPool.name} on ${this.network} is stale or invalid on block ${_blockNumber}. Generating new one`,
|
|
129
|
+
);
|
|
130
|
+
const newState = await eventPool.generateState(_blockNumber);
|
|
131
|
+
eventPool.setState(newState, _blockNumber);
|
|
132
|
+
return { state: newState, pool: eventPool };
|
|
133
|
+
} else {
|
|
134
|
+
return { state, pool: eventPool };
|
|
135
|
+
}
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async getPoolIdentifiers(
|
|
141
|
+
srcToken: Token,
|
|
142
|
+
destToken: Token,
|
|
143
|
+
side: SwapSide,
|
|
144
|
+
blockNumber: number,
|
|
145
|
+
): Promise<string[]> {
|
|
146
|
+
if (side === SwapSide.BUY) return [];
|
|
147
|
+
|
|
148
|
+
const _srcToken = this.dexHelper.config.wrapETH(srcToken);
|
|
149
|
+
const _destToken = this.dexHelper.config.wrapETH(destToken);
|
|
150
|
+
|
|
151
|
+
return this.allPools
|
|
152
|
+
.filter(pool => {
|
|
153
|
+
return (
|
|
154
|
+
pool.tokenAddresses.includes(_srcToken.address.toLowerCase()) &&
|
|
155
|
+
pool.tokenAddresses.includes(_destToken.address.toLowerCase())
|
|
156
|
+
);
|
|
157
|
+
})
|
|
158
|
+
.map(pool => Nerve.getIdentifier(this.dexKey, pool.address));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async getPricesVolume(
|
|
162
|
+
srcToken: Token,
|
|
163
|
+
destToken: Token,
|
|
164
|
+
amounts: bigint[],
|
|
165
|
+
side: SwapSide,
|
|
166
|
+
blockNumber: number,
|
|
167
|
+
limitPools?: string[],
|
|
168
|
+
): Promise<null | ExchangePrices<NerveData>> {
|
|
169
|
+
try {
|
|
170
|
+
if (side === SwapSide.BUY) return null;
|
|
171
|
+
|
|
172
|
+
const _srcToken = this.dexHelper.config.wrapETH(srcToken);
|
|
173
|
+
const _destToken = this.dexHelper.config.wrapETH(destToken);
|
|
174
|
+
|
|
175
|
+
if (
|
|
176
|
+
_srcToken.address.toLowerCase() === _destToken.address.toLowerCase()
|
|
177
|
+
) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const filterPoolsByIdentifiers = (
|
|
182
|
+
identifiers: string[],
|
|
183
|
+
pools: NerveEventPool[],
|
|
184
|
+
) => {
|
|
185
|
+
return pools.filter(pool =>
|
|
186
|
+
identifiers.includes(Nerve.getIdentifier(this.dexKey, pool.address)),
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const selectedPools = !limitPools
|
|
191
|
+
? filterPoolsByIdentifiers(
|
|
192
|
+
await this.getPoolIdentifiers(
|
|
193
|
+
_srcToken,
|
|
194
|
+
_destToken,
|
|
195
|
+
side,
|
|
196
|
+
blockNumber,
|
|
197
|
+
),
|
|
198
|
+
this.allPools,
|
|
199
|
+
)
|
|
200
|
+
: filterPoolsByIdentifiers(limitPools, this.allPools);
|
|
201
|
+
|
|
202
|
+
const statePoolPair = await this.getStates(selectedPools, blockNumber);
|
|
203
|
+
|
|
204
|
+
// here side === SwapSide.SELL
|
|
205
|
+
const unitVolume = getBigIntPow(_srcToken.decimals);
|
|
206
|
+
|
|
207
|
+
const _amounts = [unitVolume, ...amounts.slice(1)];
|
|
208
|
+
|
|
209
|
+
const result: ExchangePrices<NerveData> = [];
|
|
210
|
+
for (const { pool, state } of statePoolPair) {
|
|
211
|
+
if (state.paused) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const srcIndex = pool.tokens.findIndex(
|
|
216
|
+
token =>
|
|
217
|
+
token.address.toLowerCase() === _srcToken.address.toLowerCase(),
|
|
218
|
+
);
|
|
219
|
+
const destIndex = pool.tokens.findIndex(
|
|
220
|
+
token =>
|
|
221
|
+
token.address.toLowerCase() === _destToken.address.toLowerCase(),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
if (srcIndex === -1 || destIndex === -1) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const _prices: bigint[] = [];
|
|
229
|
+
for (const _amount of _amounts) {
|
|
230
|
+
try {
|
|
231
|
+
const out = pool.math.calculateSwap(
|
|
232
|
+
state,
|
|
233
|
+
srcIndex,
|
|
234
|
+
destIndex,
|
|
235
|
+
_amount,
|
|
236
|
+
// Actually we need here block timestamp, but +- 15 seconds shouldn't
|
|
237
|
+
// affect the calculations
|
|
238
|
+
bigIntify((Date.now() / 1000).toFixed(0)),
|
|
239
|
+
);
|
|
240
|
+
_prices.push(out.dy);
|
|
241
|
+
} catch (e) {
|
|
242
|
+
// Something unexpected happen, so set invalidated state.
|
|
243
|
+
// Later it will be regenerated
|
|
244
|
+
pool.setState({ ...state, isValid: false }, blockNumber);
|
|
245
|
+
this.logger.error(
|
|
246
|
+
`${this.dexKey} protocol ${pool.name} (${pool.address}) pool can not calculate out swap for amount ${_amount}`,
|
|
247
|
+
e,
|
|
248
|
+
);
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const unit = _prices[0];
|
|
254
|
+
|
|
255
|
+
result.push({
|
|
256
|
+
unit,
|
|
257
|
+
prices: [0n, ..._prices.slice(1)],
|
|
258
|
+
data: {
|
|
259
|
+
i: srcIndex.toString(),
|
|
260
|
+
j: destIndex.toString(),
|
|
261
|
+
exchange: pool.address,
|
|
262
|
+
deadline: (Math.floor(Date.now() / 1000) + 10 * 60).toString(),
|
|
263
|
+
},
|
|
264
|
+
poolIdentifier: Nerve.getIdentifier(this.dexKey, pool.address),
|
|
265
|
+
exchange: this.dexKey,
|
|
266
|
+
gasCost: NERVE_GAS_COST,
|
|
267
|
+
poolAddresses: [pool.address],
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
} catch (e) {
|
|
272
|
+
if (blockNumber === 0)
|
|
273
|
+
this.logger.error(
|
|
274
|
+
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
275
|
+
);
|
|
276
|
+
this.logger.error(`Error_getPrices:`, e);
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
282
|
+
getCalldataGasCost(poolPrices: PoolPrices<NerveData>): number | number[] {
|
|
283
|
+
return (
|
|
284
|
+
CALLDATA_GAS_COST.DEX_OVERHEAD +
|
|
285
|
+
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
286
|
+
CALLDATA_GAS_COST.INDEX +
|
|
287
|
+
CALLDATA_GAS_COST.INDEX +
|
|
288
|
+
CALLDATA_GAS_COST.TIMESTAMP
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
getAdapterParam(
|
|
293
|
+
srcToken: string,
|
|
294
|
+
destToken: string,
|
|
295
|
+
srcAmount: string,
|
|
296
|
+
destAmount: string,
|
|
297
|
+
data: OptimizedNerveData,
|
|
298
|
+
side: SwapSide,
|
|
299
|
+
): AdapterExchangeParam {
|
|
300
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
301
|
+
|
|
302
|
+
const { i, j, deadline } = data;
|
|
303
|
+
const payload = this.abiCoder.encodeParameter(
|
|
304
|
+
{
|
|
305
|
+
ParentStruct: {
|
|
306
|
+
i: 'int128',
|
|
307
|
+
j: 'int128',
|
|
308
|
+
deadline: 'uint256',
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
{ i, j, deadline },
|
|
312
|
+
);
|
|
313
|
+
return {
|
|
314
|
+
targetExchange: data.exchange,
|
|
315
|
+
payload,
|
|
316
|
+
networkFee: '0',
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async getSimpleParam(
|
|
321
|
+
srcToken: string,
|
|
322
|
+
destToken: string,
|
|
323
|
+
srcAmount: string,
|
|
324
|
+
destAmount: string,
|
|
325
|
+
data: OptimizedNerveData,
|
|
326
|
+
side: SwapSide,
|
|
327
|
+
): Promise<SimpleExchangeParam> {
|
|
328
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
329
|
+
|
|
330
|
+
const { exchange, i, j, deadline } = data;
|
|
331
|
+
|
|
332
|
+
const swapFunctionParam: NervePoolSwapParams = [
|
|
333
|
+
i,
|
|
334
|
+
j,
|
|
335
|
+
srcAmount,
|
|
336
|
+
this.minConversionRate,
|
|
337
|
+
deadline,
|
|
338
|
+
];
|
|
339
|
+
|
|
340
|
+
const swapData = this.nervePoolIface.encodeFunctionData(
|
|
341
|
+
NervePoolFunctions.swap,
|
|
342
|
+
swapFunctionParam,
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
return this.buildSimpleParamWithoutWETHConversion(
|
|
346
|
+
srcToken,
|
|
347
|
+
srcAmount,
|
|
348
|
+
destToken,
|
|
349
|
+
destAmount,
|
|
350
|
+
swapData,
|
|
351
|
+
exchange,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
getDexParam(
|
|
356
|
+
srcToken: Address,
|
|
357
|
+
destToken: Address,
|
|
358
|
+
srcAmount: NumberAsString,
|
|
359
|
+
destAmount: NumberAsString,
|
|
360
|
+
recipient: Address,
|
|
361
|
+
data: OptimizedNerveData,
|
|
362
|
+
side: SwapSide,
|
|
363
|
+
): DexExchangeParam {
|
|
364
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
365
|
+
|
|
366
|
+
const { exchange, i, j, deadline } = data;
|
|
367
|
+
|
|
368
|
+
const swapFunctionParam: NervePoolSwapParams = [
|
|
369
|
+
i,
|
|
370
|
+
j,
|
|
371
|
+
srcAmount,
|
|
372
|
+
this.minConversionRate,
|
|
373
|
+
deadline,
|
|
374
|
+
];
|
|
375
|
+
|
|
376
|
+
const swapData = this.nervePoolIface.encodeFunctionData(
|
|
377
|
+
NervePoolFunctions.swap,
|
|
378
|
+
swapFunctionParam,
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
needWrapNative: this.needWrapNative,
|
|
383
|
+
dexFuncHasRecipient: false,
|
|
384
|
+
exchangeData: swapData,
|
|
385
|
+
targetExchange: exchange,
|
|
386
|
+
returnAmountPos:
|
|
387
|
+
side === SwapSide.SELL
|
|
388
|
+
? extractReturnAmountPosition(
|
|
389
|
+
this.nervePoolIface,
|
|
390
|
+
NervePoolFunctions.swap,
|
|
391
|
+
)
|
|
392
|
+
: undefined,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async getTopPoolsForToken(
|
|
397
|
+
tokenAddress: Address,
|
|
398
|
+
limit: number,
|
|
399
|
+
): Promise<PoolLiquidity[]> {
|
|
400
|
+
// We set decimals to default as we don't really care of actual number.
|
|
401
|
+
// We use here only address
|
|
402
|
+
const wrappedTokenAddress = this.dexHelper.config.wrapETH({
|
|
403
|
+
address: tokenAddress,
|
|
404
|
+
decimals: 18,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const selectedPools = this.allPools.filter(pool =>
|
|
408
|
+
pool.tokenAddresses.includes(wrappedTokenAddress.address),
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
return Promise.all(
|
|
412
|
+
// As the state is readonly we spread it to receive a copy,
|
|
413
|
+
// It is not a deep copy, so we shouldn't alter the nested objects
|
|
414
|
+
[...(await this.getStates(selectedPools))]
|
|
415
|
+
.sort((a, b) => {
|
|
416
|
+
const diff = b.state.lpToken_supply - a.state.lpToken_supply;
|
|
417
|
+
if (diff === 0n) {
|
|
418
|
+
return 0;
|
|
419
|
+
} else if (diff.toString().startsWith('-')) {
|
|
420
|
+
return -1;
|
|
421
|
+
} else {
|
|
422
|
+
return 1;
|
|
423
|
+
}
|
|
424
|
+
})
|
|
425
|
+
.slice(0, limit)
|
|
426
|
+
.map(async ({ state, pool }) => {
|
|
427
|
+
const normalisedBalances = pool.math._xp(state);
|
|
428
|
+
const totalLiquidity = normalisedBalances.reduce(
|
|
429
|
+
(prev, acc) => prev + acc,
|
|
430
|
+
0n,
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
let priceInUSD = 0;
|
|
434
|
+
if (pool.poolConfig.isUSDPool) {
|
|
435
|
+
// We force here precision to be 0
|
|
436
|
+
priceInUSD = Number(
|
|
437
|
+
totalLiquidity /
|
|
438
|
+
bigIntify(10) ** pool.math.POOL_PRECISION_DECIMALS,
|
|
439
|
+
);
|
|
440
|
+
} else {
|
|
441
|
+
priceInUSD = await this.dexHelper.getTokenUSDPrice(
|
|
442
|
+
{
|
|
443
|
+
// I assume that the first indexed token is the most popular
|
|
444
|
+
address: pool.tokens[0].address,
|
|
445
|
+
decimals: Number(pool.math.POOL_PRECISION_DECIMALS),
|
|
446
|
+
},
|
|
447
|
+
totalLiquidity,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
exchange: pool.name,
|
|
453
|
+
address: pool.address,
|
|
454
|
+
connectorTokens: _(pool.tokens)
|
|
455
|
+
.uniqBy('address')
|
|
456
|
+
.filter(
|
|
457
|
+
token =>
|
|
458
|
+
token.address.toLowerCase() !==
|
|
459
|
+
wrappedTokenAddress.address.toLowerCase(),
|
|
460
|
+
)
|
|
461
|
+
.value(),
|
|
462
|
+
liquidityUSD: priceInUSD,
|
|
463
|
+
};
|
|
464
|
+
}),
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
}
|