@paraswap/dex-lib 3.9.1 → 3.9.2-bebop
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/build/abi/Aave_GSM.json +751 -0
- package/build/abi/USDM.json +807 -0
- package/build/abi/{integral/pool.json → cables/CablesMainnetRFQ.json} +553 -521
- package/build/abi/inception/inception-ineth-pool.json +1062 -0
- package/build/abi/inception/inception-ineth.json +609 -0
- package/build/abi/inception/inception-ratio-feed.json +329 -0
- package/build/abi/inception/inception-vault.json +991 -0
- package/build/config.js +5 -0
- package/build/config.js.map +1 -1
- package/build/dex/aave-v1/aave-v1.d.ts +2 -1
- package/build/dex/aave-v1/aave-v1.js +22 -0
- package/build/dex/aave-v1/aave-v1.js.map +1 -1
- package/build/dex/bebop/bebop.d.ts +0 -1
- package/build/dex/bebop/bebop.js +46 -22
- package/build/dex/bebop/bebop.js.map +1 -1
- package/build/dex/bebop/constants.d.ts +4 -0
- package/build/dex/bebop/constants.js +5 -1
- package/build/dex/bebop/constants.js.map +1 -1
- package/build/dex/bebop/types.d.ts +5 -0
- package/build/dex/bebop/types.js.map +1 -1
- package/build/dex/bebop/websocket-fetcher.d.ts +35 -0
- package/build/dex/bebop/websocket-fetcher.js +87 -0
- package/build/dex/bebop/websocket-fetcher.js.map +1 -0
- package/build/dex/cables/cables.d.ts +64 -0
- package/build/dex/cables/cables.js +445 -0
- package/build/dex/cables/cables.js.map +1 -0
- package/build/dex/cables/config.d.ts +4 -0
- package/build/dex/cables/config.js +15 -0
- package/build/dex/cables/config.js.map +1 -0
- package/build/dex/cables/constants.d.ts +19 -0
- package/build/dex/cables/constants.js +26 -0
- package/build/dex/cables/constants.js.map +1 -0
- package/build/dex/cables/rate-fetcher.d.ts +34 -0
- package/build/dex/cables/rate-fetcher.js +117 -0
- package/build/dex/cables/rate-fetcher.js.map +1 -0
- package/build/dex/cables/types.d.ts +106 -0
- package/build/dex/cables/types.js +12 -0
- package/build/dex/cables/types.js.map +1 -0
- package/build/dex/cables/validators.d.ts +5 -0
- package/build/dex/cables/validators.js +49 -0
- package/build/dex/cables/validators.js.map +1 -0
- package/build/dex/idle-dao/idle-dao-pool.d.ts +56 -0
- package/build/dex/idle-dao/idle-dao-pool.js +176 -0
- package/build/dex/idle-dao/idle-dao-pool.js.map +1 -0
- package/build/dex/idle-dao/idle-dao-pooling-pool.d.ts +16 -0
- package/build/dex/idle-dao/idle-dao-pooling-pool.js +57 -0
- package/build/dex/idle-dao/idle-dao-pooling-pool.js.map +1 -0
- package/build/dex/idle-dao/scripts.d.ts +9 -0
- package/build/dex/idle-dao/scripts.js +552 -0
- package/build/dex/idle-dao/scripts.js.map +1 -0
- package/build/dex/inception/config.d.ts +4 -0
- package/build/dex/inception/config.js +109 -0
- package/build/dex/inception/config.js.map +1 -0
- package/build/dex/inception/inception-event-pool.d.ts +16 -0
- package/build/dex/inception/inception-event-pool.js +51 -0
- package/build/dex/inception/inception-event-pool.js.map +1 -0
- package/build/dex/{integral/integral.d.ts → inception/inception-native.d.ts} +21 -19
- package/build/dex/inception/inception-native.js +131 -0
- package/build/dex/inception/inception-native.js.map +1 -0
- package/build/dex/inception/inception-pool.d.ts +18 -0
- package/build/dex/inception/inception-pool.js +32 -0
- package/build/dex/inception/inception-pool.js.map +1 -0
- package/build/dex/inception/inception-price-feed.d.ts +19 -0
- package/build/dex/inception/inception-price-feed.js +51 -0
- package/build/dex/inception/inception-price-feed.js.map +1 -0
- package/build/dex/inception/inception.d.ts +43 -0
- package/build/dex/inception/inception.js +204 -0
- package/build/dex/inception/inception.js.map +1 -0
- package/build/dex/inception/tokens.d.ts +9 -0
- package/build/dex/inception/tokens.js +28 -0
- package/build/dex/inception/tokens.js.map +1 -0
- package/build/dex/inception/types.d.ts +22 -0
- package/build/dex/inception/types.js +3 -0
- package/build/dex/inception/types.js.map +1 -0
- package/build/dex/inception/utils.d.ts +6 -0
- package/build/dex/inception/utils.js +37 -0
- package/build/dex/inception/utils.js.map +1 -0
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/jarvis.d.ts +2 -1
- package/build/dex/jarvis.js +58 -0
- package/build/dex/jarvis.js.map +1 -1
- package/build/dex/platypus/platypus.d.ts +2 -1
- package/build/dex/platypus/platypus.js +31 -0
- package/build/dex/platypus/platypus.js.map +1 -1
- package/build/dex/swaap-v1/swaap-v1.d.ts +2 -1
- package/build/dex/swaap-v1/swaap-v1.js +10 -0
- package/build/dex/swaap-v1/swaap-v1.js.map +1 -1
- package/build/dex/zerox/index.d.ts +4 -26
- package/build/dex/zerox/index.js +50 -42
- package/build/dex/zerox/index.js.map +1 -1
- package/build/dex/zerox/types.d.ts +29 -0
- package/build/dex/zerox/types.js +8 -1
- package/build/dex/zerox/types.js.map +1 -1
- package/build/executor/Executor01BytecodeBuilder.js +3 -2
- package/build/executor/Executor01BytecodeBuilder.js.map +1 -1
- package/build/executor/Executor02BytecodeBuilder.js +3 -2
- package/build/executor/Executor02BytecodeBuilder.js.map +1 -1
- package/build/executor/Executor03BytecodeBuilder.js +3 -2
- package/build/executor/Executor03BytecodeBuilder.js.map +1 -1
- package/build/types.d.ts +1 -0
- package/package.json +4 -2
- package/src/abi/bebop/BebopSettlement.abi.json +2137 -0
- package/src/config.ts +6 -0
- package/src/dex/bebop/bebop-e2e.test.ts +195 -0
- package/src/dex/bebop/bebop-integration.test.ts +170 -0
- package/src/dex/bebop/bebop.ts +874 -0
- package/src/dex/bebop/config.ts +37 -0
- package/src/dex/bebop/constants.ts +21 -0
- package/src/dex/bebop/rate-fetcher.ts +138 -0
- package/src/dex/bebop/types.ts +95 -0
- package/src/dex/bebop/validators.ts +29 -0
- package/src/dex/index.ts +2 -0
- package/src/executor/Executor01BytecodeBuilder.ts +3 -2
- package/src/executor/Executor02BytecodeBuilder.ts +3 -2
- package/src/executor/Executor03BytecodeBuilder.ts +3 -2
- package/src/lib/fetcher/wsFetcher.ts +145 -0
- package/src/types.ts +1 -0
- package/.idea/aws.xml +0 -17
- package/.idea/codeStyles/Project.xml +0 -19
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/paraswap-dex-lib.iml +0 -9
- package/.idea/prettier.xml +0 -7
- package/.idea/vcs.xml +0 -6
- package/build/abi/integral/factory.json +0 -333
- package/build/abi/integral/oracle.json +0 -501
- package/build/abi/integral/relayer.json +0 -1536
- package/build/dex/bProtocol.d.ts +0 -18
- package/build/dex/bProtocol.js +0 -39
- package/build/dex/bProtocol.js.map +0 -1
- package/build/dex/bancor.d.ts +0 -26
- package/build/dex/bancor.js +0 -58
- package/build/dex/bancor.js.map +0 -1
- package/build/dex/compound.d.ts +0 -18
- package/build/dex/compound.js +0 -46
- package/build/dex/compound.js.map +0 -1
- package/build/dex/curve-v2.d.ts +0 -58
- package/build/dex/curve-v2.js +0 -180
- package/build/dex/curve-v2.js.map +0 -1
- package/build/dex/dodo-v1.d.ts +0 -33
- package/build/dex/dodo-v1.js +0 -63
- package/build/dex/dodo-v1.js.map +0 -1
- package/build/dex/integral/config.d.ts +0 -4
- package/build/dex/integral/config.js +0 -20
- package/build/dex/integral/config.js.map +0 -1
- package/build/dex/integral/context.d.ts +0 -40
- package/build/dex/integral/context.js +0 -158
- package/build/dex/integral/context.js.map +0 -1
- package/build/dex/integral/integral-factory.d.ts +0 -24
- package/build/dex/integral/integral-factory.js +0 -95
- package/build/dex/integral/integral-factory.js.map +0 -1
- package/build/dex/integral/integral-pool.d.ts +0 -50
- package/build/dex/integral/integral-pool.js +0 -149
- package/build/dex/integral/integral-pool.js.map +0 -1
- package/build/dex/integral/integral-pricing.d.ts +0 -18
- package/build/dex/integral/integral-pricing.js +0 -114
- package/build/dex/integral/integral-pricing.js.map +0 -1
- package/build/dex/integral/integral-relayer.d.ts +0 -42
- package/build/dex/integral/integral-relayer.js +0 -192
- package/build/dex/integral/integral-relayer.js.map +0 -1
- package/build/dex/integral/integral-token.d.ts +0 -27
- package/build/dex/integral/integral-token.js +0 -59
- package/build/dex/integral/integral-token.js.map +0 -1
- package/build/dex/integral/integral.js +0 -376
- package/build/dex/integral/integral.js.map +0 -1
- package/build/dex/integral/types.d.ts +0 -85
- package/build/dex/integral/types.js +0 -9
- package/build/dex/integral/types.js.map +0 -1
- package/build/dex/integral/utils-e2e.d.ts +0 -9
- package/build/dex/integral/utils-e2e.js +0 -131
- package/build/dex/integral/utils-e2e.js.map +0 -1
- package/build/dex/integral/utils.d.ts +0 -17
- package/build/dex/integral/utils.js +0 -94
- package/build/dex/integral/utils.js.map +0 -1
- package/build/dex/lido.d.ts +0 -19
- package/build/dex/lido.js +0 -42
- package/build/dex/lido.js.map +0 -1
- package/build/dex/onebit.d.ts +0 -25
- package/build/dex/onebit.js +0 -42
- package/build/dex/onebit.js.map +0 -1
- package/build/dex/smoothy.d.ts +0 -26
- package/build/dex/smoothy.js +0 -48
- package/build/dex/smoothy.js.map +0 -1
- package/build/dex/stable-pool.d.ts +0 -28
- package/build/dex/stable-pool.js +0 -62
- package/build/dex/stable-pool.js.map +0 -1
- package/build/dex/trader-joe-v2.1/optimizer.d.ts +0 -2
- package/build/dex/trader-joe-v2.1/optimizer.js +0 -47
- package/build/dex/trader-joe-v2.1/optimizer.js.map +0 -1
- package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.d.ts +0 -7
- package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.js +0 -20
- package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.js.map +0 -1
- package/build/dex/trader-joe-v2.1.d.ts +0 -43
- package/build/dex/trader-joe-v2.1.js +0 -79
- package/build/dex/trader-joe-v2.1.js.map +0 -1
|
@@ -0,0 +1,874 @@
|
|
|
1
|
+
import { assert, AsyncOrSync } from 'ts-essentials';
|
|
2
|
+
import {
|
|
3
|
+
Token,
|
|
4
|
+
Address,
|
|
5
|
+
ExchangePrices,
|
|
6
|
+
PoolPrices,
|
|
7
|
+
AdapterExchangeParam,
|
|
8
|
+
PoolLiquidity,
|
|
9
|
+
Logger,
|
|
10
|
+
OptimalSwapExchange,
|
|
11
|
+
PreprocessTransactionOptions,
|
|
12
|
+
ExchangeTxInfo,
|
|
13
|
+
NumberAsString,
|
|
14
|
+
DexExchangeParam,
|
|
15
|
+
} from '../../types';
|
|
16
|
+
import { SwapSide, Network } from '../../constants';
|
|
17
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
18
|
+
import { getDexKeysWithNetwork, Utils } from '../../utils';
|
|
19
|
+
import { IDex } from '../../dex/idex';
|
|
20
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
21
|
+
import {
|
|
22
|
+
BebopData,
|
|
23
|
+
BebopLevel,
|
|
24
|
+
BebopPair,
|
|
25
|
+
BebopPricingResponse,
|
|
26
|
+
RestrictData,
|
|
27
|
+
RoutingInstruction,
|
|
28
|
+
SlippageError,
|
|
29
|
+
TokenDataMap,
|
|
30
|
+
} from './types';
|
|
31
|
+
import settlementABI from '../../abi/bebop/BebopSettlement.abi.json';
|
|
32
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
33
|
+
import { BebopConfig } from './config';
|
|
34
|
+
import { Interface } from 'ethers/lib/utils';
|
|
35
|
+
import { RateFetcher } from './rate-fetcher';
|
|
36
|
+
import {
|
|
37
|
+
BEBOP_API_URL,
|
|
38
|
+
BEBOP_AUTH_NAME,
|
|
39
|
+
BEBOP_ERRORS_CACHE_KEY,
|
|
40
|
+
BEBOP_GAS_COST,
|
|
41
|
+
BEBOP_INIT_TIMEOUT_MS,
|
|
42
|
+
BEBOP_PRICES_CACHE_TTL,
|
|
43
|
+
BEBOP_QUOTE_TIMEOUT_MS,
|
|
44
|
+
BEBOP_RESTRICTED_CACHE_KEY,
|
|
45
|
+
BEBOP_RESTRICT_CHECK_INTERVAL_MS,
|
|
46
|
+
BEBOP_RESTRICT_COUNT_THRESHOLD,
|
|
47
|
+
BEBOP_RESTRICT_TTL_S,
|
|
48
|
+
BEBOP_TOKENS_CACHE_TTL,
|
|
49
|
+
BEBOP_TOKENS_POLLING_INTERVAL_MS,
|
|
50
|
+
BEBOP_WS_API_URL,
|
|
51
|
+
SWAP_SINGLE_METHOD_SELECTOR,
|
|
52
|
+
SWAP_AGGREGATE_METHOD_SELECTOR,
|
|
53
|
+
SWAP_SINGLE_METHOD,
|
|
54
|
+
SWAP_AGGREGATE_METHOD,
|
|
55
|
+
} from './constants';
|
|
56
|
+
import BigNumber from 'bignumber.js';
|
|
57
|
+
import { getBigNumberPow } from '../../bignumber-constants';
|
|
58
|
+
import { utils } from 'ethers';
|
|
59
|
+
import qs from 'qs';
|
|
60
|
+
|
|
61
|
+
export class Bebop extends SimpleExchange implements IDex<BebopData> {
|
|
62
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
63
|
+
readonly needWrapNative = false;
|
|
64
|
+
|
|
65
|
+
readonly isFeeOnTransferSupported = false;
|
|
66
|
+
readonly isStatePollingDex = true;
|
|
67
|
+
|
|
68
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
69
|
+
getDexKeysWithNetwork(BebopConfig);
|
|
70
|
+
|
|
71
|
+
private rateFetcher: RateFetcher;
|
|
72
|
+
private tokensMap: TokenDataMap = {};
|
|
73
|
+
|
|
74
|
+
private pricesCacheKey: string;
|
|
75
|
+
private tokensCacheKey: string;
|
|
76
|
+
private tokensAddrCacheKey: string;
|
|
77
|
+
|
|
78
|
+
private bebopAuthToken: string;
|
|
79
|
+
|
|
80
|
+
logger: Logger;
|
|
81
|
+
|
|
82
|
+
constructor(
|
|
83
|
+
readonly network: Network,
|
|
84
|
+
readonly dexKey: string,
|
|
85
|
+
readonly dexHelper: IDexHelper,
|
|
86
|
+
readonly settlementAddress: string = BebopConfig['Bebop'][network]
|
|
87
|
+
.settlementAddress,
|
|
88
|
+
protected settlementInterface = new Interface(settlementABI),
|
|
89
|
+
) {
|
|
90
|
+
super(dexHelper, dexKey);
|
|
91
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
92
|
+
this.tokensCacheKey = `tokens`;
|
|
93
|
+
this.pricesCacheKey = `prices`;
|
|
94
|
+
this.tokensAddrCacheKey = `tokens_addr`;
|
|
95
|
+
const token = this.dexHelper.config.data.bebopAuthToken;
|
|
96
|
+
if (!token) {
|
|
97
|
+
throw new Error('Bebop auth token is not set');
|
|
98
|
+
}
|
|
99
|
+
this.bebopAuthToken = token;
|
|
100
|
+
|
|
101
|
+
this.rateFetcher = new RateFetcher(
|
|
102
|
+
this.dexHelper,
|
|
103
|
+
this.dexKey,
|
|
104
|
+
this.network,
|
|
105
|
+
this.logger,
|
|
106
|
+
{
|
|
107
|
+
rateConfig: {
|
|
108
|
+
tokensIntervalMs: BEBOP_TOKENS_POLLING_INTERVAL_MS,
|
|
109
|
+
pricesCacheKey: this.pricesCacheKey,
|
|
110
|
+
pricesCacheTTLSecs: BEBOP_PRICES_CACHE_TTL,
|
|
111
|
+
tokensCacheKey: this.tokensCacheKey,
|
|
112
|
+
tokensAddrCacheKey: this.tokensAddrCacheKey,
|
|
113
|
+
tokensCacheTTLSecs: BEBOP_TOKENS_CACHE_TTL,
|
|
114
|
+
tokensReqParams: {
|
|
115
|
+
url:
|
|
116
|
+
BEBOP_API_URL +
|
|
117
|
+
`/pmm/${BebopConfig['Bebop'][network].chainName}/v3/token-info`,
|
|
118
|
+
},
|
|
119
|
+
pricesReqParams: {
|
|
120
|
+
url:
|
|
121
|
+
BEBOP_WS_API_URL +
|
|
122
|
+
`/pmm/${BebopConfig['Bebop'][network].chainName}/v3/pricing`,
|
|
123
|
+
headers: {
|
|
124
|
+
name: BEBOP_AUTH_NAME,
|
|
125
|
+
authorization: this.bebopAuthToken,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async initializePricing(blockNumber: number) {
|
|
134
|
+
if (!this.dexHelper.config.isSlave) {
|
|
135
|
+
this.rateFetcher.start();
|
|
136
|
+
await sleep(BEBOP_INIT_TIMEOUT_MS);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await this.setTokensMap();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Returns the list of contract adapters (name and index)
|
|
143
|
+
// for a buy/sell. Return null if there are no adapters.
|
|
144
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
getPoolIdentifier(base: string, quote: string): string {
|
|
149
|
+
const identifier = `${
|
|
150
|
+
this.dexKey
|
|
151
|
+
}_${base.toLowerCase()}_${quote.toLowerCase()}`;
|
|
152
|
+
return identifier;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
invertLevels(levels: BebopLevel[]): BebopLevel[] {
|
|
156
|
+
return levels.map(([price, size]) => [1 / price, size * price]);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
invertBook(book: BebopPair): BebopPair {
|
|
160
|
+
return {
|
|
161
|
+
bids: this.invertLevels(book.asks),
|
|
162
|
+
asks: this.invertLevels(book.bids),
|
|
163
|
+
last_update_ts: book.last_update_ts,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// considers only swapside.sell
|
|
168
|
+
async calculateInstructions(
|
|
169
|
+
srcToken: Token,
|
|
170
|
+
destToken: Token,
|
|
171
|
+
side: SwapSide,
|
|
172
|
+
): Promise<RoutingInstruction[]> {
|
|
173
|
+
const prices = await this.getCachedPrices();
|
|
174
|
+
if (!prices) {
|
|
175
|
+
throw new Error('No prices available');
|
|
176
|
+
}
|
|
177
|
+
const directBook =
|
|
178
|
+
prices[
|
|
179
|
+
`${srcToken.address.toLowerCase()}/${destToken.address.toLowerCase()}`
|
|
180
|
+
];
|
|
181
|
+
if (directBook) {
|
|
182
|
+
return [
|
|
183
|
+
{
|
|
184
|
+
pair: `${srcToken.address.toLowerCase()}/${destToken.address.toLowerCase()}`,
|
|
185
|
+
side,
|
|
186
|
+
book: directBook,
|
|
187
|
+
targetQuote: side == SwapSide.BUY,
|
|
188
|
+
},
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const inverseBook =
|
|
193
|
+
prices[
|
|
194
|
+
`${destToken.address.toLowerCase()}/${srcToken.address.toLowerCase()}`
|
|
195
|
+
];
|
|
196
|
+
if (inverseBook) {
|
|
197
|
+
const invertedBook = this.invertBook(inverseBook);
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
pair: `${srcToken.address.toLowerCase()}/${destToken.address.toLowerCase()}`,
|
|
201
|
+
side,
|
|
202
|
+
book: invertedBook,
|
|
203
|
+
targetQuote: side == SwapSide.BUY,
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for (const middleToken of BebopConfig['Bebop'][this.network].middleTokens) {
|
|
209
|
+
const baseMiddle =
|
|
210
|
+
prices[
|
|
211
|
+
`${srcToken.address.toLowerCase()}/${middleToken.toLowerCase()}`
|
|
212
|
+
];
|
|
213
|
+
const quoteMiddle =
|
|
214
|
+
prices[
|
|
215
|
+
`${destToken.address.toLowerCase()}/${middleToken.toLowerCase()}`
|
|
216
|
+
];
|
|
217
|
+
if (baseMiddle && quoteMiddle) {
|
|
218
|
+
if (side == SwapSide.SELL) {
|
|
219
|
+
return [
|
|
220
|
+
{
|
|
221
|
+
pair: `${srcToken.address.toLowerCase()}/${middleToken.toLowerCase()}`,
|
|
222
|
+
side: side,
|
|
223
|
+
book: baseMiddle,
|
|
224
|
+
targetQuote: false,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
pair: `${middleToken.toLowerCase()}/${destToken.address.toLowerCase()}`,
|
|
228
|
+
side: side,
|
|
229
|
+
book: this.invertBook(quoteMiddle),
|
|
230
|
+
targetQuote: false,
|
|
231
|
+
},
|
|
232
|
+
];
|
|
233
|
+
} else {
|
|
234
|
+
return [
|
|
235
|
+
{
|
|
236
|
+
pair: `${middleToken.toLowerCase()}/${destToken.address.toLowerCase()}`,
|
|
237
|
+
side: side,
|
|
238
|
+
book: this.invertBook(quoteMiddle),
|
|
239
|
+
targetQuote: true,
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
pair: `${srcToken.address.toLowerCase()}/${middleToken.toLowerCase()}`,
|
|
243
|
+
side: side,
|
|
244
|
+
book: baseMiddle,
|
|
245
|
+
targetQuote: true,
|
|
246
|
+
},
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Returns list of pool identifiers that can be used
|
|
256
|
+
// for a given swap. poolIdentifiers must be unique
|
|
257
|
+
// across DEXes. It is recommended to use
|
|
258
|
+
// ${dexKey}_${poolAddress} as a poolIdentifier
|
|
259
|
+
async getPoolIdentifiers(
|
|
260
|
+
srcToken: Token,
|
|
261
|
+
destToken: Token,
|
|
262
|
+
side: SwapSide,
|
|
263
|
+
blockNumber: number,
|
|
264
|
+
): Promise<string[]> {
|
|
265
|
+
if (
|
|
266
|
+
(await this.calculateInstructions(srcToken, destToken, side)).length > 0
|
|
267
|
+
) {
|
|
268
|
+
const identifier = this.getPoolIdentifier(
|
|
269
|
+
srcToken.address,
|
|
270
|
+
destToken.address,
|
|
271
|
+
);
|
|
272
|
+
return [identifier];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return [];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
runInstruction(
|
|
279
|
+
instruction: RoutingInstruction,
|
|
280
|
+
amount: BigNumber,
|
|
281
|
+
): BigNumber {
|
|
282
|
+
let accumulated = BigNumber(0);
|
|
283
|
+
let output = BigNumber(0);
|
|
284
|
+
let filled = false;
|
|
285
|
+
for (const level of instruction.book.bids) {
|
|
286
|
+
const [price, size] = level;
|
|
287
|
+
const amountToAccumulate = instruction.targetQuote
|
|
288
|
+
? BigNumber(size).times(price)
|
|
289
|
+
: BigNumber(size);
|
|
290
|
+
const afterAccumulated = accumulated.plus(amountToAccumulate);
|
|
291
|
+
if (afterAccumulated.lt(amount)) {
|
|
292
|
+
accumulated = accumulated.plus(amountToAccumulate);
|
|
293
|
+
const amountToAddToOutput = instruction.targetQuote
|
|
294
|
+
? BigNumber(size)
|
|
295
|
+
: BigNumber(size).times(price);
|
|
296
|
+
output = output.plus(amountToAddToOutput);
|
|
297
|
+
if (accumulated.eq(amount)) {
|
|
298
|
+
filled = true;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
const remaining = amount.minus(accumulated);
|
|
303
|
+
output = output.plus(
|
|
304
|
+
instruction.targetQuote
|
|
305
|
+
? remaining.div(price)
|
|
306
|
+
: remaining.times(price),
|
|
307
|
+
);
|
|
308
|
+
filled = true;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (filled) {
|
|
313
|
+
return output;
|
|
314
|
+
} else {
|
|
315
|
+
return BigNumber(0);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
calculateOutput(
|
|
320
|
+
instructions: RoutingInstruction[],
|
|
321
|
+
srcToken: Token,
|
|
322
|
+
destToken: Token,
|
|
323
|
+
amounts: bigint[],
|
|
324
|
+
side: SwapSide,
|
|
325
|
+
): bigint[] {
|
|
326
|
+
const outputs = [];
|
|
327
|
+
const inputDecimals =
|
|
328
|
+
side == SwapSide.SELL ? srcToken.decimals : destToken.decimals;
|
|
329
|
+
const outputDecimals =
|
|
330
|
+
side == SwapSide.SELL ? destToken.decimals : srcToken.decimals;
|
|
331
|
+
|
|
332
|
+
for (const amount of amounts) {
|
|
333
|
+
if (amount == 0n) {
|
|
334
|
+
outputs.push(0n);
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const amountDecimals = BigNumber(amount.toString()).div(
|
|
338
|
+
getBigNumberPow(inputDecimals),
|
|
339
|
+
);
|
|
340
|
+
let output: BigNumber = BigNumber(0);
|
|
341
|
+
for (const instruction of instructions) {
|
|
342
|
+
output = this.runInstruction(
|
|
343
|
+
instruction,
|
|
344
|
+
output.gt(0) ? output : amountDecimals,
|
|
345
|
+
);
|
|
346
|
+
if (output.eq(0)) {
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (output.gt(0)) {
|
|
351
|
+
outputs.push(
|
|
352
|
+
BigInt(output.times(getBigNumberPow(outputDecimals)).toFixed(0)),
|
|
353
|
+
);
|
|
354
|
+
} else {
|
|
355
|
+
outputs.push(0n);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return outputs;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Returns pool prices for amounts.
|
|
362
|
+
// If limitPools is defined only pools in limitPools
|
|
363
|
+
// should be used. If limitPools is undefined then
|
|
364
|
+
// any pools can be used.
|
|
365
|
+
async getPricesVolume(
|
|
366
|
+
srcToken: Token,
|
|
367
|
+
destToken: Token,
|
|
368
|
+
amounts: bigint[],
|
|
369
|
+
side: SwapSide,
|
|
370
|
+
blockNumber: number,
|
|
371
|
+
limitPools?: string[],
|
|
372
|
+
): Promise<null | ExchangePrices<BebopData>> {
|
|
373
|
+
const isRestricted = await this.isRestricted();
|
|
374
|
+
if (isRestricted) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
let pools = limitPools
|
|
380
|
+
? limitPools.filter(
|
|
381
|
+
p =>
|
|
382
|
+
p === this.getPoolIdentifier(srcToken.address, destToken.address),
|
|
383
|
+
)
|
|
384
|
+
: await this.getPoolIdentifiers(srcToken, destToken, side, blockNumber);
|
|
385
|
+
|
|
386
|
+
if (pools.length === 0) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const instructions = await this.calculateInstructions(
|
|
391
|
+
srcToken,
|
|
392
|
+
destToken,
|
|
393
|
+
side,
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
if (!instructions) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const outputs = this.calculateOutput(
|
|
401
|
+
instructions,
|
|
402
|
+
srcToken,
|
|
403
|
+
destToken,
|
|
404
|
+
amounts,
|
|
405
|
+
side,
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
// Up to 3bps deviation is expected in the output compared to the on-chain result
|
|
409
|
+
// On SwapSide.Sell, outputs compared to quoting are coming out: -0.1 bips -> USDC, -1 bips Alt -> Alt.
|
|
410
|
+
// On SwapSide.Buy, outputs compared to quoteing are coming out: 0.1 bips -> USDC, 1-3 bips Alt -> Alt.
|
|
411
|
+
|
|
412
|
+
const outDecimals = SwapSide.SELL
|
|
413
|
+
? destToken.decimals
|
|
414
|
+
: srcToken.decimals;
|
|
415
|
+
|
|
416
|
+
return [
|
|
417
|
+
{
|
|
418
|
+
prices: outputs,
|
|
419
|
+
unit: BigInt(outDecimals),
|
|
420
|
+
data: {},
|
|
421
|
+
poolIdentifier: pools[0],
|
|
422
|
+
exchange: this.dexKey,
|
|
423
|
+
gasCost: BEBOP_GAS_COST,
|
|
424
|
+
poolAddresses: [this.settlementAddress],
|
|
425
|
+
},
|
|
426
|
+
];
|
|
427
|
+
} catch (e: unknown) {
|
|
428
|
+
this.logger.error(
|
|
429
|
+
`Error_getPricesVolume ${srcToken.address || srcToken.symbol}, ${
|
|
430
|
+
destToken.address || destToken.symbol
|
|
431
|
+
}, ${side}:`,
|
|
432
|
+
e,
|
|
433
|
+
);
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
439
|
+
getCalldataGasCost(poolPrices: PoolPrices<BebopData>): number | number[] {
|
|
440
|
+
// This relies heavily on exact quote. Can we note use Bebop Data to find this? either via pools or the data itself?
|
|
441
|
+
// This assumes that a single maker was used to fill this trade
|
|
442
|
+
// "order":{
|
|
443
|
+
return (
|
|
444
|
+
CALLDATA_GAS_COST.DEX_OVERHEAD +
|
|
445
|
+
// "expiry":"1725541751"
|
|
446
|
+
CALLDATA_GAS_COST.TIMESTAMP +
|
|
447
|
+
// "taker_address":"0x9008d19f58aabd9ed0d60971565aa8510560ab41"
|
|
448
|
+
CALLDATA_GAS_COST.ADDRESS +
|
|
449
|
+
// "maker_address":"0x807cf9a772d5a3f9cefbc1192e939d62f0d9bd38"
|
|
450
|
+
CALLDATA_GAS_COST.ADDRESS +
|
|
451
|
+
// "maker_nonce":"1725541661402362944"
|
|
452
|
+
CALLDATA_GAS_COST.UUID +
|
|
453
|
+
// "taker_token":"0x3429d03c6f7521aec737a0bbf2e5ddcef2c3ae31"
|
|
454
|
+
CALLDATA_GAS_COST.ADDRESS +
|
|
455
|
+
// "maker_token":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
|
|
456
|
+
CALLDATA_GAS_COST.ADDRESS +
|
|
457
|
+
// "taker_amount":"3789033775358849793876"
|
|
458
|
+
CALLDATA_GAS_COST.AMOUNT +
|
|
459
|
+
// "maker_amount":"199726023273987970"
|
|
460
|
+
CALLDATA_GAS_COST.AMOUNT +
|
|
461
|
+
// "receiver":"0x9008d19f58aabd9ed0d60971565aa8510560ab41"
|
|
462
|
+
CALLDATA_GAS_COST.ADDRESS +
|
|
463
|
+
// "packed_commands":"0"
|
|
464
|
+
CALLDATA_GAS_COST.wordNonZeroBytes(4) +
|
|
465
|
+
// "flags":"103712057072216206793253728668500811363805133225332165859664901734817333248000"
|
|
466
|
+
CALLDATA_GAS_COST.FULL_WORD +
|
|
467
|
+
// }
|
|
468
|
+
// "makerSignature":{
|
|
469
|
+
// "signatureBytes":"0x34e1fbda1d48296949f0a003d4194646ed750a4ccd408b52979f60c2e7332207190a91600a40396b25fd645422dde85bc0ff53f26b66202a985ac8b9da87b47e1c"
|
|
470
|
+
CALLDATA_GAS_COST.FULL_WORD +
|
|
471
|
+
// "flags":"1"
|
|
472
|
+
CALLDATA_GAS_COST.LENGTH_SMALL +
|
|
473
|
+
// }
|
|
474
|
+
// "filledTakerAmount":"0"
|
|
475
|
+
CALLDATA_GAS_COST.AMOUNT
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Encode params required by the exchange adapter
|
|
480
|
+
// V5: Used for multiSwap, buy & megaSwap
|
|
481
|
+
// V6: Not used, can be left blank
|
|
482
|
+
// Hint: abiCoder.encodeParameter() could be useful
|
|
483
|
+
getAdapterParam(
|
|
484
|
+
srcToken: string,
|
|
485
|
+
destToken: string,
|
|
486
|
+
srcAmount: string,
|
|
487
|
+
destAmount: string,
|
|
488
|
+
data: BebopData,
|
|
489
|
+
side: SwapSide,
|
|
490
|
+
): AdapterExchangeParam {
|
|
491
|
+
const { tx } = data;
|
|
492
|
+
|
|
493
|
+
if (!tx) {
|
|
494
|
+
throw new Error('No tx in data');
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Encode here the payload for adapter
|
|
498
|
+
const payload = tx.data;
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
targetExchange: this.settlementAddress,
|
|
502
|
+
payload,
|
|
503
|
+
networkFee: '0',
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async updatePoolState(): Promise<void> {
|
|
508
|
+
await this.setTokensMap();
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async setTokensMap() {
|
|
512
|
+
const tokens = await this.getCachedTokens();
|
|
513
|
+
|
|
514
|
+
if (tokens) {
|
|
515
|
+
this.tokensMap = tokens;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
getMaxLiquidity(levels: BebopLevel[]) {
|
|
520
|
+
return levels.reduce((acc, [price, size]) => {
|
|
521
|
+
return acc + price * size;
|
|
522
|
+
}, 0);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Returns list of top pools based on liquidity. Max
|
|
526
|
+
// limit number pools should be returned.
|
|
527
|
+
async getTopPoolsForToken(
|
|
528
|
+
tokenAddress: Address,
|
|
529
|
+
limit: number,
|
|
530
|
+
): Promise<PoolLiquidity[]> {
|
|
531
|
+
const prices = await this.getCachedPrices();
|
|
532
|
+
|
|
533
|
+
if (!prices) {
|
|
534
|
+
return [];
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const pools: PoolLiquidity[] = [];
|
|
538
|
+
|
|
539
|
+
for (const [pair, pairData] of Object.entries(prices)) {
|
|
540
|
+
let liquidityUSD = 0;
|
|
541
|
+
let token;
|
|
542
|
+
const [base, quote] = pair.split('/');
|
|
543
|
+
|
|
544
|
+
const isBase = base.toLowerCase() == tokenAddress.toLowerCase();
|
|
545
|
+
const isQuote = quote.toLowerCase() == tokenAddress.toLowerCase();
|
|
546
|
+
|
|
547
|
+
if (isBase) {
|
|
548
|
+
const liquidityInQuote = this.getMaxLiquidity(pairData.bids);
|
|
549
|
+
token = {
|
|
550
|
+
address: quote,
|
|
551
|
+
decimals: this.tokensMap[quote.toLowerCase()].decimals,
|
|
552
|
+
};
|
|
553
|
+
const quoteTokenUsd = await this.dexHelper.getTokenUSDPrice(
|
|
554
|
+
token,
|
|
555
|
+
BigInt(Math.round(liquidityInQuote)),
|
|
556
|
+
);
|
|
557
|
+
liquidityUSD = liquidityInQuote * quoteTokenUsd;
|
|
558
|
+
} else if (isQuote) {
|
|
559
|
+
const liquidityInBase = this.getMaxLiquidity(pairData.asks);
|
|
560
|
+
token = {
|
|
561
|
+
address: base,
|
|
562
|
+
decimals: this.tokensMap[base.toLowerCase()].decimals,
|
|
563
|
+
};
|
|
564
|
+
const baseTokenUsd = await this.dexHelper.getTokenUSDPrice(
|
|
565
|
+
token,
|
|
566
|
+
BigInt(Math.round(liquidityInBase)),
|
|
567
|
+
);
|
|
568
|
+
liquidityUSD = liquidityInBase * baseTokenUsd;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (liquidityUSD) {
|
|
572
|
+
assert(token, 'Token not found');
|
|
573
|
+
const address = token.address.toLowerCase();
|
|
574
|
+
|
|
575
|
+
if (pools.length === 0) {
|
|
576
|
+
pools.push({
|
|
577
|
+
exchange: this.dexKey,
|
|
578
|
+
address: this.settlementAddress,
|
|
579
|
+
connectorTokens: [
|
|
580
|
+
{
|
|
581
|
+
address: address,
|
|
582
|
+
decimals: this.tokensMap[address].decimals,
|
|
583
|
+
symbol: this.tokensMap[address].ticker,
|
|
584
|
+
},
|
|
585
|
+
],
|
|
586
|
+
liquidityUSD,
|
|
587
|
+
});
|
|
588
|
+
} else {
|
|
589
|
+
pools[0].liquidityUSD += liquidityUSD;
|
|
590
|
+
pools[0].connectorTokens.push({
|
|
591
|
+
address: address,
|
|
592
|
+
decimals: this.tokensMap[address].decimals,
|
|
593
|
+
symbol: this.tokensMap[address].ticker,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return pools
|
|
600
|
+
.sort((a, b) => b.liquidityUSD - a.liquidityUSD)
|
|
601
|
+
.slice(0, limit);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
getDexParam(
|
|
605
|
+
srcToken: Address,
|
|
606
|
+
destToken: Address,
|
|
607
|
+
srcAmount: NumberAsString,
|
|
608
|
+
destAmount: NumberAsString,
|
|
609
|
+
recipient: Address,
|
|
610
|
+
data: BebopData,
|
|
611
|
+
side: SwapSide,
|
|
612
|
+
): DexExchangeParam {
|
|
613
|
+
const { tx } = data;
|
|
614
|
+
|
|
615
|
+
assert(tx !== undefined, `${this.dexKey}-${this.network}: tx undefined`);
|
|
616
|
+
|
|
617
|
+
const isSwapSingle = tx.data.slice(0, 10) === SWAP_SINGLE_METHOD_SELECTOR;
|
|
618
|
+
const isSwapAggregate =
|
|
619
|
+
tx.data.slice(0, 10) === SWAP_AGGREGATE_METHOD_SELECTOR;
|
|
620
|
+
|
|
621
|
+
if (isSwapSingle || isSwapAggregate) {
|
|
622
|
+
const method = isSwapSingle ? SWAP_SINGLE_METHOD : SWAP_AGGREGATE_METHOD;
|
|
623
|
+
|
|
624
|
+
const decodedParam = this.settlementInterface.decodeFunctionData(
|
|
625
|
+
method,
|
|
626
|
+
tx.data,
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
const exchangeData = this.settlementInterface.encodeFunctionData(method, [
|
|
630
|
+
decodedParam[0],
|
|
631
|
+
decodedParam[1],
|
|
632
|
+
srcAmount, // modify filledTakerAmount to make insertFromAmount work
|
|
633
|
+
]);
|
|
634
|
+
|
|
635
|
+
return {
|
|
636
|
+
exchangeData: exchangeData,
|
|
637
|
+
needWrapNative: this.needWrapNative,
|
|
638
|
+
dexFuncHasRecipient: true,
|
|
639
|
+
targetExchange: this.settlementAddress,
|
|
640
|
+
returnAmountPos: undefined,
|
|
641
|
+
};
|
|
642
|
+
} else {
|
|
643
|
+
throw new Error('Not supported method');
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async preProcessTransaction(
|
|
648
|
+
optimalSwapExchange: OptimalSwapExchange<BebopData>,
|
|
649
|
+
srcToken: Token,
|
|
650
|
+
destToken: Token,
|
|
651
|
+
side: SwapSide,
|
|
652
|
+
options: PreprocessTransactionOptions,
|
|
653
|
+
): Promise<[OptimalSwapExchange<BebopData>, ExchangeTxInfo]> {
|
|
654
|
+
const isSell = side === SwapSide.SELL;
|
|
655
|
+
const isBuy = side === SwapSide.BUY;
|
|
656
|
+
|
|
657
|
+
const params = {
|
|
658
|
+
sell_tokens: utils.getAddress(srcToken.address),
|
|
659
|
+
buy_tokens: utils.getAddress(destToken.address),
|
|
660
|
+
sell_amounts: isSell ? optimalSwapExchange.srcAmount : undefined,
|
|
661
|
+
buy_amounts: isBuy ? optimalSwapExchange.destAmount : undefined,
|
|
662
|
+
taker_address: utils.getAddress(options.executionContractAddress),
|
|
663
|
+
receiver_address: utils.getAddress(options.recipient),
|
|
664
|
+
gasless: false,
|
|
665
|
+
skip_validation: true,
|
|
666
|
+
source: BEBOP_AUTH_NAME,
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
try {
|
|
670
|
+
const response: BebopData = await this.dexHelper.httpRequest.get(
|
|
671
|
+
`${BEBOP_API_URL}/pmm/${
|
|
672
|
+
BebopConfig['Bebop'][this.network].chainName
|
|
673
|
+
}/v3/quote?${qs.stringify(params)}`,
|
|
674
|
+
BEBOP_QUOTE_TIMEOUT_MS,
|
|
675
|
+
{
|
|
676
|
+
'source-auth': this.bebopAuthToken,
|
|
677
|
+
},
|
|
678
|
+
);
|
|
679
|
+
|
|
680
|
+
if (!response) {
|
|
681
|
+
throw new Error('Failed to get quote');
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (
|
|
685
|
+
!response.tx ||
|
|
686
|
+
!response.buyTokens ||
|
|
687
|
+
!response.sellTokens ||
|
|
688
|
+
!response.expiry
|
|
689
|
+
) {
|
|
690
|
+
const baseMessage = `Bebop quote failed on chain ${this.network} Sell: ${params.sell_tokens}. Buy: ${params.buy_tokens}.`;
|
|
691
|
+
if (response.error) {
|
|
692
|
+
const errorMessage = `${baseMessage} Code: ${response.error.errorCode}, Message: ${response.error.message}`;
|
|
693
|
+
throw new Error(errorMessage);
|
|
694
|
+
} else {
|
|
695
|
+
const errorMessage = `${baseMessage} Response: ${JSON.stringify(
|
|
696
|
+
response,
|
|
697
|
+
)}`;
|
|
698
|
+
throw new Error(errorMessage);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (side == SwapSide.SELL) {
|
|
703
|
+
const requiredAmount = BigInt(optimalSwapExchange.destAmount);
|
|
704
|
+
const quoteAmount = BigInt(
|
|
705
|
+
response.buyTokens[utils.getAddress(destToken.address)].amount,
|
|
706
|
+
);
|
|
707
|
+
const requiredAmountWithSlippage = new BigNumber(
|
|
708
|
+
requiredAmount.toString(),
|
|
709
|
+
)
|
|
710
|
+
.times(options.slippageFactor)
|
|
711
|
+
.toFixed(0);
|
|
712
|
+
if (quoteAmount < BigInt(requiredAmountWithSlippage)) {
|
|
713
|
+
throw new SlippageError(
|
|
714
|
+
`Slipped, factor: ${quoteAmount.toString()} < ${requiredAmountWithSlippage}`,
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
const requiredAmount = BigInt(optimalSwapExchange.srcAmount);
|
|
719
|
+
const quoteAmount = BigInt(
|
|
720
|
+
response.sellTokens[utils.getAddress(srcToken.address)].amount,
|
|
721
|
+
);
|
|
722
|
+
const requiredAmountWithSlippage = new BigNumber(
|
|
723
|
+
requiredAmount.toString(),
|
|
724
|
+
)
|
|
725
|
+
.times(options.slippageFactor)
|
|
726
|
+
.toFixed(0);
|
|
727
|
+
if (quoteAmount > BigInt(requiredAmountWithSlippage)) {
|
|
728
|
+
throw new SlippageError(
|
|
729
|
+
`Slipped, factor: ${
|
|
730
|
+
options.slippageFactor
|
|
731
|
+
} ${quoteAmount.toString()} > ${requiredAmountWithSlippage}`,
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return [
|
|
736
|
+
{
|
|
737
|
+
...optimalSwapExchange,
|
|
738
|
+
data: {
|
|
739
|
+
...response,
|
|
740
|
+
},
|
|
741
|
+
},
|
|
742
|
+
{ deadline: BigInt(response.expiry) },
|
|
743
|
+
];
|
|
744
|
+
} catch (e: any) {
|
|
745
|
+
const message = `${this.dexKey}-${this.network}: ${e}`;
|
|
746
|
+
this.logger.error(message);
|
|
747
|
+
if (!e?.isSlippageError) {
|
|
748
|
+
this.restrict();
|
|
749
|
+
}
|
|
750
|
+
throw new Error(message);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
async restrict() {
|
|
755
|
+
const errorsDataRaw = await this.dexHelper.cache.get(
|
|
756
|
+
this.dexKey,
|
|
757
|
+
this.network,
|
|
758
|
+
BEBOP_ERRORS_CACHE_KEY,
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
const errorsData: RestrictData = Utils.Parse(errorsDataRaw);
|
|
762
|
+
const ERRORS_TTL_S = Math.floor(BEBOP_RESTRICT_CHECK_INTERVAL_MS / 1000);
|
|
763
|
+
|
|
764
|
+
if (
|
|
765
|
+
!errorsData ||
|
|
766
|
+
errorsData?.addedDatetimeMs + BEBOP_RESTRICT_CHECK_INTERVAL_MS <
|
|
767
|
+
Date.now()
|
|
768
|
+
) {
|
|
769
|
+
this.logger.warn(
|
|
770
|
+
`${this.dexKey}-${this.network}: First encounter of error OR error ocurred outside of threshold, setting up counter`,
|
|
771
|
+
);
|
|
772
|
+
const data: RestrictData = {
|
|
773
|
+
count: 1,
|
|
774
|
+
addedDatetimeMs: Date.now(),
|
|
775
|
+
};
|
|
776
|
+
await this.dexHelper.cache.setex(
|
|
777
|
+
this.dexKey,
|
|
778
|
+
this.network,
|
|
779
|
+
BEBOP_ERRORS_CACHE_KEY,
|
|
780
|
+
ERRORS_TTL_S,
|
|
781
|
+
Utils.Serialize(data),
|
|
782
|
+
);
|
|
783
|
+
return;
|
|
784
|
+
} else {
|
|
785
|
+
if (errorsData.count + 1 >= BEBOP_RESTRICT_COUNT_THRESHOLD) {
|
|
786
|
+
this.logger.warn(
|
|
787
|
+
`${this.dexKey}-${this.network}: Restricting due to error count=${
|
|
788
|
+
errorsData.count + 1
|
|
789
|
+
} within ${BEBOP_RESTRICT_CHECK_INTERVAL_MS / 1000 / 60} minutes`,
|
|
790
|
+
);
|
|
791
|
+
await this.dexHelper.cache.setex(
|
|
792
|
+
this.dexKey,
|
|
793
|
+
this.network,
|
|
794
|
+
BEBOP_RESTRICTED_CACHE_KEY,
|
|
795
|
+
BEBOP_RESTRICT_TTL_S,
|
|
796
|
+
'true',
|
|
797
|
+
);
|
|
798
|
+
} else {
|
|
799
|
+
this.logger.warn(
|
|
800
|
+
`${this.dexKey}-${this.network}: Error count increased`,
|
|
801
|
+
);
|
|
802
|
+
const data: RestrictData = {
|
|
803
|
+
count: errorsData.count + 1,
|
|
804
|
+
addedDatetimeMs: errorsData.addedDatetimeMs,
|
|
805
|
+
};
|
|
806
|
+
await this.dexHelper.cache.setex(
|
|
807
|
+
this.dexKey,
|
|
808
|
+
this.network,
|
|
809
|
+
BEBOP_ERRORS_CACHE_KEY,
|
|
810
|
+
ERRORS_TTL_S,
|
|
811
|
+
Utils.Serialize(data),
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
async isRestricted(): Promise<boolean> {
|
|
818
|
+
const result = await this.dexHelper.cache.get(
|
|
819
|
+
this.dexKey,
|
|
820
|
+
this.network,
|
|
821
|
+
BEBOP_RESTRICTED_CACHE_KEY,
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
return result === 'true';
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
async getCachedPrices(): Promise<BebopPricingResponse | null> {
|
|
828
|
+
const cachedPrices = await this.dexHelper.cache.get(
|
|
829
|
+
this.dexKey,
|
|
830
|
+
this.network,
|
|
831
|
+
this.pricesCacheKey,
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
if (cachedPrices) {
|
|
835
|
+
return JSON.parse(cachedPrices) as BebopPricingResponse;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
async getCachedTokens(): Promise<TokenDataMap | null> {
|
|
842
|
+
const cachedTokens = await this.dexHelper.cache.get(
|
|
843
|
+
this.dexKey,
|
|
844
|
+
this.network,
|
|
845
|
+
this.tokensAddrCacheKey,
|
|
846
|
+
);
|
|
847
|
+
|
|
848
|
+
if (cachedTokens) {
|
|
849
|
+
return JSON.parse(cachedTokens) as TokenDataMap;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
getTokenFromAddress(address: Address): Token {
|
|
856
|
+
const bebopToken = this.tokensMap[address.toLowerCase()];
|
|
857
|
+
return {
|
|
858
|
+
address,
|
|
859
|
+
decimals: bebopToken.decimals,
|
|
860
|
+
symbol: bebopToken.ticker,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
releaseResources(): AsyncOrSync<void> {
|
|
865
|
+
if (!this.dexHelper.config.isSlave) {
|
|
866
|
+
this.rateFetcher.stop();
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const sleep = (time: number) =>
|
|
872
|
+
new Promise(resolve => {
|
|
873
|
+
setTimeout(resolve, time);
|
|
874
|
+
});
|