@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.
Files changed (197) hide show
  1. package/build/abi/Aave_GSM.json +751 -0
  2. package/build/abi/USDM.json +807 -0
  3. package/build/abi/{integral/pool.json → cables/CablesMainnetRFQ.json} +553 -521
  4. package/build/abi/inception/inception-ineth-pool.json +1062 -0
  5. package/build/abi/inception/inception-ineth.json +609 -0
  6. package/build/abi/inception/inception-ratio-feed.json +329 -0
  7. package/build/abi/inception/inception-vault.json +991 -0
  8. package/build/config.js +5 -0
  9. package/build/config.js.map +1 -1
  10. package/build/dex/aave-v1/aave-v1.d.ts +2 -1
  11. package/build/dex/aave-v1/aave-v1.js +22 -0
  12. package/build/dex/aave-v1/aave-v1.js.map +1 -1
  13. package/build/dex/bebop/bebop.d.ts +0 -1
  14. package/build/dex/bebop/bebop.js +46 -22
  15. package/build/dex/bebop/bebop.js.map +1 -1
  16. package/build/dex/bebop/constants.d.ts +4 -0
  17. package/build/dex/bebop/constants.js +5 -1
  18. package/build/dex/bebop/constants.js.map +1 -1
  19. package/build/dex/bebop/types.d.ts +5 -0
  20. package/build/dex/bebop/types.js.map +1 -1
  21. package/build/dex/bebop/websocket-fetcher.d.ts +35 -0
  22. package/build/dex/bebop/websocket-fetcher.js +87 -0
  23. package/build/dex/bebop/websocket-fetcher.js.map +1 -0
  24. package/build/dex/cables/cables.d.ts +64 -0
  25. package/build/dex/cables/cables.js +445 -0
  26. package/build/dex/cables/cables.js.map +1 -0
  27. package/build/dex/cables/config.d.ts +4 -0
  28. package/build/dex/cables/config.js +15 -0
  29. package/build/dex/cables/config.js.map +1 -0
  30. package/build/dex/cables/constants.d.ts +19 -0
  31. package/build/dex/cables/constants.js +26 -0
  32. package/build/dex/cables/constants.js.map +1 -0
  33. package/build/dex/cables/rate-fetcher.d.ts +34 -0
  34. package/build/dex/cables/rate-fetcher.js +117 -0
  35. package/build/dex/cables/rate-fetcher.js.map +1 -0
  36. package/build/dex/cables/types.d.ts +106 -0
  37. package/build/dex/cables/types.js +12 -0
  38. package/build/dex/cables/types.js.map +1 -0
  39. package/build/dex/cables/validators.d.ts +5 -0
  40. package/build/dex/cables/validators.js +49 -0
  41. package/build/dex/cables/validators.js.map +1 -0
  42. package/build/dex/idle-dao/idle-dao-pool.d.ts +56 -0
  43. package/build/dex/idle-dao/idle-dao-pool.js +176 -0
  44. package/build/dex/idle-dao/idle-dao-pool.js.map +1 -0
  45. package/build/dex/idle-dao/idle-dao-pooling-pool.d.ts +16 -0
  46. package/build/dex/idle-dao/idle-dao-pooling-pool.js +57 -0
  47. package/build/dex/idle-dao/idle-dao-pooling-pool.js.map +1 -0
  48. package/build/dex/idle-dao/scripts.d.ts +9 -0
  49. package/build/dex/idle-dao/scripts.js +552 -0
  50. package/build/dex/idle-dao/scripts.js.map +1 -0
  51. package/build/dex/inception/config.d.ts +4 -0
  52. package/build/dex/inception/config.js +109 -0
  53. package/build/dex/inception/config.js.map +1 -0
  54. package/build/dex/inception/inception-event-pool.d.ts +16 -0
  55. package/build/dex/inception/inception-event-pool.js +51 -0
  56. package/build/dex/inception/inception-event-pool.js.map +1 -0
  57. package/build/dex/{integral/integral.d.ts → inception/inception-native.d.ts} +21 -19
  58. package/build/dex/inception/inception-native.js +131 -0
  59. package/build/dex/inception/inception-native.js.map +1 -0
  60. package/build/dex/inception/inception-pool.d.ts +18 -0
  61. package/build/dex/inception/inception-pool.js +32 -0
  62. package/build/dex/inception/inception-pool.js.map +1 -0
  63. package/build/dex/inception/inception-price-feed.d.ts +19 -0
  64. package/build/dex/inception/inception-price-feed.js +51 -0
  65. package/build/dex/inception/inception-price-feed.js.map +1 -0
  66. package/build/dex/inception/inception.d.ts +43 -0
  67. package/build/dex/inception/inception.js +204 -0
  68. package/build/dex/inception/inception.js.map +1 -0
  69. package/build/dex/inception/tokens.d.ts +9 -0
  70. package/build/dex/inception/tokens.js +28 -0
  71. package/build/dex/inception/tokens.js.map +1 -0
  72. package/build/dex/inception/types.d.ts +22 -0
  73. package/build/dex/inception/types.js +3 -0
  74. package/build/dex/inception/types.js.map +1 -0
  75. package/build/dex/inception/utils.d.ts +6 -0
  76. package/build/dex/inception/utils.js +37 -0
  77. package/build/dex/inception/utils.js.map +1 -0
  78. package/build/dex/index.js +2 -0
  79. package/build/dex/index.js.map +1 -1
  80. package/build/dex/jarvis.d.ts +2 -1
  81. package/build/dex/jarvis.js +58 -0
  82. package/build/dex/jarvis.js.map +1 -1
  83. package/build/dex/platypus/platypus.d.ts +2 -1
  84. package/build/dex/platypus/platypus.js +31 -0
  85. package/build/dex/platypus/platypus.js.map +1 -1
  86. package/build/dex/swaap-v1/swaap-v1.d.ts +2 -1
  87. package/build/dex/swaap-v1/swaap-v1.js +10 -0
  88. package/build/dex/swaap-v1/swaap-v1.js.map +1 -1
  89. package/build/dex/zerox/index.d.ts +4 -26
  90. package/build/dex/zerox/index.js +50 -42
  91. package/build/dex/zerox/index.js.map +1 -1
  92. package/build/dex/zerox/types.d.ts +29 -0
  93. package/build/dex/zerox/types.js +8 -1
  94. package/build/dex/zerox/types.js.map +1 -1
  95. package/build/executor/Executor01BytecodeBuilder.js +3 -2
  96. package/build/executor/Executor01BytecodeBuilder.js.map +1 -1
  97. package/build/executor/Executor02BytecodeBuilder.js +3 -2
  98. package/build/executor/Executor02BytecodeBuilder.js.map +1 -1
  99. package/build/executor/Executor03BytecodeBuilder.js +3 -2
  100. package/build/executor/Executor03BytecodeBuilder.js.map +1 -1
  101. package/build/types.d.ts +1 -0
  102. package/package.json +4 -2
  103. package/src/abi/bebop/BebopSettlement.abi.json +2137 -0
  104. package/src/config.ts +6 -0
  105. package/src/dex/bebop/bebop-e2e.test.ts +195 -0
  106. package/src/dex/bebop/bebop-integration.test.ts +170 -0
  107. package/src/dex/bebop/bebop.ts +874 -0
  108. package/src/dex/bebop/config.ts +37 -0
  109. package/src/dex/bebop/constants.ts +21 -0
  110. package/src/dex/bebop/rate-fetcher.ts +138 -0
  111. package/src/dex/bebop/types.ts +95 -0
  112. package/src/dex/bebop/validators.ts +29 -0
  113. package/src/dex/index.ts +2 -0
  114. package/src/executor/Executor01BytecodeBuilder.ts +3 -2
  115. package/src/executor/Executor02BytecodeBuilder.ts +3 -2
  116. package/src/executor/Executor03BytecodeBuilder.ts +3 -2
  117. package/src/lib/fetcher/wsFetcher.ts +145 -0
  118. package/src/types.ts +1 -0
  119. package/.idea/aws.xml +0 -17
  120. package/.idea/codeStyles/Project.xml +0 -19
  121. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  122. package/.idea/misc.xml +0 -6
  123. package/.idea/modules.xml +0 -8
  124. package/.idea/paraswap-dex-lib.iml +0 -9
  125. package/.idea/prettier.xml +0 -7
  126. package/.idea/vcs.xml +0 -6
  127. package/build/abi/integral/factory.json +0 -333
  128. package/build/abi/integral/oracle.json +0 -501
  129. package/build/abi/integral/relayer.json +0 -1536
  130. package/build/dex/bProtocol.d.ts +0 -18
  131. package/build/dex/bProtocol.js +0 -39
  132. package/build/dex/bProtocol.js.map +0 -1
  133. package/build/dex/bancor.d.ts +0 -26
  134. package/build/dex/bancor.js +0 -58
  135. package/build/dex/bancor.js.map +0 -1
  136. package/build/dex/compound.d.ts +0 -18
  137. package/build/dex/compound.js +0 -46
  138. package/build/dex/compound.js.map +0 -1
  139. package/build/dex/curve-v2.d.ts +0 -58
  140. package/build/dex/curve-v2.js +0 -180
  141. package/build/dex/curve-v2.js.map +0 -1
  142. package/build/dex/dodo-v1.d.ts +0 -33
  143. package/build/dex/dodo-v1.js +0 -63
  144. package/build/dex/dodo-v1.js.map +0 -1
  145. package/build/dex/integral/config.d.ts +0 -4
  146. package/build/dex/integral/config.js +0 -20
  147. package/build/dex/integral/config.js.map +0 -1
  148. package/build/dex/integral/context.d.ts +0 -40
  149. package/build/dex/integral/context.js +0 -158
  150. package/build/dex/integral/context.js.map +0 -1
  151. package/build/dex/integral/integral-factory.d.ts +0 -24
  152. package/build/dex/integral/integral-factory.js +0 -95
  153. package/build/dex/integral/integral-factory.js.map +0 -1
  154. package/build/dex/integral/integral-pool.d.ts +0 -50
  155. package/build/dex/integral/integral-pool.js +0 -149
  156. package/build/dex/integral/integral-pool.js.map +0 -1
  157. package/build/dex/integral/integral-pricing.d.ts +0 -18
  158. package/build/dex/integral/integral-pricing.js +0 -114
  159. package/build/dex/integral/integral-pricing.js.map +0 -1
  160. package/build/dex/integral/integral-relayer.d.ts +0 -42
  161. package/build/dex/integral/integral-relayer.js +0 -192
  162. package/build/dex/integral/integral-relayer.js.map +0 -1
  163. package/build/dex/integral/integral-token.d.ts +0 -27
  164. package/build/dex/integral/integral-token.js +0 -59
  165. package/build/dex/integral/integral-token.js.map +0 -1
  166. package/build/dex/integral/integral.js +0 -376
  167. package/build/dex/integral/integral.js.map +0 -1
  168. package/build/dex/integral/types.d.ts +0 -85
  169. package/build/dex/integral/types.js +0 -9
  170. package/build/dex/integral/types.js.map +0 -1
  171. package/build/dex/integral/utils-e2e.d.ts +0 -9
  172. package/build/dex/integral/utils-e2e.js +0 -131
  173. package/build/dex/integral/utils-e2e.js.map +0 -1
  174. package/build/dex/integral/utils.d.ts +0 -17
  175. package/build/dex/integral/utils.js +0 -94
  176. package/build/dex/integral/utils.js.map +0 -1
  177. package/build/dex/lido.d.ts +0 -19
  178. package/build/dex/lido.js +0 -42
  179. package/build/dex/lido.js.map +0 -1
  180. package/build/dex/onebit.d.ts +0 -25
  181. package/build/dex/onebit.js +0 -42
  182. package/build/dex/onebit.js.map +0 -1
  183. package/build/dex/smoothy.d.ts +0 -26
  184. package/build/dex/smoothy.js +0 -48
  185. package/build/dex/smoothy.js.map +0 -1
  186. package/build/dex/stable-pool.d.ts +0 -28
  187. package/build/dex/stable-pool.js +0 -62
  188. package/build/dex/stable-pool.js.map +0 -1
  189. package/build/dex/trader-joe-v2.1/optimizer.d.ts +0 -2
  190. package/build/dex/trader-joe-v2.1/optimizer.js +0 -47
  191. package/build/dex/trader-joe-v2.1/optimizer.js.map +0 -1
  192. package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.d.ts +0 -7
  193. package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.js +0 -20
  194. package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.js.map +0 -1
  195. package/build/dex/trader-joe-v2.1.d.ts +0 -43
  196. package/build/dex/trader-joe-v2.1.js +0 -79
  197. 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
+ });