@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,873 +1,873 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
3
|
-
Network,
|
|
4
|
-
NULL_ADDRESS,
|
|
5
|
-
SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
6
|
-
SUBGRAPH_TIMEOUT,
|
|
7
|
-
} from '../../constants';
|
|
8
|
-
import {
|
|
9
|
-
AdapterExchangeParam,
|
|
10
|
-
Address,
|
|
11
|
-
DexExchangeParam,
|
|
12
|
-
ExchangePrices,
|
|
13
|
-
Log,
|
|
14
|
-
Logger,
|
|
15
|
-
PoolLiquidity,
|
|
16
|
-
PoolPrices,
|
|
17
|
-
SimpleExchangeParam,
|
|
18
|
-
Token,
|
|
19
|
-
TransferFeeParams,
|
|
20
|
-
} from '../../types';
|
|
21
|
-
import { IDexHelper } from '../../dex-helper';
|
|
22
|
-
import {
|
|
23
|
-
UniswapParam,
|
|
24
|
-
UniswapV2Functions,
|
|
25
|
-
UniswapV2Data,
|
|
26
|
-
UniswapPool,
|
|
27
|
-
} from '../uniswap-v2/types';
|
|
28
|
-
import { getBigIntPow, getDexKeysWithNetwork, isETHAddress } from '../../utils';
|
|
29
|
-
import camelotFactoryABI from '../../abi/camelot/CamelotFactory.json';
|
|
30
|
-
import camelotPairABI from '../../abi/camelot/CamelotPair.json';
|
|
31
|
-
import _ from 'lodash';
|
|
32
|
-
import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
|
|
33
|
-
import { NumberAsString, SwapSide } from '@paraswap/core';
|
|
34
|
-
import { Interface, AbiCoder } from '@ethersproject/abi';
|
|
35
|
-
import { SolidlyStablePool } from '../solidly/solidly-stable-pool';
|
|
36
|
-
import { Uniswapv2ConstantProductPool } from '../uniswap-v2/uniswap-v2-constant-product-pool';
|
|
37
|
-
import { CamelotPoolState, CamelotPoolOrderedParams } from './types';
|
|
38
|
-
import { CamelotConfig, Adapters } from './config';
|
|
39
|
-
import { Contract } from 'web3-eth-contract';
|
|
40
|
-
import { IDex } from '../idex';
|
|
41
|
-
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
|
|
42
|
-
import ParaSwapABI from '../../abi/IParaswap.json';
|
|
43
|
-
import UniswapV2ExchangeRouterABI from '../../abi/UniswapV2ExchangeRouter.json';
|
|
44
|
-
import { SimpleExchange } from '../simple-exchange';
|
|
45
|
-
import { applyTransferFee } from '../../lib/token-transfer-fee';
|
|
46
|
-
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
47
|
-
import { SolidlyData } from '../solidly/types';
|
|
48
|
-
import {
|
|
49
|
-
OnPoolCreatedCallback,
|
|
50
|
-
UniswapV2Factory,
|
|
51
|
-
} from '../uniswap-v2/uniswap-v2-factory';
|
|
52
|
-
import {
|
|
53
|
-
hexDataLength,
|
|
54
|
-
hexlify,
|
|
55
|
-
hexZeroPad,
|
|
56
|
-
id,
|
|
57
|
-
solidityPack,
|
|
58
|
-
} from 'ethers/lib/utils';
|
|
59
|
-
import { BigNumber } from 'ethers';
|
|
60
|
-
import { Flag, SpecialDex } from '../../executor/types';
|
|
61
|
-
|
|
62
|
-
const DefaultCamelotPoolGasCost = 90 * 1000;
|
|
63
|
-
|
|
64
|
-
const camelotPairIface = new Interface(camelotPairABI);
|
|
65
|
-
const coder = new AbiCoder();
|
|
66
|
-
|
|
67
|
-
const LogCallTopics = [
|
|
68
|
-
'0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1', // event Sync(uint112 reserve0, uint112 reserve1) // uni-V2 and most forks
|
|
69
|
-
'a4877b8ecb5a00ba277e4bceeeb187a669e7113649774dfbea05c259ce27f17b', // event FeePercentUpdated(uint16 token0FeePercent, uint16 token1FeePercent)
|
|
70
|
-
'b6a86710bde53aa7fb1b3856279e2af5b476d53e2dd0902cf17a0911b5a43a8b', // event SetStableSwap(bool prevStableSwap, bool stableSwap)
|
|
71
|
-
];
|
|
72
|
-
|
|
73
|
-
function encodePools(
|
|
74
|
-
pools: UniswapPool[],
|
|
75
|
-
feeFactor: number,
|
|
76
|
-
): NumberAsString[] {
|
|
77
|
-
return pools.map(({ fee, direction, address }) => {
|
|
78
|
-
return (
|
|
79
|
-
(BigInt(Math.ceil((feeFactor - fee) / 10)) << 161n) +
|
|
80
|
-
((direction ? 0n : 1n) << 160n) +
|
|
81
|
-
BigInt(address)
|
|
82
|
-
).toString();
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface CamelotPair {
|
|
87
|
-
token0: Token;
|
|
88
|
-
token1: Token;
|
|
89
|
-
exchange?: Address;
|
|
90
|
-
pool?: CamelotEventPool;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export class CamelotEventPool extends StatefulEventSubscriber<CamelotPoolState> {
|
|
94
|
-
decoder = (log: Log) => this.iface.parseLog(log);
|
|
95
|
-
|
|
96
|
-
constructor(
|
|
97
|
-
parentName: string,
|
|
98
|
-
protected dexHelper: IDexHelper,
|
|
99
|
-
private poolAddress: Address,
|
|
100
|
-
private token0: Token,
|
|
101
|
-
private token1: Token,
|
|
102
|
-
logger: Logger,
|
|
103
|
-
private iface: Interface = camelotPairIface,
|
|
104
|
-
private callEntries: any[],
|
|
105
|
-
private callDecoder: (data: any[]) => {
|
|
106
|
-
reserve0: string;
|
|
107
|
-
reserve1: string;
|
|
108
|
-
token0FeeCode: number;
|
|
109
|
-
token1FeeCode: number;
|
|
110
|
-
stable: boolean;
|
|
111
|
-
},
|
|
112
|
-
) {
|
|
113
|
-
super(
|
|
114
|
-
parentName,
|
|
115
|
-
(token0.symbol || token0.address) +
|
|
116
|
-
'-' +
|
|
117
|
-
(token1.symbol || token1.address) +
|
|
118
|
-
' pool',
|
|
119
|
-
dexHelper,
|
|
120
|
-
logger,
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
protected processLog(
|
|
125
|
-
state: DeepReadonly<CamelotPoolState>,
|
|
126
|
-
log: Readonly<Log>,
|
|
127
|
-
): AsyncOrSync<DeepReadonly<CamelotPoolState> | null> {
|
|
128
|
-
if (!LogCallTopics.includes(log.topics[0])) return null;
|
|
129
|
-
|
|
130
|
-
const event = this.decoder(log);
|
|
131
|
-
switch (event.name) {
|
|
132
|
-
case 'SetStableSwap':
|
|
133
|
-
return {
|
|
134
|
-
reserve0: state.reserve0,
|
|
135
|
-
reserve1: state.reserve1,
|
|
136
|
-
token0FeeCode: state.token0FeeCode,
|
|
137
|
-
token1FeeCode: state.token1FeeCode,
|
|
138
|
-
stable: event.args.stableSwap,
|
|
139
|
-
};
|
|
140
|
-
case 'FeePercentUpdated':
|
|
141
|
-
return {
|
|
142
|
-
reserve0: state.reserve0,
|
|
143
|
-
reserve1: state.reserve1,
|
|
144
|
-
token0FeeCode: parseInt(event.args.token0FeePercent.toString()),
|
|
145
|
-
token1FeeCode: parseInt(event.args.token1FeePercent.toString()),
|
|
146
|
-
stable: state.stable,
|
|
147
|
-
};
|
|
148
|
-
case 'Sync':
|
|
149
|
-
return {
|
|
150
|
-
reserve0: event.args.reserve0.toString(),
|
|
151
|
-
reserve1: event.args.reserve1.toString(),
|
|
152
|
-
token0FeeCode: state.token0FeeCode,
|
|
153
|
-
token1FeeCode: state.token1FeeCode,
|
|
154
|
-
stable: state.stable,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async generateState(
|
|
161
|
-
blockNumber: number | 'latest' = 'latest',
|
|
162
|
-
): Promise<DeepReadonly<CamelotPoolState>> {
|
|
163
|
-
const data: { returnData: any[] } =
|
|
164
|
-
await this.dexHelper.multiContract.methods
|
|
165
|
-
.aggregate(this.callEntries)
|
|
166
|
-
.call({}, blockNumber);
|
|
167
|
-
|
|
168
|
-
return this.callDecoder(data.returnData);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export class Camelot
|
|
173
|
-
extends SimpleExchange
|
|
174
|
-
implements IDex<SolidlyData, UniswapParam>
|
|
175
|
-
{
|
|
176
|
-
pairs: { [key: string]: CamelotPair } = {};
|
|
177
|
-
feeFactor = 100000;
|
|
178
|
-
factory: Contract;
|
|
179
|
-
|
|
180
|
-
needWrapNative = true;
|
|
181
|
-
routerInterface: Interface;
|
|
182
|
-
exchangeRouterInterface: Interface;
|
|
183
|
-
|
|
184
|
-
readonly hasConstantPriceLargeAmounts: boolean = false;
|
|
185
|
-
readonly isFeeOnTransferSupported: boolean = true;
|
|
186
|
-
readonly DEST_TOKEN_DEX_TRANSFERS = 1;
|
|
187
|
-
logger: Logger;
|
|
188
|
-
|
|
189
|
-
private readonly factoryInst: UniswapV2Factory;
|
|
190
|
-
|
|
191
|
-
private newlyCreatedPoolKeys: Set<string> = new Set();
|
|
192
|
-
|
|
193
|
-
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
194
|
-
getDexKeysWithNetwork(CamelotConfig);
|
|
195
|
-
|
|
196
|
-
constructor(
|
|
197
|
-
protected network: Network,
|
|
198
|
-
dexKey: string,
|
|
199
|
-
protected dexHelper: IDexHelper,
|
|
200
|
-
protected factoryAddress: Address = CamelotConfig[dexKey][network]
|
|
201
|
-
.factoryAddress,
|
|
202
|
-
protected subgraphURL: string | undefined = CamelotConfig[dexKey] &&
|
|
203
|
-
CamelotConfig[dexKey][network].subgraphURL,
|
|
204
|
-
protected initCode: string = CamelotConfig[dexKey][network].initCode,
|
|
205
|
-
protected poolGasCost: number = (CamelotConfig[dexKey] &&
|
|
206
|
-
CamelotConfig[dexKey][network].poolGasCost) ??
|
|
207
|
-
DefaultCamelotPoolGasCost,
|
|
208
|
-
protected decoderIface: Interface = camelotPairIface,
|
|
209
|
-
protected adapters = (CamelotConfig[dexKey] &&
|
|
210
|
-
CamelotConfig[dexKey][network].adapters) ??
|
|
211
|
-
Adapters[network],
|
|
212
|
-
protected routerAddress = (CamelotConfig[dexKey] &&
|
|
213
|
-
CamelotConfig[dexKey][network].router) ??
|
|
214
|
-
dexHelper.config.data.uniswapV2ExchangeRouterAddress,
|
|
215
|
-
) {
|
|
216
|
-
super(dexHelper, dexKey);
|
|
217
|
-
this.logger = dexHelper.getLogger(dexKey);
|
|
218
|
-
|
|
219
|
-
this.factory = new dexHelper.web3Provider.eth.Contract(
|
|
220
|
-
camelotFactoryABI as any,
|
|
221
|
-
factoryAddress,
|
|
222
|
-
);
|
|
223
|
-
this.routerAddress = routerAddress;
|
|
224
|
-
|
|
225
|
-
this.routerInterface = new Interface(ParaSwapABI);
|
|
226
|
-
this.exchangeRouterInterface = new Interface(UniswapV2ExchangeRouterABI);
|
|
227
|
-
|
|
228
|
-
this.factoryInst = new UniswapV2Factory(
|
|
229
|
-
dexHelper,
|
|
230
|
-
dexKey,
|
|
231
|
-
factoryAddress,
|
|
232
|
-
this.logger,
|
|
233
|
-
this.onPoolCreatedDeleteFromNonExistingSet,
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async initializePricing(blockNumber: number) {
|
|
238
|
-
// Init listening to new pools creation
|
|
239
|
-
await this.factoryInst.initialize(blockNumber);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
private getPoolIdentifier(token0: string, token1: string) {
|
|
243
|
-
const [_token0, _token1] =
|
|
244
|
-
token0.toLowerCase() < token1.toLowerCase()
|
|
245
|
-
? [token0, token1]
|
|
246
|
-
: [token1, token0];
|
|
247
|
-
|
|
248
|
-
const poolKey = `${this.dexKey}_${_token0}_${_token1}`.toLowerCase();
|
|
249
|
-
|
|
250
|
-
return poolKey;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/*
|
|
254
|
-
* When a non existing pool is queried, it's blacklisted for an arbitrary long period in order to prevent issuing too many rpc calls
|
|
255
|
-
* Once the pool is created, it gets immediately flagged
|
|
256
|
-
*/
|
|
257
|
-
onPoolCreatedDeleteFromNonExistingSet: OnPoolCreatedCallback = async ({
|
|
258
|
-
token0,
|
|
259
|
-
token1,
|
|
260
|
-
}) => {
|
|
261
|
-
const logPrefix = '[onPoolCreatedDeleteFromNonExistingSet]';
|
|
262
|
-
|
|
263
|
-
try {
|
|
264
|
-
const poolKey = this.getPoolIdentifier(token0, token1);
|
|
265
|
-
|
|
266
|
-
this.newlyCreatedPoolKeys.add(poolKey);
|
|
267
|
-
|
|
268
|
-
// delete entry locally to let local instance discover the pool
|
|
269
|
-
delete this.pairs[poolKey];
|
|
270
|
-
|
|
271
|
-
this.logger.info(`${logPrefix} discovered new pool ${poolKey}`);
|
|
272
|
-
} catch (e) {
|
|
273
|
-
this.logger.error(
|
|
274
|
-
`${logPrefix} LOGIC ERROR on ack new pool (token0=${token0},token1=${token1})`,
|
|
275
|
-
e,
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
getPoolStatesMultiCallData(pair: CamelotPair): {
|
|
281
|
-
callEntries: any[];
|
|
282
|
-
callDecoder: (data: any[]) => {
|
|
283
|
-
reserve0: string;
|
|
284
|
-
reserve1: string;
|
|
285
|
-
token0FeeCode: number;
|
|
286
|
-
token1FeeCode: number;
|
|
287
|
-
stable: boolean;
|
|
288
|
-
};
|
|
289
|
-
} {
|
|
290
|
-
const callEntries = [
|
|
291
|
-
{
|
|
292
|
-
target: pair.exchange,
|
|
293
|
-
callData: camelotPairIface.encodeFunctionData('getReserves', []),
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
target: pair.exchange,
|
|
297
|
-
callData: camelotPairIface.encodeFunctionData('stableSwap', []),
|
|
298
|
-
},
|
|
299
|
-
];
|
|
300
|
-
|
|
301
|
-
const callDecoder = (data: any[]) => {
|
|
302
|
-
const info = coder.decode(
|
|
303
|
-
['uint112', 'uint112', 'uint16', 'uint16'],
|
|
304
|
-
data[0],
|
|
305
|
-
);
|
|
306
|
-
const reserve0: string = info[0].toString();
|
|
307
|
-
const reserve1: string = info[1].toString();
|
|
308
|
-
const token0FeeCode: number = parseInt(info[2].toString());
|
|
309
|
-
const token1FeeCode: number = parseInt(info[3].toString());
|
|
310
|
-
const stable: boolean = coder.decode(['bool'], data[1])[0];
|
|
311
|
-
|
|
312
|
-
return { reserve0, reserve1, token0FeeCode, token1FeeCode, stable };
|
|
313
|
-
};
|
|
314
|
-
return { callEntries, callDecoder };
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
protected async addPool(
|
|
318
|
-
pair: CamelotPair,
|
|
319
|
-
reserve0: string,
|
|
320
|
-
reserve1: string,
|
|
321
|
-
token0FeeCode: number,
|
|
322
|
-
token1FeeCode: number,
|
|
323
|
-
stable: boolean,
|
|
324
|
-
blockNumber: number,
|
|
325
|
-
) {
|
|
326
|
-
const { callEntries, callDecoder } =
|
|
327
|
-
this.getPoolStatesMultiCallData(pair) || {};
|
|
328
|
-
pair.pool = new CamelotEventPool(
|
|
329
|
-
this.dexKey,
|
|
330
|
-
this.dexHelper,
|
|
331
|
-
pair.exchange!,
|
|
332
|
-
pair.token0,
|
|
333
|
-
pair.token1,
|
|
334
|
-
this.logger,
|
|
335
|
-
this.decoderIface,
|
|
336
|
-
callEntries,
|
|
337
|
-
callDecoder,
|
|
338
|
-
);
|
|
339
|
-
pair.pool.addressesSubscribed.push(pair.exchange!);
|
|
340
|
-
|
|
341
|
-
await pair.pool.initialize(blockNumber, {
|
|
342
|
-
state: { reserve0, reserve1, token0FeeCode, token1FeeCode, stable },
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
async getBuyPrice(
|
|
347
|
-
priceParams: CamelotPoolOrderedParams,
|
|
348
|
-
srcAmount: bigint,
|
|
349
|
-
): Promise<bigint> {
|
|
350
|
-
if (priceParams.stable) throw new Error('Buy not supported');
|
|
351
|
-
|
|
352
|
-
return Uniswapv2ConstantProductPool.getBuyPrice(
|
|
353
|
-
priceParams,
|
|
354
|
-
srcAmount,
|
|
355
|
-
this.feeFactor,
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
async getSellPrice(
|
|
360
|
-
priceParams: CamelotPoolOrderedParams,
|
|
361
|
-
srcAmount: bigint,
|
|
362
|
-
): Promise<bigint> {
|
|
363
|
-
return priceParams.stable
|
|
364
|
-
? SolidlyStablePool.getSellPrice(priceParams, srcAmount, this.feeFactor)
|
|
365
|
-
: Uniswapv2ConstantProductPool.getSellPrice(
|
|
366
|
-
priceParams,
|
|
367
|
-
srcAmount,
|
|
368
|
-
this.feeFactor,
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async getBuyPricePath(
|
|
373
|
-
amount: bigint,
|
|
374
|
-
params: CamelotPoolOrderedParams[],
|
|
375
|
-
): Promise<bigint> {
|
|
376
|
-
let price = amount;
|
|
377
|
-
for (const param of params.reverse()) {
|
|
378
|
-
price = await this.getBuyPrice(param, price);
|
|
379
|
-
}
|
|
380
|
-
return price;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
async getSellPricePath(
|
|
384
|
-
amount: bigint,
|
|
385
|
-
params: CamelotPoolOrderedParams[],
|
|
386
|
-
): Promise<bigint> {
|
|
387
|
-
let price = amount;
|
|
388
|
-
for (const param of params) {
|
|
389
|
-
price = await this.getSellPrice(param, price);
|
|
390
|
-
}
|
|
391
|
-
return price;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
async findPair(from: Token, to: Token) {
|
|
395
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
|
|
396
|
-
const [token0, token1] =
|
|
397
|
-
from.address.toLowerCase() < to.address.toLowerCase()
|
|
398
|
-
? [from, to]
|
|
399
|
-
: [to, from];
|
|
400
|
-
|
|
401
|
-
const key = this.getPoolIdentifier(token0.address, token1.address);
|
|
402
|
-
let pair = this.pairs[key];
|
|
403
|
-
if (pair) return pair;
|
|
404
|
-
const exchange = await this.factory.methods
|
|
405
|
-
.getPair(token0.address, token1.address)
|
|
406
|
-
.call();
|
|
407
|
-
if (exchange === NULL_ADDRESS) {
|
|
408
|
-
// if the pool has been newly created to not allow this op as we can run into race condition between pool discovery and concurrent pricing request touching this pool
|
|
409
|
-
if (!this.newlyCreatedPoolKeys.has(key)) {
|
|
410
|
-
pair = { token0, token1 };
|
|
411
|
-
}
|
|
412
|
-
} else {
|
|
413
|
-
pair = { token0, token1, exchange };
|
|
414
|
-
}
|
|
415
|
-
this.pairs[key] = pair;
|
|
416
|
-
return pair;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
async getManyPoolStates(
|
|
420
|
-
pairs: CamelotPair[],
|
|
421
|
-
blockNumber: number,
|
|
422
|
-
): Promise<CamelotPoolState[]> {
|
|
423
|
-
try {
|
|
424
|
-
const callData = pairs
|
|
425
|
-
.map((pair, i) => {
|
|
426
|
-
return this.getPoolStatesMultiCallData(pair).callEntries;
|
|
427
|
-
})
|
|
428
|
-
.flat();
|
|
429
|
-
|
|
430
|
-
const data: { returnData: any[] } =
|
|
431
|
-
await this.dexHelper.multiContract.methods
|
|
432
|
-
.aggregate(callData)
|
|
433
|
-
.call({}, blockNumber);
|
|
434
|
-
|
|
435
|
-
const returnData = _.chunk(data.returnData, 2);
|
|
436
|
-
|
|
437
|
-
return pairs.map((pair, i) => {
|
|
438
|
-
return this.getPoolStatesMultiCallData(pair).callDecoder(returnData[i]);
|
|
439
|
-
});
|
|
440
|
-
} catch (e) {
|
|
441
|
-
this.logger.error(
|
|
442
|
-
`Error_getManyPoolReserves could not get reserves with error:`,
|
|
443
|
-
e,
|
|
444
|
-
);
|
|
445
|
-
return [];
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
|
|
450
|
-
if (!blockNumber) return;
|
|
451
|
-
const pairsToFetch: CamelotPair[] = [];
|
|
452
|
-
for (const _pair of pairs) {
|
|
453
|
-
const pair = await this.findPair(_pair[0], _pair[1]);
|
|
454
|
-
if (!(pair && pair.exchange)) continue;
|
|
455
|
-
if (!pair.pool) {
|
|
456
|
-
pairsToFetch.push(pair);
|
|
457
|
-
} else if (!pair.pool.getState(blockNumber)) {
|
|
458
|
-
pairsToFetch.push(pair);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (!pairsToFetch.length) return;
|
|
463
|
-
|
|
464
|
-
const pairsStates = await this.getManyPoolStates(pairsToFetch, blockNumber);
|
|
465
|
-
|
|
466
|
-
if (pairsStates.length !== pairsToFetch.length) {
|
|
467
|
-
this.logger.error(
|
|
468
|
-
`Error_getManyPoolReserves didn't get any pool reserves`,
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
for (let i = 0; i < pairsToFetch.length; i++) {
|
|
473
|
-
const pairState = pairsStates[i];
|
|
474
|
-
const pair = pairsToFetch[i];
|
|
475
|
-
if (!pair.pool) {
|
|
476
|
-
await this.addPool(
|
|
477
|
-
pair,
|
|
478
|
-
pairState.reserve0,
|
|
479
|
-
pairState.reserve1,
|
|
480
|
-
pairState.token0FeeCode,
|
|
481
|
-
pairState.token1FeeCode,
|
|
482
|
-
pairState.stable,
|
|
483
|
-
blockNumber,
|
|
484
|
-
);
|
|
485
|
-
} else pair.pool.setState(pairState, blockNumber);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
async getPairOrderedParams(
|
|
490
|
-
from: Token,
|
|
491
|
-
to: Token,
|
|
492
|
-
blockNumber: number,
|
|
493
|
-
tokenDexTransferFee: number,
|
|
494
|
-
): Promise<CamelotPoolOrderedParams | null> {
|
|
495
|
-
const pair = await this.findPair(from, to);
|
|
496
|
-
if (!(pair && pair.pool && pair.exchange)) return null;
|
|
497
|
-
const pairState = pair.pool.getState(blockNumber);
|
|
498
|
-
if (!pairState) {
|
|
499
|
-
this.logger.error(
|
|
500
|
-
`Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
|
|
501
|
-
from.symbol || from.address
|
|
502
|
-
} ${to.symbol || to.address}`,
|
|
503
|
-
);
|
|
504
|
-
return null;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
const pairReversed =
|
|
508
|
-
pair.token1.address.toLowerCase() === from.address.toLowerCase();
|
|
509
|
-
if (pairReversed) {
|
|
510
|
-
return {
|
|
511
|
-
tokenIn: from.address,
|
|
512
|
-
tokenOut: to.address,
|
|
513
|
-
reservesIn: pairState.reserve1,
|
|
514
|
-
reservesOut: pairState.reserve0,
|
|
515
|
-
decimalsIn: from.decimals,
|
|
516
|
-
decimalsOut: to.decimals,
|
|
517
|
-
fee: (pairState.token1FeeCode + tokenDexTransferFee).toString(),
|
|
518
|
-
direction: false,
|
|
519
|
-
exchange: pair.exchange,
|
|
520
|
-
stable: pairState.stable,
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
return {
|
|
524
|
-
tokenIn: from.address,
|
|
525
|
-
tokenOut: to.address,
|
|
526
|
-
decimalsIn: from.decimals,
|
|
527
|
-
decimalsOut: to.decimals,
|
|
528
|
-
reservesIn: pairState.reserve0,
|
|
529
|
-
reservesOut: pairState.reserve1,
|
|
530
|
-
fee: (pairState.token0FeeCode + tokenDexTransferFee).toString(),
|
|
531
|
-
direction: true,
|
|
532
|
-
exchange: pair.exchange,
|
|
533
|
-
stable: pairState.stable,
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
async getPoolIdentifiers(
|
|
538
|
-
_from: Token,
|
|
539
|
-
_to: Token,
|
|
540
|
-
side: SwapSide,
|
|
541
|
-
blockNumber: number,
|
|
542
|
-
): Promise<string[]> {
|
|
543
|
-
if (side === SwapSide.BUY) return [];
|
|
544
|
-
|
|
545
|
-
const from = this.dexHelper.config.wrapETH(_from);
|
|
546
|
-
const to = this.dexHelper.config.wrapETH(_to);
|
|
547
|
-
|
|
548
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
549
|
-
return [];
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
const poolKey = this.getPoolIdentifier(from.address, to.address);
|
|
553
|
-
|
|
554
|
-
return [poolKey];
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
async getPricesVolume(
|
|
558
|
-
_from: Token,
|
|
559
|
-
_to: Token,
|
|
560
|
-
amounts: bigint[],
|
|
561
|
-
side: SwapSide,
|
|
562
|
-
blockNumber: number,
|
|
563
|
-
// list of pool identifiers to use for pricing, if undefined use all pools
|
|
564
|
-
limitPools?: string[],
|
|
565
|
-
transferFees: TransferFeeParams = {
|
|
566
|
-
srcFee: 0,
|
|
567
|
-
destFee: 0,
|
|
568
|
-
srcDexFee: 0,
|
|
569
|
-
destDexFee: 0,
|
|
570
|
-
},
|
|
571
|
-
): Promise<ExchangePrices<SolidlyData> | null> {
|
|
572
|
-
try {
|
|
573
|
-
const from = this.dexHelper.config.wrapETH(_from);
|
|
574
|
-
const to = this.dexHelper.config.wrapETH(_to);
|
|
575
|
-
|
|
576
|
-
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
577
|
-
return null;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
const poolIdentifier = this.getPoolIdentifier(from.address, to.address);
|
|
581
|
-
|
|
582
|
-
if (limitPools && limitPools.every(p => p !== poolIdentifier))
|
|
583
|
-
return null;
|
|
584
|
-
|
|
585
|
-
await this.batchCatchUpPairs([[from, to]], blockNumber);
|
|
586
|
-
const isSell = side === SwapSide.SELL;
|
|
587
|
-
const pairParam = await this.getPairOrderedParams(
|
|
588
|
-
from,
|
|
589
|
-
to,
|
|
590
|
-
blockNumber,
|
|
591
|
-
transferFees.srcDexFee,
|
|
592
|
-
);
|
|
593
|
-
|
|
594
|
-
if (!pairParam || (side === SwapSide.BUY && pairParam.stable))
|
|
595
|
-
return null;
|
|
596
|
-
|
|
597
|
-
const unitAmount = getBigIntPow(isSell ? from.decimals : to.decimals);
|
|
598
|
-
|
|
599
|
-
const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
|
|
600
|
-
[unitAmount, ...amounts],
|
|
601
|
-
side,
|
|
602
|
-
isSell ? transferFees.srcFee : transferFees.destFee,
|
|
603
|
-
isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
604
|
-
);
|
|
605
|
-
|
|
606
|
-
const unit = isSell
|
|
607
|
-
? await this.getSellPricePath(unitVolumeWithFee, [pairParam])
|
|
608
|
-
: await this.getBuyPricePath(unitVolumeWithFee, [pairParam]);
|
|
609
|
-
|
|
610
|
-
const prices = isSell
|
|
611
|
-
? await Promise.all(
|
|
612
|
-
amountsWithFee.map(amount =>
|
|
613
|
-
this.getSellPricePath(amount, [pairParam]),
|
|
614
|
-
),
|
|
615
|
-
)
|
|
616
|
-
: await Promise.all(
|
|
617
|
-
amountsWithFee.map(amount =>
|
|
618
|
-
this.getBuyPricePath(amount, [pairParam]),
|
|
619
|
-
),
|
|
620
|
-
);
|
|
621
|
-
|
|
622
|
-
const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
|
|
623
|
-
[unit, ...prices],
|
|
624
|
-
side,
|
|
625
|
-
// This part is confusing, because we treat differently SELL and BUY fees
|
|
626
|
-
// If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
|
|
627
|
-
// But for Sell we should apply only one dexFee
|
|
628
|
-
isSell ? transferFees.destDexFee : transferFees.srcFee,
|
|
629
|
-
isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
630
|
-
);
|
|
631
|
-
|
|
632
|
-
// As camelot just has one pool per token pair
|
|
633
|
-
return [
|
|
634
|
-
{
|
|
635
|
-
prices: outputsWithFee,
|
|
636
|
-
unit: unitOutWithFee,
|
|
637
|
-
data: {
|
|
638
|
-
router:
|
|
639
|
-
side === SwapSide.SELL
|
|
640
|
-
? this.routerAddress
|
|
641
|
-
: this.dexHelper.config.data.uniswapV2ExchangeRouterAddress,
|
|
642
|
-
path: [from.address.toLowerCase(), to.address.toLowerCase()],
|
|
643
|
-
factory: this.factoryAddress,
|
|
644
|
-
initCode: this.initCode,
|
|
645
|
-
feeFactor: this.feeFactor,
|
|
646
|
-
isFeeTokenInRoute: Object.values(transferFees).some(f => f !== 0),
|
|
647
|
-
pools: [
|
|
648
|
-
{
|
|
649
|
-
address: pairParam.exchange,
|
|
650
|
-
fee: parseInt(pairParam.fee),
|
|
651
|
-
direction: pairParam.direction,
|
|
652
|
-
},
|
|
653
|
-
],
|
|
654
|
-
},
|
|
655
|
-
exchange: this.dexKey,
|
|
656
|
-
poolIdentifier,
|
|
657
|
-
gasCost: this.poolGasCost,
|
|
658
|
-
poolAddresses: [pairParam.exchange],
|
|
659
|
-
},
|
|
660
|
-
];
|
|
661
|
-
} catch (e) {
|
|
662
|
-
if (blockNumber === 0)
|
|
663
|
-
this.logger.error(
|
|
664
|
-
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
665
|
-
);
|
|
666
|
-
this.logger.error(`Error_getPrices:`, e);
|
|
667
|
-
return null;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
672
|
-
getCalldataGasCost(poolPrices: PoolPrices<UniswapV2Data>): number | number[] {
|
|
673
|
-
return (
|
|
674
|
-
CALLDATA_GAS_COST.DEX_OVERHEAD +
|
|
675
|
-
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
676
|
-
// ParentStruct header
|
|
677
|
-
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
678
|
-
// ParentStruct -> weth
|
|
679
|
-
CALLDATA_GAS_COST.ADDRESS +
|
|
680
|
-
// ParentStruct -> pools[] header
|
|
681
|
-
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
682
|
-
// ParentStruct -> pools[]
|
|
683
|
-
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
684
|
-
// ParentStruct -> pools[0]
|
|
685
|
-
CALLDATA_GAS_COST.wordNonZeroBytes(22)
|
|
686
|
-
);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
690
|
-
return this.adapters[side];
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
async getTopPoolsForToken(
|
|
694
|
-
tokenAddress: Address,
|
|
695
|
-
count: number,
|
|
696
|
-
): Promise<PoolLiquidity[]> {
|
|
697
|
-
if (!this.subgraphURL) return [];
|
|
698
|
-
|
|
699
|
-
const query = `query ($token: Bytes!, $count: Int) {
|
|
700
|
-
pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
701
|
-
id
|
|
702
|
-
isStable
|
|
703
|
-
token0 {
|
|
704
|
-
id
|
|
705
|
-
decimals
|
|
706
|
-
}
|
|
707
|
-
token1 {
|
|
708
|
-
id
|
|
709
|
-
decimals
|
|
710
|
-
}
|
|
711
|
-
reserveUSD
|
|
712
|
-
}
|
|
713
|
-
pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
714
|
-
id
|
|
715
|
-
isStable
|
|
716
|
-
token0 {
|
|
717
|
-
id
|
|
718
|
-
decimals
|
|
719
|
-
}
|
|
720
|
-
token1 {
|
|
721
|
-
id
|
|
722
|
-
decimals
|
|
723
|
-
}
|
|
724
|
-
reserveUSD
|
|
725
|
-
}
|
|
726
|
-
}`;
|
|
727
|
-
|
|
728
|
-
const { data } = await this.dexHelper.httpRequest.post(
|
|
729
|
-
this.subgraphURL,
|
|
730
|
-
{
|
|
731
|
-
query,
|
|
732
|
-
variables: { token: tokenAddress.toLowerCase(), count },
|
|
733
|
-
},
|
|
734
|
-
SUBGRAPH_TIMEOUT,
|
|
735
|
-
);
|
|
736
|
-
|
|
737
|
-
if (!(data && data.pools0 && data.pools1))
|
|
738
|
-
throw new Error("Couldn't fetch the pools from the subgraph");
|
|
739
|
-
const pools0 = _.map(data.pools0, pool => ({
|
|
740
|
-
exchange: this.dexKey,
|
|
741
|
-
stable: pool.isStable,
|
|
742
|
-
address: pool.id.toLowerCase(),
|
|
743
|
-
connectorTokens: [
|
|
744
|
-
{
|
|
745
|
-
address: pool.token1.id.toLowerCase(),
|
|
746
|
-
decimals: parseInt(pool.token1.decimals),
|
|
747
|
-
},
|
|
748
|
-
],
|
|
749
|
-
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
750
|
-
}));
|
|
751
|
-
|
|
752
|
-
const pools1 = _.map(data.pools1, pool => ({
|
|
753
|
-
exchange: this.dexKey,
|
|
754
|
-
stable: pool.isStable,
|
|
755
|
-
address: pool.id.toLowerCase(),
|
|
756
|
-
connectorTokens: [
|
|
757
|
-
{
|
|
758
|
-
address: pool.token0.id.toLowerCase(),
|
|
759
|
-
decimals: parseInt(pool.token0.decimals),
|
|
760
|
-
},
|
|
761
|
-
],
|
|
762
|
-
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
763
|
-
}));
|
|
764
|
-
|
|
765
|
-
return _.slice(
|
|
766
|
-
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
767
|
-
0,
|
|
768
|
-
count,
|
|
769
|
-
);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
getWETHAddress(srcToken: Address, destToken: Address, weth?: Address) {
|
|
773
|
-
if (!isETHAddress(srcToken) && !isETHAddress(destToken))
|
|
774
|
-
return NULL_ADDRESS;
|
|
775
|
-
return weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
getAdapterParam(
|
|
779
|
-
srcToken: Address,
|
|
780
|
-
destToken: Address,
|
|
781
|
-
srcAmount: NumberAsString,
|
|
782
|
-
toAmount: NumberAsString, // required for buy case
|
|
783
|
-
data: SolidlyData,
|
|
784
|
-
side: SwapSide,
|
|
785
|
-
): AdapterExchangeParam {
|
|
786
|
-
if (side === SwapSide.BUY) throw new Error('Buy not supported');
|
|
787
|
-
|
|
788
|
-
const pools = encodePools(data.pools, this.feeFactor);
|
|
789
|
-
const weth = this.getWETHAddress(srcToken, destToken, data.wethAddress);
|
|
790
|
-
const payload = this.abiCoder.encodeParameter(
|
|
791
|
-
{
|
|
792
|
-
ParentStruct: {
|
|
793
|
-
weth: 'address',
|
|
794
|
-
pools: 'uint256[]',
|
|
795
|
-
isFeeTokenInRoute: 'bool',
|
|
796
|
-
},
|
|
797
|
-
},
|
|
798
|
-
{ pools, weth, isFeeTokenInRoute: data.isFeeTokenInRoute },
|
|
799
|
-
);
|
|
800
|
-
return {
|
|
801
|
-
targetExchange: data.router,
|
|
802
|
-
payload,
|
|
803
|
-
networkFee: '0',
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
async getSimpleParam(
|
|
808
|
-
src: Address,
|
|
809
|
-
dest: Address,
|
|
810
|
-
srcAmount: NumberAsString,
|
|
811
|
-
destAmount: NumberAsString,
|
|
812
|
-
data: SolidlyData,
|
|
813
|
-
side: SwapSide,
|
|
814
|
-
): Promise<SimpleExchangeParam> {
|
|
815
|
-
if (side === SwapSide.BUY) throw new Error('Buy not supported');
|
|
816
|
-
|
|
817
|
-
const pools = encodePools(data.pools, this.feeFactor);
|
|
818
|
-
const weth = this.getWETHAddress(src, dest, data.wethAddress);
|
|
819
|
-
const swapData = this.exchangeRouterInterface.encodeFunctionData(
|
|
820
|
-
side === SwapSide.SELL ? UniswapV2Functions.swap : UniswapV2Functions.buy,
|
|
821
|
-
[src, srcAmount, destAmount, weth, pools],
|
|
822
|
-
);
|
|
823
|
-
|
|
824
|
-
return this.buildSimpleParamWithoutWETHConversion(
|
|
825
|
-
src,
|
|
826
|
-
srcAmount,
|
|
827
|
-
dest,
|
|
828
|
-
destAmount,
|
|
829
|
-
swapData,
|
|
830
|
-
data.router,
|
|
831
|
-
);
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
getDexParam(
|
|
835
|
-
srcToken: Address,
|
|
836
|
-
destToken: Address,
|
|
837
|
-
srcAmount: NumberAsString,
|
|
838
|
-
destAmount: NumberAsString,
|
|
839
|
-
recipient: Address,
|
|
840
|
-
data: SolidlyData,
|
|
841
|
-
side: SwapSide,
|
|
842
|
-
): DexExchangeParam {
|
|
843
|
-
if (side === SwapSide.BUY) throw new Error('Buy not supported');
|
|
844
|
-
let exchangeDataTypes = ['bytes4', 'bytes32'];
|
|
845
|
-
|
|
846
|
-
let exchangeDataToPack = [
|
|
847
|
-
hexZeroPad(hexlify(0), 4),
|
|
848
|
-
hexZeroPad(hexlify(data.pools.length), 32),
|
|
849
|
-
];
|
|
850
|
-
|
|
851
|
-
const pools = encodePools(data.pools, this.feeFactor);
|
|
852
|
-
pools.forEach(pool => {
|
|
853
|
-
exchangeDataTypes.push('bytes32');
|
|
854
|
-
exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
const exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
|
|
858
|
-
|
|
859
|
-
return {
|
|
860
|
-
needWrapNative: this.needWrapNative,
|
|
861
|
-
dexFuncHasRecipient: true,
|
|
862
|
-
exchangeData,
|
|
863
|
-
targetExchange: recipient,
|
|
864
|
-
specialDexFlag: data.isFeeTokenInRoute
|
|
865
|
-
? SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK_WITH_FEE
|
|
866
|
-
: SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK,
|
|
867
|
-
transferSrcTokenBeforeSwap: data.isFeeTokenInRoute
|
|
868
|
-
? undefined
|
|
869
|
-
: data.pools[0].address,
|
|
870
|
-
returnAmountPos: undefined,
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
3
|
+
Network,
|
|
4
|
+
NULL_ADDRESS,
|
|
5
|
+
SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
6
|
+
SUBGRAPH_TIMEOUT,
|
|
7
|
+
} from '../../constants';
|
|
8
|
+
import {
|
|
9
|
+
AdapterExchangeParam,
|
|
10
|
+
Address,
|
|
11
|
+
DexExchangeParam,
|
|
12
|
+
ExchangePrices,
|
|
13
|
+
Log,
|
|
14
|
+
Logger,
|
|
15
|
+
PoolLiquidity,
|
|
16
|
+
PoolPrices,
|
|
17
|
+
SimpleExchangeParam,
|
|
18
|
+
Token,
|
|
19
|
+
TransferFeeParams,
|
|
20
|
+
} from '../../types';
|
|
21
|
+
import { IDexHelper } from '../../dex-helper';
|
|
22
|
+
import {
|
|
23
|
+
UniswapParam,
|
|
24
|
+
UniswapV2Functions,
|
|
25
|
+
UniswapV2Data,
|
|
26
|
+
UniswapPool,
|
|
27
|
+
} from '../uniswap-v2/types';
|
|
28
|
+
import { getBigIntPow, getDexKeysWithNetwork, isETHAddress } from '../../utils';
|
|
29
|
+
import camelotFactoryABI from '../../abi/camelot/CamelotFactory.json';
|
|
30
|
+
import camelotPairABI from '../../abi/camelot/CamelotPair.json';
|
|
31
|
+
import _ from 'lodash';
|
|
32
|
+
import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
|
|
33
|
+
import { NumberAsString, SwapSide } from '@paraswap/core';
|
|
34
|
+
import { Interface, AbiCoder } from '@ethersproject/abi';
|
|
35
|
+
import { SolidlyStablePool } from '../solidly/solidly-stable-pool';
|
|
36
|
+
import { Uniswapv2ConstantProductPool } from '../uniswap-v2/uniswap-v2-constant-product-pool';
|
|
37
|
+
import { CamelotPoolState, CamelotPoolOrderedParams } from './types';
|
|
38
|
+
import { CamelotConfig, Adapters } from './config';
|
|
39
|
+
import { Contract } from 'web3-eth-contract';
|
|
40
|
+
import { IDex } from '../idex';
|
|
41
|
+
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
|
|
42
|
+
import ParaSwapABI from '../../abi/IParaswap.json';
|
|
43
|
+
import UniswapV2ExchangeRouterABI from '../../abi/UniswapV2ExchangeRouter.json';
|
|
44
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
45
|
+
import { applyTransferFee } from '../../lib/token-transfer-fee';
|
|
46
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
47
|
+
import { SolidlyData } from '../solidly/types';
|
|
48
|
+
import {
|
|
49
|
+
OnPoolCreatedCallback,
|
|
50
|
+
UniswapV2Factory,
|
|
51
|
+
} from '../uniswap-v2/uniswap-v2-factory';
|
|
52
|
+
import {
|
|
53
|
+
hexDataLength,
|
|
54
|
+
hexlify,
|
|
55
|
+
hexZeroPad,
|
|
56
|
+
id,
|
|
57
|
+
solidityPack,
|
|
58
|
+
} from 'ethers/lib/utils';
|
|
59
|
+
import { BigNumber } from 'ethers';
|
|
60
|
+
import { Flag, SpecialDex } from '../../executor/types';
|
|
61
|
+
|
|
62
|
+
const DefaultCamelotPoolGasCost = 90 * 1000;
|
|
63
|
+
|
|
64
|
+
const camelotPairIface = new Interface(camelotPairABI);
|
|
65
|
+
const coder = new AbiCoder();
|
|
66
|
+
|
|
67
|
+
const LogCallTopics = [
|
|
68
|
+
'0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1', // event Sync(uint112 reserve0, uint112 reserve1) // uni-V2 and most forks
|
|
69
|
+
'a4877b8ecb5a00ba277e4bceeeb187a669e7113649774dfbea05c259ce27f17b', // event FeePercentUpdated(uint16 token0FeePercent, uint16 token1FeePercent)
|
|
70
|
+
'b6a86710bde53aa7fb1b3856279e2af5b476d53e2dd0902cf17a0911b5a43a8b', // event SetStableSwap(bool prevStableSwap, bool stableSwap)
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
function encodePools(
|
|
74
|
+
pools: UniswapPool[],
|
|
75
|
+
feeFactor: number,
|
|
76
|
+
): NumberAsString[] {
|
|
77
|
+
return pools.map(({ fee, direction, address }) => {
|
|
78
|
+
return (
|
|
79
|
+
(BigInt(Math.ceil((feeFactor - fee) / 10)) << 161n) +
|
|
80
|
+
((direction ? 0n : 1n) << 160n) +
|
|
81
|
+
BigInt(address)
|
|
82
|
+
).toString();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface CamelotPair {
|
|
87
|
+
token0: Token;
|
|
88
|
+
token1: Token;
|
|
89
|
+
exchange?: Address;
|
|
90
|
+
pool?: CamelotEventPool;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export class CamelotEventPool extends StatefulEventSubscriber<CamelotPoolState> {
|
|
94
|
+
decoder = (log: Log) => this.iface.parseLog(log);
|
|
95
|
+
|
|
96
|
+
constructor(
|
|
97
|
+
parentName: string,
|
|
98
|
+
protected dexHelper: IDexHelper,
|
|
99
|
+
private poolAddress: Address,
|
|
100
|
+
private token0: Token,
|
|
101
|
+
private token1: Token,
|
|
102
|
+
logger: Logger,
|
|
103
|
+
private iface: Interface = camelotPairIface,
|
|
104
|
+
private callEntries: any[],
|
|
105
|
+
private callDecoder: (data: any[]) => {
|
|
106
|
+
reserve0: string;
|
|
107
|
+
reserve1: string;
|
|
108
|
+
token0FeeCode: number;
|
|
109
|
+
token1FeeCode: number;
|
|
110
|
+
stable: boolean;
|
|
111
|
+
},
|
|
112
|
+
) {
|
|
113
|
+
super(
|
|
114
|
+
parentName,
|
|
115
|
+
(token0.symbol || token0.address) +
|
|
116
|
+
'-' +
|
|
117
|
+
(token1.symbol || token1.address) +
|
|
118
|
+
' pool',
|
|
119
|
+
dexHelper,
|
|
120
|
+
logger,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected processLog(
|
|
125
|
+
state: DeepReadonly<CamelotPoolState>,
|
|
126
|
+
log: Readonly<Log>,
|
|
127
|
+
): AsyncOrSync<DeepReadonly<CamelotPoolState> | null> {
|
|
128
|
+
if (!LogCallTopics.includes(log.topics[0])) return null;
|
|
129
|
+
|
|
130
|
+
const event = this.decoder(log);
|
|
131
|
+
switch (event.name) {
|
|
132
|
+
case 'SetStableSwap':
|
|
133
|
+
return {
|
|
134
|
+
reserve0: state.reserve0,
|
|
135
|
+
reserve1: state.reserve1,
|
|
136
|
+
token0FeeCode: state.token0FeeCode,
|
|
137
|
+
token1FeeCode: state.token1FeeCode,
|
|
138
|
+
stable: event.args.stableSwap,
|
|
139
|
+
};
|
|
140
|
+
case 'FeePercentUpdated':
|
|
141
|
+
return {
|
|
142
|
+
reserve0: state.reserve0,
|
|
143
|
+
reserve1: state.reserve1,
|
|
144
|
+
token0FeeCode: parseInt(event.args.token0FeePercent.toString()),
|
|
145
|
+
token1FeeCode: parseInt(event.args.token1FeePercent.toString()),
|
|
146
|
+
stable: state.stable,
|
|
147
|
+
};
|
|
148
|
+
case 'Sync':
|
|
149
|
+
return {
|
|
150
|
+
reserve0: event.args.reserve0.toString(),
|
|
151
|
+
reserve1: event.args.reserve1.toString(),
|
|
152
|
+
token0FeeCode: state.token0FeeCode,
|
|
153
|
+
token1FeeCode: state.token1FeeCode,
|
|
154
|
+
stable: state.stable,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async generateState(
|
|
161
|
+
blockNumber: number | 'latest' = 'latest',
|
|
162
|
+
): Promise<DeepReadonly<CamelotPoolState>> {
|
|
163
|
+
const data: { returnData: any[] } =
|
|
164
|
+
await this.dexHelper.multiContract.methods
|
|
165
|
+
.aggregate(this.callEntries)
|
|
166
|
+
.call({}, blockNumber);
|
|
167
|
+
|
|
168
|
+
return this.callDecoder(data.returnData);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export class Camelot
|
|
173
|
+
extends SimpleExchange
|
|
174
|
+
implements IDex<SolidlyData, UniswapParam>
|
|
175
|
+
{
|
|
176
|
+
pairs: { [key: string]: CamelotPair } = {};
|
|
177
|
+
feeFactor = 100000;
|
|
178
|
+
factory: Contract;
|
|
179
|
+
|
|
180
|
+
needWrapNative = true;
|
|
181
|
+
routerInterface: Interface;
|
|
182
|
+
exchangeRouterInterface: Interface;
|
|
183
|
+
|
|
184
|
+
readonly hasConstantPriceLargeAmounts: boolean = false;
|
|
185
|
+
readonly isFeeOnTransferSupported: boolean = true;
|
|
186
|
+
readonly DEST_TOKEN_DEX_TRANSFERS = 1;
|
|
187
|
+
logger: Logger;
|
|
188
|
+
|
|
189
|
+
private readonly factoryInst: UniswapV2Factory;
|
|
190
|
+
|
|
191
|
+
private newlyCreatedPoolKeys: Set<string> = new Set();
|
|
192
|
+
|
|
193
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
194
|
+
getDexKeysWithNetwork(CamelotConfig);
|
|
195
|
+
|
|
196
|
+
constructor(
|
|
197
|
+
protected network: Network,
|
|
198
|
+
dexKey: string,
|
|
199
|
+
protected dexHelper: IDexHelper,
|
|
200
|
+
protected factoryAddress: Address = CamelotConfig[dexKey][network]
|
|
201
|
+
.factoryAddress,
|
|
202
|
+
protected subgraphURL: string | undefined = CamelotConfig[dexKey] &&
|
|
203
|
+
CamelotConfig[dexKey][network].subgraphURL,
|
|
204
|
+
protected initCode: string = CamelotConfig[dexKey][network].initCode,
|
|
205
|
+
protected poolGasCost: number = (CamelotConfig[dexKey] &&
|
|
206
|
+
CamelotConfig[dexKey][network].poolGasCost) ??
|
|
207
|
+
DefaultCamelotPoolGasCost,
|
|
208
|
+
protected decoderIface: Interface = camelotPairIface,
|
|
209
|
+
protected adapters = (CamelotConfig[dexKey] &&
|
|
210
|
+
CamelotConfig[dexKey][network].adapters) ??
|
|
211
|
+
Adapters[network],
|
|
212
|
+
protected routerAddress = (CamelotConfig[dexKey] &&
|
|
213
|
+
CamelotConfig[dexKey][network].router) ??
|
|
214
|
+
dexHelper.config.data.uniswapV2ExchangeRouterAddress,
|
|
215
|
+
) {
|
|
216
|
+
super(dexHelper, dexKey);
|
|
217
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
218
|
+
|
|
219
|
+
this.factory = new dexHelper.web3Provider.eth.Contract(
|
|
220
|
+
camelotFactoryABI as any,
|
|
221
|
+
factoryAddress,
|
|
222
|
+
);
|
|
223
|
+
this.routerAddress = routerAddress;
|
|
224
|
+
|
|
225
|
+
this.routerInterface = new Interface(ParaSwapABI);
|
|
226
|
+
this.exchangeRouterInterface = new Interface(UniswapV2ExchangeRouterABI);
|
|
227
|
+
|
|
228
|
+
this.factoryInst = new UniswapV2Factory(
|
|
229
|
+
dexHelper,
|
|
230
|
+
dexKey,
|
|
231
|
+
factoryAddress,
|
|
232
|
+
this.logger,
|
|
233
|
+
this.onPoolCreatedDeleteFromNonExistingSet,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async initializePricing(blockNumber: number) {
|
|
238
|
+
// Init listening to new pools creation
|
|
239
|
+
await this.factoryInst.initialize(blockNumber);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private getPoolIdentifier(token0: string, token1: string) {
|
|
243
|
+
const [_token0, _token1] =
|
|
244
|
+
token0.toLowerCase() < token1.toLowerCase()
|
|
245
|
+
? [token0, token1]
|
|
246
|
+
: [token1, token0];
|
|
247
|
+
|
|
248
|
+
const poolKey = `${this.dexKey}_${_token0}_${_token1}`.toLowerCase();
|
|
249
|
+
|
|
250
|
+
return poolKey;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/*
|
|
254
|
+
* When a non existing pool is queried, it's blacklisted for an arbitrary long period in order to prevent issuing too many rpc calls
|
|
255
|
+
* Once the pool is created, it gets immediately flagged
|
|
256
|
+
*/
|
|
257
|
+
onPoolCreatedDeleteFromNonExistingSet: OnPoolCreatedCallback = async ({
|
|
258
|
+
token0,
|
|
259
|
+
token1,
|
|
260
|
+
}) => {
|
|
261
|
+
const logPrefix = '[onPoolCreatedDeleteFromNonExistingSet]';
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const poolKey = this.getPoolIdentifier(token0, token1);
|
|
265
|
+
|
|
266
|
+
this.newlyCreatedPoolKeys.add(poolKey);
|
|
267
|
+
|
|
268
|
+
// delete entry locally to let local instance discover the pool
|
|
269
|
+
delete this.pairs[poolKey];
|
|
270
|
+
|
|
271
|
+
this.logger.info(`${logPrefix} discovered new pool ${poolKey}`);
|
|
272
|
+
} catch (e) {
|
|
273
|
+
this.logger.error(
|
|
274
|
+
`${logPrefix} LOGIC ERROR on ack new pool (token0=${token0},token1=${token1})`,
|
|
275
|
+
e,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
getPoolStatesMultiCallData(pair: CamelotPair): {
|
|
281
|
+
callEntries: any[];
|
|
282
|
+
callDecoder: (data: any[]) => {
|
|
283
|
+
reserve0: string;
|
|
284
|
+
reserve1: string;
|
|
285
|
+
token0FeeCode: number;
|
|
286
|
+
token1FeeCode: number;
|
|
287
|
+
stable: boolean;
|
|
288
|
+
};
|
|
289
|
+
} {
|
|
290
|
+
const callEntries = [
|
|
291
|
+
{
|
|
292
|
+
target: pair.exchange,
|
|
293
|
+
callData: camelotPairIface.encodeFunctionData('getReserves', []),
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
target: pair.exchange,
|
|
297
|
+
callData: camelotPairIface.encodeFunctionData('stableSwap', []),
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
const callDecoder = (data: any[]) => {
|
|
302
|
+
const info = coder.decode(
|
|
303
|
+
['uint112', 'uint112', 'uint16', 'uint16'],
|
|
304
|
+
data[0],
|
|
305
|
+
);
|
|
306
|
+
const reserve0: string = info[0].toString();
|
|
307
|
+
const reserve1: string = info[1].toString();
|
|
308
|
+
const token0FeeCode: number = parseInt(info[2].toString());
|
|
309
|
+
const token1FeeCode: number = parseInt(info[3].toString());
|
|
310
|
+
const stable: boolean = coder.decode(['bool'], data[1])[0];
|
|
311
|
+
|
|
312
|
+
return { reserve0, reserve1, token0FeeCode, token1FeeCode, stable };
|
|
313
|
+
};
|
|
314
|
+
return { callEntries, callDecoder };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
protected async addPool(
|
|
318
|
+
pair: CamelotPair,
|
|
319
|
+
reserve0: string,
|
|
320
|
+
reserve1: string,
|
|
321
|
+
token0FeeCode: number,
|
|
322
|
+
token1FeeCode: number,
|
|
323
|
+
stable: boolean,
|
|
324
|
+
blockNumber: number,
|
|
325
|
+
) {
|
|
326
|
+
const { callEntries, callDecoder } =
|
|
327
|
+
this.getPoolStatesMultiCallData(pair) || {};
|
|
328
|
+
pair.pool = new CamelotEventPool(
|
|
329
|
+
this.dexKey,
|
|
330
|
+
this.dexHelper,
|
|
331
|
+
pair.exchange!,
|
|
332
|
+
pair.token0,
|
|
333
|
+
pair.token1,
|
|
334
|
+
this.logger,
|
|
335
|
+
this.decoderIface,
|
|
336
|
+
callEntries,
|
|
337
|
+
callDecoder,
|
|
338
|
+
);
|
|
339
|
+
pair.pool.addressesSubscribed.push(pair.exchange!);
|
|
340
|
+
|
|
341
|
+
await pair.pool.initialize(blockNumber, {
|
|
342
|
+
state: { reserve0, reserve1, token0FeeCode, token1FeeCode, stable },
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async getBuyPrice(
|
|
347
|
+
priceParams: CamelotPoolOrderedParams,
|
|
348
|
+
srcAmount: bigint,
|
|
349
|
+
): Promise<bigint> {
|
|
350
|
+
if (priceParams.stable) throw new Error('Buy not supported');
|
|
351
|
+
|
|
352
|
+
return Uniswapv2ConstantProductPool.getBuyPrice(
|
|
353
|
+
priceParams,
|
|
354
|
+
srcAmount,
|
|
355
|
+
this.feeFactor,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async getSellPrice(
|
|
360
|
+
priceParams: CamelotPoolOrderedParams,
|
|
361
|
+
srcAmount: bigint,
|
|
362
|
+
): Promise<bigint> {
|
|
363
|
+
return priceParams.stable
|
|
364
|
+
? SolidlyStablePool.getSellPrice(priceParams, srcAmount, this.feeFactor)
|
|
365
|
+
: Uniswapv2ConstantProductPool.getSellPrice(
|
|
366
|
+
priceParams,
|
|
367
|
+
srcAmount,
|
|
368
|
+
this.feeFactor,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async getBuyPricePath(
|
|
373
|
+
amount: bigint,
|
|
374
|
+
params: CamelotPoolOrderedParams[],
|
|
375
|
+
): Promise<bigint> {
|
|
376
|
+
let price = amount;
|
|
377
|
+
for (const param of params.reverse()) {
|
|
378
|
+
price = await this.getBuyPrice(param, price);
|
|
379
|
+
}
|
|
380
|
+
return price;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async getSellPricePath(
|
|
384
|
+
amount: bigint,
|
|
385
|
+
params: CamelotPoolOrderedParams[],
|
|
386
|
+
): Promise<bigint> {
|
|
387
|
+
let price = amount;
|
|
388
|
+
for (const param of params) {
|
|
389
|
+
price = await this.getSellPrice(param, price);
|
|
390
|
+
}
|
|
391
|
+
return price;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async findPair(from: Token, to: Token) {
|
|
395
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) return null;
|
|
396
|
+
const [token0, token1] =
|
|
397
|
+
from.address.toLowerCase() < to.address.toLowerCase()
|
|
398
|
+
? [from, to]
|
|
399
|
+
: [to, from];
|
|
400
|
+
|
|
401
|
+
const key = this.getPoolIdentifier(token0.address, token1.address);
|
|
402
|
+
let pair = this.pairs[key];
|
|
403
|
+
if (pair) return pair;
|
|
404
|
+
const exchange = await this.factory.methods
|
|
405
|
+
.getPair(token0.address, token1.address)
|
|
406
|
+
.call();
|
|
407
|
+
if (exchange === NULL_ADDRESS) {
|
|
408
|
+
// if the pool has been newly created to not allow this op as we can run into race condition between pool discovery and concurrent pricing request touching this pool
|
|
409
|
+
if (!this.newlyCreatedPoolKeys.has(key)) {
|
|
410
|
+
pair = { token0, token1 };
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
pair = { token0, token1, exchange };
|
|
414
|
+
}
|
|
415
|
+
this.pairs[key] = pair;
|
|
416
|
+
return pair;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async getManyPoolStates(
|
|
420
|
+
pairs: CamelotPair[],
|
|
421
|
+
blockNumber: number,
|
|
422
|
+
): Promise<CamelotPoolState[]> {
|
|
423
|
+
try {
|
|
424
|
+
const callData = pairs
|
|
425
|
+
.map((pair, i) => {
|
|
426
|
+
return this.getPoolStatesMultiCallData(pair).callEntries;
|
|
427
|
+
})
|
|
428
|
+
.flat();
|
|
429
|
+
|
|
430
|
+
const data: { returnData: any[] } =
|
|
431
|
+
await this.dexHelper.multiContract.methods
|
|
432
|
+
.aggregate(callData)
|
|
433
|
+
.call({}, blockNumber);
|
|
434
|
+
|
|
435
|
+
const returnData = _.chunk(data.returnData, 2);
|
|
436
|
+
|
|
437
|
+
return pairs.map((pair, i) => {
|
|
438
|
+
return this.getPoolStatesMultiCallData(pair).callDecoder(returnData[i]);
|
|
439
|
+
});
|
|
440
|
+
} catch (e) {
|
|
441
|
+
this.logger.error(
|
|
442
|
+
`Error_getManyPoolReserves could not get reserves with error:`,
|
|
443
|
+
e,
|
|
444
|
+
);
|
|
445
|
+
return [];
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async batchCatchUpPairs(pairs: [Token, Token][], blockNumber: number) {
|
|
450
|
+
if (!blockNumber) return;
|
|
451
|
+
const pairsToFetch: CamelotPair[] = [];
|
|
452
|
+
for (const _pair of pairs) {
|
|
453
|
+
const pair = await this.findPair(_pair[0], _pair[1]);
|
|
454
|
+
if (!(pair && pair.exchange)) continue;
|
|
455
|
+
if (!pair.pool) {
|
|
456
|
+
pairsToFetch.push(pair);
|
|
457
|
+
} else if (!pair.pool.getState(blockNumber)) {
|
|
458
|
+
pairsToFetch.push(pair);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (!pairsToFetch.length) return;
|
|
463
|
+
|
|
464
|
+
const pairsStates = await this.getManyPoolStates(pairsToFetch, blockNumber);
|
|
465
|
+
|
|
466
|
+
if (pairsStates.length !== pairsToFetch.length) {
|
|
467
|
+
this.logger.error(
|
|
468
|
+
`Error_getManyPoolReserves didn't get any pool reserves`,
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
for (let i = 0; i < pairsToFetch.length; i++) {
|
|
473
|
+
const pairState = pairsStates[i];
|
|
474
|
+
const pair = pairsToFetch[i];
|
|
475
|
+
if (!pair.pool) {
|
|
476
|
+
await this.addPool(
|
|
477
|
+
pair,
|
|
478
|
+
pairState.reserve0,
|
|
479
|
+
pairState.reserve1,
|
|
480
|
+
pairState.token0FeeCode,
|
|
481
|
+
pairState.token1FeeCode,
|
|
482
|
+
pairState.stable,
|
|
483
|
+
blockNumber,
|
|
484
|
+
);
|
|
485
|
+
} else pair.pool.setState(pairState, blockNumber);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async getPairOrderedParams(
|
|
490
|
+
from: Token,
|
|
491
|
+
to: Token,
|
|
492
|
+
blockNumber: number,
|
|
493
|
+
tokenDexTransferFee: number,
|
|
494
|
+
): Promise<CamelotPoolOrderedParams | null> {
|
|
495
|
+
const pair = await this.findPair(from, to);
|
|
496
|
+
if (!(pair && pair.pool && pair.exchange)) return null;
|
|
497
|
+
const pairState = pair.pool.getState(blockNumber);
|
|
498
|
+
if (!pairState) {
|
|
499
|
+
this.logger.error(
|
|
500
|
+
`Error_orderPairParams expected reserves, got none (maybe the pool doesn't exist) ${
|
|
501
|
+
from.symbol || from.address
|
|
502
|
+
} ${to.symbol || to.address}`,
|
|
503
|
+
);
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const pairReversed =
|
|
508
|
+
pair.token1.address.toLowerCase() === from.address.toLowerCase();
|
|
509
|
+
if (pairReversed) {
|
|
510
|
+
return {
|
|
511
|
+
tokenIn: from.address,
|
|
512
|
+
tokenOut: to.address,
|
|
513
|
+
reservesIn: pairState.reserve1,
|
|
514
|
+
reservesOut: pairState.reserve0,
|
|
515
|
+
decimalsIn: from.decimals,
|
|
516
|
+
decimalsOut: to.decimals,
|
|
517
|
+
fee: (pairState.token1FeeCode + tokenDexTransferFee).toString(),
|
|
518
|
+
direction: false,
|
|
519
|
+
exchange: pair.exchange,
|
|
520
|
+
stable: pairState.stable,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
tokenIn: from.address,
|
|
525
|
+
tokenOut: to.address,
|
|
526
|
+
decimalsIn: from.decimals,
|
|
527
|
+
decimalsOut: to.decimals,
|
|
528
|
+
reservesIn: pairState.reserve0,
|
|
529
|
+
reservesOut: pairState.reserve1,
|
|
530
|
+
fee: (pairState.token0FeeCode + tokenDexTransferFee).toString(),
|
|
531
|
+
direction: true,
|
|
532
|
+
exchange: pair.exchange,
|
|
533
|
+
stable: pairState.stable,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
async getPoolIdentifiers(
|
|
538
|
+
_from: Token,
|
|
539
|
+
_to: Token,
|
|
540
|
+
side: SwapSide,
|
|
541
|
+
blockNumber: number,
|
|
542
|
+
): Promise<string[]> {
|
|
543
|
+
if (side === SwapSide.BUY) return [];
|
|
544
|
+
|
|
545
|
+
const from = this.dexHelper.config.wrapETH(_from);
|
|
546
|
+
const to = this.dexHelper.config.wrapETH(_to);
|
|
547
|
+
|
|
548
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
549
|
+
return [];
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const poolKey = this.getPoolIdentifier(from.address, to.address);
|
|
553
|
+
|
|
554
|
+
return [poolKey];
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async getPricesVolume(
|
|
558
|
+
_from: Token,
|
|
559
|
+
_to: Token,
|
|
560
|
+
amounts: bigint[],
|
|
561
|
+
side: SwapSide,
|
|
562
|
+
blockNumber: number,
|
|
563
|
+
// list of pool identifiers to use for pricing, if undefined use all pools
|
|
564
|
+
limitPools?: string[],
|
|
565
|
+
transferFees: TransferFeeParams = {
|
|
566
|
+
srcFee: 0,
|
|
567
|
+
destFee: 0,
|
|
568
|
+
srcDexFee: 0,
|
|
569
|
+
destDexFee: 0,
|
|
570
|
+
},
|
|
571
|
+
): Promise<ExchangePrices<SolidlyData> | null> {
|
|
572
|
+
try {
|
|
573
|
+
const from = this.dexHelper.config.wrapETH(_from);
|
|
574
|
+
const to = this.dexHelper.config.wrapETH(_to);
|
|
575
|
+
|
|
576
|
+
if (from.address.toLowerCase() === to.address.toLowerCase()) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const poolIdentifier = this.getPoolIdentifier(from.address, to.address);
|
|
581
|
+
|
|
582
|
+
if (limitPools && limitPools.every(p => p !== poolIdentifier))
|
|
583
|
+
return null;
|
|
584
|
+
|
|
585
|
+
await this.batchCatchUpPairs([[from, to]], blockNumber);
|
|
586
|
+
const isSell = side === SwapSide.SELL;
|
|
587
|
+
const pairParam = await this.getPairOrderedParams(
|
|
588
|
+
from,
|
|
589
|
+
to,
|
|
590
|
+
blockNumber,
|
|
591
|
+
transferFees.srcDexFee,
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
if (!pairParam || (side === SwapSide.BUY && pairParam.stable))
|
|
595
|
+
return null;
|
|
596
|
+
|
|
597
|
+
const unitAmount = getBigIntPow(isSell ? from.decimals : to.decimals);
|
|
598
|
+
|
|
599
|
+
const [unitVolumeWithFee, ...amountsWithFee] = applyTransferFee(
|
|
600
|
+
[unitAmount, ...amounts],
|
|
601
|
+
side,
|
|
602
|
+
isSell ? transferFees.srcFee : transferFees.destFee,
|
|
603
|
+
isSell ? SRC_TOKEN_PARASWAP_TRANSFERS : DEST_TOKEN_PARASWAP_TRANSFERS,
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
const unit = isSell
|
|
607
|
+
? await this.getSellPricePath(unitVolumeWithFee, [pairParam])
|
|
608
|
+
: await this.getBuyPricePath(unitVolumeWithFee, [pairParam]);
|
|
609
|
+
|
|
610
|
+
const prices = isSell
|
|
611
|
+
? await Promise.all(
|
|
612
|
+
amountsWithFee.map(amount =>
|
|
613
|
+
this.getSellPricePath(amount, [pairParam]),
|
|
614
|
+
),
|
|
615
|
+
)
|
|
616
|
+
: await Promise.all(
|
|
617
|
+
amountsWithFee.map(amount =>
|
|
618
|
+
this.getBuyPricePath(amount, [pairParam]),
|
|
619
|
+
),
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
const [unitOutWithFee, ...outputsWithFee] = applyTransferFee(
|
|
623
|
+
[unit, ...prices],
|
|
624
|
+
side,
|
|
625
|
+
// This part is confusing, because we treat differently SELL and BUY fees
|
|
626
|
+
// If Buy, we should apply transfer fee on srcToken on top of dexFee applied earlier
|
|
627
|
+
// But for Sell we should apply only one dexFee
|
|
628
|
+
isSell ? transferFees.destDexFee : transferFees.srcFee,
|
|
629
|
+
isSell ? this.DEST_TOKEN_DEX_TRANSFERS : SRC_TOKEN_PARASWAP_TRANSFERS,
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
// As camelot just has one pool per token pair
|
|
633
|
+
return [
|
|
634
|
+
{
|
|
635
|
+
prices: outputsWithFee,
|
|
636
|
+
unit: unitOutWithFee,
|
|
637
|
+
data: {
|
|
638
|
+
router:
|
|
639
|
+
side === SwapSide.SELL
|
|
640
|
+
? this.routerAddress
|
|
641
|
+
: this.dexHelper.config.data.uniswapV2ExchangeRouterAddress,
|
|
642
|
+
path: [from.address.toLowerCase(), to.address.toLowerCase()],
|
|
643
|
+
factory: this.factoryAddress,
|
|
644
|
+
initCode: this.initCode,
|
|
645
|
+
feeFactor: this.feeFactor,
|
|
646
|
+
isFeeTokenInRoute: Object.values(transferFees).some(f => f !== 0),
|
|
647
|
+
pools: [
|
|
648
|
+
{
|
|
649
|
+
address: pairParam.exchange,
|
|
650
|
+
fee: parseInt(pairParam.fee),
|
|
651
|
+
direction: pairParam.direction,
|
|
652
|
+
},
|
|
653
|
+
],
|
|
654
|
+
},
|
|
655
|
+
exchange: this.dexKey,
|
|
656
|
+
poolIdentifier,
|
|
657
|
+
gasCost: this.poolGasCost,
|
|
658
|
+
poolAddresses: [pairParam.exchange],
|
|
659
|
+
},
|
|
660
|
+
];
|
|
661
|
+
} catch (e) {
|
|
662
|
+
if (blockNumber === 0)
|
|
663
|
+
this.logger.error(
|
|
664
|
+
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
665
|
+
);
|
|
666
|
+
this.logger.error(`Error_getPrices:`, e);
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
672
|
+
getCalldataGasCost(poolPrices: PoolPrices<UniswapV2Data>): number | number[] {
|
|
673
|
+
return (
|
|
674
|
+
CALLDATA_GAS_COST.DEX_OVERHEAD +
|
|
675
|
+
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
676
|
+
// ParentStruct header
|
|
677
|
+
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
678
|
+
// ParentStruct -> weth
|
|
679
|
+
CALLDATA_GAS_COST.ADDRESS +
|
|
680
|
+
// ParentStruct -> pools[] header
|
|
681
|
+
CALLDATA_GAS_COST.OFFSET_SMALL +
|
|
682
|
+
// ParentStruct -> pools[]
|
|
683
|
+
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
684
|
+
// ParentStruct -> pools[0]
|
|
685
|
+
CALLDATA_GAS_COST.wordNonZeroBytes(22)
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
690
|
+
return this.adapters[side];
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
async getTopPoolsForToken(
|
|
694
|
+
tokenAddress: Address,
|
|
695
|
+
count: number,
|
|
696
|
+
): Promise<PoolLiquidity[]> {
|
|
697
|
+
if (!this.subgraphURL) return [];
|
|
698
|
+
|
|
699
|
+
const query = `query ($token: Bytes!, $count: Int) {
|
|
700
|
+
pools0: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token0: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
701
|
+
id
|
|
702
|
+
isStable
|
|
703
|
+
token0 {
|
|
704
|
+
id
|
|
705
|
+
decimals
|
|
706
|
+
}
|
|
707
|
+
token1 {
|
|
708
|
+
id
|
|
709
|
+
decimals
|
|
710
|
+
}
|
|
711
|
+
reserveUSD
|
|
712
|
+
}
|
|
713
|
+
pools1: pairs(first: $count, orderBy: reserveUSD, orderDirection: desc, where: {token1: $token, reserve0_gt: 1, reserve1_gt: 1}) {
|
|
714
|
+
id
|
|
715
|
+
isStable
|
|
716
|
+
token0 {
|
|
717
|
+
id
|
|
718
|
+
decimals
|
|
719
|
+
}
|
|
720
|
+
token1 {
|
|
721
|
+
id
|
|
722
|
+
decimals
|
|
723
|
+
}
|
|
724
|
+
reserveUSD
|
|
725
|
+
}
|
|
726
|
+
}`;
|
|
727
|
+
|
|
728
|
+
const { data } = await this.dexHelper.httpRequest.post(
|
|
729
|
+
this.subgraphURL,
|
|
730
|
+
{
|
|
731
|
+
query,
|
|
732
|
+
variables: { token: tokenAddress.toLowerCase(), count },
|
|
733
|
+
},
|
|
734
|
+
SUBGRAPH_TIMEOUT,
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
if (!(data && data.pools0 && data.pools1))
|
|
738
|
+
throw new Error("Couldn't fetch the pools from the subgraph");
|
|
739
|
+
const pools0 = _.map(data.pools0, pool => ({
|
|
740
|
+
exchange: this.dexKey,
|
|
741
|
+
stable: pool.isStable,
|
|
742
|
+
address: pool.id.toLowerCase(),
|
|
743
|
+
connectorTokens: [
|
|
744
|
+
{
|
|
745
|
+
address: pool.token1.id.toLowerCase(),
|
|
746
|
+
decimals: parseInt(pool.token1.decimals),
|
|
747
|
+
},
|
|
748
|
+
],
|
|
749
|
+
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
750
|
+
}));
|
|
751
|
+
|
|
752
|
+
const pools1 = _.map(data.pools1, pool => ({
|
|
753
|
+
exchange: this.dexKey,
|
|
754
|
+
stable: pool.isStable,
|
|
755
|
+
address: pool.id.toLowerCase(),
|
|
756
|
+
connectorTokens: [
|
|
757
|
+
{
|
|
758
|
+
address: pool.token0.id.toLowerCase(),
|
|
759
|
+
decimals: parseInt(pool.token0.decimals),
|
|
760
|
+
},
|
|
761
|
+
],
|
|
762
|
+
liquidityUSD: parseFloat(pool.reserveUSD),
|
|
763
|
+
}));
|
|
764
|
+
|
|
765
|
+
return _.slice(
|
|
766
|
+
_.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]),
|
|
767
|
+
0,
|
|
768
|
+
count,
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
getWETHAddress(srcToken: Address, destToken: Address, weth?: Address) {
|
|
773
|
+
if (!isETHAddress(srcToken) && !isETHAddress(destToken))
|
|
774
|
+
return NULL_ADDRESS;
|
|
775
|
+
return weth || this.dexHelper.config.data.wrappedNativeTokenAddress;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
getAdapterParam(
|
|
779
|
+
srcToken: Address,
|
|
780
|
+
destToken: Address,
|
|
781
|
+
srcAmount: NumberAsString,
|
|
782
|
+
toAmount: NumberAsString, // required for buy case
|
|
783
|
+
data: SolidlyData,
|
|
784
|
+
side: SwapSide,
|
|
785
|
+
): AdapterExchangeParam {
|
|
786
|
+
if (side === SwapSide.BUY) throw new Error('Buy not supported');
|
|
787
|
+
|
|
788
|
+
const pools = encodePools(data.pools, this.feeFactor);
|
|
789
|
+
const weth = this.getWETHAddress(srcToken, destToken, data.wethAddress);
|
|
790
|
+
const payload = this.abiCoder.encodeParameter(
|
|
791
|
+
{
|
|
792
|
+
ParentStruct: {
|
|
793
|
+
weth: 'address',
|
|
794
|
+
pools: 'uint256[]',
|
|
795
|
+
isFeeTokenInRoute: 'bool',
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
{ pools, weth, isFeeTokenInRoute: data.isFeeTokenInRoute },
|
|
799
|
+
);
|
|
800
|
+
return {
|
|
801
|
+
targetExchange: data.router,
|
|
802
|
+
payload,
|
|
803
|
+
networkFee: '0',
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
async getSimpleParam(
|
|
808
|
+
src: Address,
|
|
809
|
+
dest: Address,
|
|
810
|
+
srcAmount: NumberAsString,
|
|
811
|
+
destAmount: NumberAsString,
|
|
812
|
+
data: SolidlyData,
|
|
813
|
+
side: SwapSide,
|
|
814
|
+
): Promise<SimpleExchangeParam> {
|
|
815
|
+
if (side === SwapSide.BUY) throw new Error('Buy not supported');
|
|
816
|
+
|
|
817
|
+
const pools = encodePools(data.pools, this.feeFactor);
|
|
818
|
+
const weth = this.getWETHAddress(src, dest, data.wethAddress);
|
|
819
|
+
const swapData = this.exchangeRouterInterface.encodeFunctionData(
|
|
820
|
+
side === SwapSide.SELL ? UniswapV2Functions.swap : UniswapV2Functions.buy,
|
|
821
|
+
[src, srcAmount, destAmount, weth, pools],
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
return this.buildSimpleParamWithoutWETHConversion(
|
|
825
|
+
src,
|
|
826
|
+
srcAmount,
|
|
827
|
+
dest,
|
|
828
|
+
destAmount,
|
|
829
|
+
swapData,
|
|
830
|
+
data.router,
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
getDexParam(
|
|
835
|
+
srcToken: Address,
|
|
836
|
+
destToken: Address,
|
|
837
|
+
srcAmount: NumberAsString,
|
|
838
|
+
destAmount: NumberAsString,
|
|
839
|
+
recipient: Address,
|
|
840
|
+
data: SolidlyData,
|
|
841
|
+
side: SwapSide,
|
|
842
|
+
): DexExchangeParam {
|
|
843
|
+
if (side === SwapSide.BUY) throw new Error('Buy not supported');
|
|
844
|
+
let exchangeDataTypes = ['bytes4', 'bytes32'];
|
|
845
|
+
|
|
846
|
+
let exchangeDataToPack = [
|
|
847
|
+
hexZeroPad(hexlify(0), 4),
|
|
848
|
+
hexZeroPad(hexlify(data.pools.length), 32),
|
|
849
|
+
];
|
|
850
|
+
|
|
851
|
+
const pools = encodePools(data.pools, this.feeFactor);
|
|
852
|
+
pools.forEach(pool => {
|
|
853
|
+
exchangeDataTypes.push('bytes32');
|
|
854
|
+
exchangeDataToPack.push(hexZeroPad(hexlify(BigNumber.from(pool)), 32));
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
const exchangeData = solidityPack(exchangeDataTypes, exchangeDataToPack);
|
|
858
|
+
|
|
859
|
+
return {
|
|
860
|
+
needWrapNative: this.needWrapNative,
|
|
861
|
+
dexFuncHasRecipient: true,
|
|
862
|
+
exchangeData,
|
|
863
|
+
targetExchange: recipient,
|
|
864
|
+
specialDexFlag: data.isFeeTokenInRoute
|
|
865
|
+
? SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK_WITH_FEE
|
|
866
|
+
: SpecialDex.SWAP_ON_DYSTOPIA_UNISWAP_V2_FORK,
|
|
867
|
+
transferSrcTokenBeforeSwap: data.isFeeTokenInRoute
|
|
868
|
+
? undefined
|
|
869
|
+
: data.pools[0].address,
|
|
870
|
+
returnAmountPos: undefined,
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
}
|