@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
|
@@ -1,537 +1,537 @@
|
|
|
1
|
-
import { Interface } from '@ethersproject/abi';
|
|
2
|
-
import _ from 'lodash';
|
|
3
|
-
import {
|
|
4
|
-
Token,
|
|
5
|
-
Address,
|
|
6
|
-
ExchangePrices,
|
|
7
|
-
PoolPrices,
|
|
8
|
-
AdapterExchangeParam,
|
|
9
|
-
SimpleExchangeParam,
|
|
10
|
-
PoolLiquidity,
|
|
11
|
-
Logger,
|
|
12
|
-
DexExchangeParam,
|
|
13
|
-
} from '../../types';
|
|
14
|
-
import { SwapSide, Network } from '../../constants';
|
|
15
|
-
import { getDexKeysWithNetwork, getBigIntPow, isTruthy } from '../../utils';
|
|
16
|
-
import { IDex } from '../../dex/idex';
|
|
17
|
-
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
18
|
-
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
19
|
-
import {
|
|
20
|
-
MaverickV1Data,
|
|
21
|
-
MaverickV1Functions,
|
|
22
|
-
MaverickV1Param,
|
|
23
|
-
SubgraphPoolBase,
|
|
24
|
-
} from './types';
|
|
25
|
-
import {
|
|
26
|
-
getLocalDeadlineAsFriendlyPlaceholder,
|
|
27
|
-
SimpleExchange,
|
|
28
|
-
} from '../simple-exchange';
|
|
29
|
-
import {
|
|
30
|
-
MaverickV1Config,
|
|
31
|
-
Adapters,
|
|
32
|
-
MAV_V1_BASE_GAS_COST,
|
|
33
|
-
MAV_V1_TICK_GAS_COST,
|
|
34
|
-
MAV_V1_KIND_GAS_COST,
|
|
35
|
-
} from './config';
|
|
36
|
-
import { MaverickV1EventPool } from './maverick-v1-pool';
|
|
37
|
-
import {
|
|
38
|
-
fetchAllPools,
|
|
39
|
-
fetchPoolsSortedByBalanceUsd,
|
|
40
|
-
} from './subgraph-queries';
|
|
41
|
-
import { SUBGRAPH_TIMEOUT } from '../../constants';
|
|
42
|
-
import RouterABI from '../../abi/maverick-v1/router.json';
|
|
43
|
-
import { NumberAsString } from '@paraswap/core';
|
|
44
|
-
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
45
|
-
|
|
46
|
-
const MAX_POOL_CNT = 1000;
|
|
47
|
-
|
|
48
|
-
const EFFICIENCY_FACTOR = 3;
|
|
49
|
-
export class MaverickV1
|
|
50
|
-
extends SimpleExchange
|
|
51
|
-
implements IDex<MaverickV1Data, MaverickV1Param>
|
|
52
|
-
{
|
|
53
|
-
pools: { [key: string]: MaverickV1EventPool } = {};
|
|
54
|
-
|
|
55
|
-
readonly isFeeOnTransferSupported: boolean = false;
|
|
56
|
-
readonly hasConstantPriceLargeAmounts = false;
|
|
57
|
-
readonly needWrapNative = true;
|
|
58
|
-
|
|
59
|
-
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
60
|
-
getDexKeysWithNetwork(MaverickV1Config);
|
|
61
|
-
|
|
62
|
-
logger: Logger;
|
|
63
|
-
|
|
64
|
-
routerAddress: string;
|
|
65
|
-
|
|
66
|
-
constructor(
|
|
67
|
-
protected network: Network,
|
|
68
|
-
dexKey: string,
|
|
69
|
-
protected dexHelper: IDexHelper,
|
|
70
|
-
protected adapters = Adapters[network] || {}, // TODO: add any additional optional params to support other fork DEXes
|
|
71
|
-
readonly routerIface = new Interface(RouterABI),
|
|
72
|
-
protected config = MaverickV1Config[dexKey][network],
|
|
73
|
-
protected subgraphURL: string = MaverickV1Config[dexKey][network]
|
|
74
|
-
.subgraphURL,
|
|
75
|
-
) {
|
|
76
|
-
super(dexHelper, dexKey);
|
|
77
|
-
this.logger = dexHelper.getLogger(dexKey);
|
|
78
|
-
this.routerAddress =
|
|
79
|
-
MaverickV1Config[this.dexKey][this.network].routerAddress;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async setupEventPools(blockNumber: number) {
|
|
83
|
-
const pools = await this.fetchAllSubgraphPools();
|
|
84
|
-
|
|
85
|
-
await Promise.all(
|
|
86
|
-
pools.map(async (pool: any) => {
|
|
87
|
-
const eventPool = new MaverickV1EventPool(
|
|
88
|
-
this.dexKey,
|
|
89
|
-
this.network,
|
|
90
|
-
this.dexHelper,
|
|
91
|
-
{
|
|
92
|
-
address: pool.tokenA.id,
|
|
93
|
-
symbol: pool.tokenA.symbol,
|
|
94
|
-
decimals: pool.tokenA.decimals,
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
address: pool.tokenB.id,
|
|
98
|
-
symbol: pool.tokenB.symbol,
|
|
99
|
-
decimals: pool.tokenB.decimals,
|
|
100
|
-
},
|
|
101
|
-
pool.fee,
|
|
102
|
-
pool.tickSpacing,
|
|
103
|
-
pool.protocolFeeRatio,
|
|
104
|
-
pool.lookback,
|
|
105
|
-
pool.id,
|
|
106
|
-
MaverickV1Config[this.dexKey][this.network].poolInspectorAddress,
|
|
107
|
-
this.logger,
|
|
108
|
-
);
|
|
109
|
-
const onChainState = await eventPool.generateState(blockNumber);
|
|
110
|
-
if (blockNumber) {
|
|
111
|
-
eventPool.setState(onChainState, blockNumber);
|
|
112
|
-
this.dexHelper.blockManager.subscribeToLogs(
|
|
113
|
-
eventPool,
|
|
114
|
-
eventPool.addressesSubscribed,
|
|
115
|
-
blockNumber,
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
this.pools[eventPool.address] = eventPool;
|
|
119
|
-
}),
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Initialize pricing is called once in the start of
|
|
124
|
-
// pricing service. It is intended to setup the integration
|
|
125
|
-
// for pricing requests. It is optional for a DEX to
|
|
126
|
-
// implement this function
|
|
127
|
-
async initializePricing(blockNumber: number) {
|
|
128
|
-
await this.setupEventPools(blockNumber);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Returns the list of contract adapters (name and index)
|
|
132
|
-
// for a buy/sell. Return null if there are no adapters.
|
|
133
|
-
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
134
|
-
return this.adapters[side] ? this.adapters[side] : null;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async fetchAllSubgraphPools(): Promise<SubgraphPoolBase[]> {
|
|
138
|
-
this.logger.info(
|
|
139
|
-
`Fetching ${this.dexKey}_${this.network} Pools from subgraph`,
|
|
140
|
-
);
|
|
141
|
-
const { data } = await this.dexHelper.httpRequest.post(
|
|
142
|
-
this.subgraphURL,
|
|
143
|
-
{ query: fetchAllPools, count: MAX_POOL_CNT },
|
|
144
|
-
SUBGRAPH_TIMEOUT,
|
|
145
|
-
);
|
|
146
|
-
return data.pools;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Returns list of pool identifiers that can be used
|
|
150
|
-
// for a given swap. poolIdentifiers must be unique
|
|
151
|
-
// across DEXes. It is recommended to use
|
|
152
|
-
// ${dexKey}_${poolAddress} as a poolIdentifier
|
|
153
|
-
async getPoolIdentifiers(
|
|
154
|
-
srcToken: Token,
|
|
155
|
-
destToken: Token,
|
|
156
|
-
side: SwapSide,
|
|
157
|
-
blockNumber: number,
|
|
158
|
-
): Promise<string[]> {
|
|
159
|
-
const from = this.dexHelper.config.wrapETH(srcToken);
|
|
160
|
-
const to = this.dexHelper.config.wrapETH(destToken);
|
|
161
|
-
|
|
162
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
163
|
-
return [];
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const pools = await this.getPools(from, to);
|
|
167
|
-
return pools.map(
|
|
168
|
-
(pool: any) => `${this.dexKey}_${pool.address.toLowerCase()}`,
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async getPools(srcToken: Token, destToken: Token) {
|
|
173
|
-
return Object.values(this.pools).filter((pool: MaverickV1EventPool) => {
|
|
174
|
-
return (
|
|
175
|
-
(pool.tokenA.address.toLowerCase() == srcToken.address.toLowerCase() ||
|
|
176
|
-
pool.tokenA.address.toLowerCase() ==
|
|
177
|
-
destToken.address.toLowerCase()) &&
|
|
178
|
-
(pool.tokenB.address.toLowerCase() == srcToken.address.toLowerCase() ||
|
|
179
|
-
pool.tokenB.address.toLowerCase() == destToken.address.toLowerCase())
|
|
180
|
-
);
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
185
|
-
getCalldataGasCost(
|
|
186
|
-
poolPrices: PoolPrices<MaverickV1Data>,
|
|
187
|
-
): number | number[] {
|
|
188
|
-
const gasCost = CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
189
|
-
|
|
190
|
-
const arr = new Array(poolPrices.prices.length);
|
|
191
|
-
poolPrices.prices.forEach((p, index) => {
|
|
192
|
-
if (p == 0n) {
|
|
193
|
-
arr[index] = 0;
|
|
194
|
-
} else {
|
|
195
|
-
arr[index] = gasCost;
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
return arr;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Returns pool prices for amounts.
|
|
202
|
-
// If limitPools is defined only pools in limitPools
|
|
203
|
-
// should be used. If limitPools is undefined then
|
|
204
|
-
// any pools can be used.
|
|
205
|
-
async getPricesVolume(
|
|
206
|
-
srcToken: Token,
|
|
207
|
-
destToken: Token,
|
|
208
|
-
amounts: bigint[],
|
|
209
|
-
side: SwapSide,
|
|
210
|
-
blockNumber: number,
|
|
211
|
-
limitPools?: string[],
|
|
212
|
-
): Promise<null | ExchangePrices<MaverickV1Data>> {
|
|
213
|
-
try {
|
|
214
|
-
const from = this.dexHelper.config.wrapETH(srcToken);
|
|
215
|
-
const to = this.dexHelper.config.wrapETH(destToken);
|
|
216
|
-
|
|
217
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const allPools = await this.getPools(from, to);
|
|
222
|
-
|
|
223
|
-
const allowedPools = limitPools
|
|
224
|
-
? allPools.filter(pool =>
|
|
225
|
-
limitPools.includes(`${this.dexKey}_${pool.address.toLowerCase()}`),
|
|
226
|
-
)
|
|
227
|
-
: allPools;
|
|
228
|
-
if (!allowedPools.length) return null;
|
|
229
|
-
|
|
230
|
-
const unitAmount = getBigIntPow(
|
|
231
|
-
side == SwapSide.BUY ? to.decimals : from.decimals,
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
return (
|
|
235
|
-
await Promise.all(
|
|
236
|
-
allowedPools.map(async (pool: MaverickV1EventPool) => {
|
|
237
|
-
try {
|
|
238
|
-
let state = pool.getState(blockNumber);
|
|
239
|
-
if (state === null) {
|
|
240
|
-
state = await pool.generateState(blockNumber);
|
|
241
|
-
pool.setState(state, blockNumber);
|
|
242
|
-
}
|
|
243
|
-
if (state === null) {
|
|
244
|
-
this.logger.debug(
|
|
245
|
-
`Received null state for pool ${pool.address}`,
|
|
246
|
-
);
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const [unit] = pool.swap(
|
|
251
|
-
unitAmount,
|
|
252
|
-
from,
|
|
253
|
-
to,
|
|
254
|
-
side == SwapSide.BUY,
|
|
255
|
-
);
|
|
256
|
-
// We stop iterating if it becomes 0n at some point
|
|
257
|
-
let lastOutput = 1n;
|
|
258
|
-
let dataList: [bigint, number][] = await Promise.all(
|
|
259
|
-
amounts.map(amount => {
|
|
260
|
-
if (amount === 0n) {
|
|
261
|
-
return [0n, 0];
|
|
262
|
-
}
|
|
263
|
-
// We don't want to proceed with calculations if lower amount was not fillable
|
|
264
|
-
if (lastOutput === 0n) {
|
|
265
|
-
return [0n, 0];
|
|
266
|
-
}
|
|
267
|
-
const output = pool.swap(
|
|
268
|
-
amount,
|
|
269
|
-
from,
|
|
270
|
-
to,
|
|
271
|
-
side == SwapSide.BUY,
|
|
272
|
-
);
|
|
273
|
-
lastOutput = output[0];
|
|
274
|
-
return output;
|
|
275
|
-
}),
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
let prices = dataList.map(d => d[0]);
|
|
279
|
-
let gasCosts: number[] = dataList.map(
|
|
280
|
-
([d, t]: [BigInt, number]) => {
|
|
281
|
-
if (d == 0n) return 0;
|
|
282
|
-
// I think it is reasonable estimation assuming "kind" gas cost is almost everytime around 1
|
|
283
|
-
return (
|
|
284
|
-
MAV_V1_BASE_GAS_COST +
|
|
285
|
-
(MAV_V1_TICK_GAS_COST + MAV_V1_KIND_GAS_COST) * t
|
|
286
|
-
);
|
|
287
|
-
},
|
|
288
|
-
);
|
|
289
|
-
return {
|
|
290
|
-
prices: prices,
|
|
291
|
-
unit: BigInt(unit),
|
|
292
|
-
data: {
|
|
293
|
-
fee: pool.fee,
|
|
294
|
-
exchange: this.routerAddress,
|
|
295
|
-
pool: pool.address,
|
|
296
|
-
tokenA: pool.tokenA.address,
|
|
297
|
-
tokenB: pool.tokenB.address,
|
|
298
|
-
tickSpacing: pool.tickSpacing,
|
|
299
|
-
protocolFeeRatio: pool.protocolFeeRatio,
|
|
300
|
-
lookback: pool.lookback,
|
|
301
|
-
},
|
|
302
|
-
exchange: this.dexKey,
|
|
303
|
-
poolIdentifier: pool.name,
|
|
304
|
-
gasCost: gasCosts,
|
|
305
|
-
poolAddresses: [pool.address],
|
|
306
|
-
};
|
|
307
|
-
} catch (e) {
|
|
308
|
-
this.logger.debug(
|
|
309
|
-
`Failed to get prices for pool ${pool.address}, from=${from.address}, to=${to.address}`,
|
|
310
|
-
e,
|
|
311
|
-
);
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
}),
|
|
315
|
-
)
|
|
316
|
-
).filter(isTruthy);
|
|
317
|
-
} catch (e) {
|
|
318
|
-
this.logger.error(
|
|
319
|
-
`Error_getPricesVolume ${srcToken.symbol || srcToken.address}, ${
|
|
320
|
-
destToken.symbol || destToken.address
|
|
321
|
-
}, ${side}:`,
|
|
322
|
-
e,
|
|
323
|
-
);
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Encode params required by the exchange adapter
|
|
329
|
-
// Used for multiSwap, buy & megaSwap
|
|
330
|
-
// Hint: abiCoder.encodeParameter() could be useful
|
|
331
|
-
getAdapterParam(
|
|
332
|
-
srcToken: string,
|
|
333
|
-
destToken: string,
|
|
334
|
-
srcAmount: string,
|
|
335
|
-
destAmount: string,
|
|
336
|
-
data: MaverickV1Data,
|
|
337
|
-
side: SwapSide,
|
|
338
|
-
): AdapterExchangeParam {
|
|
339
|
-
const { pool } = data;
|
|
340
|
-
const payload = this.abiCoder.encodeParameter(
|
|
341
|
-
{
|
|
342
|
-
ParentStruct: {
|
|
343
|
-
pool: 'address',
|
|
344
|
-
deadline: 'uint256',
|
|
345
|
-
},
|
|
346
|
-
},
|
|
347
|
-
{
|
|
348
|
-
pool,
|
|
349
|
-
deadline: getLocalDeadlineAsFriendlyPlaceholder(), // FIXME: more gas efficient to pass block.timestamp in adapter
|
|
350
|
-
},
|
|
351
|
-
);
|
|
352
|
-
|
|
353
|
-
return {
|
|
354
|
-
targetExchange: this.routerAddress,
|
|
355
|
-
payload,
|
|
356
|
-
networkFee: '0',
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
getDexParam(
|
|
361
|
-
srcToken: Address,
|
|
362
|
-
destToken: Address,
|
|
363
|
-
srcAmount: NumberAsString,
|
|
364
|
-
destAmount: NumberAsString,
|
|
365
|
-
recipient: Address,
|
|
366
|
-
data: MaverickV1Data,
|
|
367
|
-
side: SwapSide,
|
|
368
|
-
): DexExchangeParam {
|
|
369
|
-
const swapFunction =
|
|
370
|
-
side === SwapSide.SELL
|
|
371
|
-
? MaverickV1Functions.exactInputSingle
|
|
372
|
-
: MaverickV1Functions.exactOutputSingle;
|
|
373
|
-
|
|
374
|
-
const swapFunctionParams: MaverickV1Param =
|
|
375
|
-
side === SwapSide.SELL
|
|
376
|
-
? {
|
|
377
|
-
recipient,
|
|
378
|
-
deadline: getLocalDeadlineAsFriendlyPlaceholder(),
|
|
379
|
-
amountIn: srcAmount,
|
|
380
|
-
amountOutMinimum: destAmount,
|
|
381
|
-
tokenIn: srcToken,
|
|
382
|
-
tokenOut: destToken,
|
|
383
|
-
pool: data.pool,
|
|
384
|
-
sqrtPriceLimitD18: '0',
|
|
385
|
-
}
|
|
386
|
-
: {
|
|
387
|
-
recipient,
|
|
388
|
-
deadline: getLocalDeadlineAsFriendlyPlaceholder(),
|
|
389
|
-
amountOut: destAmount,
|
|
390
|
-
amountInMaximum: srcAmount,
|
|
391
|
-
tokenIn: srcToken,
|
|
392
|
-
tokenOut: destToken,
|
|
393
|
-
pool: data.pool,
|
|
394
|
-
sqrtPriceLimitD18: '0',
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
const exchangeData = this.routerIface.encodeFunctionData(swapFunction, [
|
|
398
|
-
swapFunctionParams,
|
|
399
|
-
]);
|
|
400
|
-
|
|
401
|
-
return {
|
|
402
|
-
needWrapNative: this.needWrapNative,
|
|
403
|
-
dexFuncHasRecipient: true,
|
|
404
|
-
exchangeData,
|
|
405
|
-
targetExchange: this.config.routerAddress,
|
|
406
|
-
returnAmountPos:
|
|
407
|
-
side === SwapSide.SELL
|
|
408
|
-
? extractReturnAmountPosition(
|
|
409
|
-
this.routerIface,
|
|
410
|
-
MaverickV1Functions.exactInputSingle,
|
|
411
|
-
'amountOut',
|
|
412
|
-
)
|
|
413
|
-
: undefined,
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
async getSimpleParam(
|
|
418
|
-
srcToken: string,
|
|
419
|
-
destToken: string,
|
|
420
|
-
srcAmount: string,
|
|
421
|
-
destAmount: string,
|
|
422
|
-
data: MaverickV1Data,
|
|
423
|
-
side: SwapSide,
|
|
424
|
-
): Promise<SimpleExchangeParam> {
|
|
425
|
-
const swapFunction =
|
|
426
|
-
side === SwapSide.SELL
|
|
427
|
-
? MaverickV1Functions.exactInputSingle
|
|
428
|
-
: MaverickV1Functions.exactOutputSingle;
|
|
429
|
-
|
|
430
|
-
const swapFunctionParams: MaverickV1Param =
|
|
431
|
-
side === SwapSide.SELL
|
|
432
|
-
? {
|
|
433
|
-
recipient: this.augustusAddress,
|
|
434
|
-
deadline: getLocalDeadlineAsFriendlyPlaceholder(),
|
|
435
|
-
amountIn: srcAmount,
|
|
436
|
-
amountOutMinimum: destAmount,
|
|
437
|
-
tokenIn: srcToken,
|
|
438
|
-
tokenOut: destToken,
|
|
439
|
-
pool: data.pool,
|
|
440
|
-
sqrtPriceLimitD18: '0',
|
|
441
|
-
}
|
|
442
|
-
: {
|
|
443
|
-
recipient: this.augustusAddress,
|
|
444
|
-
deadline: getLocalDeadlineAsFriendlyPlaceholder(),
|
|
445
|
-
amountOut: destAmount,
|
|
446
|
-
amountInMaximum: srcAmount,
|
|
447
|
-
tokenIn: srcToken,
|
|
448
|
-
tokenOut: destToken,
|
|
449
|
-
pool: data.pool,
|
|
450
|
-
sqrtPriceLimitD18: '0',
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
const swapData = this.routerIface.encodeFunctionData(swapFunction, [
|
|
454
|
-
swapFunctionParams,
|
|
455
|
-
]);
|
|
456
|
-
|
|
457
|
-
return this.buildSimpleParamWithoutWETHConversion(
|
|
458
|
-
srcToken,
|
|
459
|
-
srcAmount,
|
|
460
|
-
destToken,
|
|
461
|
-
destAmount,
|
|
462
|
-
swapData,
|
|
463
|
-
this.config.routerAddress,
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// shoot duplication
|
|
468
|
-
private async _querySubgraph(
|
|
469
|
-
query: string,
|
|
470
|
-
variables: Object,
|
|
471
|
-
timeout = 30000,
|
|
472
|
-
) {
|
|
473
|
-
try {
|
|
474
|
-
const res = await this.dexHelper.httpRequest.post(
|
|
475
|
-
this.subgraphURL,
|
|
476
|
-
{ query, variables },
|
|
477
|
-
undefined,
|
|
478
|
-
{ timeout: timeout },
|
|
479
|
-
);
|
|
480
|
-
return res.data;
|
|
481
|
-
} catch (e) {
|
|
482
|
-
this.logger.error(`${this.dexKey}: can not query subgraph: `, e);
|
|
483
|
-
return {};
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Fixme: duplicate of UniswapV3
|
|
488
|
-
async getTopPoolsForToken(
|
|
489
|
-
tokenAddress: Address,
|
|
490
|
-
limit: number,
|
|
491
|
-
): Promise<PoolLiquidity[]> {
|
|
492
|
-
const _tokenAddress = tokenAddress.toLowerCase();
|
|
493
|
-
|
|
494
|
-
const res = await this._querySubgraph(fetchPoolsSortedByBalanceUsd, {
|
|
495
|
-
token: _tokenAddress,
|
|
496
|
-
count: limit,
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
if (!(res && res.pools0 && res.pools1)) {
|
|
500
|
-
this.logger.error(
|
|
501
|
-
`Error_${this.dexKey}_Subgraph: couldn't fetch the pools from the subgraph`,
|
|
502
|
-
);
|
|
503
|
-
return [];
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
const pools0 = _.map(res.pools0, pool => ({
|
|
507
|
-
exchange: this.dexKey,
|
|
508
|
-
address: pool.id.toLowerCase(),
|
|
509
|
-
connectorTokens: [
|
|
510
|
-
{
|
|
511
|
-
address: pool.tokenB.id.toLowerCase(),
|
|
512
|
-
decimals: parseInt(pool.tokenB.decimals),
|
|
513
|
-
},
|
|
514
|
-
],
|
|
515
|
-
liquidityUSD: parseFloat(pool.balanceUSD) * EFFICIENCY_FACTOR,
|
|
516
|
-
}));
|
|
517
|
-
|
|
518
|
-
const pools1 = _.map(res.pools1, pool => ({
|
|
519
|
-
exchange: this.dexKey,
|
|
520
|
-
address: pool.id.toLowerCase(),
|
|
521
|
-
connectorTokens: [
|
|
522
|
-
{
|
|
523
|
-
address: pool.tokenA.id.toLowerCase(),
|
|
524
|
-
decimals: parseInt(pool.tokenA.decimals),
|
|
525
|
-
},
|
|
526
|
-
],
|
|
527
|
-
liquidityUSD: parseFloat(pool.balanceUSD) * EFFICIENCY_FACTOR,
|
|
528
|
-
}));
|
|
529
|
-
|
|
530
|
-
const pools = _.slice(
|
|
531
|
-
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
532
|
-
0,
|
|
533
|
-
limit,
|
|
534
|
-
);
|
|
535
|
-
return pools;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
1
|
+
import { Interface } from '@ethersproject/abi';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import {
|
|
4
|
+
Token,
|
|
5
|
+
Address,
|
|
6
|
+
ExchangePrices,
|
|
7
|
+
PoolPrices,
|
|
8
|
+
AdapterExchangeParam,
|
|
9
|
+
SimpleExchangeParam,
|
|
10
|
+
PoolLiquidity,
|
|
11
|
+
Logger,
|
|
12
|
+
DexExchangeParam,
|
|
13
|
+
} from '../../types';
|
|
14
|
+
import { SwapSide, Network } from '../../constants';
|
|
15
|
+
import { getDexKeysWithNetwork, getBigIntPow, isTruthy } from '../../utils';
|
|
16
|
+
import { IDex } from '../../dex/idex';
|
|
17
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
18
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
19
|
+
import {
|
|
20
|
+
MaverickV1Data,
|
|
21
|
+
MaverickV1Functions,
|
|
22
|
+
MaverickV1Param,
|
|
23
|
+
SubgraphPoolBase,
|
|
24
|
+
} from './types';
|
|
25
|
+
import {
|
|
26
|
+
getLocalDeadlineAsFriendlyPlaceholder,
|
|
27
|
+
SimpleExchange,
|
|
28
|
+
} from '../simple-exchange';
|
|
29
|
+
import {
|
|
30
|
+
MaverickV1Config,
|
|
31
|
+
Adapters,
|
|
32
|
+
MAV_V1_BASE_GAS_COST,
|
|
33
|
+
MAV_V1_TICK_GAS_COST,
|
|
34
|
+
MAV_V1_KIND_GAS_COST,
|
|
35
|
+
} from './config';
|
|
36
|
+
import { MaverickV1EventPool } from './maverick-v1-pool';
|
|
37
|
+
import {
|
|
38
|
+
fetchAllPools,
|
|
39
|
+
fetchPoolsSortedByBalanceUsd,
|
|
40
|
+
} from './subgraph-queries';
|
|
41
|
+
import { SUBGRAPH_TIMEOUT } from '../../constants';
|
|
42
|
+
import RouterABI from '../../abi/maverick-v1/router.json';
|
|
43
|
+
import { NumberAsString } from '@paraswap/core';
|
|
44
|
+
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
45
|
+
|
|
46
|
+
const MAX_POOL_CNT = 1000;
|
|
47
|
+
|
|
48
|
+
const EFFICIENCY_FACTOR = 3;
|
|
49
|
+
export class MaverickV1
|
|
50
|
+
extends SimpleExchange
|
|
51
|
+
implements IDex<MaverickV1Data, MaverickV1Param>
|
|
52
|
+
{
|
|
53
|
+
pools: { [key: string]: MaverickV1EventPool } = {};
|
|
54
|
+
|
|
55
|
+
readonly isFeeOnTransferSupported: boolean = false;
|
|
56
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
57
|
+
readonly needWrapNative = true;
|
|
58
|
+
|
|
59
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
60
|
+
getDexKeysWithNetwork(MaverickV1Config);
|
|
61
|
+
|
|
62
|
+
logger: Logger;
|
|
63
|
+
|
|
64
|
+
routerAddress: string;
|
|
65
|
+
|
|
66
|
+
constructor(
|
|
67
|
+
protected network: Network,
|
|
68
|
+
dexKey: string,
|
|
69
|
+
protected dexHelper: IDexHelper,
|
|
70
|
+
protected adapters = Adapters[network] || {}, // TODO: add any additional optional params to support other fork DEXes
|
|
71
|
+
readonly routerIface = new Interface(RouterABI),
|
|
72
|
+
protected config = MaverickV1Config[dexKey][network],
|
|
73
|
+
protected subgraphURL: string = MaverickV1Config[dexKey][network]
|
|
74
|
+
.subgraphURL,
|
|
75
|
+
) {
|
|
76
|
+
super(dexHelper, dexKey);
|
|
77
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
78
|
+
this.routerAddress =
|
|
79
|
+
MaverickV1Config[this.dexKey][this.network].routerAddress;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async setupEventPools(blockNumber: number) {
|
|
83
|
+
const pools = await this.fetchAllSubgraphPools();
|
|
84
|
+
|
|
85
|
+
await Promise.all(
|
|
86
|
+
pools.map(async (pool: any) => {
|
|
87
|
+
const eventPool = new MaverickV1EventPool(
|
|
88
|
+
this.dexKey,
|
|
89
|
+
this.network,
|
|
90
|
+
this.dexHelper,
|
|
91
|
+
{
|
|
92
|
+
address: pool.tokenA.id,
|
|
93
|
+
symbol: pool.tokenA.symbol,
|
|
94
|
+
decimals: pool.tokenA.decimals,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
address: pool.tokenB.id,
|
|
98
|
+
symbol: pool.tokenB.symbol,
|
|
99
|
+
decimals: pool.tokenB.decimals,
|
|
100
|
+
},
|
|
101
|
+
pool.fee,
|
|
102
|
+
pool.tickSpacing,
|
|
103
|
+
pool.protocolFeeRatio,
|
|
104
|
+
pool.lookback,
|
|
105
|
+
pool.id,
|
|
106
|
+
MaverickV1Config[this.dexKey][this.network].poolInspectorAddress,
|
|
107
|
+
this.logger,
|
|
108
|
+
);
|
|
109
|
+
const onChainState = await eventPool.generateState(blockNumber);
|
|
110
|
+
if (blockNumber) {
|
|
111
|
+
eventPool.setState(onChainState, blockNumber);
|
|
112
|
+
this.dexHelper.blockManager.subscribeToLogs(
|
|
113
|
+
eventPool,
|
|
114
|
+
eventPool.addressesSubscribed,
|
|
115
|
+
blockNumber,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
this.pools[eventPool.address] = eventPool;
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Initialize pricing is called once in the start of
|
|
124
|
+
// pricing service. It is intended to setup the integration
|
|
125
|
+
// for pricing requests. It is optional for a DEX to
|
|
126
|
+
// implement this function
|
|
127
|
+
async initializePricing(blockNumber: number) {
|
|
128
|
+
await this.setupEventPools(blockNumber);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Returns the list of contract adapters (name and index)
|
|
132
|
+
// for a buy/sell. Return null if there are no adapters.
|
|
133
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
134
|
+
return this.adapters[side] ? this.adapters[side] : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async fetchAllSubgraphPools(): Promise<SubgraphPoolBase[]> {
|
|
138
|
+
this.logger.info(
|
|
139
|
+
`Fetching ${this.dexKey}_${this.network} Pools from subgraph`,
|
|
140
|
+
);
|
|
141
|
+
const { data } = await this.dexHelper.httpRequest.post(
|
|
142
|
+
this.subgraphURL,
|
|
143
|
+
{ query: fetchAllPools, count: MAX_POOL_CNT },
|
|
144
|
+
SUBGRAPH_TIMEOUT,
|
|
145
|
+
);
|
|
146
|
+
return data.pools;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Returns list of pool identifiers that can be used
|
|
150
|
+
// for a given swap. poolIdentifiers must be unique
|
|
151
|
+
// across DEXes. It is recommended to use
|
|
152
|
+
// ${dexKey}_${poolAddress} as a poolIdentifier
|
|
153
|
+
async getPoolIdentifiers(
|
|
154
|
+
srcToken: Token,
|
|
155
|
+
destToken: Token,
|
|
156
|
+
side: SwapSide,
|
|
157
|
+
blockNumber: number,
|
|
158
|
+
): Promise<string[]> {
|
|
159
|
+
const from = this.dexHelper.config.wrapETH(srcToken);
|
|
160
|
+
const to = this.dexHelper.config.wrapETH(destToken);
|
|
161
|
+
|
|
162
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const pools = await this.getPools(from, to);
|
|
167
|
+
return pools.map(
|
|
168
|
+
(pool: any) => `${this.dexKey}_${pool.address.toLowerCase()}`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async getPools(srcToken: Token, destToken: Token) {
|
|
173
|
+
return Object.values(this.pools).filter((pool: MaverickV1EventPool) => {
|
|
174
|
+
return (
|
|
175
|
+
(pool.tokenA.address.toLowerCase() == srcToken.address.toLowerCase() ||
|
|
176
|
+
pool.tokenA.address.toLowerCase() ==
|
|
177
|
+
destToken.address.toLowerCase()) &&
|
|
178
|
+
(pool.tokenB.address.toLowerCase() == srcToken.address.toLowerCase() ||
|
|
179
|
+
pool.tokenB.address.toLowerCase() == destToken.address.toLowerCase())
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
185
|
+
getCalldataGasCost(
|
|
186
|
+
poolPrices: PoolPrices<MaverickV1Data>,
|
|
187
|
+
): number | number[] {
|
|
188
|
+
const gasCost = CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
189
|
+
|
|
190
|
+
const arr = new Array(poolPrices.prices.length);
|
|
191
|
+
poolPrices.prices.forEach((p, index) => {
|
|
192
|
+
if (p == 0n) {
|
|
193
|
+
arr[index] = 0;
|
|
194
|
+
} else {
|
|
195
|
+
arr[index] = gasCost;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
return arr;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Returns pool prices for amounts.
|
|
202
|
+
// If limitPools is defined only pools in limitPools
|
|
203
|
+
// should be used. If limitPools is undefined then
|
|
204
|
+
// any pools can be used.
|
|
205
|
+
async getPricesVolume(
|
|
206
|
+
srcToken: Token,
|
|
207
|
+
destToken: Token,
|
|
208
|
+
amounts: bigint[],
|
|
209
|
+
side: SwapSide,
|
|
210
|
+
blockNumber: number,
|
|
211
|
+
limitPools?: string[],
|
|
212
|
+
): Promise<null | ExchangePrices<MaverickV1Data>> {
|
|
213
|
+
try {
|
|
214
|
+
const from = this.dexHelper.config.wrapETH(srcToken);
|
|
215
|
+
const to = this.dexHelper.config.wrapETH(destToken);
|
|
216
|
+
|
|
217
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const allPools = await this.getPools(from, to);
|
|
222
|
+
|
|
223
|
+
const allowedPools = limitPools
|
|
224
|
+
? allPools.filter(pool =>
|
|
225
|
+
limitPools.includes(`${this.dexKey}_${pool.address.toLowerCase()}`),
|
|
226
|
+
)
|
|
227
|
+
: allPools;
|
|
228
|
+
if (!allowedPools.length) return null;
|
|
229
|
+
|
|
230
|
+
const unitAmount = getBigIntPow(
|
|
231
|
+
side == SwapSide.BUY ? to.decimals : from.decimals,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
await Promise.all(
|
|
236
|
+
allowedPools.map(async (pool: MaverickV1EventPool) => {
|
|
237
|
+
try {
|
|
238
|
+
let state = pool.getState(blockNumber);
|
|
239
|
+
if (state === null) {
|
|
240
|
+
state = await pool.generateState(blockNumber);
|
|
241
|
+
pool.setState(state, blockNumber);
|
|
242
|
+
}
|
|
243
|
+
if (state === null) {
|
|
244
|
+
this.logger.debug(
|
|
245
|
+
`Received null state for pool ${pool.address}`,
|
|
246
|
+
);
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const [unit] = pool.swap(
|
|
251
|
+
unitAmount,
|
|
252
|
+
from,
|
|
253
|
+
to,
|
|
254
|
+
side == SwapSide.BUY,
|
|
255
|
+
);
|
|
256
|
+
// We stop iterating if it becomes 0n at some point
|
|
257
|
+
let lastOutput = 1n;
|
|
258
|
+
let dataList: [bigint, number][] = await Promise.all(
|
|
259
|
+
amounts.map(amount => {
|
|
260
|
+
if (amount === 0n) {
|
|
261
|
+
return [0n, 0];
|
|
262
|
+
}
|
|
263
|
+
// We don't want to proceed with calculations if lower amount was not fillable
|
|
264
|
+
if (lastOutput === 0n) {
|
|
265
|
+
return [0n, 0];
|
|
266
|
+
}
|
|
267
|
+
const output = pool.swap(
|
|
268
|
+
amount,
|
|
269
|
+
from,
|
|
270
|
+
to,
|
|
271
|
+
side == SwapSide.BUY,
|
|
272
|
+
);
|
|
273
|
+
lastOutput = output[0];
|
|
274
|
+
return output;
|
|
275
|
+
}),
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
let prices = dataList.map(d => d[0]);
|
|
279
|
+
let gasCosts: number[] = dataList.map(
|
|
280
|
+
([d, t]: [BigInt, number]) => {
|
|
281
|
+
if (d == 0n) return 0;
|
|
282
|
+
// I think it is reasonable estimation assuming "kind" gas cost is almost everytime around 1
|
|
283
|
+
return (
|
|
284
|
+
MAV_V1_BASE_GAS_COST +
|
|
285
|
+
(MAV_V1_TICK_GAS_COST + MAV_V1_KIND_GAS_COST) * t
|
|
286
|
+
);
|
|
287
|
+
},
|
|
288
|
+
);
|
|
289
|
+
return {
|
|
290
|
+
prices: prices,
|
|
291
|
+
unit: BigInt(unit),
|
|
292
|
+
data: {
|
|
293
|
+
fee: pool.fee,
|
|
294
|
+
exchange: this.routerAddress,
|
|
295
|
+
pool: pool.address,
|
|
296
|
+
tokenA: pool.tokenA.address,
|
|
297
|
+
tokenB: pool.tokenB.address,
|
|
298
|
+
tickSpacing: pool.tickSpacing,
|
|
299
|
+
protocolFeeRatio: pool.protocolFeeRatio,
|
|
300
|
+
lookback: pool.lookback,
|
|
301
|
+
},
|
|
302
|
+
exchange: this.dexKey,
|
|
303
|
+
poolIdentifier: pool.name,
|
|
304
|
+
gasCost: gasCosts,
|
|
305
|
+
poolAddresses: [pool.address],
|
|
306
|
+
};
|
|
307
|
+
} catch (e) {
|
|
308
|
+
this.logger.debug(
|
|
309
|
+
`Failed to get prices for pool ${pool.address}, from=${from.address}, to=${to.address}`,
|
|
310
|
+
e,
|
|
311
|
+
);
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
}),
|
|
315
|
+
)
|
|
316
|
+
).filter(isTruthy);
|
|
317
|
+
} catch (e) {
|
|
318
|
+
this.logger.error(
|
|
319
|
+
`Error_getPricesVolume ${srcToken.symbol || srcToken.address}, ${
|
|
320
|
+
destToken.symbol || destToken.address
|
|
321
|
+
}, ${side}:`,
|
|
322
|
+
e,
|
|
323
|
+
);
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Encode params required by the exchange adapter
|
|
329
|
+
// Used for multiSwap, buy & megaSwap
|
|
330
|
+
// Hint: abiCoder.encodeParameter() could be useful
|
|
331
|
+
getAdapterParam(
|
|
332
|
+
srcToken: string,
|
|
333
|
+
destToken: string,
|
|
334
|
+
srcAmount: string,
|
|
335
|
+
destAmount: string,
|
|
336
|
+
data: MaverickV1Data,
|
|
337
|
+
side: SwapSide,
|
|
338
|
+
): AdapterExchangeParam {
|
|
339
|
+
const { pool } = data;
|
|
340
|
+
const payload = this.abiCoder.encodeParameter(
|
|
341
|
+
{
|
|
342
|
+
ParentStruct: {
|
|
343
|
+
pool: 'address',
|
|
344
|
+
deadline: 'uint256',
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
pool,
|
|
349
|
+
deadline: getLocalDeadlineAsFriendlyPlaceholder(), // FIXME: more gas efficient to pass block.timestamp in adapter
|
|
350
|
+
},
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
targetExchange: this.routerAddress,
|
|
355
|
+
payload,
|
|
356
|
+
networkFee: '0',
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
getDexParam(
|
|
361
|
+
srcToken: Address,
|
|
362
|
+
destToken: Address,
|
|
363
|
+
srcAmount: NumberAsString,
|
|
364
|
+
destAmount: NumberAsString,
|
|
365
|
+
recipient: Address,
|
|
366
|
+
data: MaverickV1Data,
|
|
367
|
+
side: SwapSide,
|
|
368
|
+
): DexExchangeParam {
|
|
369
|
+
const swapFunction =
|
|
370
|
+
side === SwapSide.SELL
|
|
371
|
+
? MaverickV1Functions.exactInputSingle
|
|
372
|
+
: MaverickV1Functions.exactOutputSingle;
|
|
373
|
+
|
|
374
|
+
const swapFunctionParams: MaverickV1Param =
|
|
375
|
+
side === SwapSide.SELL
|
|
376
|
+
? {
|
|
377
|
+
recipient,
|
|
378
|
+
deadline: getLocalDeadlineAsFriendlyPlaceholder(),
|
|
379
|
+
amountIn: srcAmount,
|
|
380
|
+
amountOutMinimum: destAmount,
|
|
381
|
+
tokenIn: srcToken,
|
|
382
|
+
tokenOut: destToken,
|
|
383
|
+
pool: data.pool,
|
|
384
|
+
sqrtPriceLimitD18: '0',
|
|
385
|
+
}
|
|
386
|
+
: {
|
|
387
|
+
recipient,
|
|
388
|
+
deadline: getLocalDeadlineAsFriendlyPlaceholder(),
|
|
389
|
+
amountOut: destAmount,
|
|
390
|
+
amountInMaximum: srcAmount,
|
|
391
|
+
tokenIn: srcToken,
|
|
392
|
+
tokenOut: destToken,
|
|
393
|
+
pool: data.pool,
|
|
394
|
+
sqrtPriceLimitD18: '0',
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const exchangeData = this.routerIface.encodeFunctionData(swapFunction, [
|
|
398
|
+
swapFunctionParams,
|
|
399
|
+
]);
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
needWrapNative: this.needWrapNative,
|
|
403
|
+
dexFuncHasRecipient: true,
|
|
404
|
+
exchangeData,
|
|
405
|
+
targetExchange: this.config.routerAddress,
|
|
406
|
+
returnAmountPos:
|
|
407
|
+
side === SwapSide.SELL
|
|
408
|
+
? extractReturnAmountPosition(
|
|
409
|
+
this.routerIface,
|
|
410
|
+
MaverickV1Functions.exactInputSingle,
|
|
411
|
+
'amountOut',
|
|
412
|
+
)
|
|
413
|
+
: undefined,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async getSimpleParam(
|
|
418
|
+
srcToken: string,
|
|
419
|
+
destToken: string,
|
|
420
|
+
srcAmount: string,
|
|
421
|
+
destAmount: string,
|
|
422
|
+
data: MaverickV1Data,
|
|
423
|
+
side: SwapSide,
|
|
424
|
+
): Promise<SimpleExchangeParam> {
|
|
425
|
+
const swapFunction =
|
|
426
|
+
side === SwapSide.SELL
|
|
427
|
+
? MaverickV1Functions.exactInputSingle
|
|
428
|
+
: MaverickV1Functions.exactOutputSingle;
|
|
429
|
+
|
|
430
|
+
const swapFunctionParams: MaverickV1Param =
|
|
431
|
+
side === SwapSide.SELL
|
|
432
|
+
? {
|
|
433
|
+
recipient: this.augustusAddress,
|
|
434
|
+
deadline: getLocalDeadlineAsFriendlyPlaceholder(),
|
|
435
|
+
amountIn: srcAmount,
|
|
436
|
+
amountOutMinimum: destAmount,
|
|
437
|
+
tokenIn: srcToken,
|
|
438
|
+
tokenOut: destToken,
|
|
439
|
+
pool: data.pool,
|
|
440
|
+
sqrtPriceLimitD18: '0',
|
|
441
|
+
}
|
|
442
|
+
: {
|
|
443
|
+
recipient: this.augustusAddress,
|
|
444
|
+
deadline: getLocalDeadlineAsFriendlyPlaceholder(),
|
|
445
|
+
amountOut: destAmount,
|
|
446
|
+
amountInMaximum: srcAmount,
|
|
447
|
+
tokenIn: srcToken,
|
|
448
|
+
tokenOut: destToken,
|
|
449
|
+
pool: data.pool,
|
|
450
|
+
sqrtPriceLimitD18: '0',
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const swapData = this.routerIface.encodeFunctionData(swapFunction, [
|
|
454
|
+
swapFunctionParams,
|
|
455
|
+
]);
|
|
456
|
+
|
|
457
|
+
return this.buildSimpleParamWithoutWETHConversion(
|
|
458
|
+
srcToken,
|
|
459
|
+
srcAmount,
|
|
460
|
+
destToken,
|
|
461
|
+
destAmount,
|
|
462
|
+
swapData,
|
|
463
|
+
this.config.routerAddress,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// shoot duplication
|
|
468
|
+
private async _querySubgraph(
|
|
469
|
+
query: string,
|
|
470
|
+
variables: Object,
|
|
471
|
+
timeout = 30000,
|
|
472
|
+
) {
|
|
473
|
+
try {
|
|
474
|
+
const res = await this.dexHelper.httpRequest.post(
|
|
475
|
+
this.subgraphURL,
|
|
476
|
+
{ query, variables },
|
|
477
|
+
undefined,
|
|
478
|
+
{ timeout: timeout },
|
|
479
|
+
);
|
|
480
|
+
return res.data;
|
|
481
|
+
} catch (e) {
|
|
482
|
+
this.logger.error(`${this.dexKey}: can not query subgraph: `, e);
|
|
483
|
+
return {};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Fixme: duplicate of UniswapV3
|
|
488
|
+
async getTopPoolsForToken(
|
|
489
|
+
tokenAddress: Address,
|
|
490
|
+
limit: number,
|
|
491
|
+
): Promise<PoolLiquidity[]> {
|
|
492
|
+
const _tokenAddress = tokenAddress.toLowerCase();
|
|
493
|
+
|
|
494
|
+
const res = await this._querySubgraph(fetchPoolsSortedByBalanceUsd, {
|
|
495
|
+
token: _tokenAddress,
|
|
496
|
+
count: limit,
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
if (!(res && res.pools0 && res.pools1)) {
|
|
500
|
+
this.logger.error(
|
|
501
|
+
`Error_${this.dexKey}_Subgraph: couldn't fetch the pools from the subgraph`,
|
|
502
|
+
);
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const pools0 = _.map(res.pools0, pool => ({
|
|
507
|
+
exchange: this.dexKey,
|
|
508
|
+
address: pool.id.toLowerCase(),
|
|
509
|
+
connectorTokens: [
|
|
510
|
+
{
|
|
511
|
+
address: pool.tokenB.id.toLowerCase(),
|
|
512
|
+
decimals: parseInt(pool.tokenB.decimals),
|
|
513
|
+
},
|
|
514
|
+
],
|
|
515
|
+
liquidityUSD: parseFloat(pool.balanceUSD) * EFFICIENCY_FACTOR,
|
|
516
|
+
}));
|
|
517
|
+
|
|
518
|
+
const pools1 = _.map(res.pools1, pool => ({
|
|
519
|
+
exchange: this.dexKey,
|
|
520
|
+
address: pool.id.toLowerCase(),
|
|
521
|
+
connectorTokens: [
|
|
522
|
+
{
|
|
523
|
+
address: pool.tokenA.id.toLowerCase(),
|
|
524
|
+
decimals: parseInt(pool.tokenA.decimals),
|
|
525
|
+
},
|
|
526
|
+
],
|
|
527
|
+
liquidityUSD: parseFloat(pool.balanceUSD) * EFFICIENCY_FACTOR,
|
|
528
|
+
}));
|
|
529
|
+
|
|
530
|
+
const pools = _.slice(
|
|
531
|
+
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
532
|
+
0,
|
|
533
|
+
limit,
|
|
534
|
+
);
|
|
535
|
+
return pools;
|
|
536
|
+
}
|
|
537
|
+
}
|