@evaafi/sdk 0.6.1 → 0.6.2-b

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 (73) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +2 -1
  3. package/dist/api/liquidation.js +1 -3
  4. package/dist/api/math.d.ts +10 -1
  5. package/dist/api/math.js +45 -6
  6. package/dist/api/parser.js +49 -27
  7. package/dist/api/prices.d.ts +5 -2
  8. package/dist/api/prices.js +35 -13
  9. package/dist/constants/assets.d.ts +8 -0
  10. package/dist/constants/assets.js +31 -1
  11. package/dist/constants/general.d.ts +5 -2
  12. package/dist/constants/general.js +12 -22
  13. package/dist/constants/pools.d.ts +1 -0
  14. package/dist/constants/pools.js +19 -3
  15. package/dist/contracts/MasterContract.d.ts +5 -0
  16. package/dist/contracts/MasterContract.js +6 -0
  17. package/dist/index.d.ts +4 -4
  18. package/dist/index.js +10 -2
  19. package/dist/prices/Prices.d.ts +9 -0
  20. package/dist/prices/Prices.js +43 -0
  21. package/dist/prices/PricesCollector.d.ts +12 -0
  22. package/dist/prices/PricesCollector.js +123 -0
  23. package/dist/prices/Types.d.ts +33 -0
  24. package/dist/prices/Types.js +11 -0
  25. package/dist/prices/constants.d.ts +1 -0
  26. package/dist/prices/constants.js +4 -0
  27. package/dist/prices/index.d.ts +6 -0
  28. package/dist/prices/index.js +22 -0
  29. package/dist/prices/sources/Backend.d.ts +13 -0
  30. package/dist/prices/sources/Backend.js +52 -0
  31. package/dist/prices/sources/Icp.d.ts +10 -0
  32. package/dist/prices/sources/Icp.js +23 -0
  33. package/dist/prices/sources/Iota.d.ts +39 -0
  34. package/dist/prices/sources/Iota.js +49 -0
  35. package/dist/prices/sources/PriceSource.d.ts +14 -0
  36. package/dist/prices/sources/PriceSource.js +26 -0
  37. package/dist/prices/sources/index.d.ts +4 -0
  38. package/dist/prices/sources/index.js +20 -0
  39. package/dist/prices/utils.d.ts +23 -0
  40. package/dist/prices/utils.js +148 -0
  41. package/dist/types/Master.d.ts +2 -1
  42. package/dist/types/User.d.ts +11 -1
  43. package/dist/utils/userJettonWallet.js +42 -43
  44. package/dist/utils/utils.d.ts +1 -0
  45. package/dist/utils/utils.js +5 -1
  46. package/package.json +11 -9
  47. package/src/api/liquidation.ts +0 -1
  48. package/src/api/math.ts +66 -5
  49. package/src/api/parser.ts +56 -31
  50. package/src/api/prices.ts +20 -7
  51. package/src/constants/assets.ts +57 -0
  52. package/src/constants/general.ts +11 -23
  53. package/src/constants/pools.ts +21 -5
  54. package/src/contracts/MasterContract.ts +8 -1
  55. package/src/index.ts +9 -2
  56. package/src/prices/Prices.ts +32 -0
  57. package/src/prices/PricesCollector.ts +139 -0
  58. package/src/prices/Types.ts +44 -0
  59. package/src/prices/constants.ts +1 -0
  60. package/src/prices/index.ts +6 -0
  61. package/src/prices/sources/Backend.ts +62 -0
  62. package/src/prices/sources/Icp.ts +27 -0
  63. package/src/prices/sources/Iota.ts +90 -0
  64. package/src/prices/sources/PriceSource.ts +35 -0
  65. package/src/prices/sources/index.ts +4 -0
  66. package/src/prices/utils.ts +170 -0
  67. package/src/types/Master.ts +3 -2
  68. package/src/types/User.ts +13 -3
  69. package/src/utils/userJettonWallet.ts +43 -53
  70. package/src/utils/utils.ts +5 -1
  71. package/src/config.ts +0 -1
  72. package/src/types/Common.ts +0 -16
  73. package/src/utils/priceUtils.ts +0 -177
package/src/api/parser.ts CHANGED
@@ -12,8 +12,7 @@ import {
12
12
  } from './math';
13
13
  import { loadMaybeMyRef, loadMyRef } from './helpers';
14
14
  import { BalanceType, UserBalance, UserData, UserLiteData, UserRewards } from '../types/User';
15
- import { MAINNET_POOL_CONFIG, TESTNET_POOL_CONFIG } from '../constants/pools';
16
- import { basename } from 'path';
15
+ import { checkNotInDebtAtAll } from "../api/math";
17
16
 
18
17
  export function createUserRewards(): DictionaryValue<UserRewards> {
19
18
  return {
@@ -34,29 +33,24 @@ export function createAssetData(): DictionaryValue<AssetData> {
34
33
  serialize: (src: any, buidler: any) => {
35
34
  buidler.storeUint(src.sRate, 64);
36
35
  buidler.storeUint(src.bRate, 64);
37
- buidler.storeUint(src.totalSupply, 64);
38
- buidler.storeUint(src.totalBorrow, 64);
36
+ buidler.storeInt(src.totalSupply, 64);
37
+ buidler.storeInt(src.totalBorrow, 64);
39
38
  buidler.storeUint(src.lastAccural, 32);
40
39
  buidler.storeUint(src.balance, 64);
41
40
  buidler.storeUint(src.trackingSupplyIndex, 64);
42
41
  buidler.storeUint(src.trackingBorrowIndex, 64);
43
- if (src.awaitedSupply) {
44
- buidler.storeUint(src.awaitedSupply, 64);
45
- }
42
+ buidler.storeUint(src.awaitedSupply, 64);
46
43
  },
47
44
  parse: (src: Slice) => {
48
- const sRate = BigInt(src.loadInt(64));
49
- const bRate = BigInt(src.loadInt(64));
50
- const totalSupply = BigInt(src.loadInt(64));
51
- const totalBorrow = BigInt(src.loadInt(64));
52
- const lastAccural = BigInt(src.loadInt(32));
53
- const balance = BigInt(src.loadInt(64));
54
- const trackingSupplyIndex = BigInt(src.loadUint(64));
55
- const trackingBorrowIndex = BigInt(src.loadUint(64));
56
- let awaitedSupply: bigint | undefined = undefined;
57
- if (src.remainingBits == 64) {
58
- awaitedSupply = BigInt(src.loadUint(64));
59
- }
45
+ const sRate = BigInt(src.loadUintBig(64));
46
+ const bRate = BigInt(src.loadUintBig(64));
47
+ const totalSupply = BigInt(src.loadIntBig(64));
48
+ const totalBorrow = BigInt(src.loadIntBig(64));
49
+ const lastAccural = BigInt(src.loadUintBig(32));
50
+ const balance = BigInt(src.loadUintBig(64));
51
+ const trackingSupplyIndex = BigInt(src.loadUintBig(64));
52
+ const trackingBorrowIndex = BigInt(src.loadUintBig(64));
53
+ const awaitedSupply = BigInt(src.loadUintBig(64));
60
54
 
61
55
  return { sRate, bRate, totalSupply, totalBorrow, lastAccural, balance, trackingSupplyIndex, trackingBorrowIndex, awaitedSupply};
62
56
  },
@@ -205,7 +199,7 @@ export function parseUserLiteData(
205
199
  assetsData: ExtendedAssetsData,
206
200
  assetsConfig: ExtendedAssetsConfig,
207
201
  poolConfig: PoolConfig,
208
- applyDust: boolean = true
202
+ applyDust: boolean = false
209
203
  ): UserLiteData {
210
204
  const poolAssetsConfig = poolConfig.poolAssetsConfig;
211
205
  const masterConstants = poolConfig.masterConstants;
@@ -215,7 +209,8 @@ export function parseUserLiteData(
215
209
  const codeVersion = userSlice.loadCoins();
216
210
  const masterAddress = userSlice.loadAddress();
217
211
  const userAddress = userSlice.loadAddress();
218
- const principalsDict = userSlice.loadDict(Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(64));
212
+ const realPrincipals = userSlice.loadDict(Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(64));
213
+ const principalsDict = Dictionary.empty(Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(64));
219
214
  const userState = userSlice.loadInt(64);
220
215
 
221
216
  let trackingSupplyIndex = 0n;
@@ -244,7 +239,7 @@ export function parseUserLiteData(
244
239
  const assetData = assetsData.get(asset.assetId) as ExtendedAssetData;
245
240
  const assetConfig = assetsConfig.get(asset.assetId) as AssetConfig;
246
241
 
247
- let principal = principalsDict.get(asset.assetId) || 0n;
242
+ let principal = realPrincipals.get(asset.assetId) || 0n;
248
243
  let balance = presentValue(assetData.sRate, assetData.bRate, principal, masterConstants);
249
244
 
250
245
  if (applyDust && (principal > 0 && (principal < assetConfig.dust))) {
@@ -254,6 +249,8 @@ export function parseUserLiteData(
254
249
  type: BalanceType.supply,
255
250
  };
256
251
  principalsDict.set(asset.assetId, 0n);
252
+ } else {
253
+ principalsDict.set(asset.assetId, principal);
257
254
  }
258
255
  userBalances.set(asset.assetId, balance);
259
256
  }
@@ -264,12 +261,14 @@ export function parseUserLiteData(
264
261
  masterAddress: masterAddress,
265
262
  ownerAddress: userAddress,
266
263
  principals: principalsDict,
264
+ realPrincipals: realPrincipals,
267
265
  state: userState,
268
266
  balances: userBalances,
269
267
  trackingSupplyIndex: trackingSupplyIndex,
270
268
  trackingBorrowIndex: trackingBorrowIndex,
271
269
  dutchAuctionStart: dutchAuctionStart,
272
270
  backupCell: backupCell,
271
+ fullyParsed: false,
273
272
 
274
273
  rewards: rewards,
275
274
  backupCell1: backupCell1,
@@ -283,8 +282,11 @@ export function parseUserData(
283
282
  assetsConfig: ExtendedAssetsConfig,
284
283
  prices: Dictionary<bigint, bigint>,
285
284
  poolConfig: PoolConfig,
286
- applyDust: boolean = true
285
+ applyDust: boolean = false
287
286
  ): UserData {
287
+ userLiteData.fullyParsed = true;
288
+ let havePrincipalWithoutPrice = false;
289
+
288
290
  const poolAssetsConfig = poolConfig.poolAssetsConfig;
289
291
  const masterConstants = poolConfig.masterConstants;
290
292
 
@@ -294,6 +296,16 @@ export function parseUserData(
294
296
  let supplyBalance = 0n;
295
297
  let borrowBalance = 0n;
296
298
 
299
+ for (const [assetId, principal] of userLiteData.realPrincipals) {
300
+ if (!prices.has(assetId)) {
301
+ userLiteData.fullyParsed = false;
302
+
303
+ if (principal != 0n) {
304
+ havePrincipalWithoutPrice = true;
305
+ }
306
+ }
307
+ }
308
+
297
309
  for (const [_, asset] of Object.entries(poolAssetsConfig)) {
298
310
  const assetData = assetsData.get(asset.assetId) as ExtendedAssetData;
299
311
  const assetConfig = assetsConfig.get(asset.assetId) as AssetConfig;
@@ -310,6 +322,10 @@ export function parseUserData(
310
322
  }
311
323
 
312
324
  for (const [_, asset] of Object.entries(poolAssetsConfig)) {
325
+ if (!prices.has(asset.assetId)) {
326
+ continue;
327
+ }
328
+
313
329
  const assetConfig = assetsConfig.get(asset.assetId) as AssetConfig;
314
330
  const balance = userLiteData.balances.get(asset.assetId) as UserBalance;
315
331
 
@@ -321,19 +337,25 @@ export function parseUserData(
321
337
  }
322
338
  }
323
339
 
324
- const availableToBorrow = getAvailableToBorrow(assetsConfig, assetsData, userLiteData.principals, prices, masterConstants);
340
+ const availableToBorrow = getAvailableToBorrow(assetsConfig, assetsData, userLiteData.realPrincipals, prices, masterConstants);
341
+
325
342
  for (const [_, asset] of Object.entries(poolAssetsConfig)) {
343
+ const balance = userLiteData.balances.get(asset.assetId) as UserBalance;
326
344
  const assetConfig = assetsConfig.get(asset.assetId) as AssetConfig;
327
345
  const assetData = assetsData.get(asset.assetId) as ExtendedAssetData;
328
- const balance = userLiteData.balances.get(asset.assetId) as UserBalance;
329
346
 
330
347
  if (balance.type === BalanceType.supply) {
331
348
  withdrawalLimits.set(
332
349
  asset.assetId,
333
- bigIntMin(calculateMaximumWithdrawAmount(assetsConfig, assetsData, userLiteData.principals, prices, masterConstants, asset.assetId), assetData.balance)
350
+ bigIntMin(calculateMaximumWithdrawAmount(assetsConfig, assetsData, userLiteData.realPrincipals, prices, masterConstants, asset.assetId), assetData.balance)
334
351
  );
335
352
  }
336
353
 
354
+ if (!prices.has(asset.assetId)) {
355
+ borrowLimits.set(asset.assetId, 0n);
356
+ continue;
357
+ }
358
+
337
359
  borrowLimits.set(
338
360
  asset.assetId,
339
361
  bigIntMax(0n, bigIntMin((availableToBorrow * 10n ** assetConfig.decimals) / prices.get(asset.assetId)!, assetData.balance, assetData.totalSupply - assetData.totalBorrow)),
@@ -346,12 +368,14 @@ export function parseUserData(
346
368
  ? 0
347
369
  : Number(BigInt(1e9) - (availableToBorrow * BigInt(1e9)) / (borrowBalance + availableToBorrow)) / 1e7;
348
370
 
349
- const liquidationData = calculateLiquidationData(assetsConfig, assetsData, userLiteData.principals, prices, poolConfig);
350
371
  let healthFactor = 1;
351
- if (liquidationData.totalLimit != 0n) {
352
- healthFactor = 1 - Number(liquidationData.totalDebt) / Number(liquidationData.totalLimit);
353
- }
354
-
372
+ let liquidationData;
373
+ if (!havePrincipalWithoutPrice) {
374
+ liquidationData = calculateLiquidationData(assetsConfig, assetsData, userLiteData.realPrincipals, prices, poolConfig);
375
+ if (liquidationData.totalLimit != 0n) {
376
+ healthFactor = 1 - Number(liquidationData.totalDebt) / Number(liquidationData.totalLimit);
377
+ }
378
+ }
355
379
  return {
356
380
  ...userLiteData,
357
381
  withdrawalLimits: withdrawalLimits,
@@ -363,5 +387,6 @@ export function parseUserData(
363
387
  limitUsed: limitUsed,
364
388
  liquidationData: liquidationData,
365
389
  healthFactor: healthFactor,
390
+ havePrincipalWithoutPrice: havePrincipalWithoutPrice
366
391
  };
367
392
  }
package/src/api/prices.ts CHANGED
@@ -1,14 +1,27 @@
1
- import { Dictionary } from '@ton/core';
2
- import { PriceData, RawPriceData } from '../types/Common';
3
- import { getMedianPrice, loadPrices, packAssetsData, packOraclesData, packPrices, parsePrices, verifyPrices } from '../utils/priceUtils';
4
- import { OracleNFT, PoolConfig } from '../types/Master';
1
+ import { PoolConfig } from '../types/Master';
5
2
  import { MAINNET_POOL_CONFIG } from '../constants/pools';
3
+ import { DefaultPriceSourcesConfig, PriceData, PricesCollector, PriceSource, PriceSourcesConfig } from '../prices';
6
4
 
7
- export async function getPrices(endpoints: String[] = ["api.stardust-mainnet.iotaledger.net", "iota.evaa.finance"], poolConfig: PoolConfig = MAINNET_POOL_CONFIG): Promise<PriceData> {
5
+ /**
6
+ * @deprecated Use PriceCollector istead of getPrices
7
+ */
8
+ export async function getPrices(endpoints: string[] = ["api.stardust-mainnet.iotaledger.net"], poolConfig: PoolConfig = MAINNET_POOL_CONFIG): Promise<PriceData> {
8
9
  if (endpoints.length == 0) {
9
10
  throw new Error("Empty endpoint list");
10
11
  }
11
-
12
+
13
+ const sources: PriceSourcesConfig = {
14
+ iotaEndpoints: endpoints,
15
+ icpEndpoints: DefaultPriceSourcesConfig.icpEndpoints,
16
+ backendEndpoints: DefaultPriceSourcesConfig.backendEndpoints,
17
+ }
18
+
19
+ const priceCollector = new PricesCollector(poolConfig, sources);
20
+ const prices = await priceCollector.getPrices();
21
+
22
+ return { dict: prices.dict, dataCell: prices.dataCell };
23
+ /*
24
+ Old code
12
25
  const prices = await Promise.all(poolConfig.oracles.map(async x => await parsePrices(await loadPrices(x.address, endpoints), x.id)));
13
26
 
14
27
  let acceptedPrices: RawPriceData[] = prices.filter(verifyPrices(poolConfig.poolAssetsConfig));
@@ -41,5 +54,5 @@ export async function getPrices(endpoints: String[] = ["api.stardust-mainnet.iot
41
54
  return {
42
55
  dict: dict,
43
56
  dataCell: packPrices(packedMedianData, packedOracleData)
44
- };
57
+ };*/
45
58
  }
@@ -10,10 +10,18 @@ export const ASSET_ID = {
10
10
  jUSDC: sha256Hash('jUSDC'),
11
11
  stTON: sha256Hash('stTON'),
12
12
  tsTON: sha256Hash('tsTON'),
13
+ uTON: sha256Hash('uTON'),
14
+
15
+ // LP
13
16
  TONUSDT_DEDUST: sha256Hash('TONUSDT_DEDUST'),
14
17
  TONUSDT_STONFI: sha256Hash('TONUSDT_STONFI'),
15
18
  TON_STORM: sha256Hash('TON_STORM'),
16
19
  USDT_STORM: sha256Hash('USDT_STORM'),
20
+
21
+ // ALTS
22
+ NOT: sha256Hash('NOT'),
23
+ DOGS: sha256Hash('DOGS'),
24
+ CATI: sha256Hash('CATI'),
17
25
  };
18
26
 
19
27
  export const UNDEFINED_ASSET: PoolAssetConfig = {
@@ -137,3 +145,52 @@ export const USDT_STORM_MAINNET: PoolAssetConfig = {
137
145
  ),
138
146
  )[0],
139
147
  }
148
+
149
+ export const CATI_MAINNET: PoolAssetConfig = {
150
+ name: 'CATI',
151
+ assetId: ASSET_ID.CATI,
152
+ jettonMasterAddress: Address.parse('EQD-cvR0Nz6XAyRBvbhz-abTrRC6sI5tvHvvpeQraV9UAAD7'),
153
+ jettonWalletCode: Cell.fromBoc(
154
+ Buffer.from(
155
+ 'b5ee9c7241021101000323000114ff00f4a413f4bcf2c80b0102016202030202cc0405001ba0f605da89a1f401f481f481a8610201d40607020120080900c30831c02497c138007434c0c05c6c2544d7c0fc03383e903e900c7e800c5c75c87e800c7e800c1cea6d0000b4c7e08403e29fa954882ea54c4d167c0278208405e3514654882ea58c511100fc02b80d60841657c1ef2ea4d67c02f817c12103fcbc2000113e910c1c2ebcb853600201200a0b0083d40106b90f6a2687d007d207d206a1802698fc1080bc6a28ca9105d41083deecbef09dd0958f97162e99f98fd001809d02811e428027d012c678b00e78b6664f6aa401f1503d33ffa00fa4021f001ed44d0fa00fa40fa40d4305136a1522ac705f2e2c128c2fff2e2c254344270542013541403c85004fa0258cf1601cf16ccc922c8cb0112f400f400cb00c920f9007074c8cb02ca07cbffc9d004fa40f40431fa0020d749c200f2e2c4778018c8cb055008cf1670fa0217cb6b13cc80c0201200d0e009e8210178d4519c8cb1f19cb3f5007fa0222cf165006cf1625fa025003cf16c95005cc2391729171e25008a813a08209c9c380a014bcf2e2c504c98040fb001023c85004fa0258cf1601cf16ccc9ed5402f73b51343e803e903e90350c0234cffe80145468017e903e9014d6f1c1551cdb5c150804d50500f214013e809633c58073c5b33248b232c044bd003d0032c0327e401c1d3232c0b281f2fff274140371c1472c7cb8b0c2be80146a2860822625a019ad822860822625a028062849e5c412440e0dd7c138c34975c2c0600f1000d73b51343e803e903e90350c01f4cffe803e900c145468549271c17cb8b049f0bffcb8b08160824c4b402805af3cb8b0e0841ef765f7b232c7c572cfd400fe8088b3c58073c5b25c60063232c14933c59c3e80b2dab33260103ec01004f214013e809633c58073c5b3327b552000705279a018a182107362d09cc8cb1f5230cb3f58fa025007cf165007cf16c9718010c8cb0524cf165006fa0215cb6a14ccc971fb0010241023007cc30023c200b08e218210d53276db708010c8cb055008cf165004fa0216cb6a12cb1f12cb3fc972fb0093356c21e203c85004fa0258cf1601cf16ccc9ed5495eaedd7',
156
+ 'hex',
157
+ ),
158
+ )[0],
159
+ }
160
+
161
+ export const DOGS_MAINNET: PoolAssetConfig = {
162
+ name: 'DOGS',
163
+ assetId: ASSET_ID.DOGS,
164
+ jettonMasterAddress: Address.parse('EQCvxJy4eG8hyHBFsZ7eePxrRsUQSFE_jpptRAYBmcG_DOGS'),
165
+ jettonWalletCode: Cell.fromBoc(
166
+ Buffer.from(
167
+ 'b5ee9c7241010101002300084202ba2918c8947e9b25af9ac1b883357754173e5812f807a3d6e642a14709595395237ae3c3',
168
+ 'hex',
169
+ ),
170
+ )[0],
171
+ }
172
+
173
+ export const NOT_MAINNET: PoolAssetConfig = {
174
+ name: 'NOT',
175
+ assetId: ASSET_ID.NOT,
176
+ jettonMasterAddress: Address.parse('EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT'),
177
+ jettonWalletCode: Cell.fromBoc(
178
+ Buffer.from(
179
+ 'b5ee9c7201010101002300084202ba2918c8947e9b25af9ac1b883357754173e5812f807a3d6e642a14709595395',
180
+ 'hex',
181
+ ),
182
+ )[0],
183
+ }
184
+
185
+ export const UTON_MAINNET: PoolAssetConfig = {
186
+ name: 'uTON',
187
+ assetId: ASSET_ID.uTON,
188
+ jettonMasterAddress: Address.parse('EQAfF5j3JMIpZlLmACv7Ub7RH7WmiVMuV4ivcgNYHvNnqHTz'),
189
+ jettonWalletCode: Cell.fromBoc(
190
+ Buffer.from(
191
+ 'b5ee9c7201021301000439000114ff00f4a413f4bcf2c80b0102016202030202cc0405020120111200bbd906380492f827000e8698180b8d84a89af81f807707d207d2018fd0018b8eb90fd0018fd001801698f90c10807c53f52dd4a989a2cf805f010c1080bc6a28cdd4b18a22201f8067000c1082caf83de5d4aa22201f806f02f82c207f9784020120060701f7f01e99ffd007d20381140816000fd23182c5d797a76a2687d00699ffd207d206a18027c30817c317c31fc327c32fc2091d0fc30fc21a8036382f97160fc20e17ff971617c227c22b82a300209aa0a82e42802fd0109e59f80e78b00e78b666490e4658089fa00097a00658064907c80383a6465816503e5ffe4e802c080201200a0b01fefa40f40431fa0020d749c200f2e2c4778018c8cb055009cf1670fa0218cb6b13cc8210178d4519c8cb1f15cb3f5003fa02f843cf1658cf1621fa025004cf16c901cc2291729171e25004a812a08208989680a08208989680a08208989680a0bcf2e2c5f841f842f843f844f845c85005fa0213cb3f01cf1601cf16ccc9ed5409000ac98040fb000201200c0d00b948020d721ed44d0fa00d33ffa40fa40d43004f86102f862f863f864f865d31f218210178d4519ba0282107bdd97deba12b1f2e2c5d33f31fa0030f84101a0f861f841f842f843f844f845c85005fa0213cb3f01cf1601cf16ccc9ed54801f53b51343e8034cffe903e90350c013e1840be18be18fe193e194134cffe803e1048a83e187e903e903e113e1149165c15180104d505417214017e8084f2cfc073c58073c5b332487232c044fd0004bd0032c0327e401c1d3232c0b281f2fff2740a31c17e11140271c1462c7cb8b0c1be80145a2860822625a019a00e01f73b51343e8034cffe903e90350c013e1840be18be18fe193e194134cffe803e903d010c1c0060083d03dbe87cb8b13434c7fe80204c0048b0803cbd350c3e10be10a93e18be1049a87e187e10d402b1c17cb8b07e1070bffcb8b0945e6860822625a019ad82284820822625a0281401e820822625a028086814a42f201001f2b608a18208989680a018a1278e355275a014a182107362d09cc8cb1f5230cb3f58fa025003cf165003cf16c9718018c8cb05f843cf165006fa0215cb6a14ccc971fb0094375b6c21e221d70b01c30023c200b08e208210d53276db708010c8cb055004cf165004fa0212cb6a12cb1fcb3fc972fb00925f03e20f0038f841f842f843f844f845c85005fa0213cb3f01cf1601cf16ccc9ed5400c0f2e2c5058208989680a018a1f841f842f843f844f845c85005fa0213cb3f01cf1601cf16ccc9ed5482107bdd97dec8cb1f14cb3ff843cf1616cb3f01fa0215cb1f5003cf1658fa02ccc9718018c8cb05f844cf165003fa0212cb6accc970fb000047bfd8176a2687d00699ffd207d206a18027c30817c317c31fc327c32fc20fc21fc227c22c004bbdd79f6a2687d00699ffd207d206a18027c30817c317c31fc327c32fc20fc217c21fc227c22c',
192
+ 'hex',
193
+ ),
194
+ )[0],
195
+ }
196
+
@@ -1,5 +1,4 @@
1
1
  import { Address, Cell, toNano } from '@ton/core';
2
- import { sha256Hash } from '../utils/sha256BigInt';
3
2
  import { OracleNFT } from '../types/Master';
4
3
 
5
4
 
@@ -21,37 +20,26 @@ export const MASTER_CONSTANTS = {
21
20
 
22
21
  export const NULL_ADDRESS = Address.parse('UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKZ');
23
22
 
24
-
25
23
  export const EVAA_MASTER_MAINNET = Address.parse('EQC8rUZqR_pWV1BylWUlPNBzyiTYVoBEmQkMIQDZXICfnuRr');
26
24
  export const MAINNET_VERSION = 6;
27
25
  export const EVAA_MASTER_TESTNET = Address.parse('EQDLsg3w-iBj26Gww7neYoJAxiT2t77Zo8ro56b0yuHsPp3C');
28
- export const TESTNET_VERSION = 0;
26
+ export const TESTNET_VERSION = 1;
29
27
  export const EVAA_LP_MAINNET = Address.parse('EQBIlZX2URWkXCSg3QF2MJZU-wC5XkBoLww-hdWk2G37Jc6N');
30
- export const EVAA_LP_MAINNET_VERSION = 2;
28
+ export const EVAA_LP_MAINNET_VERSION = 3;
29
+ export const EVAA_ALTS_MAINNET = Address.parse('EQANURVS3fhBO9bivig34iyJQi97FhMbpivo1aUEAS2GYSu-');
30
+ export const EVAA_ALTS_MAINNET_VERSION = 0;
31
31
 
32
32
  export const ORACLES_MAINNET: OracleNFT[] = [
33
- {id: 0, address: '0xd3a8c0b9fd44fd25a49289c631e3ac45689281f2f8cf0744400b4c65bed38e5d'},
34
- {id: 1, address: '0x2c21cabdaa89739de16bde7bc44e86401fac334a3c7e55305fe5e7563043e191'},
35
- {id: 2, address: '0x2eb258ce7b5d02466ab8a178ad8b0ba6ffa7b58ef21de3dc3b6dd359a1e16af0'},
36
- {id: 3, address: '0xf9a0769954b4430bca95149fb3d876deb7799d8f74852e0ad4ccc5778ce68b52'},
33
+ {id: 0, address: '0xd3a8c0b9fd44fd25a49289c631e3ac45689281f2f8cf0744400b4c65bed38e5d', pubkey: Buffer.from('b404f4a2ebb62f2623b370c89189748a0276c071965b1646b996407f10d72eb9', 'hex') },
34
+ {id: 1, address: '0x2c21cabdaa89739de16bde7bc44e86401fac334a3c7e55305fe5e7563043e191', pubkey: Buffer.from('9ad115087520d91b6b45d6a8521eb4616ee6914af07fabdc2e9d1826dbb17078', 'hex') },
35
+ {id: 2, address: '0x2eb258ce7b5d02466ab8a178ad8b0ba6ffa7b58ef21de3dc3b6dd359a1e16af0', pubkey: Buffer.from('e503e02e8a9226b34e7c9deb463cbf7f19bce589362eb448a69a8ee7b2fca631', 'hex') },
36
+ {id: 3, address: '0xf9a0769954b4430bca95149fb3d876deb7799d8f74852e0ad4ccc5778ce68b52', pubkey: Buffer.from('9cbf8374cf1f2cf17110134871d580198416e101683f4a61f54cf2a3e4e32070', 'hex') },
37
37
  ];
38
38
 
39
- export const ORACLES_TESTNET: OracleNFT[] = [
40
- {id: 0, address: '0x3bb147a37b7a7f874c39320440f352bddd2c9337e31a778731910f0266391650'},
41
- {id: 1, address: '0x676767e93b05a21aec9023a65f73cffe1c725709c3c964a7c3f0fd4229089bfe'},
42
- {id: 2, address: '0x9c9e65951b0c5920c286bdb3410babcaf21f85bc9c90c13172988630f1244e0f'},
43
- {id: 3, address: '0x9dcf880229bfb68d7344fd294624b64f1e0b43b9d858f0fdb1bc6434616c08f5'},
44
- {id: 4, address: '0x4d1afcf7c0426ca61c405c8cfaef0053a0f0d143740ffed04c8716beb99cd614'},
45
- {id: 5, address: '0x11c6baa608ed10733051fd74134441d384e471722fbc496b43ea4e3c6652485f'},
46
- {id: 6, address: '0x2b685672f38dc2fce59013bb740bf24c6037049a1c267bb3b5f6f55d5b195f5f'},
47
- ];
39
+ export const ORACLES_TESTNET: OracleNFT[] = ORACLES_MAINNET;
48
40
 
49
- export const ORACLES_LP: OracleNFT[] = [
50
- {id: 0, address: '0xd3a8c0b9fd44fd25a49289c631e3ac45689281f2f8cf0744400b4c65bed38e5d'},
51
- {id: 1, address: '0x2c21cabdaa89739de16bde7bc44e86401fac334a3c7e55305fe5e7563043e191'},
52
- {id: 2, address: '0x2eb258ce7b5d02466ab8a178ad8b0ba6ffa7b58ef21de3dc3b6dd359a1e16af0'},
53
- {id: 3, address: '0xf9a0769954b4430bca95149fb3d876deb7799d8f74852e0ad4ccc5778ce68b52'},
54
- ];
41
+ export const ORACLES_LP: OracleNFT[] = ORACLES_MAINNET;
42
+ export const ORACLES_ALTS: OracleNFT[] = ORACLES_MAINNET;
55
43
 
56
44
  export const LENDING_CODE = Cell.fromBoc(
57
45
  Buffer.from(
@@ -1,7 +1,6 @@
1
- import { Address } from "@ton/core";
2
- import { JUSDC_MAINNET, JUSDC_TESTNET, JUSDT_MAINNET, JUSDT_TESTNET, STTON_MAINNET, STTON_TESTNET, TON_MAINNET, TON_STORM_MAINNET, TONUSDT_DEDUST_MAINNET, TSTON_MAINNET, USDT_MAINNET, USDT_STORM_MAINNET } from "./assets";
1
+ import { CATI_MAINNET, DOGS_MAINNET, JUSDC_MAINNET, JUSDC_TESTNET, JUSDT_MAINNET, JUSDT_TESTNET, NOT_MAINNET, STTON_MAINNET, STTON_TESTNET, TON_MAINNET, TON_STORM_MAINNET, TONUSDT_DEDUST_MAINNET, TSTON_MAINNET, USDT_MAINNET, USDT_STORM_MAINNET, UTON_MAINNET } from "./assets";
3
2
  import { PoolConfig } from "../types/Master";
4
- import { EVAA_MASTER_MAINNET, EVAA_MASTER_TESTNET, LENDING_CODE, MAINNET_VERSION, MASTER_CONSTANTS, TESTNET_VERSION, EVAA_LP_MAINNET, EVAA_LP_MAINNET_VERSION, ORACLES_MAINNET, ORACLES_LP, ORACLES_TESTNET } from "./general";
3
+ import { EVAA_MASTER_MAINNET, EVAA_MASTER_TESTNET, LENDING_CODE, MAINNET_VERSION, MASTER_CONSTANTS, TESTNET_VERSION, EVAA_LP_MAINNET, EVAA_LP_MAINNET_VERSION, ORACLES_MAINNET, ORACLES_LP, ORACLES_TESTNET, EVAA_ALTS_MAINNET, EVAA_ALTS_MAINNET_VERSION, ORACLES_ALTS } from "./general";
5
4
 
6
5
  export const MAINNET_POOL_CONFIG: PoolConfig = {
7
6
  masterAddress: EVAA_MASTER_MAINNET,
@@ -15,7 +14,8 @@ export const MAINNET_POOL_CONFIG: PoolConfig = {
15
14
  JUSDC_MAINNET,
16
15
  STTON_MAINNET,
17
16
  TSTON_MAINNET,
18
- USDT_MAINNET
17
+ USDT_MAINNET,
18
+ // UTON_MAINNET // announce
19
19
  ],
20
20
  lendingCode: LENDING_CODE
21
21
  };
@@ -25,7 +25,7 @@ export const TESTNET_POOL_CONFIG: PoolConfig = {
25
25
  masterVersion: TESTNET_VERSION,
26
26
  masterConstants: MASTER_CONSTANTS,
27
27
  oracles: ORACLES_TESTNET,
28
- minimalOracles: 5,
28
+ minimalOracles: 3,
29
29
  poolAssetsConfig: [
30
30
  TON_MAINNET,
31
31
  JUSDT_TESTNET,
@@ -50,3 +50,19 @@ export const MAINNET_LP_POOL_CONFIG: PoolConfig = {
50
50
  ],
51
51
  lendingCode: LENDING_CODE
52
52
  };
53
+
54
+ export const MAINNET_ALTS_POOL_CONFIG: PoolConfig = {
55
+ masterAddress: EVAA_ALTS_MAINNET,
56
+ masterVersion: EVAA_ALTS_MAINNET_VERSION,
57
+ masterConstants: MASTER_CONSTANTS,
58
+ oracles: ORACLES_ALTS,
59
+ minimalOracles: 3,
60
+ poolAssetsConfig: [
61
+ TON_MAINNET,
62
+ USDT_MAINNET,
63
+ CATI_MAINNET,
64
+ NOT_MAINNET,
65
+ DOGS_MAINNET
66
+ ],
67
+ lendingCode: LENDING_CODE
68
+ };
@@ -20,7 +20,7 @@ import { parseMasterData } from '../api/parser';
20
20
  import { MasterData, PoolAssetConfig, PoolConfig} from '../types/Master';
21
21
  import { JettonWallet } from './JettonWallet';
22
22
  import { getUserJettonWallet } from '../utils/userJettonWallet';
23
- import { getPrices, isTonAsset, isTonAssetId, MAINNET_POOL_CONFIG } from '..';
23
+ import { DefaultPriceSourcesConfig, getPrices, isTonAsset, isTonAssetId, MAINNET_POOL_CONFIG, PricesCollector, PriceSourcesConfig } from '..';
24
24
 
25
25
  /**
26
26
  * Parameters for the Evaa contract
@@ -393,6 +393,9 @@ export class Evaa implements Contract {
393
393
  }
394
394
  }
395
395
 
396
+ /**
397
+ * @deprecated Use PriceCollector (createPriceCollector) istead of getPrices
398
+ */
396
399
  async getPrices(provider: ContractProvider, endpoints?: string[]) {
397
400
  if ((endpoints?.length ?? 0) > 0) {
398
401
  return await getPrices(endpoints, this._poolConfig);
@@ -400,4 +403,8 @@ export class Evaa implements Contract {
400
403
  return await getPrices(undefined, this._poolConfig);
401
404
  }
402
405
  }
406
+
407
+ createPriceCollector(priceSourcesConfig: PriceSourcesConfig = DefaultPriceSourcesConfig) : PricesCollector {
408
+ return new PricesCollector(this._poolConfig, priceSourcesConfig);
409
+ }
403
410
  }
package/src/index.ts CHANGED
@@ -14,6 +14,8 @@ export {
14
14
  calculateLiquidationData,
15
15
  predictHealthFactor,
16
16
  calculateHealthParams,
17
+ calculateInterestWithSupplyBorrow,
18
+ predictAPY,
17
19
  BigMath,
18
20
  } from './api/math';
19
21
 
@@ -56,7 +58,6 @@ export {
56
58
  export { EvaaUser } from './contracts/UserContract';
57
59
 
58
60
  // Types
59
- export { PriceData } from './types/Common';
60
61
  export {
61
62
  UpgradeConfig,
62
63
  AssetConfig,
@@ -102,7 +103,8 @@ export {
102
103
  export {
103
104
  MAINNET_POOL_CONFIG,
104
105
  TESTNET_POOL_CONFIG,
105
- MAINNET_LP_POOL_CONFIG
106
+ MAINNET_LP_POOL_CONFIG,
107
+ MAINNET_ALTS_POOL_CONFIG
106
108
  } from './constants/pools';
107
109
 
108
110
  export {
@@ -113,6 +115,10 @@ export {
113
115
  TONUSDT_DEDUST_MAINNET,
114
116
  TON_STORM_MAINNET,
115
117
  USDT_STORM_MAINNET,
118
+ DOGS_MAINNET,
119
+ CATI_MAINNET,
120
+ UTON_MAINNET,
121
+ NOT_MAINNET,
116
122
  JUSDT_MAINNET,
117
123
  JUSDC_MAINNET,
118
124
  STTON_MAINNET,
@@ -124,6 +130,7 @@ export {
124
130
 
125
131
  export * from './constants/assets';
126
132
  export * from './utils/utils';
133
+ export * from './prices';
127
134
 
128
135
  // Utils
129
136
  export { getLastSentBoc, getTonConnectSender } from './utils/tonConnectSender';
@@ -0,0 +1,32 @@
1
+ import { Cell, Dictionary } from "@ton/core";
2
+ import { PoolAssetConfig } from "../types/Master";
3
+
4
+ export class Prices {
5
+ #dict: Dictionary<bigint, bigint>;
6
+ #dataCell: Cell;
7
+ constructor(dict: Dictionary<bigint, bigint>, dataCell: Cell) {
8
+ this.#dict = dict;
9
+ this.#dataCell = dataCell;
10
+ }
11
+
12
+ get dict() {
13
+ const dict = Dictionary.empty<bigint, bigint>();
14
+ for (const [key, value] of this.#dict) {
15
+ dict.set(key, value);
16
+ }
17
+ return dict;
18
+ }
19
+
20
+ get dataCell() {
21
+ return new Cell(this.#dataCell);
22
+ }
23
+
24
+ getAssetPrice<T extends bigint | PoolAssetConfig>(asset: T): bigint | undefined {
25
+ const assetId = this.#extractAssetId(asset);
26
+ return this.#dict.get(assetId);
27
+ }
28
+
29
+ #extractAssetId(asset: bigint | PoolAssetConfig): bigint {
30
+ return typeof asset === 'bigint' ? asset : asset.assetId;
31
+ }
32
+ }
@@ -0,0 +1,139 @@
1
+ import { Cell, Dictionary } from "@ton/core";
2
+ import { MAINNET_POOL_CONFIG } from "../constants/pools";
3
+ import { PoolAssetConfig, PoolAssetsConfig, PoolConfig } from "../types/Master";
4
+ import { PriceSource } from "./sources";
5
+ import { DefaultPriceSourcesConfig, PriceSourcesConfig, RawPriceData } from "./Types";
6
+ import { collectAndFilterPrices, generatePriceSources, getMedianPrice, packAssetsData, packOraclesData, packPrices, verifyPricesSign, verifyPricesTimestamp } from "./utils";
7
+ import { delay } from "../utils/utils";
8
+ import { Prices } from "./Prices";
9
+ import { checkNotInDebtAtAll } from "../api/math";
10
+
11
+
12
+ export class PricesCollector {
13
+ #prices: RawPriceData[];
14
+ #poolConfig: PoolConfig;
15
+ #sourcesConfig: PriceSourcesConfig;
16
+ #priceSources: PriceSource[];
17
+
18
+ constructor(poolConfig: PoolConfig = MAINNET_POOL_CONFIG, sourcesConfig: PriceSourcesConfig = DefaultPriceSourcesConfig, additionalPriceSources?: PriceSource[]) {
19
+ this.#poolConfig = poolConfig;
20
+ this.#sourcesConfig = sourcesConfig;
21
+ this.#priceSources = generatePriceSources(this.#sourcesConfig, this.#poolConfig.oracles);
22
+
23
+ if (additionalPriceSources) {
24
+ this.#priceSources.push(...additionalPriceSources);
25
+ }
26
+
27
+ this.#prices = [];
28
+ }
29
+
30
+ // TODO Make UserData class and incapsulate raw bigintegers
31
+
32
+ async getPricesForLiquidate(realPrincipals: Dictionary<bigint, bigint>, retries: number = 1, timeout: number = 3000): Promise<Prices> {
33
+ const assets = this.#filterEmptyPrincipalsAndAssets(realPrincipals);
34
+ if (assets.includes(undefined)) {
35
+ throw new Error("User from another pool");
36
+ }
37
+ return await this.getPrices(assets.map(x => x!), retries, timeout);
38
+ }
39
+
40
+
41
+ async getPricesForWithdraw(realPrincipals: Dictionary<bigint, bigint>, withdrawAsset: PoolAssetConfig, collateralToDebt = false, retries: number = 1, timeout: number = 3000): Promise<Prices> {
42
+ let assets = this.#filterEmptyPrincipalsAndAssets(realPrincipals);
43
+ if (checkNotInDebtAtAll(realPrincipals) && (realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n && !collateralToDebt) {
44
+ return new Prices(Dictionary.empty<bigint, bigint>(), Cell.EMPTY);
45
+ }
46
+
47
+ if (assets.includes(undefined)) {
48
+ throw new Error("User from another pool");
49
+ }
50
+
51
+ if (!assets.includes(withdrawAsset)) {
52
+ assets.push(withdrawAsset);
53
+ }
54
+
55
+ if (collateralToDebt && assets.length == 1) {
56
+ throw new Error("Cannot debt only one supplied asset");
57
+ }
58
+
59
+ return await this.getPrices(assets.map(x => x!), retries, timeout);
60
+ }
61
+
62
+ async getPrices(assets: PoolAssetsConfig = this.#poolConfig.poolAssetsConfig, retries: number = 1, timeout: number = 3000): Promise<Prices> {
63
+ console.debug('[getPrices] Assets length', assets.length);
64
+
65
+ if (assets.length == 0) {
66
+ return new Prices(Dictionary.empty<bigint, bigint>(), Cell.EMPTY);
67
+ }
68
+
69
+ for (let i = 0; i <= retries; i++) { // attemts = retries + 1
70
+ if (!this.#prices || this.#filterPrices() < this.#poolConfig.minimalOracles) {
71
+ //console.debug('[getPrices] Load prices attemp', i + 1)
72
+ if (i > 0) {
73
+ await delay(timeout);
74
+ }
75
+ await this.#collectPrices();
76
+ } else {
77
+ break;
78
+ }
79
+ }
80
+
81
+ if (this.#prices.length < this.#poolConfig.minimalOracles) {
82
+ throw new Error(`Error per updating prices, valid ${this.#prices.length} of ${this.#poolConfig.minimalOracles}`); // if still not enough data after retries
83
+ }
84
+ const prices = this.#getPricesByAssetList(assets);
85
+ return new Prices(prices.dict, prices.dataCell);
86
+ }
87
+
88
+ #getPricesByAssetList(assets: PoolAssetsConfig) {
89
+ //console.debug('[getPricesByAssetList] start')
90
+ let pricesFiltered = this.#prices; // for strict check this.#prices.filter(x => assets.every(asset => x.dict.has(asset.assetId)));
91
+
92
+ if (pricesFiltered.length < this.#poolConfig.minimalOracles) {
93
+ throw new Error("Not enough price data");
94
+ }
95
+
96
+ if (pricesFiltered.length > this.#poolConfig.minimalOracles) {
97
+ const sortedByTimestamp = pricesFiltered.slice().sort((a, b) => b.timestamp - a.timestamp);
98
+ const newerPrices = sortedByTimestamp.slice(0, this.#poolConfig.minimalOracles);
99
+ pricesFiltered = newerPrices.sort((a, b) => a.oracleId - b.oracleId);
100
+ }
101
+
102
+ const medianData = assets.map(asset => ({ assetId: asset.assetId, medianPrice: getMedianPrice(this.#prices, asset.assetId)}));
103
+
104
+ const nonEmptymedianData = medianData.filter(x => x.medianPrice != null) as { assetId: bigint, medianPrice: bigint }[];
105
+
106
+ const packedMedianData = packAssetsData(nonEmptymedianData);
107
+
108
+ const oraclesData = this.#prices.map(x => ({oracle: {id: x.oracleId, pubkey: x.pubkey}, data: {timestamp: x.timestamp, prices: x.dict}, signature: x.signature}));
109
+ const packedOracleData = packOraclesData(oraclesData, nonEmptymedianData.map(x => x.assetId));
110
+
111
+ const dict = Dictionary.empty<bigint, bigint>();
112
+ for (const medianDataAsset of nonEmptymedianData) {
113
+ dict.set(medianDataAsset.assetId, medianDataAsset.medianPrice);
114
+ }
115
+
116
+ return {
117
+ dict: dict,
118
+ dataCell: packPrices(packedMedianData, packedOracleData)
119
+ }
120
+ }
121
+
122
+ async #collectPrices(): Promise<boolean> {
123
+ try {
124
+ this.#prices = await Promise.any(this.#priceSources.map(x => collectAndFilterPrices(x, this.#poolConfig)));
125
+ return true;
126
+ }
127
+ catch { }
128
+ return false;
129
+ }
130
+
131
+ #filterPrices(): number { // filter again for expire check
132
+ this.#prices = this.#prices.filter(verifyPricesTimestamp());
133
+ return this.#prices.length;
134
+ }
135
+
136
+ #filterEmptyPrincipalsAndAssets(principals: Dictionary<bigint, bigint>) {
137
+ return principals.keys().filter(x => principals.get(x)! != 0n).map(x => this.#poolConfig.poolAssetsConfig.find(asset => asset.assetId == x));
138
+ }
139
+ }