@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.
Files changed (110) hide show
  1. package/README.md +377 -377
  2. package/build/config.js +9 -5
  3. package/build/config.js.map +1 -1
  4. package/build/dex/algebra/algebra.js +25 -25
  5. package/build/dex/balancer-v1/balancer-v1.js +34 -34
  6. package/build/dex/balancer-v2/balancer-v2.d.ts +1 -1
  7. package/build/dex/balancer-v2/balancer-v2.js +65 -78
  8. package/build/dex/balancer-v2/balancer-v2.js.map +1 -1
  9. package/build/dex/camelot/camelot.js +27 -27
  10. package/build/dex/kyberdmm/kyberdmm.js +27 -27
  11. package/build/dex/pancakeswap-v3/pancakeswap-v3.js +25 -25
  12. package/build/dex/solidly/solidly.js +27 -27
  13. package/build/dex/solidly-v3/solidly-v3.js +25 -25
  14. package/build/dex/swaap-v2/config.js +15 -0
  15. package/build/dex/swaap-v2/config.js.map +1 -1
  16. package/build/dex/uniswap-v2/uniswap-v2.js +26 -26
  17. package/build/dex/uniswap-v3/uniswap-v3.js +25 -25
  18. package/funding.json +5 -5
  19. package/package.json +1 -1
  20. package/src/config.ts +500 -496
  21. package/src/constants.ts +67 -67
  22. package/src/dex/aave-v2/aave-v2.ts +409 -409
  23. package/src/dex/aave-v3/aave-v3.ts +320 -320
  24. package/src/dex/algebra/algebra.ts +1180 -1180
  25. package/src/dex/angle-staked-stable/angle-staked-stable.ts +347 -347
  26. package/src/dex/augustus-rfq.ts +180 -180
  27. package/src/dex/bProtocol/bProtocol.ts +108 -108
  28. package/src/dex/balancer-v1/balancer-v1.ts +572 -572
  29. package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +1699 -1699
  30. package/src/dex/balancer-v2/balancer-v2.ts +1669 -1701
  31. package/src/dex/bancor/bancor.ts +128 -128
  32. package/src/dex/camelot/camelot.ts +873 -873
  33. package/src/dex/compound/compound.ts +107 -107
  34. package/src/dex/curve-v1/config.ts +1179 -1179
  35. package/src/dex/curve-v1/curve-v1-e2e.test.ts +507 -507
  36. package/src/dex/curve-v1/curve-v1.ts +1315 -1315
  37. package/src/dex/curve-v1-factory/config.ts +1018 -1018
  38. package/src/dex/curve-v1-factory/curve-v1-factory.ts +1288 -1288
  39. package/src/dex/curve-v2/curve-v2.ts +440 -440
  40. package/src/dex/dexalot/dexalot.ts +1100 -1100
  41. package/src/dex/dodo-v1/dodo-v1.ts +132 -132
  42. package/src/dex/dodo-v2.ts +266 -266
  43. package/src/dex/etherfi.ts +239 -239
  44. package/src/dex/generic-rfq/generic-rfq.ts +431 -431
  45. package/src/dex/gmx/gmx.ts +321 -321
  46. package/src/dex/hashflow/hashflow.ts +1135 -1135
  47. package/src/dex/idex.ts +254 -254
  48. package/src/dex/index.ts +357 -357
  49. package/src/dex/jarvis-v6/jarvis-v6.ts +509 -509
  50. package/src/dex/jarvis.ts +262 -262
  51. package/src/dex/kyberdmm/kyberdmm.ts +649 -649
  52. package/src/dex/lido/lido.ts +110 -110
  53. package/src/dex/maker-psm/maker-psm.ts +592 -592
  54. package/src/dex/maverick-v1/maverick-v1.ts +537 -537
  55. package/src/dex/nerve/config.ts +635 -635
  56. package/src/dex/nerve/nerve-e2e.test.ts +731 -731
  57. package/src/dex/nerve/nerve-events.test.ts +911 -911
  58. package/src/dex/nerve/nerve.ts +467 -467
  59. package/src/dex/onebit/onebit.ts +113 -113
  60. package/src/dex/pancakeswap-v3/pancakeswap-v3.ts +1150 -1150
  61. package/src/dex/paraswap-limit-orders/config.ts +60 -60
  62. package/src/dex/paraswap-limit-orders/paraswap-limit-orders-e2e.test.ts +137 -137
  63. package/src/dex/paraswap-limit-orders/paraswap-limit-orders-integration.test.ts +328 -328
  64. package/src/dex/paraswap-limit-orders/paraswap-limit-orders.ts +631 -631
  65. package/src/dex/polygon-migrator/polygon-migrator.ts +194 -194
  66. package/src/dex/quick-perps/quick-perps.ts +337 -337
  67. package/src/dex/quickswap/config.ts +16 -16
  68. package/src/dex/quickswap/quickswap-e2e.test.ts +384 -384
  69. package/src/dex/smoothy/smoothy.ts +110 -110
  70. package/src/dex/solidly/config.ts +225 -225
  71. package/src/dex/solidly/solidly-e2e.test.ts +1421 -1421
  72. package/src/dex/solidly/solidly-integration.test.ts +1753 -1753
  73. package/src/dex/solidly/solidly.ts +687 -687
  74. package/src/dex/solidly-v3/solidly-v3.ts +968 -968
  75. package/src/dex/spark/spark.ts +284 -284
  76. package/src/dex/stable-pool/stable-pool.ts +129 -129
  77. package/src/dex/swaap-v2/config.ts +15 -0
  78. package/src/dex/swaap-v2/swaap-v2-e2e.test.ts +233 -17
  79. package/src/dex/swaap-v2/swaap-v2-integration.test.ts +1 -1
  80. package/src/dex/swaap-v2/swaap-v2.ts +1056 -1056
  81. package/src/dex/swell/swell.ts +312 -312
  82. package/src/dex/synthetix/synthetix.ts +420 -420
  83. package/src/dex/trader-joe-v2.1/trader-joe-v2.1.ts +180 -180
  84. package/src/dex/uniswap-v2/config.ts +637 -637
  85. package/src/dex/uniswap-v2/constants.ts +58 -58
  86. package/src/dex/uniswap-v2/uniswap-v2-e2e-avalanche.test.ts +1257 -1257
  87. package/src/dex/uniswap-v2/uniswap-v2-e2e-base.test.ts +42 -42
  88. package/src/dex/uniswap-v2/uniswap-v2-e2e-bsc.test.ts +1295 -1295
  89. package/src/dex/uniswap-v2/uniswap-v2-e2e-mainnet.test.ts +1834 -1834
  90. package/src/dex/uniswap-v2/uniswap-v2-e2e-polygon.test.ts +871 -871
  91. package/src/dex/uniswap-v2/uniswap-v2.ts +1117 -1117
  92. package/src/dex/uniswap-v3/uniswap-v3.ts +1501 -1501
  93. package/src/dex/uniswap-v3.ts +242 -242
  94. package/src/dex/weth/config.ts +73 -73
  95. package/src/dex/weth/weth.ts +230 -230
  96. package/src/dex/wombat/wombat.ts +467 -467
  97. package/src/dex/woo-fi-v2/woo-fi-v2.ts +529 -529
  98. package/src/dex/wsteth/wsteth.ts +304 -304
  99. package/src/dex/zerox/index.ts +328 -328
  100. package/src/executor/Executor01BytecodeBuilder.ts +474 -474
  101. package/src/executor/Executor02BytecodeBuilder.ts +1392 -1392
  102. package/src/executor/ExecutorBytecodeBuilder.ts +390 -390
  103. package/src/executor/ExecutorDetector.ts +131 -131
  104. package/src/executor/constants.ts +66 -66
  105. package/src/executor/executor01-bytecode-builder-e2e.test.ts +1478 -1478
  106. package/src/executor/utils.ts +62 -62
  107. package/src/router/simpleswap.ts +366 -366
  108. package/src/types.ts +342 -342
  109. package/tests/constants-e2e.ts +1632 -1622
  110. package/tests/generate-new-limit-order.ts +107 -107
@@ -1,592 +1,592 @@
1
- import { Interface } from '@ethersproject/abi';
2
- import { DeepReadonly } from 'ts-essentials';
3
- import { Contract } from 'web3-eth-contract';
4
- import {
5
- Token,
6
- Address,
7
- ExchangePrices,
8
- PoolPrices,
9
- Log,
10
- AdapterExchangeParam,
11
- SimpleExchangeParam,
12
- PoolLiquidity,
13
- Logger,
14
- NumberAsString,
15
- DexExchangeParam,
16
- } from '../../types';
17
- import { SwapSide, Network } from '../../constants';
18
- import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
19
- import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
20
- import { getDexKeysWithNetwork, getBigIntPow } from '../../utils';
21
- import { IDex } from '../../dex/idex';
22
- import { IDexHelper } from '../../dex-helper/idex-helper';
23
- import { MakerPsmData, PoolState, PoolConfig } from './types';
24
- import { SimpleExchange } from '../simple-exchange';
25
- import { MakerPsmConfig, Adapters } from './config';
26
- import PsmABI from '../../abi/maker-psm/psm.json';
27
- import VatABI from '../../abi/maker-psm/vat.json';
28
- import { BI_POWS } from '../../bigint-constants';
29
- import { SpecialDex } from '../../executor/types';
30
- import { hexConcat, hexZeroPad, hexlify } from '@ethersproject/bytes';
31
- import { ZEROS_12_BYTES } from '../../executor/constants';
32
-
33
- const vatInterface = new Interface(VatABI);
34
- const psmInterface = new Interface(PsmABI);
35
- const WAD = BI_POWS[18];
36
-
37
- const bigIntify = (b: any) => BigInt(b.toString());
38
- const ceilDiv = (a: bigint, b: bigint) => (a + b - 1n) / b;
39
-
40
- async function getOnChainState(
41
- multiContract: Contract,
42
- poolConfigs: PoolConfig[],
43
- vatAddress: Address,
44
- blockNumber: number | 'latest',
45
- ): Promise<PoolState[]> {
46
- const callData = poolConfigs
47
- .map(c => [
48
- {
49
- target: c.psmAddress,
50
- callData: psmInterface.encodeFunctionData('tin', []),
51
- },
52
- {
53
- target: c.psmAddress,
54
- callData: psmInterface.encodeFunctionData('tout', []),
55
- },
56
- {
57
- target: vatAddress,
58
- callData: vatInterface.encodeFunctionData('ilks', [c.identifier]),
59
- },
60
- ])
61
- .flat();
62
-
63
- const res = await multiContract.methods
64
- .aggregate(callData)
65
- .call({}, blockNumber);
66
-
67
- let i = 0;
68
- return poolConfigs.map(c => {
69
- const tin = bigIntify(
70
- psmInterface.decodeFunctionResult('tin', res.returnData[i++])[0],
71
- );
72
- const tout = bigIntify(
73
- psmInterface.decodeFunctionResult('tout', res.returnData[i++])[0],
74
- );
75
- const ilks = vatInterface.decodeFunctionResult('ilks', res.returnData[i++]);
76
- const Art = bigIntify(ilks.Art);
77
- const line = bigIntify(ilks.line);
78
- const rate = bigIntify(ilks.rate);
79
- return {
80
- tin,
81
- tout,
82
- Art,
83
- line,
84
- rate,
85
- };
86
- });
87
- }
88
-
89
- export class MakerPsmEventPool extends StatefulEventSubscriber<PoolState> {
90
- handlers: {
91
- [event: string]: (event: any, pool: PoolState, log: Log) => PoolState;
92
- } = {};
93
-
94
- logDecoder: (log: Log) => any;
95
-
96
- to18ConversionFactor: bigint;
97
- bytes32Tout =
98
- '0x746f757400000000000000000000000000000000000000000000000000000000'; // bytes32('tout')
99
- bytes32Tin =
100
- '0x74696e0000000000000000000000000000000000000000000000000000000000'; // bytes32('tin')
101
-
102
- constructor(
103
- parentName: string,
104
- protected network: number,
105
- protected dexHelper: IDexHelper,
106
- logger: Logger,
107
- public poolConfig: PoolConfig,
108
- protected vatAddress: Address,
109
- ) {
110
- super(parentName, poolConfig.identifier, dexHelper, logger);
111
-
112
- this.logDecoder = (log: Log) => psmInterface.parseLog(log);
113
- this.addressesSubscribed = [poolConfig.psmAddress];
114
- this.to18ConversionFactor = getBigIntPow(18 - poolConfig.gem.decimals);
115
-
116
- // Add handlers
117
- this.handlers['File'] = this.handleFile.bind(this);
118
- this.handlers['SellGem'] = this.handleSellGem.bind(this);
119
- this.handlers['BuyGem'] = this.handleBuyGem.bind(this);
120
- }
121
-
122
- handleFile(event: any, pool: PoolState, log: Log): PoolState {
123
- if (event.args.what === this.bytes32Tin) {
124
- pool.tin = bigIntify(event.args.data);
125
- } else if (event.args.what === this.bytes32Tout) {
126
- pool.tout = bigIntify(event.args.data);
127
- }
128
- return pool;
129
- }
130
-
131
- handleSellGem(event: any, pool: PoolState, log: Log): PoolState {
132
- pool.Art += bigIntify(event.args.value) * this.to18ConversionFactor;
133
- return pool;
134
- }
135
-
136
- handleBuyGem(event: any, pool: PoolState, log: Log): PoolState {
137
- pool.Art -= bigIntify(event.args.value) * this.to18ConversionFactor;
138
- return pool;
139
- }
140
-
141
- getIdentifier(): string {
142
- return `${this.parentName}_${this.poolConfig.psmAddress}`.toLowerCase();
143
- }
144
-
145
- /**
146
- * The function is called every time any of the subscribed
147
- * addresses release log. The function accepts the current
148
- * state, updates the state according to the log, and returns
149
- * the updated state.
150
- * @param state - Current state of event subscriber
151
- * @param log - Log released by one of the subscribed addresses
152
- * @returns Updates state of the event subscriber after the log
153
- */
154
- protected processLog(
155
- state: DeepReadonly<PoolState>,
156
- log: Readonly<Log>,
157
- ): DeepReadonly<PoolState> | null {
158
- try {
159
- const event = this.logDecoder(log);
160
- if (event.name in this.handlers) {
161
- return this.handlers[event.name](event, state, log);
162
- }
163
- return state;
164
- } catch (e) {
165
- this.logger.error(
166
- `Error_${this.parentName}_processLog could not parse the log with topic ${log.topics}:`,
167
- e,
168
- );
169
- return null;
170
- }
171
- }
172
-
173
- /**
174
- * The function generates state using on-chain calls. This
175
- * function is called to regenerate state if the event based
176
- * system fails to fetch events and the local state is no
177
- * more correct.
178
- * @param blockNumber - Blocknumber for which the state should
179
- * should be generated
180
- * @returns state of the event subscriber at blocknumber
181
- */
182
- async generateState(blockNumber: number): Promise<Readonly<PoolState>> {
183
- return (
184
- await getOnChainState(
185
- this.dexHelper.multiContract,
186
- [this.poolConfig],
187
- this.vatAddress,
188
- blockNumber,
189
- )
190
- )[0];
191
- }
192
- }
193
-
194
- export class MakerPsm extends SimpleExchange implements IDex<MakerPsmData> {
195
- protected eventPools: { [gemAddress: string]: MakerPsmEventPool };
196
-
197
- // warning: There is limit on swap
198
- readonly hasConstantPriceLargeAmounts = true;
199
- readonly isFeeOnTransferSupported = false;
200
-
201
- public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
202
- getDexKeysWithNetwork(MakerPsmConfig);
203
-
204
- logger: Logger;
205
-
206
- constructor(
207
- protected network: Network,
208
- dexKey: string,
209
- protected dexHelper: IDexHelper,
210
- protected adapters = Adapters[network],
211
- protected dai: Token = MakerPsmConfig[dexKey][network].dai,
212
- protected vatAddress: Address = MakerPsmConfig[dexKey][network].vatAddress,
213
- protected poolConfigs: PoolConfig[] = MakerPsmConfig[dexKey][network].pools,
214
- ) {
215
- super(dexHelper, dexKey);
216
- this.logger = dexHelper.getLogger(dexKey);
217
- this.eventPools = {};
218
- poolConfigs.forEach(
219
- p =>
220
- (this.eventPools[p.gem.address.toLowerCase()] = new MakerPsmEventPool(
221
- dexKey,
222
- network,
223
- dexHelper,
224
- this.logger,
225
- p,
226
- this.vatAddress,
227
- )),
228
- );
229
- }
230
-
231
- async initializePricing(blockNumber: number) {
232
- const poolStates = await getOnChainState(
233
- this.dexHelper.multiContract,
234
- this.poolConfigs,
235
- this.vatAddress,
236
- blockNumber,
237
- );
238
- await Promise.all(
239
- this.poolConfigs.map(async (p, i) => {
240
- const eventPool = this.eventPools[p.gem.address.toLowerCase()];
241
- await eventPool.initialize(blockNumber, {
242
- state: poolStates[i],
243
- });
244
- }),
245
- );
246
- }
247
-
248
- // Returns the list of contract adapters (name and index)
249
- // for a buy/sell. Return null if there are no adapters.
250
- getAdapters(side: SwapSide): { name: string; index: number }[] | null {
251
- return this.adapters[side];
252
- }
253
-
254
- getEventPool(srcToken: Token, destToken: Token): MakerPsmEventPool | null {
255
- const srcAddress = srcToken.address.toLowerCase();
256
- const destAddress = destToken.address.toLowerCase();
257
- return (
258
- (srcAddress === this.dai.address && this.eventPools[destAddress]) ||
259
- (destAddress === this.dai.address && this.eventPools[srcAddress]) ||
260
- null
261
- );
262
- }
263
-
264
- // Returns list of pool identifiers that can be used
265
- // for a given swap. poolIdentifiers must be unique
266
- // across DEXes.
267
- async getPoolIdentifiers(
268
- srcToken: Token,
269
- destToken: Token,
270
- side: SwapSide,
271
- blockNumber: number,
272
- ): Promise<string[]> {
273
- const eventPool = this.getEventPool(srcToken, destToken);
274
- if (!eventPool) return [];
275
- return [eventPool.getIdentifier()];
276
- }
277
-
278
- async getPoolState(
279
- pool: MakerPsmEventPool,
280
- blockNumber: number,
281
- ): Promise<PoolState> {
282
- const eventState = pool.getState(blockNumber);
283
- if (eventState) return eventState;
284
- const onChainState = await pool.generateState(blockNumber);
285
- pool.setState(onChainState, blockNumber);
286
- return onChainState;
287
- }
288
-
289
- computePrices(
290
- isSrcDai: boolean,
291
- to18ConversionFactor: bigint,
292
- side: SwapSide,
293
- amounts: bigint[],
294
- poolState: PoolState,
295
- ): bigint[] {
296
- const sellGemCheck = (dart: bigint) =>
297
- (dart + poolState.Art) * poolState.rate <= poolState.line;
298
- const buyGemCheck = (dart: bigint) => dart <= poolState.Art;
299
-
300
- return amounts.map(a => {
301
- if (side === SwapSide.SELL) {
302
- if (isSrcDai) {
303
- const gemAmt18 = (a * WAD) / (WAD + poolState.tout);
304
- if (buyGemCheck(gemAmt18)) return gemAmt18 / to18ConversionFactor;
305
- } else {
306
- const gemAmt18 = to18ConversionFactor * a;
307
- if (sellGemCheck(gemAmt18))
308
- return gemAmt18 - (gemAmt18 * poolState.tin) / WAD;
309
- }
310
- } else {
311
- if (isSrcDai) {
312
- const gemAmt18 = to18ConversionFactor * a;
313
- if (buyGemCheck(gemAmt18))
314
- return gemAmt18 + (gemAmt18 * poolState.tout) / WAD;
315
- } else {
316
- const gemAmt18 = (a * WAD) / (WAD - poolState.tin);
317
- if (sellGemCheck(gemAmt18)) return gemAmt18 / to18ConversionFactor;
318
- }
319
- }
320
- return 0n;
321
- });
322
- }
323
-
324
- // Returns pool prices for amounts.
325
- // If limitPools is defined only pools in limitPools
326
- // should be used. If limitPools is undefined then
327
- // any pools can be used.
328
- async getPricesVolume(
329
- srcToken: Token,
330
- destToken: Token,
331
- amounts: bigint[],
332
- side: SwapSide,
333
- blockNumber: number,
334
- limitPools?: string[],
335
- ): Promise<null | ExchangePrices<MakerPsmData>> {
336
- const eventPool = this.getEventPool(srcToken, destToken);
337
- if (!eventPool) return null;
338
-
339
- const poolIdentifier = eventPool.getIdentifier();
340
- if (limitPools && !limitPools.includes(poolIdentifier)) return null;
341
-
342
- const poolState = await this.getPoolState(eventPool, blockNumber);
343
-
344
- const unitVolume = getBigIntPow(
345
- (side === SwapSide.SELL ? srcToken : destToken).decimals,
346
- );
347
-
348
- const isSrcDai = srcToken.address.toLowerCase() === this.dai.address;
349
- const gem = isSrcDai ? destToken : srcToken;
350
- const toll =
351
- (side === SwapSide.SELL && isSrcDai) ||
352
- (side === SwapSide.BUY && !isSrcDai)
353
- ? poolState.tout
354
- : poolState.tin;
355
-
356
- const [unit, ...prices] = this.computePrices(
357
- isSrcDai,
358
- eventPool.to18ConversionFactor,
359
- side,
360
- [unitVolume, ...amounts],
361
- poolState,
362
- );
363
-
364
- return [
365
- {
366
- prices,
367
- unit,
368
- data: {
369
- toll: toll.toString(),
370
- psmAddress: eventPool.poolConfig.psmAddress,
371
- gemJoinAddress: eventPool.poolConfig.gemJoinAddress,
372
- gemDecimals: gem.decimals,
373
- },
374
- poolAddresses: [eventPool.poolConfig.psmAddress],
375
- exchange: this.dexKey,
376
- gasCost: 100 * 1000, //TODO: simulate and fix the gas cost
377
- poolIdentifier,
378
- },
379
- ];
380
- }
381
-
382
- // Returns estimated gas cost of calldata for this DEX in multiSwap
383
- getCalldataGasCost(poolPrices: PoolPrices<MakerPsmData>): number | number[] {
384
- return (
385
- CALLDATA_GAS_COST.DEX_OVERHEAD +
386
- CALLDATA_GAS_COST.LENGTH_SMALL +
387
- CALLDATA_GAS_COST.ADDRESS +
388
- // TODO: pools have toll as zero currently but may change
389
- CALLDATA_GAS_COST.ZERO +
390
- // Either 1 (18 decimals) or 1e12 = 0xe8d4a51000 (6 decimals)
391
- CALLDATA_GAS_COST.wordNonZeroBytes(
392
- poolPrices.data.gemDecimals === 6 ? 4 : 1,
393
- )
394
- );
395
- }
396
-
397
- getPsmParams(
398
- srcToken: string,
399
- srcAmount: string,
400
- destAmount: string,
401
- data: MakerPsmData,
402
- side: SwapSide,
403
- ): { isGemSell: boolean; gemAmount: string } {
404
- const isSrcDai = srcToken.toLowerCase() === this.dai.address;
405
- const to18ConversionFactor = getBigIntPow(18 - data.gemDecimals);
406
- if (side === SwapSide.SELL) {
407
- if (isSrcDai) {
408
- const gemAmt18 = (BigInt(srcAmount) * WAD) / (WAD + BigInt(data.toll));
409
- return {
410
- isGemSell: false,
411
- gemAmount: (gemAmt18 / to18ConversionFactor).toString(),
412
- };
413
- } else {
414
- return { isGemSell: true, gemAmount: srcAmount };
415
- }
416
- } else {
417
- if (isSrcDai) {
418
- return { isGemSell: false, gemAmount: destAmount };
419
- } else {
420
- const gemAmt = ceilDiv(
421
- BigInt(destAmount) * WAD,
422
- (WAD - BigInt(data.toll)) * to18ConversionFactor,
423
- );
424
- return {
425
- isGemSell: true,
426
- gemAmount: gemAmt.toString(),
427
- };
428
- }
429
- }
430
- }
431
-
432
- // Encode params required by the exchange adapter
433
- // Used for multiSwap, buy & megaSwap
434
- getAdapterParam(
435
- srcToken: string,
436
- destToken: string,
437
- srcAmount: string,
438
- destAmount: string,
439
- data: MakerPsmData,
440
- side: SwapSide,
441
- ): AdapterExchangeParam {
442
- const to18ConversionFactor = getBigIntPow(18 - data.gemDecimals);
443
- const payload = this.abiCoder.encodeParameter(
444
- {
445
- ParentStruct: {
446
- gemJoinAddress: 'address',
447
- toll: 'uint256',
448
- to18ConversionFactor: 'uint256',
449
- },
450
- },
451
- {
452
- gemJoinAddress: data.gemJoinAddress,
453
- toll: data.toll,
454
- to18ConversionFactor,
455
- },
456
- );
457
-
458
- return {
459
- targetExchange: data.psmAddress,
460
- networkFee: '0',
461
- payload,
462
- };
463
- }
464
-
465
- // Encode call data used by simpleSwap like routers
466
- // Used for simpleSwap & simpleBuy
467
- async getSimpleParam(
468
- srcToken: string,
469
- destToken: string,
470
- srcAmount: string,
471
- destAmount: string,
472
- data: MakerPsmData,
473
- side: SwapSide,
474
- ): Promise<SimpleExchangeParam> {
475
- const { isGemSell, gemAmount } = this.getPsmParams(
476
- srcToken,
477
- srcAmount,
478
- destAmount,
479
- data,
480
- side,
481
- );
482
-
483
- const swapData = psmInterface.encodeFunctionData(
484
- isGemSell ? 'sellGem' : 'buyGem',
485
- [this.augustusAddress, gemAmount],
486
- );
487
-
488
- return this.buildSimpleParamWithoutWETHConversion(
489
- srcToken,
490
- srcAmount,
491
- destToken,
492
- destAmount,
493
- swapData,
494
- data.psmAddress,
495
- isGemSell ? data.gemJoinAddress : data.psmAddress,
496
- );
497
- }
498
-
499
- getDexParam(
500
- srcToken: Address,
501
- destToken: Address,
502
- srcAmount: NumberAsString,
503
- destAmount: NumberAsString,
504
- recipient: Address,
505
- data: MakerPsmData,
506
- side: SwapSide,
507
- ): DexExchangeParam {
508
- const { isGemSell, gemAmount } = this.getPsmParams(
509
- srcToken,
510
- srcAmount,
511
- destAmount,
512
- data,
513
- side,
514
- );
515
-
516
- let exchangeData = psmInterface.encodeFunctionData(
517
- isGemSell ? 'sellGem' : 'buyGem',
518
- [recipient, gemAmount],
519
- );
520
-
521
- // append toll and to18ConversionFactor & set specialDexFlag = SWAP_ON_MAKER_PSM to
522
- // - `buyGem` on Ex1 & Ex2
523
- // - `sellGem` on Ex3
524
- let specialDexFlag = SpecialDex.DEFAULT;
525
- if (
526
- (side === SwapSide.SELL && !isGemSell) ||
527
- (side === SwapSide.BUY && isGemSell)
528
- ) {
529
- exchangeData = hexConcat([
530
- exchangeData,
531
- hexZeroPad(hexlify(BigInt(data.toll)), 32),
532
- hexZeroPad(hexlify(getBigIntPow(18 - data.gemDecimals)), 32),
533
- ]);
534
- specialDexFlag = SpecialDex.SWAP_ON_MAKER_PSM;
535
- }
536
-
537
- return {
538
- needWrapNative: this.needWrapNative,
539
- dexFuncHasRecipient: true,
540
- exchangeData,
541
- targetExchange: data.psmAddress,
542
- specialDexFlag,
543
- spender: isGemSell ? data.gemJoinAddress : data.psmAddress,
544
- returnAmountPos: undefined,
545
- };
546
- }
547
- // Returns list of top pools based on liquidity. Max
548
- // limit number pools should be returned.
549
- async getTopPoolsForToken(
550
- tokenAddress: Address,
551
- limit: number,
552
- ): Promise<PoolLiquidity[]> {
553
- const _tokenAddress = tokenAddress.toLowerCase();
554
- // Liquidity depends on the swapping side hence we simply use the min
555
- // Its always in terms of stable coin hence liquidityUSD = liquidity
556
- const minLiq = (poolState: PoolState) => {
557
- const buyLimit = poolState.Art;
558
- const sellLimit =
559
- (poolState.line - poolState.Art * poolState.rate) / poolState.rate;
560
- return (
561
- 2 *
562
- parseInt(
563
- (
564
- (buyLimit > sellLimit ? sellLimit : buyLimit) / BI_POWS[18]
565
- ).toString(),
566
- )
567
- );
568
- };
569
-
570
- const isDai = _tokenAddress === this.dai.address;
571
-
572
- const validPoolConfigs = isDai
573
- ? this.poolConfigs
574
- : this.eventPools[_tokenAddress]
575
- ? [this.eventPools[_tokenAddress].poolConfig]
576
- : [];
577
- if (!validPoolConfigs.length) return [];
578
-
579
- const poolStates = await getOnChainState(
580
- this.dexHelper.multiContract,
581
- validPoolConfigs,
582
- this.vatAddress,
583
- 'latest',
584
- );
585
- return validPoolConfigs.map((p, i) => ({
586
- exchange: this.dexKey,
587
- address: p.psmAddress,
588
- liquidityUSD: minLiq(poolStates[i]),
589
- connectorTokens: [isDai ? p.gem : this.dai],
590
- }));
591
- }
592
- }
1
+ import { Interface } from '@ethersproject/abi';
2
+ import { DeepReadonly } from 'ts-essentials';
3
+ import { Contract } from 'web3-eth-contract';
4
+ import {
5
+ Token,
6
+ Address,
7
+ ExchangePrices,
8
+ PoolPrices,
9
+ Log,
10
+ AdapterExchangeParam,
11
+ SimpleExchangeParam,
12
+ PoolLiquidity,
13
+ Logger,
14
+ NumberAsString,
15
+ DexExchangeParam,
16
+ } from '../../types';
17
+ import { SwapSide, Network } from '../../constants';
18
+ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
19
+ import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
20
+ import { getDexKeysWithNetwork, getBigIntPow } from '../../utils';
21
+ import { IDex } from '../../dex/idex';
22
+ import { IDexHelper } from '../../dex-helper/idex-helper';
23
+ import { MakerPsmData, PoolState, PoolConfig } from './types';
24
+ import { SimpleExchange } from '../simple-exchange';
25
+ import { MakerPsmConfig, Adapters } from './config';
26
+ import PsmABI from '../../abi/maker-psm/psm.json';
27
+ import VatABI from '../../abi/maker-psm/vat.json';
28
+ import { BI_POWS } from '../../bigint-constants';
29
+ import { SpecialDex } from '../../executor/types';
30
+ import { hexConcat, hexZeroPad, hexlify } from '@ethersproject/bytes';
31
+ import { ZEROS_12_BYTES } from '../../executor/constants';
32
+
33
+ const vatInterface = new Interface(VatABI);
34
+ const psmInterface = new Interface(PsmABI);
35
+ const WAD = BI_POWS[18];
36
+
37
+ const bigIntify = (b: any) => BigInt(b.toString());
38
+ const ceilDiv = (a: bigint, b: bigint) => (a + b - 1n) / b;
39
+
40
+ async function getOnChainState(
41
+ multiContract: Contract,
42
+ poolConfigs: PoolConfig[],
43
+ vatAddress: Address,
44
+ blockNumber: number | 'latest',
45
+ ): Promise<PoolState[]> {
46
+ const callData = poolConfigs
47
+ .map(c => [
48
+ {
49
+ target: c.psmAddress,
50
+ callData: psmInterface.encodeFunctionData('tin', []),
51
+ },
52
+ {
53
+ target: c.psmAddress,
54
+ callData: psmInterface.encodeFunctionData('tout', []),
55
+ },
56
+ {
57
+ target: vatAddress,
58
+ callData: vatInterface.encodeFunctionData('ilks', [c.identifier]),
59
+ },
60
+ ])
61
+ .flat();
62
+
63
+ const res = await multiContract.methods
64
+ .aggregate(callData)
65
+ .call({}, blockNumber);
66
+
67
+ let i = 0;
68
+ return poolConfigs.map(c => {
69
+ const tin = bigIntify(
70
+ psmInterface.decodeFunctionResult('tin', res.returnData[i++])[0],
71
+ );
72
+ const tout = bigIntify(
73
+ psmInterface.decodeFunctionResult('tout', res.returnData[i++])[0],
74
+ );
75
+ const ilks = vatInterface.decodeFunctionResult('ilks', res.returnData[i++]);
76
+ const Art = bigIntify(ilks.Art);
77
+ const line = bigIntify(ilks.line);
78
+ const rate = bigIntify(ilks.rate);
79
+ return {
80
+ tin,
81
+ tout,
82
+ Art,
83
+ line,
84
+ rate,
85
+ };
86
+ });
87
+ }
88
+
89
+ export class MakerPsmEventPool extends StatefulEventSubscriber<PoolState> {
90
+ handlers: {
91
+ [event: string]: (event: any, pool: PoolState, log: Log) => PoolState;
92
+ } = {};
93
+
94
+ logDecoder: (log: Log) => any;
95
+
96
+ to18ConversionFactor: bigint;
97
+ bytes32Tout =
98
+ '0x746f757400000000000000000000000000000000000000000000000000000000'; // bytes32('tout')
99
+ bytes32Tin =
100
+ '0x74696e0000000000000000000000000000000000000000000000000000000000'; // bytes32('tin')
101
+
102
+ constructor(
103
+ parentName: string,
104
+ protected network: number,
105
+ protected dexHelper: IDexHelper,
106
+ logger: Logger,
107
+ public poolConfig: PoolConfig,
108
+ protected vatAddress: Address,
109
+ ) {
110
+ super(parentName, poolConfig.identifier, dexHelper, logger);
111
+
112
+ this.logDecoder = (log: Log) => psmInterface.parseLog(log);
113
+ this.addressesSubscribed = [poolConfig.psmAddress];
114
+ this.to18ConversionFactor = getBigIntPow(18 - poolConfig.gem.decimals);
115
+
116
+ // Add handlers
117
+ this.handlers['File'] = this.handleFile.bind(this);
118
+ this.handlers['SellGem'] = this.handleSellGem.bind(this);
119
+ this.handlers['BuyGem'] = this.handleBuyGem.bind(this);
120
+ }
121
+
122
+ handleFile(event: any, pool: PoolState, log: Log): PoolState {
123
+ if (event.args.what === this.bytes32Tin) {
124
+ pool.tin = bigIntify(event.args.data);
125
+ } else if (event.args.what === this.bytes32Tout) {
126
+ pool.tout = bigIntify(event.args.data);
127
+ }
128
+ return pool;
129
+ }
130
+
131
+ handleSellGem(event: any, pool: PoolState, log: Log): PoolState {
132
+ pool.Art += bigIntify(event.args.value) * this.to18ConversionFactor;
133
+ return pool;
134
+ }
135
+
136
+ handleBuyGem(event: any, pool: PoolState, log: Log): PoolState {
137
+ pool.Art -= bigIntify(event.args.value) * this.to18ConversionFactor;
138
+ return pool;
139
+ }
140
+
141
+ getIdentifier(): string {
142
+ return `${this.parentName}_${this.poolConfig.psmAddress}`.toLowerCase();
143
+ }
144
+
145
+ /**
146
+ * The function is called every time any of the subscribed
147
+ * addresses release log. The function accepts the current
148
+ * state, updates the state according to the log, and returns
149
+ * the updated state.
150
+ * @param state - Current state of event subscriber
151
+ * @param log - Log released by one of the subscribed addresses
152
+ * @returns Updates state of the event subscriber after the log
153
+ */
154
+ protected processLog(
155
+ state: DeepReadonly<PoolState>,
156
+ log: Readonly<Log>,
157
+ ): DeepReadonly<PoolState> | null {
158
+ try {
159
+ const event = this.logDecoder(log);
160
+ if (event.name in this.handlers) {
161
+ return this.handlers[event.name](event, state, log);
162
+ }
163
+ return state;
164
+ } catch (e) {
165
+ this.logger.error(
166
+ `Error_${this.parentName}_processLog could not parse the log with topic ${log.topics}:`,
167
+ e,
168
+ );
169
+ return null;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * The function generates state using on-chain calls. This
175
+ * function is called to regenerate state if the event based
176
+ * system fails to fetch events and the local state is no
177
+ * more correct.
178
+ * @param blockNumber - Blocknumber for which the state should
179
+ * should be generated
180
+ * @returns state of the event subscriber at blocknumber
181
+ */
182
+ async generateState(blockNumber: number): Promise<Readonly<PoolState>> {
183
+ return (
184
+ await getOnChainState(
185
+ this.dexHelper.multiContract,
186
+ [this.poolConfig],
187
+ this.vatAddress,
188
+ blockNumber,
189
+ )
190
+ )[0];
191
+ }
192
+ }
193
+
194
+ export class MakerPsm extends SimpleExchange implements IDex<MakerPsmData> {
195
+ protected eventPools: { [gemAddress: string]: MakerPsmEventPool };
196
+
197
+ // warning: There is limit on swap
198
+ readonly hasConstantPriceLargeAmounts = true;
199
+ readonly isFeeOnTransferSupported = false;
200
+
201
+ public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
202
+ getDexKeysWithNetwork(MakerPsmConfig);
203
+
204
+ logger: Logger;
205
+
206
+ constructor(
207
+ protected network: Network,
208
+ dexKey: string,
209
+ protected dexHelper: IDexHelper,
210
+ protected adapters = Adapters[network],
211
+ protected dai: Token = MakerPsmConfig[dexKey][network].dai,
212
+ protected vatAddress: Address = MakerPsmConfig[dexKey][network].vatAddress,
213
+ protected poolConfigs: PoolConfig[] = MakerPsmConfig[dexKey][network].pools,
214
+ ) {
215
+ super(dexHelper, dexKey);
216
+ this.logger = dexHelper.getLogger(dexKey);
217
+ this.eventPools = {};
218
+ poolConfigs.forEach(
219
+ p =>
220
+ (this.eventPools[p.gem.address.toLowerCase()] = new MakerPsmEventPool(
221
+ dexKey,
222
+ network,
223
+ dexHelper,
224
+ this.logger,
225
+ p,
226
+ this.vatAddress,
227
+ )),
228
+ );
229
+ }
230
+
231
+ async initializePricing(blockNumber: number) {
232
+ const poolStates = await getOnChainState(
233
+ this.dexHelper.multiContract,
234
+ this.poolConfigs,
235
+ this.vatAddress,
236
+ blockNumber,
237
+ );
238
+ await Promise.all(
239
+ this.poolConfigs.map(async (p, i) => {
240
+ const eventPool = this.eventPools[p.gem.address.toLowerCase()];
241
+ await eventPool.initialize(blockNumber, {
242
+ state: poolStates[i],
243
+ });
244
+ }),
245
+ );
246
+ }
247
+
248
+ // Returns the list of contract adapters (name and index)
249
+ // for a buy/sell. Return null if there are no adapters.
250
+ getAdapters(side: SwapSide): { name: string; index: number }[] | null {
251
+ return this.adapters[side];
252
+ }
253
+
254
+ getEventPool(srcToken: Token, destToken: Token): MakerPsmEventPool | null {
255
+ const srcAddress = srcToken.address.toLowerCase();
256
+ const destAddress = destToken.address.toLowerCase();
257
+ return (
258
+ (srcAddress === this.dai.address && this.eventPools[destAddress]) ||
259
+ (destAddress === this.dai.address && this.eventPools[srcAddress]) ||
260
+ null
261
+ );
262
+ }
263
+
264
+ // Returns list of pool identifiers that can be used
265
+ // for a given swap. poolIdentifiers must be unique
266
+ // across DEXes.
267
+ async getPoolIdentifiers(
268
+ srcToken: Token,
269
+ destToken: Token,
270
+ side: SwapSide,
271
+ blockNumber: number,
272
+ ): Promise<string[]> {
273
+ const eventPool = this.getEventPool(srcToken, destToken);
274
+ if (!eventPool) return [];
275
+ return [eventPool.getIdentifier()];
276
+ }
277
+
278
+ async getPoolState(
279
+ pool: MakerPsmEventPool,
280
+ blockNumber: number,
281
+ ): Promise<PoolState> {
282
+ const eventState = pool.getState(blockNumber);
283
+ if (eventState) return eventState;
284
+ const onChainState = await pool.generateState(blockNumber);
285
+ pool.setState(onChainState, blockNumber);
286
+ return onChainState;
287
+ }
288
+
289
+ computePrices(
290
+ isSrcDai: boolean,
291
+ to18ConversionFactor: bigint,
292
+ side: SwapSide,
293
+ amounts: bigint[],
294
+ poolState: PoolState,
295
+ ): bigint[] {
296
+ const sellGemCheck = (dart: bigint) =>
297
+ (dart + poolState.Art) * poolState.rate <= poolState.line;
298
+ const buyGemCheck = (dart: bigint) => dart <= poolState.Art;
299
+
300
+ return amounts.map(a => {
301
+ if (side === SwapSide.SELL) {
302
+ if (isSrcDai) {
303
+ const gemAmt18 = (a * WAD) / (WAD + poolState.tout);
304
+ if (buyGemCheck(gemAmt18)) return gemAmt18 / to18ConversionFactor;
305
+ } else {
306
+ const gemAmt18 = to18ConversionFactor * a;
307
+ if (sellGemCheck(gemAmt18))
308
+ return gemAmt18 - (gemAmt18 * poolState.tin) / WAD;
309
+ }
310
+ } else {
311
+ if (isSrcDai) {
312
+ const gemAmt18 = to18ConversionFactor * a;
313
+ if (buyGemCheck(gemAmt18))
314
+ return gemAmt18 + (gemAmt18 * poolState.tout) / WAD;
315
+ } else {
316
+ const gemAmt18 = (a * WAD) / (WAD - poolState.tin);
317
+ if (sellGemCheck(gemAmt18)) return gemAmt18 / to18ConversionFactor;
318
+ }
319
+ }
320
+ return 0n;
321
+ });
322
+ }
323
+
324
+ // Returns pool prices for amounts.
325
+ // If limitPools is defined only pools in limitPools
326
+ // should be used. If limitPools is undefined then
327
+ // any pools can be used.
328
+ async getPricesVolume(
329
+ srcToken: Token,
330
+ destToken: Token,
331
+ amounts: bigint[],
332
+ side: SwapSide,
333
+ blockNumber: number,
334
+ limitPools?: string[],
335
+ ): Promise<null | ExchangePrices<MakerPsmData>> {
336
+ const eventPool = this.getEventPool(srcToken, destToken);
337
+ if (!eventPool) return null;
338
+
339
+ const poolIdentifier = eventPool.getIdentifier();
340
+ if (limitPools && !limitPools.includes(poolIdentifier)) return null;
341
+
342
+ const poolState = await this.getPoolState(eventPool, blockNumber);
343
+
344
+ const unitVolume = getBigIntPow(
345
+ (side === SwapSide.SELL ? srcToken : destToken).decimals,
346
+ );
347
+
348
+ const isSrcDai = srcToken.address.toLowerCase() === this.dai.address;
349
+ const gem = isSrcDai ? destToken : srcToken;
350
+ const toll =
351
+ (side === SwapSide.SELL && isSrcDai) ||
352
+ (side === SwapSide.BUY && !isSrcDai)
353
+ ? poolState.tout
354
+ : poolState.tin;
355
+
356
+ const [unit, ...prices] = this.computePrices(
357
+ isSrcDai,
358
+ eventPool.to18ConversionFactor,
359
+ side,
360
+ [unitVolume, ...amounts],
361
+ poolState,
362
+ );
363
+
364
+ return [
365
+ {
366
+ prices,
367
+ unit,
368
+ data: {
369
+ toll: toll.toString(),
370
+ psmAddress: eventPool.poolConfig.psmAddress,
371
+ gemJoinAddress: eventPool.poolConfig.gemJoinAddress,
372
+ gemDecimals: gem.decimals,
373
+ },
374
+ poolAddresses: [eventPool.poolConfig.psmAddress],
375
+ exchange: this.dexKey,
376
+ gasCost: 100 * 1000, //TODO: simulate and fix the gas cost
377
+ poolIdentifier,
378
+ },
379
+ ];
380
+ }
381
+
382
+ // Returns estimated gas cost of calldata for this DEX in multiSwap
383
+ getCalldataGasCost(poolPrices: PoolPrices<MakerPsmData>): number | number[] {
384
+ return (
385
+ CALLDATA_GAS_COST.DEX_OVERHEAD +
386
+ CALLDATA_GAS_COST.LENGTH_SMALL +
387
+ CALLDATA_GAS_COST.ADDRESS +
388
+ // TODO: pools have toll as zero currently but may change
389
+ CALLDATA_GAS_COST.ZERO +
390
+ // Either 1 (18 decimals) or 1e12 = 0xe8d4a51000 (6 decimals)
391
+ CALLDATA_GAS_COST.wordNonZeroBytes(
392
+ poolPrices.data.gemDecimals === 6 ? 4 : 1,
393
+ )
394
+ );
395
+ }
396
+
397
+ getPsmParams(
398
+ srcToken: string,
399
+ srcAmount: string,
400
+ destAmount: string,
401
+ data: MakerPsmData,
402
+ side: SwapSide,
403
+ ): { isGemSell: boolean; gemAmount: string } {
404
+ const isSrcDai = srcToken.toLowerCase() === this.dai.address;
405
+ const to18ConversionFactor = getBigIntPow(18 - data.gemDecimals);
406
+ if (side === SwapSide.SELL) {
407
+ if (isSrcDai) {
408
+ const gemAmt18 = (BigInt(srcAmount) * WAD) / (WAD + BigInt(data.toll));
409
+ return {
410
+ isGemSell: false,
411
+ gemAmount: (gemAmt18 / to18ConversionFactor).toString(),
412
+ };
413
+ } else {
414
+ return { isGemSell: true, gemAmount: srcAmount };
415
+ }
416
+ } else {
417
+ if (isSrcDai) {
418
+ return { isGemSell: false, gemAmount: destAmount };
419
+ } else {
420
+ const gemAmt = ceilDiv(
421
+ BigInt(destAmount) * WAD,
422
+ (WAD - BigInt(data.toll)) * to18ConversionFactor,
423
+ );
424
+ return {
425
+ isGemSell: true,
426
+ gemAmount: gemAmt.toString(),
427
+ };
428
+ }
429
+ }
430
+ }
431
+
432
+ // Encode params required by the exchange adapter
433
+ // Used for multiSwap, buy & megaSwap
434
+ getAdapterParam(
435
+ srcToken: string,
436
+ destToken: string,
437
+ srcAmount: string,
438
+ destAmount: string,
439
+ data: MakerPsmData,
440
+ side: SwapSide,
441
+ ): AdapterExchangeParam {
442
+ const to18ConversionFactor = getBigIntPow(18 - data.gemDecimals);
443
+ const payload = this.abiCoder.encodeParameter(
444
+ {
445
+ ParentStruct: {
446
+ gemJoinAddress: 'address',
447
+ toll: 'uint256',
448
+ to18ConversionFactor: 'uint256',
449
+ },
450
+ },
451
+ {
452
+ gemJoinAddress: data.gemJoinAddress,
453
+ toll: data.toll,
454
+ to18ConversionFactor,
455
+ },
456
+ );
457
+
458
+ return {
459
+ targetExchange: data.psmAddress,
460
+ networkFee: '0',
461
+ payload,
462
+ };
463
+ }
464
+
465
+ // Encode call data used by simpleSwap like routers
466
+ // Used for simpleSwap & simpleBuy
467
+ async getSimpleParam(
468
+ srcToken: string,
469
+ destToken: string,
470
+ srcAmount: string,
471
+ destAmount: string,
472
+ data: MakerPsmData,
473
+ side: SwapSide,
474
+ ): Promise<SimpleExchangeParam> {
475
+ const { isGemSell, gemAmount } = this.getPsmParams(
476
+ srcToken,
477
+ srcAmount,
478
+ destAmount,
479
+ data,
480
+ side,
481
+ );
482
+
483
+ const swapData = psmInterface.encodeFunctionData(
484
+ isGemSell ? 'sellGem' : 'buyGem',
485
+ [this.augustusAddress, gemAmount],
486
+ );
487
+
488
+ return this.buildSimpleParamWithoutWETHConversion(
489
+ srcToken,
490
+ srcAmount,
491
+ destToken,
492
+ destAmount,
493
+ swapData,
494
+ data.psmAddress,
495
+ isGemSell ? data.gemJoinAddress : data.psmAddress,
496
+ );
497
+ }
498
+
499
+ getDexParam(
500
+ srcToken: Address,
501
+ destToken: Address,
502
+ srcAmount: NumberAsString,
503
+ destAmount: NumberAsString,
504
+ recipient: Address,
505
+ data: MakerPsmData,
506
+ side: SwapSide,
507
+ ): DexExchangeParam {
508
+ const { isGemSell, gemAmount } = this.getPsmParams(
509
+ srcToken,
510
+ srcAmount,
511
+ destAmount,
512
+ data,
513
+ side,
514
+ );
515
+
516
+ let exchangeData = psmInterface.encodeFunctionData(
517
+ isGemSell ? 'sellGem' : 'buyGem',
518
+ [recipient, gemAmount],
519
+ );
520
+
521
+ // append toll and to18ConversionFactor & set specialDexFlag = SWAP_ON_MAKER_PSM to
522
+ // - `buyGem` on Ex1 & Ex2
523
+ // - `sellGem` on Ex3
524
+ let specialDexFlag = SpecialDex.DEFAULT;
525
+ if (
526
+ (side === SwapSide.SELL && !isGemSell) ||
527
+ (side === SwapSide.BUY && isGemSell)
528
+ ) {
529
+ exchangeData = hexConcat([
530
+ exchangeData,
531
+ hexZeroPad(hexlify(BigInt(data.toll)), 32),
532
+ hexZeroPad(hexlify(getBigIntPow(18 - data.gemDecimals)), 32),
533
+ ]);
534
+ specialDexFlag = SpecialDex.SWAP_ON_MAKER_PSM;
535
+ }
536
+
537
+ return {
538
+ needWrapNative: this.needWrapNative,
539
+ dexFuncHasRecipient: true,
540
+ exchangeData,
541
+ targetExchange: data.psmAddress,
542
+ specialDexFlag,
543
+ spender: isGemSell ? data.gemJoinAddress : data.psmAddress,
544
+ returnAmountPos: undefined,
545
+ };
546
+ }
547
+ // Returns list of top pools based on liquidity. Max
548
+ // limit number pools should be returned.
549
+ async getTopPoolsForToken(
550
+ tokenAddress: Address,
551
+ limit: number,
552
+ ): Promise<PoolLiquidity[]> {
553
+ const _tokenAddress = tokenAddress.toLowerCase();
554
+ // Liquidity depends on the swapping side hence we simply use the min
555
+ // Its always in terms of stable coin hence liquidityUSD = liquidity
556
+ const minLiq = (poolState: PoolState) => {
557
+ const buyLimit = poolState.Art;
558
+ const sellLimit =
559
+ (poolState.line - poolState.Art * poolState.rate) / poolState.rate;
560
+ return (
561
+ 2 *
562
+ parseInt(
563
+ (
564
+ (buyLimit > sellLimit ? sellLimit : buyLimit) / BI_POWS[18]
565
+ ).toString(),
566
+ )
567
+ );
568
+ };
569
+
570
+ const isDai = _tokenAddress === this.dai.address;
571
+
572
+ const validPoolConfigs = isDai
573
+ ? this.poolConfigs
574
+ : this.eventPools[_tokenAddress]
575
+ ? [this.eventPools[_tokenAddress].poolConfig]
576
+ : [];
577
+ if (!validPoolConfigs.length) return [];
578
+
579
+ const poolStates = await getOnChainState(
580
+ this.dexHelper.multiContract,
581
+ validPoolConfigs,
582
+ this.vatAddress,
583
+ 'latest',
584
+ );
585
+ return validPoolConfigs.map((p, i) => ({
586
+ exchange: this.dexKey,
587
+ address: p.psmAddress,
588
+ liquidityUSD: minLiq(poolStates[i]),
589
+ connectorTokens: [isDai ? p.gem : this.dai],
590
+ }));
591
+ }
592
+ }