@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,337 +1,337 @@
|
|
|
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
|
-
NumberAsString,
|
|
13
|
-
DexExchangeParam,
|
|
14
|
-
} from '../../types';
|
|
15
|
-
import { SwapSide, Network } from '../../constants';
|
|
16
|
-
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
17
|
-
import { getDexKeysWithNetwork, getBigIntPow } from '../../utils';
|
|
18
|
-
import { IDex } from '../idex';
|
|
19
|
-
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
20
|
-
import { QuickPerpsData, DexParams } from './types';
|
|
21
|
-
import { QuickPerpsEventPool } from './pool';
|
|
22
|
-
import { SimpleExchange } from '../simple-exchange';
|
|
23
|
-
import { QuickPerpsConfig, Adapters } from './config';
|
|
24
|
-
import { Vault } from './vault';
|
|
25
|
-
import ERC20ABI from '../../abi/erc20.json';
|
|
26
|
-
import { solidityPack } from 'ethers/lib/utils';
|
|
27
|
-
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
28
|
-
|
|
29
|
-
const QuickPerpsGasCost = 300 * 1000;
|
|
30
|
-
|
|
31
|
-
export class QuickPerps extends SimpleExchange implements IDex<QuickPerpsData> {
|
|
32
|
-
protected pool: QuickPerpsEventPool | null = null;
|
|
33
|
-
protected supportedTokensMap: { [address: string]: boolean } = {};
|
|
34
|
-
// supportedTokens is only used by the pooltracker
|
|
35
|
-
protected supportedTokens: Token[] = [];
|
|
36
|
-
|
|
37
|
-
readonly hasConstantPriceLargeAmounts = false;
|
|
38
|
-
readonly needWrapNative = true;
|
|
39
|
-
readonly isFeeOnTransferSupported = false;
|
|
40
|
-
|
|
41
|
-
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
42
|
-
getDexKeysWithNetwork(QuickPerpsConfig);
|
|
43
|
-
|
|
44
|
-
public static erc20Interface = new Interface(ERC20ABI);
|
|
45
|
-
|
|
46
|
-
vaultUSDBalance: number = 0;
|
|
47
|
-
|
|
48
|
-
logger: Logger;
|
|
49
|
-
|
|
50
|
-
constructor(
|
|
51
|
-
protected network: Network,
|
|
52
|
-
dexKey: string,
|
|
53
|
-
protected dexHelper: IDexHelper,
|
|
54
|
-
protected adapters = Adapters[network],
|
|
55
|
-
protected params: DexParams = QuickPerpsConfig[dexKey][network],
|
|
56
|
-
) {
|
|
57
|
-
super(dexHelper, dexKey);
|
|
58
|
-
this.logger = dexHelper.getLogger(dexKey);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async initializePricing(blockNumber: number) {
|
|
62
|
-
const config = await QuickPerpsEventPool.getConfig(
|
|
63
|
-
this.params,
|
|
64
|
-
blockNumber,
|
|
65
|
-
this.dexHelper.multiContract,
|
|
66
|
-
);
|
|
67
|
-
config.tokenAddresses.forEach(
|
|
68
|
-
(token: Address) => (this.supportedTokensMap[token] = true),
|
|
69
|
-
);
|
|
70
|
-
this.pool = new QuickPerpsEventPool(
|
|
71
|
-
this.dexKey,
|
|
72
|
-
this.network,
|
|
73
|
-
this.dexHelper,
|
|
74
|
-
config,
|
|
75
|
-
);
|
|
76
|
-
await this.pool.initialize(blockNumber);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Returns the list of contract adapters (name and index)
|
|
80
|
-
// for a buy/sell. Return null if there are no adapters.
|
|
81
|
-
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
82
|
-
return this.adapters[side];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Returns list of pool identifiers that can be used
|
|
86
|
-
// for a given swap. poolIdentifiers must be unique
|
|
87
|
-
// across DEXes.
|
|
88
|
-
async getPoolIdentifiers(
|
|
89
|
-
srcToken: Token,
|
|
90
|
-
destToken: Token,
|
|
91
|
-
side: SwapSide,
|
|
92
|
-
blockNumber: number,
|
|
93
|
-
): Promise<string[]> {
|
|
94
|
-
if (side === SwapSide.BUY || !this.pool) return [];
|
|
95
|
-
const srcAddress = this.dexHelper.config
|
|
96
|
-
.wrapETH(srcToken)
|
|
97
|
-
.address.toLowerCase();
|
|
98
|
-
const destAddress = this.dexHelper.config
|
|
99
|
-
.wrapETH(destToken)
|
|
100
|
-
.address.toLowerCase();
|
|
101
|
-
if (
|
|
102
|
-
srcAddress !== destAddress &&
|
|
103
|
-
this.supportedTokensMap[srcAddress] &&
|
|
104
|
-
this.supportedTokensMap[destAddress]
|
|
105
|
-
) {
|
|
106
|
-
return [`${this.dexKey}_${srcAddress}`, `${this.dexKey}_${destAddress}`];
|
|
107
|
-
}
|
|
108
|
-
return [];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Returns pool prices for amounts.
|
|
112
|
-
// If limitPools is defined only pools in limitPools
|
|
113
|
-
// should be used. If limitPools is undefined then
|
|
114
|
-
// any pools can be used.
|
|
115
|
-
async getPricesVolume(
|
|
116
|
-
srcToken: Token,
|
|
117
|
-
destToken: Token,
|
|
118
|
-
amounts: bigint[],
|
|
119
|
-
side: SwapSide,
|
|
120
|
-
blockNumber: number,
|
|
121
|
-
limitPools?: string[],
|
|
122
|
-
): Promise<null | ExchangePrices<QuickPerpsData>> {
|
|
123
|
-
if (side === SwapSide.BUY || !this.pool) return null;
|
|
124
|
-
try {
|
|
125
|
-
const srcAddress = this.dexHelper.config
|
|
126
|
-
.wrapETH(srcToken)
|
|
127
|
-
.address.toLowerCase();
|
|
128
|
-
const destAddress = this.dexHelper.config
|
|
129
|
-
.wrapETH(destToken)
|
|
130
|
-
.address.toLowerCase();
|
|
131
|
-
if (
|
|
132
|
-
srcAddress === destAddress ||
|
|
133
|
-
!(
|
|
134
|
-
this.supportedTokensMap[srcAddress] &&
|
|
135
|
-
this.supportedTokensMap[destAddress]
|
|
136
|
-
)
|
|
137
|
-
)
|
|
138
|
-
return null;
|
|
139
|
-
const srcPoolIdentifier = `${this.dexKey}_${srcAddress}`;
|
|
140
|
-
const destPoolIdentifier = `${this.dexKey}_${destAddress}`;
|
|
141
|
-
const pools = [srcPoolIdentifier, destPoolIdentifier];
|
|
142
|
-
if (limitPools && pools.some(p => !limitPools.includes(p))) return null;
|
|
143
|
-
|
|
144
|
-
const unitVolume = getBigIntPow(srcToken.decimals);
|
|
145
|
-
const prices = await this.pool.getAmountOut(
|
|
146
|
-
srcAddress,
|
|
147
|
-
destAddress,
|
|
148
|
-
[unitVolume, ...amounts],
|
|
149
|
-
blockNumber,
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
if (!prices) return null;
|
|
153
|
-
|
|
154
|
-
return [
|
|
155
|
-
{
|
|
156
|
-
prices: prices.slice(1),
|
|
157
|
-
unit: prices[0],
|
|
158
|
-
gasCost: QuickPerpsGasCost,
|
|
159
|
-
exchange: this.dexKey,
|
|
160
|
-
data: {},
|
|
161
|
-
poolAddresses: [this.params.vault],
|
|
162
|
-
},
|
|
163
|
-
];
|
|
164
|
-
} catch (e) {
|
|
165
|
-
this.logger.error(
|
|
166
|
-
`Error_getPrices ${srcToken.symbol || srcToken.address}, ${
|
|
167
|
-
destToken.symbol || destToken.address
|
|
168
|
-
}, ${side}: `,
|
|
169
|
-
e,
|
|
170
|
-
);
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
176
|
-
getCalldataGasCost(
|
|
177
|
-
poolPrices: PoolPrices<QuickPerpsData>,
|
|
178
|
-
): number | number[] {
|
|
179
|
-
return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
getAdapterParam(
|
|
183
|
-
srcToken: string,
|
|
184
|
-
destToken: string,
|
|
185
|
-
srcAmount: string,
|
|
186
|
-
destAmount: string,
|
|
187
|
-
data: QuickPerpsData,
|
|
188
|
-
side: SwapSide,
|
|
189
|
-
): AdapterExchangeParam {
|
|
190
|
-
return {
|
|
191
|
-
targetExchange: this.params.vault,
|
|
192
|
-
payload: '0x',
|
|
193
|
-
networkFee: '0',
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
getSimpleParam(
|
|
198
|
-
srcToken: string,
|
|
199
|
-
destToken: string,
|
|
200
|
-
srcAmount: string,
|
|
201
|
-
destAmount: string,
|
|
202
|
-
data: QuickPerpsData,
|
|
203
|
-
side: SwapSide,
|
|
204
|
-
): SimpleExchangeParam {
|
|
205
|
-
return {
|
|
206
|
-
callees: [srcToken, this.params.vault],
|
|
207
|
-
calldata: [
|
|
208
|
-
QuickPerps.erc20Interface.encodeFunctionData('transfer', [
|
|
209
|
-
this.params.vault,
|
|
210
|
-
srcAmount,
|
|
211
|
-
]),
|
|
212
|
-
Vault.interface.encodeFunctionData('swap', [
|
|
213
|
-
srcToken,
|
|
214
|
-
destToken,
|
|
215
|
-
this.augustusAddress,
|
|
216
|
-
]),
|
|
217
|
-
],
|
|
218
|
-
values: ['0', '0'],
|
|
219
|
-
networkFee: '0',
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
getDexParam(
|
|
224
|
-
srcToken: Address,
|
|
225
|
-
destToken: Address,
|
|
226
|
-
srcAmount: NumberAsString,
|
|
227
|
-
destAmount: NumberAsString,
|
|
228
|
-
recipient: Address,
|
|
229
|
-
data: QuickPerpsData,
|
|
230
|
-
side: SwapSide,
|
|
231
|
-
): DexExchangeParam {
|
|
232
|
-
const iface = Vault.interface;
|
|
233
|
-
const functionName = 'swap';
|
|
234
|
-
const swapData = iface.encodeFunctionData(functionName, [
|
|
235
|
-
srcToken,
|
|
236
|
-
destToken,
|
|
237
|
-
recipient,
|
|
238
|
-
]);
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
needWrapNative: this.needWrapNative,
|
|
242
|
-
dexFuncHasRecipient: true,
|
|
243
|
-
exchangeData: swapData,
|
|
244
|
-
targetExchange: this.params.vault,
|
|
245
|
-
swappedAmountNotPresentInExchangeData: true,
|
|
246
|
-
transferSrcTokenBeforeSwap: this.params.vault,
|
|
247
|
-
returnAmountPos:
|
|
248
|
-
side === SwapSide.SELL
|
|
249
|
-
? extractReturnAmountPosition(iface, functionName)
|
|
250
|
-
: undefined,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
async updatePoolState(): Promise<void> {
|
|
255
|
-
if (!this.supportedTokens.length) {
|
|
256
|
-
const tokenAddresses = await QuickPerpsEventPool.getWhitelistedTokens(
|
|
257
|
-
this.params.vault,
|
|
258
|
-
'latest',
|
|
259
|
-
this.dexHelper.multiContract,
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
const decimalsCallData =
|
|
263
|
-
QuickPerps.erc20Interface.encodeFunctionData('decimals');
|
|
264
|
-
const tokenBalanceMultiCall = tokenAddresses.map(t => ({
|
|
265
|
-
target: t,
|
|
266
|
-
callData: decimalsCallData,
|
|
267
|
-
}));
|
|
268
|
-
const res = (
|
|
269
|
-
await this.dexHelper.multiContract.methods
|
|
270
|
-
.aggregate(tokenBalanceMultiCall)
|
|
271
|
-
.call()
|
|
272
|
-
).returnData;
|
|
273
|
-
|
|
274
|
-
const tokenDecimals = res.map((r: any) =>
|
|
275
|
-
parseInt(
|
|
276
|
-
QuickPerps.erc20Interface
|
|
277
|
-
.decodeFunctionResult('decimals', r)[0]
|
|
278
|
-
.toString(),
|
|
279
|
-
),
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
this.supportedTokens = tokenAddresses.map((t, i) => ({
|
|
283
|
-
address: t,
|
|
284
|
-
decimals: tokenDecimals[i],
|
|
285
|
-
}));
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const erc20BalanceCalldata = QuickPerps.erc20Interface.encodeFunctionData(
|
|
289
|
-
'balanceOf',
|
|
290
|
-
[this.params.vault],
|
|
291
|
-
);
|
|
292
|
-
const tokenBalanceMultiCall = this.supportedTokens.map(t => ({
|
|
293
|
-
target: t.address,
|
|
294
|
-
callData: erc20BalanceCalldata,
|
|
295
|
-
}));
|
|
296
|
-
const res = (
|
|
297
|
-
await this.dexHelper.multiContract.methods
|
|
298
|
-
.aggregate(tokenBalanceMultiCall)
|
|
299
|
-
.call()
|
|
300
|
-
).returnData;
|
|
301
|
-
const tokenBalances = res.map((r: any) =>
|
|
302
|
-
BigInt(
|
|
303
|
-
QuickPerps.erc20Interface
|
|
304
|
-
.decodeFunctionResult('balanceOf', r)[0]
|
|
305
|
-
.toString(),
|
|
306
|
-
),
|
|
307
|
-
);
|
|
308
|
-
const tokenBalancesUSD = await Promise.all(
|
|
309
|
-
this.supportedTokens.map((t, i) =>
|
|
310
|
-
this.dexHelper.getTokenUSDPrice(t, tokenBalances[i]),
|
|
311
|
-
),
|
|
312
|
-
);
|
|
313
|
-
this.vaultUSDBalance = tokenBalancesUSD.reduce(
|
|
314
|
-
(sum: number, curr: number) => sum + curr,
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Returns list of top pools based on liquidity. Max
|
|
319
|
-
// limit number pools should be returned.
|
|
320
|
-
async getTopPoolsForToken(
|
|
321
|
-
_tokenAddress: Address,
|
|
322
|
-
limit: number,
|
|
323
|
-
): Promise<PoolLiquidity[]> {
|
|
324
|
-
const tokenAddress = _tokenAddress.toLowerCase();
|
|
325
|
-
if (!this.supportedTokens.some(t => t.address === tokenAddress)) return [];
|
|
326
|
-
return [
|
|
327
|
-
{
|
|
328
|
-
exchange: this.dexKey,
|
|
329
|
-
address: this.params.vault,
|
|
330
|
-
connectorTokens: this.supportedTokens.filter(
|
|
331
|
-
t => t.address !== tokenAddress,
|
|
332
|
-
),
|
|
333
|
-
liquidityUSD: this.vaultUSDBalance,
|
|
334
|
-
},
|
|
335
|
-
];
|
|
336
|
-
}
|
|
337
|
-
}
|
|
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
|
+
NumberAsString,
|
|
13
|
+
DexExchangeParam,
|
|
14
|
+
} from '../../types';
|
|
15
|
+
import { SwapSide, Network } from '../../constants';
|
|
16
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
17
|
+
import { getDexKeysWithNetwork, getBigIntPow } from '../../utils';
|
|
18
|
+
import { IDex } from '../idex';
|
|
19
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
20
|
+
import { QuickPerpsData, DexParams } from './types';
|
|
21
|
+
import { QuickPerpsEventPool } from './pool';
|
|
22
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
23
|
+
import { QuickPerpsConfig, Adapters } from './config';
|
|
24
|
+
import { Vault } from './vault';
|
|
25
|
+
import ERC20ABI from '../../abi/erc20.json';
|
|
26
|
+
import { solidityPack } from 'ethers/lib/utils';
|
|
27
|
+
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
28
|
+
|
|
29
|
+
const QuickPerpsGasCost = 300 * 1000;
|
|
30
|
+
|
|
31
|
+
export class QuickPerps extends SimpleExchange implements IDex<QuickPerpsData> {
|
|
32
|
+
protected pool: QuickPerpsEventPool | null = null;
|
|
33
|
+
protected supportedTokensMap: { [address: string]: boolean } = {};
|
|
34
|
+
// supportedTokens is only used by the pooltracker
|
|
35
|
+
protected supportedTokens: Token[] = [];
|
|
36
|
+
|
|
37
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
38
|
+
readonly needWrapNative = true;
|
|
39
|
+
readonly isFeeOnTransferSupported = false;
|
|
40
|
+
|
|
41
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
42
|
+
getDexKeysWithNetwork(QuickPerpsConfig);
|
|
43
|
+
|
|
44
|
+
public static erc20Interface = new Interface(ERC20ABI);
|
|
45
|
+
|
|
46
|
+
vaultUSDBalance: number = 0;
|
|
47
|
+
|
|
48
|
+
logger: Logger;
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
protected network: Network,
|
|
52
|
+
dexKey: string,
|
|
53
|
+
protected dexHelper: IDexHelper,
|
|
54
|
+
protected adapters = Adapters[network],
|
|
55
|
+
protected params: DexParams = QuickPerpsConfig[dexKey][network],
|
|
56
|
+
) {
|
|
57
|
+
super(dexHelper, dexKey);
|
|
58
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async initializePricing(blockNumber: number) {
|
|
62
|
+
const config = await QuickPerpsEventPool.getConfig(
|
|
63
|
+
this.params,
|
|
64
|
+
blockNumber,
|
|
65
|
+
this.dexHelper.multiContract,
|
|
66
|
+
);
|
|
67
|
+
config.tokenAddresses.forEach(
|
|
68
|
+
(token: Address) => (this.supportedTokensMap[token] = true),
|
|
69
|
+
);
|
|
70
|
+
this.pool = new QuickPerpsEventPool(
|
|
71
|
+
this.dexKey,
|
|
72
|
+
this.network,
|
|
73
|
+
this.dexHelper,
|
|
74
|
+
config,
|
|
75
|
+
);
|
|
76
|
+
await this.pool.initialize(blockNumber);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Returns the list of contract adapters (name and index)
|
|
80
|
+
// for a buy/sell. Return null if there are no adapters.
|
|
81
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
82
|
+
return this.adapters[side];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Returns list of pool identifiers that can be used
|
|
86
|
+
// for a given swap. poolIdentifiers must be unique
|
|
87
|
+
// across DEXes.
|
|
88
|
+
async getPoolIdentifiers(
|
|
89
|
+
srcToken: Token,
|
|
90
|
+
destToken: Token,
|
|
91
|
+
side: SwapSide,
|
|
92
|
+
blockNumber: number,
|
|
93
|
+
): Promise<string[]> {
|
|
94
|
+
if (side === SwapSide.BUY || !this.pool) return [];
|
|
95
|
+
const srcAddress = this.dexHelper.config
|
|
96
|
+
.wrapETH(srcToken)
|
|
97
|
+
.address.toLowerCase();
|
|
98
|
+
const destAddress = this.dexHelper.config
|
|
99
|
+
.wrapETH(destToken)
|
|
100
|
+
.address.toLowerCase();
|
|
101
|
+
if (
|
|
102
|
+
srcAddress !== destAddress &&
|
|
103
|
+
this.supportedTokensMap[srcAddress] &&
|
|
104
|
+
this.supportedTokensMap[destAddress]
|
|
105
|
+
) {
|
|
106
|
+
return [`${this.dexKey}_${srcAddress}`, `${this.dexKey}_${destAddress}`];
|
|
107
|
+
}
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Returns pool prices for amounts.
|
|
112
|
+
// If limitPools is defined only pools in limitPools
|
|
113
|
+
// should be used. If limitPools is undefined then
|
|
114
|
+
// any pools can be used.
|
|
115
|
+
async getPricesVolume(
|
|
116
|
+
srcToken: Token,
|
|
117
|
+
destToken: Token,
|
|
118
|
+
amounts: bigint[],
|
|
119
|
+
side: SwapSide,
|
|
120
|
+
blockNumber: number,
|
|
121
|
+
limitPools?: string[],
|
|
122
|
+
): Promise<null | ExchangePrices<QuickPerpsData>> {
|
|
123
|
+
if (side === SwapSide.BUY || !this.pool) return null;
|
|
124
|
+
try {
|
|
125
|
+
const srcAddress = this.dexHelper.config
|
|
126
|
+
.wrapETH(srcToken)
|
|
127
|
+
.address.toLowerCase();
|
|
128
|
+
const destAddress = this.dexHelper.config
|
|
129
|
+
.wrapETH(destToken)
|
|
130
|
+
.address.toLowerCase();
|
|
131
|
+
if (
|
|
132
|
+
srcAddress === destAddress ||
|
|
133
|
+
!(
|
|
134
|
+
this.supportedTokensMap[srcAddress] &&
|
|
135
|
+
this.supportedTokensMap[destAddress]
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
return null;
|
|
139
|
+
const srcPoolIdentifier = `${this.dexKey}_${srcAddress}`;
|
|
140
|
+
const destPoolIdentifier = `${this.dexKey}_${destAddress}`;
|
|
141
|
+
const pools = [srcPoolIdentifier, destPoolIdentifier];
|
|
142
|
+
if (limitPools && pools.some(p => !limitPools.includes(p))) return null;
|
|
143
|
+
|
|
144
|
+
const unitVolume = getBigIntPow(srcToken.decimals);
|
|
145
|
+
const prices = await this.pool.getAmountOut(
|
|
146
|
+
srcAddress,
|
|
147
|
+
destAddress,
|
|
148
|
+
[unitVolume, ...amounts],
|
|
149
|
+
blockNumber,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
if (!prices) return null;
|
|
153
|
+
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
prices: prices.slice(1),
|
|
157
|
+
unit: prices[0],
|
|
158
|
+
gasCost: QuickPerpsGasCost,
|
|
159
|
+
exchange: this.dexKey,
|
|
160
|
+
data: {},
|
|
161
|
+
poolAddresses: [this.params.vault],
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
} catch (e) {
|
|
165
|
+
this.logger.error(
|
|
166
|
+
`Error_getPrices ${srcToken.symbol || srcToken.address}, ${
|
|
167
|
+
destToken.symbol || destToken.address
|
|
168
|
+
}, ${side}: `,
|
|
169
|
+
e,
|
|
170
|
+
);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
176
|
+
getCalldataGasCost(
|
|
177
|
+
poolPrices: PoolPrices<QuickPerpsData>,
|
|
178
|
+
): number | number[] {
|
|
179
|
+
return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getAdapterParam(
|
|
183
|
+
srcToken: string,
|
|
184
|
+
destToken: string,
|
|
185
|
+
srcAmount: string,
|
|
186
|
+
destAmount: string,
|
|
187
|
+
data: QuickPerpsData,
|
|
188
|
+
side: SwapSide,
|
|
189
|
+
): AdapterExchangeParam {
|
|
190
|
+
return {
|
|
191
|
+
targetExchange: this.params.vault,
|
|
192
|
+
payload: '0x',
|
|
193
|
+
networkFee: '0',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
getSimpleParam(
|
|
198
|
+
srcToken: string,
|
|
199
|
+
destToken: string,
|
|
200
|
+
srcAmount: string,
|
|
201
|
+
destAmount: string,
|
|
202
|
+
data: QuickPerpsData,
|
|
203
|
+
side: SwapSide,
|
|
204
|
+
): SimpleExchangeParam {
|
|
205
|
+
return {
|
|
206
|
+
callees: [srcToken, this.params.vault],
|
|
207
|
+
calldata: [
|
|
208
|
+
QuickPerps.erc20Interface.encodeFunctionData('transfer', [
|
|
209
|
+
this.params.vault,
|
|
210
|
+
srcAmount,
|
|
211
|
+
]),
|
|
212
|
+
Vault.interface.encodeFunctionData('swap', [
|
|
213
|
+
srcToken,
|
|
214
|
+
destToken,
|
|
215
|
+
this.augustusAddress,
|
|
216
|
+
]),
|
|
217
|
+
],
|
|
218
|
+
values: ['0', '0'],
|
|
219
|
+
networkFee: '0',
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
getDexParam(
|
|
224
|
+
srcToken: Address,
|
|
225
|
+
destToken: Address,
|
|
226
|
+
srcAmount: NumberAsString,
|
|
227
|
+
destAmount: NumberAsString,
|
|
228
|
+
recipient: Address,
|
|
229
|
+
data: QuickPerpsData,
|
|
230
|
+
side: SwapSide,
|
|
231
|
+
): DexExchangeParam {
|
|
232
|
+
const iface = Vault.interface;
|
|
233
|
+
const functionName = 'swap';
|
|
234
|
+
const swapData = iface.encodeFunctionData(functionName, [
|
|
235
|
+
srcToken,
|
|
236
|
+
destToken,
|
|
237
|
+
recipient,
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
needWrapNative: this.needWrapNative,
|
|
242
|
+
dexFuncHasRecipient: true,
|
|
243
|
+
exchangeData: swapData,
|
|
244
|
+
targetExchange: this.params.vault,
|
|
245
|
+
swappedAmountNotPresentInExchangeData: true,
|
|
246
|
+
transferSrcTokenBeforeSwap: this.params.vault,
|
|
247
|
+
returnAmountPos:
|
|
248
|
+
side === SwapSide.SELL
|
|
249
|
+
? extractReturnAmountPosition(iface, functionName)
|
|
250
|
+
: undefined,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async updatePoolState(): Promise<void> {
|
|
255
|
+
if (!this.supportedTokens.length) {
|
|
256
|
+
const tokenAddresses = await QuickPerpsEventPool.getWhitelistedTokens(
|
|
257
|
+
this.params.vault,
|
|
258
|
+
'latest',
|
|
259
|
+
this.dexHelper.multiContract,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const decimalsCallData =
|
|
263
|
+
QuickPerps.erc20Interface.encodeFunctionData('decimals');
|
|
264
|
+
const tokenBalanceMultiCall = tokenAddresses.map(t => ({
|
|
265
|
+
target: t,
|
|
266
|
+
callData: decimalsCallData,
|
|
267
|
+
}));
|
|
268
|
+
const res = (
|
|
269
|
+
await this.dexHelper.multiContract.methods
|
|
270
|
+
.aggregate(tokenBalanceMultiCall)
|
|
271
|
+
.call()
|
|
272
|
+
).returnData;
|
|
273
|
+
|
|
274
|
+
const tokenDecimals = res.map((r: any) =>
|
|
275
|
+
parseInt(
|
|
276
|
+
QuickPerps.erc20Interface
|
|
277
|
+
.decodeFunctionResult('decimals', r)[0]
|
|
278
|
+
.toString(),
|
|
279
|
+
),
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
this.supportedTokens = tokenAddresses.map((t, i) => ({
|
|
283
|
+
address: t,
|
|
284
|
+
decimals: tokenDecimals[i],
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const erc20BalanceCalldata = QuickPerps.erc20Interface.encodeFunctionData(
|
|
289
|
+
'balanceOf',
|
|
290
|
+
[this.params.vault],
|
|
291
|
+
);
|
|
292
|
+
const tokenBalanceMultiCall = this.supportedTokens.map(t => ({
|
|
293
|
+
target: t.address,
|
|
294
|
+
callData: erc20BalanceCalldata,
|
|
295
|
+
}));
|
|
296
|
+
const res = (
|
|
297
|
+
await this.dexHelper.multiContract.methods
|
|
298
|
+
.aggregate(tokenBalanceMultiCall)
|
|
299
|
+
.call()
|
|
300
|
+
).returnData;
|
|
301
|
+
const tokenBalances = res.map((r: any) =>
|
|
302
|
+
BigInt(
|
|
303
|
+
QuickPerps.erc20Interface
|
|
304
|
+
.decodeFunctionResult('balanceOf', r)[0]
|
|
305
|
+
.toString(),
|
|
306
|
+
),
|
|
307
|
+
);
|
|
308
|
+
const tokenBalancesUSD = await Promise.all(
|
|
309
|
+
this.supportedTokens.map((t, i) =>
|
|
310
|
+
this.dexHelper.getTokenUSDPrice(t, tokenBalances[i]),
|
|
311
|
+
),
|
|
312
|
+
);
|
|
313
|
+
this.vaultUSDBalance = tokenBalancesUSD.reduce(
|
|
314
|
+
(sum: number, curr: number) => sum + curr,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Returns list of top pools based on liquidity. Max
|
|
319
|
+
// limit number pools should be returned.
|
|
320
|
+
async getTopPoolsForToken(
|
|
321
|
+
_tokenAddress: Address,
|
|
322
|
+
limit: number,
|
|
323
|
+
): Promise<PoolLiquidity[]> {
|
|
324
|
+
const tokenAddress = _tokenAddress.toLowerCase();
|
|
325
|
+
if (!this.supportedTokens.some(t => t.address === tokenAddress)) return [];
|
|
326
|
+
return [
|
|
327
|
+
{
|
|
328
|
+
exchange: this.dexKey,
|
|
329
|
+
address: this.params.vault,
|
|
330
|
+
connectorTokens: this.supportedTokens.filter(
|
|
331
|
+
t => t.address !== tokenAddress,
|
|
332
|
+
),
|
|
333
|
+
liquidityUSD: this.vaultUSDBalance,
|
|
334
|
+
},
|
|
335
|
+
];
|
|
336
|
+
}
|
|
337
|
+
}
|