@paraswap/dex-lib 3.8.37 → 3.8.39-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 (107) hide show
  1. package/build/abi/USDM.json +807 -0
  2. package/build/abi/bebop/BebopSettlement.abi.json +2137 -0
  3. package/build/abi/cables/CablesMainnetRFQ.json +1073 -0
  4. package/build/abi/wUSDM.json +757 -0
  5. package/build/config.js +7 -0
  6. package/build/config.js.map +1 -1
  7. package/build/dex/bebop/bebop.d.ts +55 -0
  8. package/build/dex/bebop/bebop.js +524 -0
  9. package/build/dex/bebop/bebop.js.map +1 -0
  10. package/build/dex/bebop/config.d.ts +3 -0
  11. package/build/dex/bebop/config.js +38 -0
  12. package/build/dex/bebop/config.js.map +1 -0
  13. package/build/dex/bebop/constants.d.ts +9 -0
  14. package/build/dex/bebop/constants.js +13 -0
  15. package/build/dex/bebop/constants.js.map +1 -0
  16. package/build/dex/bebop/rate-fetcher.d.ts +22 -0
  17. package/build/dex/bebop/rate-fetcher.js +80 -0
  18. package/build/dex/bebop/rate-fetcher.js.map +1 -0
  19. package/build/dex/bebop/types.d.ts +76 -0
  20. package/build/dex/bebop/types.js +3 -0
  21. package/build/dex/bebop/types.js.map +1 -0
  22. package/build/dex/bebop/validators.d.ts +4 -0
  23. package/build/dex/bebop/validators.js +42 -0
  24. package/build/dex/bebop/validators.js.map +1 -0
  25. package/build/dex/bebop/websocket-fetcher.d.ts +35 -0
  26. package/build/dex/bebop/websocket-fetcher.js +87 -0
  27. package/build/dex/bebop/websocket-fetcher.js.map +1 -0
  28. package/build/dex/cables/cables.d.ts +64 -0
  29. package/build/dex/cables/cables.js +445 -0
  30. package/build/dex/cables/cables.js.map +1 -0
  31. package/build/dex/cables/config.d.ts +4 -0
  32. package/build/dex/cables/config.js +15 -0
  33. package/build/dex/cables/config.js.map +1 -0
  34. package/build/dex/cables/constants.d.ts +19 -0
  35. package/build/dex/cables/constants.js +26 -0
  36. package/build/dex/cables/constants.js.map +1 -0
  37. package/build/dex/cables/rate-fetcher.d.ts +34 -0
  38. package/build/dex/cables/rate-fetcher.js +117 -0
  39. package/build/dex/cables/rate-fetcher.js.map +1 -0
  40. package/build/dex/cables/types.d.ts +106 -0
  41. package/build/dex/cables/types.js +12 -0
  42. package/build/dex/cables/types.js.map +1 -0
  43. package/build/dex/cables/validators.d.ts +5 -0
  44. package/build/dex/cables/validators.js +49 -0
  45. package/build/dex/cables/validators.js.map +1 -0
  46. package/build/dex/index.js +2 -0
  47. package/build/dex/index.js.map +1 -1
  48. package/build/dex/spark/config.js +25 -0
  49. package/build/dex/spark/config.js.map +1 -1
  50. package/build/dex/spark/scripts/validate-state.js +1 -1
  51. package/build/dex/spark/scripts/validate-state.js.map +1 -1
  52. package/build/dex/spark/spark-sdai-pool.d.ts +7 -3
  53. package/build/dex/spark/spark-sdai-pool.js +24 -28
  54. package/build/dex/spark/spark-sdai-pool.js.map +1 -1
  55. package/build/dex/spark/spark.d.ts +10 -5
  56. package/build/dex/spark/spark.js +46 -31
  57. package/build/dex/spark/spark.js.map +1 -1
  58. package/build/dex/spark/types.d.ts +6 -0
  59. package/build/dex/spark/types.js.map +1 -1
  60. package/build/dex/spark/utils.d.ts +3 -1
  61. package/build/dex/spark/utils.js +43 -7
  62. package/build/dex/spark/utils.js.map +1 -1
  63. package/build/dex/woo-fi-v2/woo-fi-v2-pool.d.ts +2 -2
  64. package/build/dex/woo-fi-v2/woo-fi-v2-pool.js +30 -9
  65. package/build/dex/woo-fi-v2/woo-fi-v2-pool.js.map +1 -1
  66. package/build/dex/woo-fi-v2/woo-fi-v2.js +1 -1
  67. package/build/dex/woo-fi-v2/woo-fi-v2.js.map +1 -1
  68. package/build/dex/wusdm/config.d.ts +3 -0
  69. package/build/dex/wusdm/config.js +29 -0
  70. package/build/dex/wusdm/config.js.map +1 -0
  71. package/build/dex/wusdm/types.d.ts +10 -0
  72. package/build/dex/wusdm/types.js +3 -0
  73. package/build/dex/wusdm/types.js.map +1 -0
  74. package/build/dex/wusdm/wusdm.d.ts +39 -0
  75. package/build/dex/wusdm/wusdm.js +212 -0
  76. package/build/dex/wusdm/wusdm.js.map +1 -0
  77. package/build/lib/fetcher/wsFetcher.d.ts +35 -0
  78. package/build/lib/fetcher/wsFetcher.js +87 -0
  79. package/build/lib/fetcher/wsFetcher.js.map +1 -0
  80. package/build/types.d.ts +1 -0
  81. package/package.json +4 -2
  82. package/src/abi/bebop/BebopSettlement.abi.json +2137 -0
  83. package/src/abi/sdai/SavingsUSDS.abi.json +720 -0
  84. package/src/config.ts +8 -0
  85. package/src/dex/bebop/bebop-e2e.test.ts +195 -0
  86. package/src/dex/bebop/bebop-integration.test.ts +170 -0
  87. package/src/dex/bebop/bebop.ts +732 -0
  88. package/src/dex/bebop/config.ts +37 -0
  89. package/src/dex/bebop/constants.ts +9 -0
  90. package/src/dex/bebop/rate-fetcher.ts +138 -0
  91. package/src/dex/bebop/types.ts +81 -0
  92. package/src/dex/bebop/validators.ts +43 -0
  93. package/src/dex/index.ts +2 -0
  94. package/src/dex/spark/config.ts +24 -0
  95. package/src/dex/spark/scripts/validate-state.ts +1 -0
  96. package/src/dex/spark/spark-e2e.test.ts +25 -21
  97. package/src/dex/spark/spark-events.test.ts +77 -62
  98. package/src/dex/spark/spark-integration.test.ts +290 -7
  99. package/src/dex/spark/spark-sdai-pool.ts +22 -19
  100. package/src/dex/spark/spark.ts +62 -36
  101. package/src/dex/spark/types.ts +6 -0
  102. package/src/dex/spark/utils.ts +83 -19
  103. package/src/dex/woo-fi-v2/woo-fi-v2-pool.ts +58 -27
  104. package/src/dex/woo-fi-v2/woo-fi-v2.ts +1 -2
  105. package/src/lib/fetcher/wsFetcher.ts +126 -0
  106. package/src/types.ts +1 -0
  107. package/tests/constants-e2e.ts +5 -0
@@ -7,23 +7,25 @@ import { checkPoolPrices, checkPoolsLiquidity } from '../../../tests/utils';
7
7
  import { Tokens } from '../../../tests/constants-e2e';
8
8
  import { BI_POWS } from '../../bigint-constants';
9
9
  import { Spark } from './spark';
10
+ import { MultiCallParams } from '../../lib/multi-wrapper';
11
+ import { uint256ToBigInt } from '../../lib/decoders';
10
12
 
11
13
  const network = Network.MAINNET;
12
14
 
13
- const SDaiSymbol = 'sDAI';
14
- const SDaiToken = Tokens[network][SDaiSymbol];
15
-
16
- const DaiSymbol = 'DAI';
17
- const DaiToken = Tokens[network][DaiSymbol];
18
-
19
15
  const amounts = [0n, BI_POWS[18], 2000000000000000000n];
20
16
 
21
- const dexKey = 'Spark';
22
17
  const dexHelper = new DummyDexHelper(network);
23
18
  let blocknumber: number;
24
19
  let spark: Spark;
25
20
 
26
21
  describe('Spark', function () {
22
+ const dexKey = 'Spark';
23
+ const SDaiSymbol = 'sDAI';
24
+ const SDaiToken = Tokens[network][SDaiSymbol];
25
+
26
+ const DaiSymbol = 'DAI';
27
+ const DaiToken = Tokens[network][DaiSymbol];
28
+
27
29
  beforeAll(async () => {
28
30
  blocknumber = await dexHelper.web3Provider.eth.getBlockNumber();
29
31
  spark = new Spark(network, dexKey, dexHelper);
@@ -149,3 +151,284 @@ describe('Spark', function () {
149
151
  checkPoolsLiquidity(poolLiquidity, SDaiToken.address, dexKey);
150
152
  });
151
153
  });
154
+
155
+ describe('sUSDS', function () {
156
+ const dexKey = 'sUSDS';
157
+ const SDaiSymbol = 'sUSDS';
158
+ const SDaiToken = Tokens[network][SDaiSymbol];
159
+
160
+ const DaiSymbol = 'USDS';
161
+ const DaiToken = Tokens[network][DaiSymbol];
162
+
163
+ beforeAll(async () => {
164
+ blocknumber = await dexHelper.web3Provider.eth.getBlockNumber();
165
+ spark = new Spark(network, dexKey, dexHelper);
166
+ if (spark.initializePricing) {
167
+ await spark.initializePricing(blocknumber);
168
+ }
169
+ });
170
+
171
+ it('getPoolIdentifiers and getPricesVolume USDS -> sUSDS SELL', async function () {
172
+ const pools = await spark.getPoolIdentifiers(
173
+ DaiToken,
174
+ SDaiToken,
175
+ SwapSide.SELL,
176
+ blocknumber,
177
+ );
178
+ console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Identifiers: `, pools);
179
+
180
+ expect(pools.length).toBeGreaterThan(0);
181
+
182
+ const poolPrices = await spark.getPricesVolume(
183
+ DaiToken,
184
+ SDaiToken,
185
+ amounts,
186
+ SwapSide.SELL,
187
+ blocknumber,
188
+ pools,
189
+ );
190
+ console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Prices: `, poolPrices);
191
+
192
+ const onChainPrices = await Promise.all(
193
+ amounts.map(async amount => {
194
+ const callData: MultiCallParams<bigint>[] = [
195
+ {
196
+ target: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD',
197
+ callData: spark.abiInterface.encodeFunctionData('previewDeposit', [
198
+ amount,
199
+ ]),
200
+ decodeFunction: uint256ToBigInt,
201
+ },
202
+ ];
203
+
204
+ const results = await spark.dexHelper.multiWrapper.aggregate<bigint>(
205
+ callData,
206
+ blocknumber,
207
+ );
208
+ return results[0];
209
+ }),
210
+ );
211
+
212
+ console.log('On-chain price:', onChainPrices);
213
+
214
+ const chiCalldata: MultiCallParams<bigint>[] = [
215
+ {
216
+ target: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD',
217
+ callData: spark.abiInterface.encodeFunctionData('chi', []),
218
+ decodeFunction: uint256ToBigInt,
219
+ },
220
+ ];
221
+
222
+ const results = await spark.dexHelper.multiWrapper.aggregate<bigint>(
223
+ chiCalldata,
224
+ blocknumber,
225
+ );
226
+ console.log(`ON_CHAIN_CHI at block=${blocknumber}`, results[0]);
227
+
228
+ expect(poolPrices).not.toBeNull();
229
+ expect(poolPrices?.[0].prices).toEqual(onChainPrices);
230
+ checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey);
231
+ });
232
+
233
+ it('getPoolIdentifiers and getPricesVolume sUSDS -> USDS SELL', async function () {
234
+ const pools = await spark.getPoolIdentifiers(
235
+ SDaiToken,
236
+ DaiToken,
237
+ SwapSide.SELL,
238
+ blocknumber,
239
+ );
240
+ console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Identifiers: `, pools);
241
+
242
+ expect(pools.length).toBeGreaterThan(0);
243
+
244
+ const poolPrices = await spark.getPricesVolume(
245
+ SDaiToken,
246
+ DaiToken,
247
+ amounts,
248
+ SwapSide.SELL,
249
+ blocknumber,
250
+ pools,
251
+ );
252
+ console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Prices: `, poolPrices);
253
+
254
+ const onChainPrices = await Promise.all(
255
+ amounts.map(async amount => {
256
+ const callData: MultiCallParams<bigint>[] = [
257
+ {
258
+ target: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD',
259
+ callData: spark.abiInterface.encodeFunctionData('previewRedeem', [
260
+ amount,
261
+ ]),
262
+ decodeFunction: uint256ToBigInt,
263
+ },
264
+ ];
265
+
266
+ const results = await spark.dexHelper.multiWrapper.aggregate<bigint>(
267
+ callData,
268
+ blocknumber,
269
+ );
270
+ return results[0];
271
+ }),
272
+ );
273
+
274
+ console.log('On-chain price:', onChainPrices);
275
+
276
+ const chiCalldata: MultiCallParams<bigint>[] = [
277
+ {
278
+ target: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD',
279
+ callData: spark.abiInterface.encodeFunctionData('chi', []),
280
+ decodeFunction: uint256ToBigInt,
281
+ },
282
+ ];
283
+
284
+ const results = await spark.dexHelper.multiWrapper.aggregate<bigint>(
285
+ chiCalldata,
286
+ blocknumber,
287
+ );
288
+ console.log('ON_CHAIN_CHI:', results[0]);
289
+
290
+ expect(poolPrices).not.toBeNull();
291
+ expect(poolPrices?.[0].prices).toEqual(onChainPrices);
292
+ checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey);
293
+ });
294
+
295
+ it('getPoolIdentifiers and getPricesVolume USDS -> sUSDS BUY', async function () {
296
+ const pools = await spark.getPoolIdentifiers(
297
+ DaiToken,
298
+ SDaiToken,
299
+ SwapSide.BUY,
300
+ blocknumber,
301
+ );
302
+ console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Identifiers: `, pools);
303
+
304
+ expect(pools.length).toBeGreaterThan(0);
305
+
306
+ const poolPrices = await spark.getPricesVolume(
307
+ DaiToken,
308
+ SDaiToken,
309
+ amounts,
310
+ SwapSide.BUY,
311
+ blocknumber,
312
+ pools,
313
+ );
314
+ console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Prices: `, poolPrices);
315
+
316
+ const onChainPrices = await Promise.all(
317
+ amounts.map(async amount => {
318
+ const callData: MultiCallParams<bigint>[] = [
319
+ {
320
+ target: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD',
321
+ callData: spark.abiInterface.encodeFunctionData('previewMint', [
322
+ amount,
323
+ ]),
324
+ decodeFunction: uint256ToBigInt,
325
+ },
326
+ ];
327
+
328
+ const results = await spark.dexHelper.multiWrapper.aggregate<bigint>(
329
+ callData,
330
+ blocknumber,
331
+ );
332
+ return results[0];
333
+ }),
334
+ );
335
+
336
+ console.log('On-chain price:', onChainPrices);
337
+
338
+ const chiCalldata: MultiCallParams<bigint>[] = [
339
+ {
340
+ target: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD',
341
+ callData: spark.abiInterface.encodeFunctionData('chi', []),
342
+ decodeFunction: uint256ToBigInt,
343
+ },
344
+ ];
345
+
346
+ const results = await spark.dexHelper.multiWrapper.aggregate<bigint>(
347
+ chiCalldata,
348
+ blocknumber,
349
+ );
350
+ console.log('ON_CHAIN_CHI:', results[0]);
351
+ expect(poolPrices).not.toBeNull();
352
+ expect(poolPrices?.[0].prices).toEqual(onChainPrices);
353
+ checkPoolPrices(poolPrices!, amounts, SwapSide.BUY, dexKey);
354
+ });
355
+
356
+ it('getPoolIdentifiers and getPricesVolume sUSDS -> USDS BUY', async function () {
357
+ const pools = await spark.getPoolIdentifiers(
358
+ SDaiToken,
359
+ DaiToken,
360
+ SwapSide.BUY,
361
+ blocknumber,
362
+ );
363
+ console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Identifiers: `, pools);
364
+
365
+ expect(pools.length).toBeGreaterThan(0);
366
+
367
+ const poolPrices = await spark.getPricesVolume(
368
+ SDaiToken,
369
+ DaiToken,
370
+ amounts,
371
+ SwapSide.BUY,
372
+ blocknumber,
373
+ pools,
374
+ );
375
+ console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Prices: `, poolPrices);
376
+
377
+ const onChainPrices = await Promise.all(
378
+ amounts.map(async amount => {
379
+ const callData: MultiCallParams<bigint>[] = [
380
+ {
381
+ target: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD',
382
+ callData: spark.abiInterface.encodeFunctionData('previewWithdraw', [
383
+ amount,
384
+ ]),
385
+ decodeFunction: uint256ToBigInt,
386
+ },
387
+ ];
388
+
389
+ const results = await spark.dexHelper.multiWrapper.aggregate<bigint>(
390
+ callData,
391
+ blocknumber,
392
+ );
393
+ return results[0];
394
+ }),
395
+ );
396
+
397
+ console.log('On-chain price:', onChainPrices);
398
+
399
+ const chiCalldata: MultiCallParams<bigint>[] = [
400
+ {
401
+ target: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD',
402
+ callData: spark.abiInterface.encodeFunctionData('chi', []),
403
+ decodeFunction: uint256ToBigInt,
404
+ },
405
+ ];
406
+
407
+ const results = await spark.dexHelper.multiWrapper.aggregate<bigint>(
408
+ chiCalldata,
409
+ blocknumber,
410
+ );
411
+ console.log('ON_CHAIN_CHI:', results[0]);
412
+
413
+ expect(poolPrices).not.toBeNull();
414
+ expect(poolPrices?.[0].prices).toEqual(onChainPrices);
415
+ checkPoolPrices(poolPrices!, amounts, SwapSide.BUY, dexKey);
416
+ });
417
+
418
+ it('USDS getTopPoolsForToken', async function () {
419
+ const poolLiquidity = await spark.getTopPoolsForToken(DaiToken.address, 10);
420
+ console.log(`${DaiSymbol} Top Pools:`, poolLiquidity);
421
+
422
+ checkPoolsLiquidity(poolLiquidity, DaiToken.address, dexKey);
423
+ });
424
+
425
+ it('sUSDS getTopPoolsForToken', async function () {
426
+ const poolLiquidity = await spark.getTopPoolsForToken(
427
+ SDaiToken.address,
428
+ 10,
429
+ );
430
+ console.log(`${SDaiSymbol} Top Pools:`, poolLiquidity);
431
+
432
+ checkPoolsLiquidity(poolLiquidity, SDaiToken.address, dexKey);
433
+ });
434
+ });
@@ -7,8 +7,10 @@ import type { AsyncOrSync, DeepReadonly } from 'ts-essentials';
7
7
  import type { Address, BlockHeader, Log, Logger } from '../../types';
8
8
  import type { SparkSDaiPoolState } from './types';
9
9
  import { getOnChainState } from './utils';
10
+ import { SDaiConfig } from './config';
11
+ import { Network } from '../../constants';
10
12
 
11
- const RAY = BI_POWS[27];
13
+ export const RAY = BI_POWS[27];
12
14
  const ZERO = BigInt(0);
13
15
  const TWO = BigInt(2);
14
16
  const HALF = RAY / TWO;
@@ -17,10 +19,6 @@ const HALF = RAY / TWO;
17
19
  const FILE_TOPICHASH = `0x29ae811400000000000000000000000000000000000000000000000000000000`;
18
20
  // function drip()
19
21
  const DRIP_TOPICHASH = `0x9f678cca00000000000000000000000000000000000000000000000000000000`;
20
- // function cage()
21
- const CAGE_TOPICHASH = `0x6924500900000000000000000000000000000000000000000000000000000000`;
22
- // bytes32 repr of "dsr" string
23
- const DSR_TOPIC = `0x6473720000000000000000000000000000000000000000000000000000000000`;
24
22
 
25
23
  const rpow = (x: bigint, n: bigint): bigint => {
26
24
  if (!x && !n) return RAY;
@@ -38,7 +36,10 @@ const rpow = (x: bigint, n: bigint): bigint => {
38
36
  return z;
39
37
  };
40
38
 
41
- const calcChi = (state: SparkSDaiPoolState, currentTimestamp?: number) => {
39
+ export const calcChi = (
40
+ state: SparkSDaiPoolState,
41
+ currentTimestamp?: number,
42
+ ) => {
42
43
  currentTimestamp ||= Math.floor(Date.now() / 1000);
43
44
  if (!state.live) return RAY;
44
45
 
@@ -54,11 +55,14 @@ const calcChi = (state: SparkSDaiPoolState, currentTimestamp?: number) => {
54
55
  export class SparkSDaiEventPool extends StatefulEventSubscriber<SparkSDaiPoolState> {
55
56
  constructor(
56
57
  parentName: string,
58
+ network: Network,
57
59
  poolName: string,
58
60
  protected dexHelper: IDexHelper,
59
61
  private potAddress: Address,
60
62
  private potInterface: Interface,
61
63
  logger: Logger,
64
+ private savingsRateTopic: string,
65
+ private savingsRateSymbol: 'ssr' | 'dsr',
62
66
  ) {
63
67
  super(parentName, poolName, dexHelper, logger);
64
68
  this.addressesSubscribed = [potAddress];
@@ -69,7 +73,10 @@ export class SparkSDaiEventPool extends StatefulEventSubscriber<SparkSDaiPoolSta
69
73
  log: Readonly<Log>,
70
74
  blockHeader: Readonly<BlockHeader>,
71
75
  ): AsyncOrSync<DeepReadonly<SparkSDaiPoolState> | null> {
72
- if (log.topics[0] === FILE_TOPICHASH && log.topics[2] === DSR_TOPIC) {
76
+ if (
77
+ log.topics[0] === FILE_TOPICHASH &&
78
+ log.topics[2] === this.savingsRateTopic
79
+ ) {
73
80
  return {
74
81
  ...state,
75
82
  dsr: BigInt(log.topics[3]).toString(),
@@ -94,21 +101,17 @@ export class SparkSDaiEventPool extends StatefulEventSubscriber<SparkSDaiPoolSta
94
101
  this.dexHelper.multiContract,
95
102
  this.potAddress,
96
103
  this.potInterface,
104
+ this.savingsRateSymbol,
97
105
  blockNumber,
98
106
  );
99
107
  }
100
108
 
101
- convertToSDai(daiAmount: bigint, blockNumber: number): bigint {
102
- const state = this.getState(blockNumber);
103
- if (!state) throw new Error(`SDai state at ${blockNumber} does not exists`);
104
-
105
- return (daiAmount * RAY) / calcChi(state);
106
- }
107
-
108
- convertToDai(sdaiAmount: bigint, blockNumber: number): bigint {
109
- const state = this.getState(blockNumber);
110
- if (!state) throw new Error(`SDai state at ${blockNumber} does not exists`);
111
-
112
- return (sdaiAmount * calcChi(state)) / RAY;
109
+ async getOrGenerateState(blockNumber: number): Promise<SparkSDaiPoolState> {
110
+ let state = this.getState(blockNumber);
111
+ if (!state) {
112
+ state = await this.generateState(blockNumber);
113
+ this.setState(state, blockNumber);
114
+ }
115
+ return state;
113
116
  }
114
117
  }
@@ -1,6 +1,11 @@
1
1
  import { SimpleExchange } from '../simple-exchange';
2
2
  import { Context, IDex } from '../idex';
3
- import { SparkParams, SparkData, SparkSDaiFunctions } from './types';
3
+ import {
4
+ SparkParams,
5
+ SparkData,
6
+ SparkSDaiFunctions,
7
+ SparkSDaiPoolState,
8
+ } from './types';
4
9
  import { Network, SwapSide } from '../../constants';
5
10
  import { getDexKeysWithNetwork } from '../../utils';
6
11
  import { Adapters, SDaiConfig } from './config';
@@ -21,7 +26,7 @@ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
21
26
  import PotAbi from '../../abi/maker-psm/pot.json';
22
27
  import SavingsDaiAbi from '../../abi/sdai/SavingsDai.abi.json';
23
28
  import { Interface } from 'ethers/lib/utils';
24
- import { SparkSDaiEventPool } from './spark-sdai-pool';
29
+ import { calcChi, RAY, SparkSDaiEventPool } from './spark-sdai-pool';
25
30
  import { BI_POWS } from '../../bigint-constants';
26
31
  import { SDAI_DEPOSIT_GAS_COST, SDAI_REDEEM_GAS_COST } from './constants';
27
32
  import { extractReturnAmountPosition } from '../../executor/utils';
@@ -42,25 +47,29 @@ export class Spark
42
47
  constructor(
43
48
  protected network: Network,
44
49
  dexKey: string,
45
- protected dexHelper: IDexHelper,
50
+ readonly dexHelper: IDexHelper,
46
51
 
47
52
  readonly daiAddress: string = SDaiConfig[dexKey][network].daiAddress,
48
53
  readonly sdaiAddress: string = SDaiConfig[dexKey][network].sdaiAddress,
49
54
  readonly potAddress: string = SDaiConfig[dexKey][network].potAddress,
55
+ readonly abiInterface: Interface = SDaiConfig[dexKey][network]
56
+ .poolInterface,
50
57
 
51
58
  protected adapters = Adapters[network] || {},
52
59
  protected sdaiInterface = new Interface(SavingsDaiAbi),
53
- protected potInterface = new Interface(PotAbi),
54
60
  ) {
55
61
  super(dexHelper, dexKey);
56
62
  this.logger = dexHelper.getLogger(dexKey);
57
63
  this.eventPool = new SparkSDaiEventPool(
58
64
  this.dexKey,
65
+ this.network,
59
66
  `${this.daiAddress}_${this.sdaiAddress}`,
60
67
  dexHelper,
61
68
  this.potAddress,
62
- this.potInterface,
69
+ this.abiInterface,
63
70
  this.logger,
71
+ SDaiConfig[dexKey][network].savingsRate.topic,
72
+ SDaiConfig[dexKey][network].savingsRate.symbol,
64
73
  );
65
74
  }
66
75
 
@@ -107,41 +116,38 @@ export class Spark
107
116
  limitPools?: string[],
108
117
  ): Promise<null | ExchangePrices<SparkData>> {
109
118
  if (!this.isAppropriatePair(srcToken, destToken)) return null;
110
- if (!this.eventPool.getState(blockNumber)) return null;
119
+ const state = this.eventPool.getState(blockNumber);
120
+ if (!state) return null;
121
+
122
+ const isSrcAsset = this.isDai(srcToken.address);
123
+
124
+ let calcFunction: Function;
111
125
 
112
126
  if (side === SwapSide.SELL) {
113
- const calcSellFn = (blockNumber: number, amountIn: bigint) =>
114
- this.isDai(srcToken.address)
115
- ? this.eventPool.convertToSDai(amountIn, blockNumber)
116
- : this.eventPool.convertToDai(amountIn, blockNumber);
117
-
118
- return [
119
- {
120
- prices: amounts.map(amount => calcSellFn(blockNumber, amount)),
121
- unit: calcSellFn(blockNumber, BI_POWS[18]),
122
- gasCost: SDAI_DEPOSIT_GAS_COST,
123
- exchange: this.dexKey,
124
- data: { exchange: `${this.sdaiAddress}` },
125
- poolAddresses: [`${this.sdaiAddress}`],
126
- },
127
- ];
127
+ if (isSrcAsset) {
128
+ calcFunction = this.previewDeposit.bind(this);
129
+ } else {
130
+ calcFunction = this.previewRedeem.bind(this);
131
+ }
128
132
  } else {
129
- const calcBuyFn = (blockNumber: number, amountIn: bigint) =>
130
- this.isDai(srcToken.address)
131
- ? this.eventPool.convertToDai(amountIn, blockNumber)
132
- : this.eventPool.convertToSDai(amountIn, blockNumber);
133
-
134
- return [
135
- {
136
- prices: amounts.map(amount => calcBuyFn(blockNumber, amount)),
137
- unit: calcBuyFn(blockNumber, BI_POWS[18]),
138
- gasCost: SDAI_REDEEM_GAS_COST,
139
- exchange: this.dexKey,
140
- data: { exchange: `${this.sdaiAddress}` },
141
- poolAddresses: [`${this.sdaiAddress}`],
142
- },
143
- ];
133
+ if (isSrcAsset) {
134
+ calcFunction = this.previewMint.bind(this);
135
+ } else {
136
+ calcFunction = this.previewWithdraw.bind(this);
137
+ }
144
138
  }
139
+
140
+ return [
141
+ {
142
+ // cannot produce 1:1 price without making an rpc call for block.timestamp and using it for price calculation in `calcChi` function
143
+ prices: amounts.map(amount => calcFunction(amount, state)),
144
+ unit: BI_POWS[18],
145
+ gasCost: SDAI_DEPOSIT_GAS_COST,
146
+ exchange: this.dexKey,
147
+ data: { exchange: `${this.sdaiAddress}` },
148
+ poolAddresses: [`${this.sdaiAddress}`],
149
+ },
150
+ ];
145
151
  }
146
152
 
147
153
  // Returns estimated gas cost of calldata for this DEX in multiSwap
@@ -286,4 +292,24 @@ export class Spark
286
292
  networkFee: '0',
287
293
  };
288
294
  }
295
+
296
+ previewRedeem(shares: bigint, state: SparkSDaiPoolState) {
297
+ return (shares * calcChi(state)) / RAY;
298
+ }
299
+
300
+ previewMint(shares: bigint, state: SparkSDaiPoolState) {
301
+ return this.divUp(shares * calcChi(state), RAY);
302
+ }
303
+
304
+ previewWithdraw(assets: bigint, state: SparkSDaiPoolState) {
305
+ return this.divUp(assets * RAY, calcChi(state));
306
+ }
307
+
308
+ previewDeposit(assets: bigint, state: SparkSDaiPoolState) {
309
+ return (assets * RAY) / calcChi(state);
310
+ }
311
+
312
+ divUp(x: bigint, y: bigint): bigint {
313
+ return x !== 0n ? (x - 1n) / y + 1n : 0n;
314
+ }
289
315
  }
@@ -1,3 +1,4 @@
1
+ import { Interface } from '@ethersproject/abi';
1
2
  import { Address } from '../../types';
2
3
 
3
4
  export type SparkData = { exchange: Address };
@@ -6,6 +7,11 @@ export type SparkParams = {
6
7
  sdaiAddress: Address;
7
8
  daiAddress: Address;
8
9
  potAddress: Address;
10
+ savingsRate: {
11
+ symbol: 'dsr' | 'ssr';
12
+ topic: string;
13
+ };
14
+ poolInterface: Interface;
9
15
  };
10
16
 
11
17
  export enum SparkSDaiFunctions {
@@ -4,34 +4,60 @@ import { Interface, AbiCoder } from '@ethersproject/abi';
4
4
 
5
5
  const coder = new AbiCoder();
6
6
 
7
- // - `dsr`: the Dai Savings Rate
7
+ // - `dsr` || `ssr`: the Dai Savings Rate or USDS savings rate
8
8
  // - `chi`: the Rate Accumulator
9
9
  // - `rho`: time of last drip
10
10
  export async function getOnChainState(
11
11
  multiContract: Contract,
12
12
  potAddress: string,
13
13
  potInterface: Interface,
14
+ savingsRateSymbol: 'dsr' | 'ssr',
14
15
  blockNumber: number | 'latest',
15
16
  ): Promise<SparkSDaiPoolState> {
17
+ if (savingsRateSymbol === 'dsr') {
18
+ return getOnChainStateSDAI(
19
+ multiContract,
20
+ potAddress,
21
+ potInterface,
22
+ blockNumber,
23
+ );
24
+ }
25
+
26
+ return getOnChainStateUSDS(
27
+ multiContract,
28
+ potAddress,
29
+ potInterface,
30
+ blockNumber,
31
+ );
32
+ }
33
+
34
+ export async function getOnChainStateSDAI(
35
+ multiContract: Contract,
36
+ potAddress: string,
37
+ potInterface: Interface,
38
+ blockNumber: number | 'latest',
39
+ ): Promise<SparkSDaiPoolState> {
40
+ const calls = [
41
+ {
42
+ target: potAddress,
43
+ callData: potInterface.encodeFunctionData('dsr', []),
44
+ },
45
+ {
46
+ target: potAddress,
47
+ callData: potInterface.encodeFunctionData('chi', []),
48
+ },
49
+ {
50
+ target: potAddress,
51
+ callData: potInterface.encodeFunctionData('rho', []),
52
+ },
53
+ {
54
+ target: potAddress,
55
+ callData: potInterface.encodeFunctionData('live', []),
56
+ },
57
+ ];
58
+
16
59
  const data: { returnData: any[] } = await multiContract.methods
17
- .aggregate([
18
- {
19
- target: potAddress,
20
- callData: potInterface.encodeFunctionData('dsr', []),
21
- },
22
- {
23
- target: potAddress,
24
- callData: potInterface.encodeFunctionData('chi', []),
25
- },
26
- {
27
- target: potAddress,
28
- callData: potInterface.encodeFunctionData('rho', []),
29
- },
30
- {
31
- target: potAddress,
32
- callData: potInterface.encodeFunctionData('live', []),
33
- },
34
- ])
60
+ .aggregate(calls)
35
61
  .call({}, blockNumber);
36
62
 
37
63
  const [dsr, chi, rho, live] = data.returnData.map(item =>
@@ -45,3 +71,41 @@ export async function getOnChainState(
45
71
  rho,
46
72
  };
47
73
  }
74
+
75
+ export async function getOnChainStateUSDS(
76
+ multiContract: Contract,
77
+ potAddress: string,
78
+ potInterface: Interface,
79
+ blockNumber: number | 'latest',
80
+ ): Promise<SparkSDaiPoolState> {
81
+ const calls = [
82
+ {
83
+ target: potAddress,
84
+ callData: potInterface.encodeFunctionData('ssr', []),
85
+ },
86
+ {
87
+ target: potAddress,
88
+ callData: potInterface.encodeFunctionData('chi', []),
89
+ },
90
+ {
91
+ target: potAddress,
92
+ callData: potInterface.encodeFunctionData('rho', []),
93
+ },
94
+ ];
95
+
96
+ const data: { returnData: any[] } = await multiContract.methods
97
+ .aggregate(calls)
98
+ .call({}, blockNumber);
99
+
100
+ const [dsr, chi, rho] = data.returnData.map(item =>
101
+ coder.decode(['uint256'], item)[0].toString(),
102
+ );
103
+
104
+ return {
105
+ // 'hack' - sUSDS doesn't have global shutdown and no notion of `live`, so it's always `live`
106
+ live: true,
107
+ dsr,
108
+ chi,
109
+ rho,
110
+ };
111
+ }