@paraswap/dex-lib 3.4.2-balancer-v2-single-swap.0 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +377 -377
- package/build/config.js +9 -5
- package/build/config.js.map +1 -1
- package/build/dex/algebra/algebra.js +25 -25
- package/build/dex/balancer-v1/balancer-v1.js +34 -34
- package/build/dex/balancer-v2/balancer-v2.d.ts +1 -1
- package/build/dex/balancer-v2/balancer-v2.js +65 -78
- package/build/dex/balancer-v2/balancer-v2.js.map +1 -1
- package/build/dex/camelot/camelot.js +27 -27
- package/build/dex/kyberdmm/kyberdmm.js +27 -27
- package/build/dex/pancakeswap-v3/pancakeswap-v3.js +25 -25
- package/build/dex/solidly/solidly.js +27 -27
- package/build/dex/solidly-v3/solidly-v3.js +25 -25
- package/build/dex/swaap-v2/config.js +15 -0
- package/build/dex/swaap-v2/config.js.map +1 -1
- package/build/dex/uniswap-v2/uniswap-v2.js +26 -26
- package/build/dex/uniswap-v3/uniswap-v3.js +25 -25
- package/funding.json +5 -5
- package/package.json +1 -1
- package/src/config.ts +500 -496
- package/src/constants.ts +67 -67
- package/src/dex/aave-v2/aave-v2.ts +409 -409
- package/src/dex/aave-v3/aave-v3.ts +320 -320
- package/src/dex/algebra/algebra.ts +1180 -1180
- package/src/dex/angle-staked-stable/angle-staked-stable.ts +347 -347
- package/src/dex/augustus-rfq.ts +180 -180
- package/src/dex/bProtocol/bProtocol.ts +108 -108
- package/src/dex/balancer-v1/balancer-v1.ts +572 -572
- package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +1699 -1699
- package/src/dex/balancer-v2/balancer-v2.ts +1669 -1701
- package/src/dex/bancor/bancor.ts +128 -128
- package/src/dex/camelot/camelot.ts +873 -873
- package/src/dex/compound/compound.ts +107 -107
- package/src/dex/curve-v1/config.ts +1179 -1179
- package/src/dex/curve-v1/curve-v1-e2e.test.ts +507 -507
- package/src/dex/curve-v1/curve-v1.ts +1315 -1315
- package/src/dex/curve-v1-factory/config.ts +1018 -1018
- package/src/dex/curve-v1-factory/curve-v1-factory.ts +1288 -1288
- package/src/dex/curve-v2/curve-v2.ts +440 -440
- package/src/dex/dexalot/dexalot.ts +1100 -1100
- package/src/dex/dodo-v1/dodo-v1.ts +132 -132
- package/src/dex/dodo-v2.ts +266 -266
- package/src/dex/etherfi.ts +239 -239
- package/src/dex/generic-rfq/generic-rfq.ts +431 -431
- package/src/dex/gmx/gmx.ts +321 -321
- package/src/dex/hashflow/hashflow.ts +1135 -1135
- package/src/dex/idex.ts +254 -254
- package/src/dex/index.ts +357 -357
- package/src/dex/jarvis-v6/jarvis-v6.ts +509 -509
- package/src/dex/jarvis.ts +262 -262
- package/src/dex/kyberdmm/kyberdmm.ts +649 -649
- package/src/dex/lido/lido.ts +110 -110
- package/src/dex/maker-psm/maker-psm.ts +592 -592
- package/src/dex/maverick-v1/maverick-v1.ts +537 -537
- package/src/dex/nerve/config.ts +635 -635
- package/src/dex/nerve/nerve-e2e.test.ts +731 -731
- package/src/dex/nerve/nerve-events.test.ts +911 -911
- package/src/dex/nerve/nerve.ts +467 -467
- package/src/dex/onebit/onebit.ts +113 -113
- package/src/dex/pancakeswap-v3/pancakeswap-v3.ts +1150 -1150
- package/src/dex/paraswap-limit-orders/config.ts +60 -60
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders-e2e.test.ts +137 -137
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders-integration.test.ts +328 -328
- package/src/dex/paraswap-limit-orders/paraswap-limit-orders.ts +631 -631
- package/src/dex/polygon-migrator/polygon-migrator.ts +194 -194
- package/src/dex/quick-perps/quick-perps.ts +337 -337
- package/src/dex/quickswap/config.ts +16 -16
- package/src/dex/quickswap/quickswap-e2e.test.ts +384 -384
- package/src/dex/smoothy/smoothy.ts +110 -110
- package/src/dex/solidly/config.ts +225 -225
- package/src/dex/solidly/solidly-e2e.test.ts +1421 -1421
- package/src/dex/solidly/solidly-integration.test.ts +1753 -1753
- package/src/dex/solidly/solidly.ts +687 -687
- package/src/dex/solidly-v3/solidly-v3.ts +968 -968
- package/src/dex/spark/spark.ts +284 -284
- package/src/dex/stable-pool/stable-pool.ts +129 -129
- package/src/dex/swaap-v2/config.ts +15 -0
- package/src/dex/swaap-v2/swaap-v2-e2e.test.ts +233 -17
- package/src/dex/swaap-v2/swaap-v2-integration.test.ts +1 -1
- package/src/dex/swaap-v2/swaap-v2.ts +1056 -1056
- package/src/dex/swell/swell.ts +312 -312
- package/src/dex/synthetix/synthetix.ts +420 -420
- package/src/dex/trader-joe-v2.1/trader-joe-v2.1.ts +180 -180
- package/src/dex/uniswap-v2/config.ts +637 -637
- package/src/dex/uniswap-v2/constants.ts +58 -58
- package/src/dex/uniswap-v2/uniswap-v2-e2e-avalanche.test.ts +1257 -1257
- package/src/dex/uniswap-v2/uniswap-v2-e2e-base.test.ts +42 -42
- package/src/dex/uniswap-v2/uniswap-v2-e2e-bsc.test.ts +1295 -1295
- package/src/dex/uniswap-v2/uniswap-v2-e2e-mainnet.test.ts +1834 -1834
- package/src/dex/uniswap-v2/uniswap-v2-e2e-polygon.test.ts +871 -871
- package/src/dex/uniswap-v2/uniswap-v2.ts +1117 -1117
- package/src/dex/uniswap-v3/uniswap-v3.ts +1501 -1501
- package/src/dex/uniswap-v3.ts +242 -242
- package/src/dex/weth/config.ts +73 -73
- package/src/dex/weth/weth.ts +230 -230
- package/src/dex/wombat/wombat.ts +467 -467
- package/src/dex/woo-fi-v2/woo-fi-v2.ts +529 -529
- package/src/dex/wsteth/wsteth.ts +304 -304
- package/src/dex/zerox/index.ts +328 -328
- package/src/executor/Executor01BytecodeBuilder.ts +474 -474
- package/src/executor/Executor02BytecodeBuilder.ts +1392 -1392
- package/src/executor/ExecutorBytecodeBuilder.ts +390 -390
- package/src/executor/ExecutorDetector.ts +131 -131
- package/src/executor/constants.ts +66 -66
- package/src/executor/executor01-bytecode-builder-e2e.test.ts +1478 -1478
- package/src/executor/utils.ts +62 -62
- package/src/router/simpleswap.ts +366 -366
- package/src/types.ts +342 -342
- package/tests/constants-e2e.ts +1632 -1622
- package/tests/generate-new-limit-order.ts +107 -107
package/src/dex/wombat/wombat.ts
CHANGED
|
@@ -1,467 +1,467 @@
|
|
|
1
|
-
import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
|
|
2
|
-
import { Interface } from '@ethersproject/abi';
|
|
3
|
-
import { NumberAsString, SwapSide } from '@paraswap/core';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
AdapterExchangeParam,
|
|
7
|
-
Address,
|
|
8
|
-
DexExchangeParam,
|
|
9
|
-
ExchangePrices,
|
|
10
|
-
Logger,
|
|
11
|
-
PoolLiquidity,
|
|
12
|
-
PoolPrices,
|
|
13
|
-
SimpleExchangeParam,
|
|
14
|
-
Token,
|
|
15
|
-
} from '../../types';
|
|
16
|
-
import { Network } from '../../constants';
|
|
17
|
-
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
18
|
-
import { getBigIntPow, getDexKeysWithNetwork } from '../../utils';
|
|
19
|
-
import { IDex } from '../idex';
|
|
20
|
-
import { IDexHelper } from '../../dex-helper';
|
|
21
|
-
import { DexParams, PoolState, WombatData } from './types';
|
|
22
|
-
import {
|
|
23
|
-
getLocalDeadlineAsFriendlyPlaceholder,
|
|
24
|
-
SimpleExchange,
|
|
25
|
-
} from '../simple-exchange';
|
|
26
|
-
import { Adapters, WombatConfig } from './config';
|
|
27
|
-
import PoolABI from '../../abi/wombat/pool-v2.json';
|
|
28
|
-
import AssetABI from '../../abi/wombat/asset.json';
|
|
29
|
-
import ERC20ABI from '../../abi/erc20.json';
|
|
30
|
-
import { WombatQuoter } from './wombat-quoter';
|
|
31
|
-
import { WombatBmw } from './wombat-bmw';
|
|
32
|
-
import { fromWad } from './utils';
|
|
33
|
-
import { WombatPool } from './wombat-pool';
|
|
34
|
-
import { StatePollingManager } from '../../lib/stateful-rpc-poller/state-polling-manager';
|
|
35
|
-
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
36
|
-
|
|
37
|
-
export class Wombat extends SimpleExchange implements IDex<WombatData> {
|
|
38
|
-
// contract interfaces
|
|
39
|
-
static readonly erc20Interface = new Interface(ERC20ABI);
|
|
40
|
-
static readonly poolInterface = new Interface(PoolABI);
|
|
41
|
-
static readonly assetInterface = new Interface(AssetABI);
|
|
42
|
-
protected pollingManager: StatePollingManager;
|
|
43
|
-
|
|
44
|
-
protected config: DexParams;
|
|
45
|
-
protected poolLiquidityUSD?: { [poolAddress: string]: number };
|
|
46
|
-
public bmw: WombatBmw;
|
|
47
|
-
public pools: { [poolAddress: string]: WombatPool } = {};
|
|
48
|
-
|
|
49
|
-
readonly isStatePollingDex = true;
|
|
50
|
-
|
|
51
|
-
readonly hasConstantPriceLargeAmounts = false;
|
|
52
|
-
readonly needWrapNative = true;
|
|
53
|
-
|
|
54
|
-
readonly isFeeOnTransferSupported = false;
|
|
55
|
-
|
|
56
|
-
public static dexKeysWithNetwork: {
|
|
57
|
-
key: string;
|
|
58
|
-
networks: Network[];
|
|
59
|
-
}[] = getDexKeysWithNetwork(WombatConfig);
|
|
60
|
-
|
|
61
|
-
logger: Logger;
|
|
62
|
-
|
|
63
|
-
constructor(
|
|
64
|
-
readonly network: Network,
|
|
65
|
-
readonly dexKey: string,
|
|
66
|
-
readonly dexHelper: IDexHelper,
|
|
67
|
-
protected adapters = Adapters[network] || {}, // TODO: add any additional optional params to support other fork DEXes
|
|
68
|
-
) {
|
|
69
|
-
super(dexHelper, dexKey);
|
|
70
|
-
this.logger = dexHelper.getLogger(dexKey);
|
|
71
|
-
this.config = WombatConfig[dexKey][network];
|
|
72
|
-
this.pollingManager = StatePollingManager.getInstance(dexHelper);
|
|
73
|
-
|
|
74
|
-
this.bmw = new WombatBmw(
|
|
75
|
-
dexKey,
|
|
76
|
-
this.config.bmwAddress,
|
|
77
|
-
network,
|
|
78
|
-
dexHelper,
|
|
79
|
-
this.logger,
|
|
80
|
-
this.config.bmwAddress,
|
|
81
|
-
this.onAssetAdded.bind(this),
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async init(blockNumber: number) {
|
|
86
|
-
if (!this.bmw.isInitialized) {
|
|
87
|
-
await this.bmw.initialize(blockNumber);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Initialize pricing is called once in the start of
|
|
92
|
-
// pricing service. It is intended to setup the integration
|
|
93
|
-
// for pricing requests. It is optional for a DEX to
|
|
94
|
-
// implement this function
|
|
95
|
-
async initializePricing(blockNumber: number) {
|
|
96
|
-
await this.init(blockNumber);
|
|
97
|
-
const bmwState = this.bmw.getState(blockNumber);
|
|
98
|
-
if (!bmwState) {
|
|
99
|
-
throw new Error('initializePricing: bmwState still null after init');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
onAssetAdded = async (pool: Address, blockNumber: number): Promise<void> => {
|
|
104
|
-
if (!this.pools[pool]) {
|
|
105
|
-
this.pools[pool] = new WombatPool(
|
|
106
|
-
this.dexKey,
|
|
107
|
-
this.getPoolIdentifier(pool),
|
|
108
|
-
this.network,
|
|
109
|
-
this.dexHelper,
|
|
110
|
-
this.logger,
|
|
111
|
-
pool,
|
|
112
|
-
);
|
|
113
|
-
await this.pools[pool].initialize(blockNumber);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
// Returns the list of contract adapters (name and index)
|
|
118
|
-
// for a buy/sell. Return null if there are no adapters.
|
|
119
|
-
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
120
|
-
return this.adapters[side] ? this.adapters[side] : null;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Returns list of pool identifiers that can be used
|
|
124
|
-
// for a given swap. poolIdentifiers must be unique
|
|
125
|
-
// across DEXes. It is recommended to use
|
|
126
|
-
// ${dexKey}_${poolAddress} as a poolIdentifier
|
|
127
|
-
async getPoolIdentifiers(
|
|
128
|
-
srcToken: Token,
|
|
129
|
-
destToken: Token,
|
|
130
|
-
side: SwapSide,
|
|
131
|
-
blockNumber: number,
|
|
132
|
-
): Promise<string[]> {
|
|
133
|
-
if (side === SwapSide.BUY) return [];
|
|
134
|
-
return (
|
|
135
|
-
await this.findPools(
|
|
136
|
-
this.dexHelper.config.wrapETH(srcToken).address.toLowerCase(),
|
|
137
|
-
this.dexHelper.config.wrapETH(destToken).address.toLowerCase(),
|
|
138
|
-
blockNumber,
|
|
139
|
-
)
|
|
140
|
-
).map(p => this.getPoolIdentifier(p));
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
protected getPoolIdentifier(poolAddress: Address): string {
|
|
144
|
-
return `${this.dexKey}_${poolAddress}`;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Returns pool prices for amounts.
|
|
148
|
-
// If limitPools is defined only pools in limitPools
|
|
149
|
-
// should be used. If limitPools is undefined then
|
|
150
|
-
// any pools can be used.
|
|
151
|
-
async getPricesVolume(
|
|
152
|
-
srcToken: Token,
|
|
153
|
-
destToken: Token,
|
|
154
|
-
amounts: bigint[],
|
|
155
|
-
side: SwapSide,
|
|
156
|
-
blockNumber: number,
|
|
157
|
-
limitPools?: string[],
|
|
158
|
-
): Promise<null | ExchangePrices<WombatData>> {
|
|
159
|
-
if (side === SwapSide.BUY) return null; // Buy side not implemented yet
|
|
160
|
-
if (!this.pools) {
|
|
161
|
-
this.logger.error(`Missing pools for ${this.dexKey} in getPricesVolume`);
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
const srcTokenAddress = this.dexHelper.config
|
|
165
|
-
.wrapETH(srcToken)
|
|
166
|
-
.address.toLowerCase();
|
|
167
|
-
const destTokenAddress = this.dexHelper.config
|
|
168
|
-
.wrapETH(destToken)
|
|
169
|
-
.address.toLowerCase();
|
|
170
|
-
if (srcTokenAddress === destTokenAddress) return null;
|
|
171
|
-
|
|
172
|
-
const pools = (
|
|
173
|
-
await this.findPools(srcTokenAddress, destTokenAddress, blockNumber)
|
|
174
|
-
).filter(
|
|
175
|
-
poolAddress =>
|
|
176
|
-
!limitPools || limitPools.includes(this.getPoolIdentifier(poolAddress)),
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
const promises = [];
|
|
180
|
-
for (const poolAddress of pools) {
|
|
181
|
-
let state = await this.pools![poolAddress].getState(blockNumber);
|
|
182
|
-
if (!state) {
|
|
183
|
-
this.logger.warn(
|
|
184
|
-
`State of pool ${poolAddress} is null in getPricesVolume, skipping...`,
|
|
185
|
-
);
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
const [unit, ...prices] = this.computePrices(
|
|
189
|
-
srcTokenAddress,
|
|
190
|
-
destTokenAddress,
|
|
191
|
-
[getBigIntPow(srcToken.decimals), ...amounts],
|
|
192
|
-
side,
|
|
193
|
-
state,
|
|
194
|
-
);
|
|
195
|
-
promises.push({
|
|
196
|
-
prices,
|
|
197
|
-
unit,
|
|
198
|
-
data: {
|
|
199
|
-
exchange: poolAddress,
|
|
200
|
-
},
|
|
201
|
-
poolAddresses: [poolAddress],
|
|
202
|
-
exchange: this.dexKey,
|
|
203
|
-
/** @todo specify gas cost */
|
|
204
|
-
gasCost: 260 * 1000,
|
|
205
|
-
poolIdentifier: this.getPoolIdentifier(poolAddress),
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return await Promise.all(promises);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// take PoolState to compute price
|
|
213
|
-
protected computePrices(
|
|
214
|
-
srcTokenAddress: Address,
|
|
215
|
-
destTokenAddress: Address,
|
|
216
|
-
amounts: bigint[],
|
|
217
|
-
side: SwapSide,
|
|
218
|
-
state: DeepReadonly<PoolState>,
|
|
219
|
-
): bigint[] {
|
|
220
|
-
const srcAsset = state.asset[srcTokenAddress];
|
|
221
|
-
const destAsset = state.asset[destTokenAddress];
|
|
222
|
-
const quoter = new WombatQuoter(state.params);
|
|
223
|
-
|
|
224
|
-
return amounts.map(fromAmount => {
|
|
225
|
-
return side === SwapSide.SELL
|
|
226
|
-
? quoter.getQuote(srcAsset, destAsset, fromAmount)
|
|
227
|
-
: quoter.getQuote(destAsset, srcAsset, -fromAmount);
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
protected async findPools(
|
|
232
|
-
srcTokenAddress: Address,
|
|
233
|
-
destTokenAddress: Address,
|
|
234
|
-
blockNumber: number,
|
|
235
|
-
): Promise<Address[]> {
|
|
236
|
-
const pools: Address[] = [];
|
|
237
|
-
for (const [poolAddress, pool] of Object.entries(this.pools)) {
|
|
238
|
-
const state = await pool.getState(blockNumber);
|
|
239
|
-
if (!state) {
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (
|
|
244
|
-
state &&
|
|
245
|
-
!state.params.paused &&
|
|
246
|
-
state.asset[srcTokenAddress] &&
|
|
247
|
-
state.asset[destTokenAddress]
|
|
248
|
-
) {
|
|
249
|
-
pools.push(poolAddress);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return pools;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
257
|
-
getCalldataGasCost(poolPrices: PoolPrices<WombatData>): number | number[] {
|
|
258
|
-
// TODO: update if there is any payload in getAdapterParam
|
|
259
|
-
return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
getAdapterParam(
|
|
263
|
-
srcToken: string,
|
|
264
|
-
destToken: string,
|
|
265
|
-
srcAmount: string,
|
|
266
|
-
destAmount: string,
|
|
267
|
-
data: WombatData,
|
|
268
|
-
side: SwapSide,
|
|
269
|
-
): AdapterExchangeParam {
|
|
270
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
271
|
-
|
|
272
|
-
const { exchange } = data;
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
targetExchange: exchange,
|
|
276
|
-
payload: '0x',
|
|
277
|
-
networkFee: '0',
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Encode call data used by simpleSwap like routers
|
|
282
|
-
// Used for simpleSwap & simpleBuy
|
|
283
|
-
// Hint: this.buildSimpleParamWithoutWETHConversion
|
|
284
|
-
// could be useful
|
|
285
|
-
async getSimpleParam(
|
|
286
|
-
srcToken: string,
|
|
287
|
-
destToken: string,
|
|
288
|
-
srcAmount: string,
|
|
289
|
-
destAmount: string,
|
|
290
|
-
data: WombatData,
|
|
291
|
-
side: SwapSide,
|
|
292
|
-
): Promise<SimpleExchangeParam> {
|
|
293
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
294
|
-
const { exchange } = data;
|
|
295
|
-
|
|
296
|
-
// Encode here the transaction arguments
|
|
297
|
-
const swapData = Wombat.poolInterface.encodeFunctionData('swap', [
|
|
298
|
-
srcToken,
|
|
299
|
-
destToken,
|
|
300
|
-
srcAmount,
|
|
301
|
-
destAmount,
|
|
302
|
-
this.augustusAddress,
|
|
303
|
-
getLocalDeadlineAsFriendlyPlaceholder(),
|
|
304
|
-
]);
|
|
305
|
-
|
|
306
|
-
return this.buildSimpleParamWithoutWETHConversion(
|
|
307
|
-
srcToken,
|
|
308
|
-
srcAmount,
|
|
309
|
-
destToken,
|
|
310
|
-
destAmount,
|
|
311
|
-
swapData,
|
|
312
|
-
exchange,
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
getDexParam(
|
|
317
|
-
srcToken: Address,
|
|
318
|
-
destToken: Address,
|
|
319
|
-
srcAmount: NumberAsString,
|
|
320
|
-
destAmount: NumberAsString,
|
|
321
|
-
recipient: Address,
|
|
322
|
-
data: WombatData,
|
|
323
|
-
side: SwapSide,
|
|
324
|
-
): DexExchangeParam {
|
|
325
|
-
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
326
|
-
const { exchange } = data;
|
|
327
|
-
|
|
328
|
-
// Encode here the transaction arguments
|
|
329
|
-
const swapData = Wombat.poolInterface.encodeFunctionData('swap', [
|
|
330
|
-
srcToken,
|
|
331
|
-
destToken,
|
|
332
|
-
srcAmount,
|
|
333
|
-
destAmount,
|
|
334
|
-
recipient,
|
|
335
|
-
getLocalDeadlineAsFriendlyPlaceholder(),
|
|
336
|
-
]);
|
|
337
|
-
|
|
338
|
-
return {
|
|
339
|
-
needWrapNative: this.needWrapNative,
|
|
340
|
-
dexFuncHasRecipient: true,
|
|
341
|
-
exchangeData: swapData,
|
|
342
|
-
targetExchange: exchange,
|
|
343
|
-
returnAmountPos:
|
|
344
|
-
side === SwapSide.SELL
|
|
345
|
-
? extractReturnAmountPosition(
|
|
346
|
-
Wombat.poolInterface,
|
|
347
|
-
'swap',
|
|
348
|
-
'actualToAmount',
|
|
349
|
-
)
|
|
350
|
-
: undefined,
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// This is called once before getTopPoolsForToken is
|
|
355
|
-
// called for multiple tokens. This can be helpful to
|
|
356
|
-
// update common state required for calculating
|
|
357
|
-
// getTopPoolsForToken. For example, poolLiquidityUSD.
|
|
358
|
-
async updatePoolState(): Promise<void> {
|
|
359
|
-
const blockNumber = await this.dexHelper.provider.getBlockNumber();
|
|
360
|
-
await this.init(blockNumber);
|
|
361
|
-
const bmwState = this.bmw.getState(blockNumber);
|
|
362
|
-
if (!bmwState) {
|
|
363
|
-
throw new Error('updatePoolState: bmwState still null after init');
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// All tokens are USD stablecoins so to estimate liquidity can just add
|
|
367
|
-
// the cash balances of all the tokens
|
|
368
|
-
const poolLiquidityUSD: { [poolAddress: string]: number } = {};
|
|
369
|
-
const usdPromises = [];
|
|
370
|
-
const poolStates: { [poolAddress: string]: DeepReadonly<PoolState> } = {};
|
|
371
|
-
const poolStateObjs = await Promise.all(
|
|
372
|
-
Object.values(this.pools).map(pool => pool.getState(blockNumber)),
|
|
373
|
-
);
|
|
374
|
-
|
|
375
|
-
for (const [poolAddress, _] of Object.entries(this.pools)) {
|
|
376
|
-
const index = Object.keys(this.pools).indexOf(poolAddress);
|
|
377
|
-
let state = poolStateObjs[index];
|
|
378
|
-
if (!state) {
|
|
379
|
-
this.logger.warn(
|
|
380
|
-
`State of ${poolAddress} is null in updatePoolState, skipping...`,
|
|
381
|
-
);
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
poolStates[poolAddress] = state;
|
|
385
|
-
for (const [tokenAddress, assetState] of Object.entries(state.asset)) {
|
|
386
|
-
usdPromises.push(
|
|
387
|
-
this.dexHelper.getTokenUSDPrice(
|
|
388
|
-
{
|
|
389
|
-
address: tokenAddress,
|
|
390
|
-
decimals: assetState.underlyingTokenDecimals,
|
|
391
|
-
},
|
|
392
|
-
fromWad(
|
|
393
|
-
assetState.cash,
|
|
394
|
-
BigInt(assetState.underlyingTokenDecimals),
|
|
395
|
-
),
|
|
396
|
-
),
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
const usdValues = await Promise.all(usdPromises);
|
|
401
|
-
|
|
402
|
-
for (const [poolAddress, poolState] of Object.entries(poolStates)) {
|
|
403
|
-
poolLiquidityUSD[poolAddress] = 0;
|
|
404
|
-
for (let i = 0; i < poolState.underlyingAddresses.length; i++) {
|
|
405
|
-
poolLiquidityUSD[poolAddress] += usdValues[i];
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
this.poolLiquidityUSD = poolLiquidityUSD;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Returns list of top pools based on liquidity. Max
|
|
413
|
-
// limit number pools should be returned.
|
|
414
|
-
async getTopPoolsForToken(
|
|
415
|
-
tokenAddress: Address,
|
|
416
|
-
limit: number,
|
|
417
|
-
): Promise<PoolLiquidity[]> {
|
|
418
|
-
// getTopPoolsForToken shouldn't use block manager
|
|
419
|
-
// TODO: update getTopPoolsForToken to work without block manager
|
|
420
|
-
return [];
|
|
421
|
-
// if (!this.poolLiquidityUSD) await this.updatePoolState();
|
|
422
|
-
// tokenAddress = (
|
|
423
|
-
// isETHAddress(tokenAddress)
|
|
424
|
-
// ? this.dexHelper.config.data.wrappedNativeTokenAddress
|
|
425
|
-
// : tokenAddress
|
|
426
|
-
// ).toLowerCase();
|
|
427
|
-
// const pools: string[] = [];
|
|
428
|
-
// const poolStates: { [poolAddress: string]: DeepReadonly<PoolState> } = {};
|
|
429
|
-
// for (const [poolAddress, eventPool] of Object.entries(this.pools)) {
|
|
430
|
-
// let state = await eventPool.getState();
|
|
431
|
-
// if (!state) {
|
|
432
|
-
// this.logger.warn(
|
|
433
|
-
// `State of ${poolAddress} is null in getTopPoolsForToken, skipping...`,
|
|
434
|
-
// );
|
|
435
|
-
// continue;
|
|
436
|
-
// }
|
|
437
|
-
// if (
|
|
438
|
-
// state.value.underlyingAddresses.includes(tokenAddress) &&
|
|
439
|
-
// this.poolLiquidityUSD![poolAddress]
|
|
440
|
-
// ) {
|
|
441
|
-
// poolStates[poolAddress] = state.value;
|
|
442
|
-
// pools.push(poolAddress);
|
|
443
|
-
// }
|
|
444
|
-
// }
|
|
445
|
-
|
|
446
|
-
// // sort by liquidity
|
|
447
|
-
// pools.sort((a, b) => this.poolLiquidityUSD![b] - this.poolLiquidityUSD![a]);
|
|
448
|
-
// return pools.slice(0, limit).map(poolAddress => ({
|
|
449
|
-
// exchange: this.dexKey,
|
|
450
|
-
// address: poolAddress,
|
|
451
|
-
// // other tokens in the same pool
|
|
452
|
-
// connectorTokens: poolStates[poolAddress].underlyingAddresses
|
|
453
|
-
// .filter(t => t !== tokenAddress)
|
|
454
|
-
// .map(t => ({
|
|
455
|
-
// decimals: poolStates[poolAddress].asset[t].underlyingTokenDecimals,
|
|
456
|
-
// address: t,
|
|
457
|
-
// })),
|
|
458
|
-
// liquidityUSD: this.poolLiquidityUSD![poolAddress],
|
|
459
|
-
// }));
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// This is optional function in case if your implementation has acquired any resources
|
|
463
|
-
// you need to release for graceful shutdown. For example, it may be any interval timer
|
|
464
|
-
releaseResources(): AsyncOrSync<void> {
|
|
465
|
-
// TODO: complete me!
|
|
466
|
-
}
|
|
467
|
-
}
|
|
1
|
+
import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
|
|
2
|
+
import { Interface } from '@ethersproject/abi';
|
|
3
|
+
import { NumberAsString, SwapSide } from '@paraswap/core';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
AdapterExchangeParam,
|
|
7
|
+
Address,
|
|
8
|
+
DexExchangeParam,
|
|
9
|
+
ExchangePrices,
|
|
10
|
+
Logger,
|
|
11
|
+
PoolLiquidity,
|
|
12
|
+
PoolPrices,
|
|
13
|
+
SimpleExchangeParam,
|
|
14
|
+
Token,
|
|
15
|
+
} from '../../types';
|
|
16
|
+
import { Network } from '../../constants';
|
|
17
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
18
|
+
import { getBigIntPow, getDexKeysWithNetwork } from '../../utils';
|
|
19
|
+
import { IDex } from '../idex';
|
|
20
|
+
import { IDexHelper } from '../../dex-helper';
|
|
21
|
+
import { DexParams, PoolState, WombatData } from './types';
|
|
22
|
+
import {
|
|
23
|
+
getLocalDeadlineAsFriendlyPlaceholder,
|
|
24
|
+
SimpleExchange,
|
|
25
|
+
} from '../simple-exchange';
|
|
26
|
+
import { Adapters, WombatConfig } from './config';
|
|
27
|
+
import PoolABI from '../../abi/wombat/pool-v2.json';
|
|
28
|
+
import AssetABI from '../../abi/wombat/asset.json';
|
|
29
|
+
import ERC20ABI from '../../abi/erc20.json';
|
|
30
|
+
import { WombatQuoter } from './wombat-quoter';
|
|
31
|
+
import { WombatBmw } from './wombat-bmw';
|
|
32
|
+
import { fromWad } from './utils';
|
|
33
|
+
import { WombatPool } from './wombat-pool';
|
|
34
|
+
import { StatePollingManager } from '../../lib/stateful-rpc-poller/state-polling-manager';
|
|
35
|
+
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
36
|
+
|
|
37
|
+
export class Wombat extends SimpleExchange implements IDex<WombatData> {
|
|
38
|
+
// contract interfaces
|
|
39
|
+
static readonly erc20Interface = new Interface(ERC20ABI);
|
|
40
|
+
static readonly poolInterface = new Interface(PoolABI);
|
|
41
|
+
static readonly assetInterface = new Interface(AssetABI);
|
|
42
|
+
protected pollingManager: StatePollingManager;
|
|
43
|
+
|
|
44
|
+
protected config: DexParams;
|
|
45
|
+
protected poolLiquidityUSD?: { [poolAddress: string]: number };
|
|
46
|
+
public bmw: WombatBmw;
|
|
47
|
+
public pools: { [poolAddress: string]: WombatPool } = {};
|
|
48
|
+
|
|
49
|
+
readonly isStatePollingDex = true;
|
|
50
|
+
|
|
51
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
52
|
+
readonly needWrapNative = true;
|
|
53
|
+
|
|
54
|
+
readonly isFeeOnTransferSupported = false;
|
|
55
|
+
|
|
56
|
+
public static dexKeysWithNetwork: {
|
|
57
|
+
key: string;
|
|
58
|
+
networks: Network[];
|
|
59
|
+
}[] = getDexKeysWithNetwork(WombatConfig);
|
|
60
|
+
|
|
61
|
+
logger: Logger;
|
|
62
|
+
|
|
63
|
+
constructor(
|
|
64
|
+
readonly network: Network,
|
|
65
|
+
readonly dexKey: string,
|
|
66
|
+
readonly dexHelper: IDexHelper,
|
|
67
|
+
protected adapters = Adapters[network] || {}, // TODO: add any additional optional params to support other fork DEXes
|
|
68
|
+
) {
|
|
69
|
+
super(dexHelper, dexKey);
|
|
70
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
71
|
+
this.config = WombatConfig[dexKey][network];
|
|
72
|
+
this.pollingManager = StatePollingManager.getInstance(dexHelper);
|
|
73
|
+
|
|
74
|
+
this.bmw = new WombatBmw(
|
|
75
|
+
dexKey,
|
|
76
|
+
this.config.bmwAddress,
|
|
77
|
+
network,
|
|
78
|
+
dexHelper,
|
|
79
|
+
this.logger,
|
|
80
|
+
this.config.bmwAddress,
|
|
81
|
+
this.onAssetAdded.bind(this),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async init(blockNumber: number) {
|
|
86
|
+
if (!this.bmw.isInitialized) {
|
|
87
|
+
await this.bmw.initialize(blockNumber);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Initialize pricing is called once in the start of
|
|
92
|
+
// pricing service. It is intended to setup the integration
|
|
93
|
+
// for pricing requests. It is optional for a DEX to
|
|
94
|
+
// implement this function
|
|
95
|
+
async initializePricing(blockNumber: number) {
|
|
96
|
+
await this.init(blockNumber);
|
|
97
|
+
const bmwState = this.bmw.getState(blockNumber);
|
|
98
|
+
if (!bmwState) {
|
|
99
|
+
throw new Error('initializePricing: bmwState still null after init');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
onAssetAdded = async (pool: Address, blockNumber: number): Promise<void> => {
|
|
104
|
+
if (!this.pools[pool]) {
|
|
105
|
+
this.pools[pool] = new WombatPool(
|
|
106
|
+
this.dexKey,
|
|
107
|
+
this.getPoolIdentifier(pool),
|
|
108
|
+
this.network,
|
|
109
|
+
this.dexHelper,
|
|
110
|
+
this.logger,
|
|
111
|
+
pool,
|
|
112
|
+
);
|
|
113
|
+
await this.pools[pool].initialize(blockNumber);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Returns the list of contract adapters (name and index)
|
|
118
|
+
// for a buy/sell. Return null if there are no adapters.
|
|
119
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
120
|
+
return this.adapters[side] ? this.adapters[side] : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Returns list of pool identifiers that can be used
|
|
124
|
+
// for a given swap. poolIdentifiers must be unique
|
|
125
|
+
// across DEXes. It is recommended to use
|
|
126
|
+
// ${dexKey}_${poolAddress} as a poolIdentifier
|
|
127
|
+
async getPoolIdentifiers(
|
|
128
|
+
srcToken: Token,
|
|
129
|
+
destToken: Token,
|
|
130
|
+
side: SwapSide,
|
|
131
|
+
blockNumber: number,
|
|
132
|
+
): Promise<string[]> {
|
|
133
|
+
if (side === SwapSide.BUY) return [];
|
|
134
|
+
return (
|
|
135
|
+
await this.findPools(
|
|
136
|
+
this.dexHelper.config.wrapETH(srcToken).address.toLowerCase(),
|
|
137
|
+
this.dexHelper.config.wrapETH(destToken).address.toLowerCase(),
|
|
138
|
+
blockNumber,
|
|
139
|
+
)
|
|
140
|
+
).map(p => this.getPoolIdentifier(p));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
protected getPoolIdentifier(poolAddress: Address): string {
|
|
144
|
+
return `${this.dexKey}_${poolAddress}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Returns pool prices for amounts.
|
|
148
|
+
// If limitPools is defined only pools in limitPools
|
|
149
|
+
// should be used. If limitPools is undefined then
|
|
150
|
+
// any pools can be used.
|
|
151
|
+
async getPricesVolume(
|
|
152
|
+
srcToken: Token,
|
|
153
|
+
destToken: Token,
|
|
154
|
+
amounts: bigint[],
|
|
155
|
+
side: SwapSide,
|
|
156
|
+
blockNumber: number,
|
|
157
|
+
limitPools?: string[],
|
|
158
|
+
): Promise<null | ExchangePrices<WombatData>> {
|
|
159
|
+
if (side === SwapSide.BUY) return null; // Buy side not implemented yet
|
|
160
|
+
if (!this.pools) {
|
|
161
|
+
this.logger.error(`Missing pools for ${this.dexKey} in getPricesVolume`);
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
const srcTokenAddress = this.dexHelper.config
|
|
165
|
+
.wrapETH(srcToken)
|
|
166
|
+
.address.toLowerCase();
|
|
167
|
+
const destTokenAddress = this.dexHelper.config
|
|
168
|
+
.wrapETH(destToken)
|
|
169
|
+
.address.toLowerCase();
|
|
170
|
+
if (srcTokenAddress === destTokenAddress) return null;
|
|
171
|
+
|
|
172
|
+
const pools = (
|
|
173
|
+
await this.findPools(srcTokenAddress, destTokenAddress, blockNumber)
|
|
174
|
+
).filter(
|
|
175
|
+
poolAddress =>
|
|
176
|
+
!limitPools || limitPools.includes(this.getPoolIdentifier(poolAddress)),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const promises = [];
|
|
180
|
+
for (const poolAddress of pools) {
|
|
181
|
+
let state = await this.pools![poolAddress].getState(blockNumber);
|
|
182
|
+
if (!state) {
|
|
183
|
+
this.logger.warn(
|
|
184
|
+
`State of pool ${poolAddress} is null in getPricesVolume, skipping...`,
|
|
185
|
+
);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const [unit, ...prices] = this.computePrices(
|
|
189
|
+
srcTokenAddress,
|
|
190
|
+
destTokenAddress,
|
|
191
|
+
[getBigIntPow(srcToken.decimals), ...amounts],
|
|
192
|
+
side,
|
|
193
|
+
state,
|
|
194
|
+
);
|
|
195
|
+
promises.push({
|
|
196
|
+
prices,
|
|
197
|
+
unit,
|
|
198
|
+
data: {
|
|
199
|
+
exchange: poolAddress,
|
|
200
|
+
},
|
|
201
|
+
poolAddresses: [poolAddress],
|
|
202
|
+
exchange: this.dexKey,
|
|
203
|
+
/** @todo specify gas cost */
|
|
204
|
+
gasCost: 260 * 1000,
|
|
205
|
+
poolIdentifier: this.getPoolIdentifier(poolAddress),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return await Promise.all(promises);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// take PoolState to compute price
|
|
213
|
+
protected computePrices(
|
|
214
|
+
srcTokenAddress: Address,
|
|
215
|
+
destTokenAddress: Address,
|
|
216
|
+
amounts: bigint[],
|
|
217
|
+
side: SwapSide,
|
|
218
|
+
state: DeepReadonly<PoolState>,
|
|
219
|
+
): bigint[] {
|
|
220
|
+
const srcAsset = state.asset[srcTokenAddress];
|
|
221
|
+
const destAsset = state.asset[destTokenAddress];
|
|
222
|
+
const quoter = new WombatQuoter(state.params);
|
|
223
|
+
|
|
224
|
+
return amounts.map(fromAmount => {
|
|
225
|
+
return side === SwapSide.SELL
|
|
226
|
+
? quoter.getQuote(srcAsset, destAsset, fromAmount)
|
|
227
|
+
: quoter.getQuote(destAsset, srcAsset, -fromAmount);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
protected async findPools(
|
|
232
|
+
srcTokenAddress: Address,
|
|
233
|
+
destTokenAddress: Address,
|
|
234
|
+
blockNumber: number,
|
|
235
|
+
): Promise<Address[]> {
|
|
236
|
+
const pools: Address[] = [];
|
|
237
|
+
for (const [poolAddress, pool] of Object.entries(this.pools)) {
|
|
238
|
+
const state = await pool.getState(blockNumber);
|
|
239
|
+
if (!state) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (
|
|
244
|
+
state &&
|
|
245
|
+
!state.params.paused &&
|
|
246
|
+
state.asset[srcTokenAddress] &&
|
|
247
|
+
state.asset[destTokenAddress]
|
|
248
|
+
) {
|
|
249
|
+
pools.push(poolAddress);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return pools;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
257
|
+
getCalldataGasCost(poolPrices: PoolPrices<WombatData>): number | number[] {
|
|
258
|
+
// TODO: update if there is any payload in getAdapterParam
|
|
259
|
+
return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
getAdapterParam(
|
|
263
|
+
srcToken: string,
|
|
264
|
+
destToken: string,
|
|
265
|
+
srcAmount: string,
|
|
266
|
+
destAmount: string,
|
|
267
|
+
data: WombatData,
|
|
268
|
+
side: SwapSide,
|
|
269
|
+
): AdapterExchangeParam {
|
|
270
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
271
|
+
|
|
272
|
+
const { exchange } = data;
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
targetExchange: exchange,
|
|
276
|
+
payload: '0x',
|
|
277
|
+
networkFee: '0',
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Encode call data used by simpleSwap like routers
|
|
282
|
+
// Used for simpleSwap & simpleBuy
|
|
283
|
+
// Hint: this.buildSimpleParamWithoutWETHConversion
|
|
284
|
+
// could be useful
|
|
285
|
+
async getSimpleParam(
|
|
286
|
+
srcToken: string,
|
|
287
|
+
destToken: string,
|
|
288
|
+
srcAmount: string,
|
|
289
|
+
destAmount: string,
|
|
290
|
+
data: WombatData,
|
|
291
|
+
side: SwapSide,
|
|
292
|
+
): Promise<SimpleExchangeParam> {
|
|
293
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
294
|
+
const { exchange } = data;
|
|
295
|
+
|
|
296
|
+
// Encode here the transaction arguments
|
|
297
|
+
const swapData = Wombat.poolInterface.encodeFunctionData('swap', [
|
|
298
|
+
srcToken,
|
|
299
|
+
destToken,
|
|
300
|
+
srcAmount,
|
|
301
|
+
destAmount,
|
|
302
|
+
this.augustusAddress,
|
|
303
|
+
getLocalDeadlineAsFriendlyPlaceholder(),
|
|
304
|
+
]);
|
|
305
|
+
|
|
306
|
+
return this.buildSimpleParamWithoutWETHConversion(
|
|
307
|
+
srcToken,
|
|
308
|
+
srcAmount,
|
|
309
|
+
destToken,
|
|
310
|
+
destAmount,
|
|
311
|
+
swapData,
|
|
312
|
+
exchange,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
getDexParam(
|
|
317
|
+
srcToken: Address,
|
|
318
|
+
destToken: Address,
|
|
319
|
+
srcAmount: NumberAsString,
|
|
320
|
+
destAmount: NumberAsString,
|
|
321
|
+
recipient: Address,
|
|
322
|
+
data: WombatData,
|
|
323
|
+
side: SwapSide,
|
|
324
|
+
): DexExchangeParam {
|
|
325
|
+
if (side === SwapSide.BUY) throw new Error(`Buy not supported`);
|
|
326
|
+
const { exchange } = data;
|
|
327
|
+
|
|
328
|
+
// Encode here the transaction arguments
|
|
329
|
+
const swapData = Wombat.poolInterface.encodeFunctionData('swap', [
|
|
330
|
+
srcToken,
|
|
331
|
+
destToken,
|
|
332
|
+
srcAmount,
|
|
333
|
+
destAmount,
|
|
334
|
+
recipient,
|
|
335
|
+
getLocalDeadlineAsFriendlyPlaceholder(),
|
|
336
|
+
]);
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
needWrapNative: this.needWrapNative,
|
|
340
|
+
dexFuncHasRecipient: true,
|
|
341
|
+
exchangeData: swapData,
|
|
342
|
+
targetExchange: exchange,
|
|
343
|
+
returnAmountPos:
|
|
344
|
+
side === SwapSide.SELL
|
|
345
|
+
? extractReturnAmountPosition(
|
|
346
|
+
Wombat.poolInterface,
|
|
347
|
+
'swap',
|
|
348
|
+
'actualToAmount',
|
|
349
|
+
)
|
|
350
|
+
: undefined,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// This is called once before getTopPoolsForToken is
|
|
355
|
+
// called for multiple tokens. This can be helpful to
|
|
356
|
+
// update common state required for calculating
|
|
357
|
+
// getTopPoolsForToken. For example, poolLiquidityUSD.
|
|
358
|
+
async updatePoolState(): Promise<void> {
|
|
359
|
+
const blockNumber = await this.dexHelper.provider.getBlockNumber();
|
|
360
|
+
await this.init(blockNumber);
|
|
361
|
+
const bmwState = this.bmw.getState(blockNumber);
|
|
362
|
+
if (!bmwState) {
|
|
363
|
+
throw new Error('updatePoolState: bmwState still null after init');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// All tokens are USD stablecoins so to estimate liquidity can just add
|
|
367
|
+
// the cash balances of all the tokens
|
|
368
|
+
const poolLiquidityUSD: { [poolAddress: string]: number } = {};
|
|
369
|
+
const usdPromises = [];
|
|
370
|
+
const poolStates: { [poolAddress: string]: DeepReadonly<PoolState> } = {};
|
|
371
|
+
const poolStateObjs = await Promise.all(
|
|
372
|
+
Object.values(this.pools).map(pool => pool.getState(blockNumber)),
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
for (const [poolAddress, _] of Object.entries(this.pools)) {
|
|
376
|
+
const index = Object.keys(this.pools).indexOf(poolAddress);
|
|
377
|
+
let state = poolStateObjs[index];
|
|
378
|
+
if (!state) {
|
|
379
|
+
this.logger.warn(
|
|
380
|
+
`State of ${poolAddress} is null in updatePoolState, skipping...`,
|
|
381
|
+
);
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
poolStates[poolAddress] = state;
|
|
385
|
+
for (const [tokenAddress, assetState] of Object.entries(state.asset)) {
|
|
386
|
+
usdPromises.push(
|
|
387
|
+
this.dexHelper.getTokenUSDPrice(
|
|
388
|
+
{
|
|
389
|
+
address: tokenAddress,
|
|
390
|
+
decimals: assetState.underlyingTokenDecimals,
|
|
391
|
+
},
|
|
392
|
+
fromWad(
|
|
393
|
+
assetState.cash,
|
|
394
|
+
BigInt(assetState.underlyingTokenDecimals),
|
|
395
|
+
),
|
|
396
|
+
),
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
const usdValues = await Promise.all(usdPromises);
|
|
401
|
+
|
|
402
|
+
for (const [poolAddress, poolState] of Object.entries(poolStates)) {
|
|
403
|
+
poolLiquidityUSD[poolAddress] = 0;
|
|
404
|
+
for (let i = 0; i < poolState.underlyingAddresses.length; i++) {
|
|
405
|
+
poolLiquidityUSD[poolAddress] += usdValues[i];
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
this.poolLiquidityUSD = poolLiquidityUSD;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Returns list of top pools based on liquidity. Max
|
|
413
|
+
// limit number pools should be returned.
|
|
414
|
+
async getTopPoolsForToken(
|
|
415
|
+
tokenAddress: Address,
|
|
416
|
+
limit: number,
|
|
417
|
+
): Promise<PoolLiquidity[]> {
|
|
418
|
+
// getTopPoolsForToken shouldn't use block manager
|
|
419
|
+
// TODO: update getTopPoolsForToken to work without block manager
|
|
420
|
+
return [];
|
|
421
|
+
// if (!this.poolLiquidityUSD) await this.updatePoolState();
|
|
422
|
+
// tokenAddress = (
|
|
423
|
+
// isETHAddress(tokenAddress)
|
|
424
|
+
// ? this.dexHelper.config.data.wrappedNativeTokenAddress
|
|
425
|
+
// : tokenAddress
|
|
426
|
+
// ).toLowerCase();
|
|
427
|
+
// const pools: string[] = [];
|
|
428
|
+
// const poolStates: { [poolAddress: string]: DeepReadonly<PoolState> } = {};
|
|
429
|
+
// for (const [poolAddress, eventPool] of Object.entries(this.pools)) {
|
|
430
|
+
// let state = await eventPool.getState();
|
|
431
|
+
// if (!state) {
|
|
432
|
+
// this.logger.warn(
|
|
433
|
+
// `State of ${poolAddress} is null in getTopPoolsForToken, skipping...`,
|
|
434
|
+
// );
|
|
435
|
+
// continue;
|
|
436
|
+
// }
|
|
437
|
+
// if (
|
|
438
|
+
// state.value.underlyingAddresses.includes(tokenAddress) &&
|
|
439
|
+
// this.poolLiquidityUSD![poolAddress]
|
|
440
|
+
// ) {
|
|
441
|
+
// poolStates[poolAddress] = state.value;
|
|
442
|
+
// pools.push(poolAddress);
|
|
443
|
+
// }
|
|
444
|
+
// }
|
|
445
|
+
|
|
446
|
+
// // sort by liquidity
|
|
447
|
+
// pools.sort((a, b) => this.poolLiquidityUSD![b] - this.poolLiquidityUSD![a]);
|
|
448
|
+
// return pools.slice(0, limit).map(poolAddress => ({
|
|
449
|
+
// exchange: this.dexKey,
|
|
450
|
+
// address: poolAddress,
|
|
451
|
+
// // other tokens in the same pool
|
|
452
|
+
// connectorTokens: poolStates[poolAddress].underlyingAddresses
|
|
453
|
+
// .filter(t => t !== tokenAddress)
|
|
454
|
+
// .map(t => ({
|
|
455
|
+
// decimals: poolStates[poolAddress].asset[t].underlyingTokenDecimals,
|
|
456
|
+
// address: t,
|
|
457
|
+
// })),
|
|
458
|
+
// liquidityUSD: this.poolLiquidityUSD![poolAddress],
|
|
459
|
+
// }));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// This is optional function in case if your implementation has acquired any resources
|
|
463
|
+
// you need to release for graceful shutdown. For example, it may be any interval timer
|
|
464
|
+
releaseResources(): AsyncOrSync<void> {
|
|
465
|
+
// TODO: complete me!
|
|
466
|
+
}
|
|
467
|
+
}
|