@evaafi/sdk 0.5.5 → 0.5.6

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 (46) hide show
  1. package/dist/api/helpers.js +2 -3
  2. package/dist/api/math.d.ts +22 -3
  3. package/dist/api/math.js +146 -63
  4. package/dist/api/parser.d.ts +3 -3
  5. package/dist/api/parser.js +37 -21
  6. package/dist/api/prices.js +1 -2
  7. package/dist/config.d.ts +2 -0
  8. package/dist/config.js +5 -0
  9. package/dist/constants/assets.d.ts +2 -3
  10. package/dist/constants/assets.js +10 -2
  11. package/dist/constants/general.d.ts +3 -2
  12. package/dist/constants/general.js +6 -5
  13. package/dist/constants/pools.d.ts +0 -1
  14. package/dist/constants/pools.js +1 -14
  15. package/dist/constants.d.ts +5 -34
  16. package/dist/constants.js +90 -37
  17. package/dist/contracts/MasterContract.d.ts +13 -45
  18. package/dist/contracts/MasterContract.js +8 -10
  19. package/dist/contracts/UserContract.js +7 -7
  20. package/dist/index.d.ts +4 -3
  21. package/dist/index.js +3 -2
  22. package/dist/types/Master.d.ts +8 -7
  23. package/dist/types/User.d.ts +5 -5
  24. package/dist/utils/merkleProof.d.ts +4 -0
  25. package/dist/utils/merkleProof.js +107 -0
  26. package/dist/utils/priceUtils.d.ts +55 -0
  27. package/dist/utils/priceUtils.js +117 -0
  28. package/dist/utils/sha256BigInt.js +1 -2
  29. package/dist/utils/tonConnectSender.js +2 -3
  30. package/dist/utils/userJettonWallet.d.ts +2 -2
  31. package/dist/utils/userJettonWallet.js +5 -2
  32. package/dist/utils/utils.d.ts +2 -0
  33. package/dist/utils/utils.js +6 -0
  34. package/package.json +2 -3
  35. package/src/api/math.ts +162 -49
  36. package/src/api/parser.ts +50 -25
  37. package/src/constants/assets.ts +11 -3
  38. package/src/constants/general.ts +6 -4
  39. package/src/constants/pools.ts +1 -15
  40. package/src/contracts/MasterContract.ts +23 -65
  41. package/src/contracts/UserContract.ts +8 -10
  42. package/src/index.ts +2 -6
  43. package/src/types/Master.ts +9 -8
  44. package/src/types/User.ts +5 -5
  45. package/src/utils/userJettonWallet.ts +6 -2
  46. package/src/utils/utils.ts +6 -0
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertToMerkleProof = convertToMerkleProof;
4
+ exports.generateMerkleProofDirect = generateMerkleProofDirect;
5
+ exports.generateMerkleProof = generateMerkleProof;
6
+ const core_1 = require("@ton/core");
7
+ function readUnaryLength(slice) {
8
+ let res = 0;
9
+ while (slice.loadBit()) {
10
+ res++;
11
+ }
12
+ return res;
13
+ }
14
+ function endExoticCell(b) {
15
+ let c = b.endCell();
16
+ return new core_1.Cell({ exotic: true, bits: c.bits, refs: c.refs });
17
+ }
18
+ function convertToMerkleProof(c) {
19
+ return endExoticCell((0, core_1.beginCell)()
20
+ .storeUint(3, 8)
21
+ .storeBuffer(c.hash(0))
22
+ .storeUint(c.depth(0), 16)
23
+ .storeRef(c));
24
+ }
25
+ function convertToPrunedBranch(c) {
26
+ return endExoticCell((0, core_1.beginCell)()
27
+ .storeUint(1, 8)
28
+ .storeUint(1, 8)
29
+ .storeBuffer(c.hash(0))
30
+ .storeUint(c.depth(0), 16));
31
+ }
32
+ function doGenerateMerkleProof(prefix, slice, n, keys) {
33
+ // Reading label
34
+ const originalCell = slice.asCell();
35
+ if (keys.length == 0) {
36
+ // no keys to prove, prune the whole subdict
37
+ return convertToPrunedBranch(originalCell);
38
+ }
39
+ let lb0 = slice.loadBit() ? 1 : 0;
40
+ let prefixLength = 0;
41
+ let pp = prefix;
42
+ if (lb0 === 0) {
43
+ // Short label detected
44
+ // Read
45
+ prefixLength = readUnaryLength(slice);
46
+ // Read prefix
47
+ for (let i = 0; i < prefixLength; i++) {
48
+ pp += slice.loadBit() ? '1' : '0';
49
+ }
50
+ }
51
+ else {
52
+ let lb1 = slice.loadBit() ? 1 : 0;
53
+ if (lb1 === 0) {
54
+ // Long label detected
55
+ prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1)));
56
+ for (let i = 0; i < prefixLength; i++) {
57
+ pp += slice.loadBit() ? '1' : '0';
58
+ }
59
+ }
60
+ else {
61
+ // Same label detected
62
+ let bit = slice.loadBit() ? '1' : '0';
63
+ prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1)));
64
+ for (let i = 0; i < prefixLength; i++) {
65
+ pp += bit;
66
+ }
67
+ }
68
+ }
69
+ if (n - prefixLength === 0) {
70
+ return originalCell;
71
+ }
72
+ else {
73
+ let sl = originalCell.beginParse();
74
+ let left = sl.loadRef();
75
+ let right = sl.loadRef();
76
+ // NOTE: Left and right branches are implicitly contain prefixes '0' and '1'
77
+ if (!left.isExotic) {
78
+ const leftKeys = keys.filter((key) => {
79
+ return pp + '0' === key.slice(0, pp.length + 1);
80
+ });
81
+ left = doGenerateMerkleProof(pp + '0', left.beginParse(), n - prefixLength - 1, leftKeys);
82
+ }
83
+ if (!right.isExotic) {
84
+ const rightKeys = keys.filter((key) => {
85
+ return pp + '1' === key.slice(0, pp.length + 1);
86
+ });
87
+ right = doGenerateMerkleProof(pp + '1', right.beginParse(), n - prefixLength - 1, rightKeys);
88
+ }
89
+ return (0, core_1.beginCell)()
90
+ .storeSlice(sl)
91
+ .storeRef(left)
92
+ .storeRef(right)
93
+ .endCell();
94
+ }
95
+ }
96
+ function generateMerkleProofDirect(dict, keys, keyObject) {
97
+ keys.forEach((key) => {
98
+ if (!dict.has(key)) {
99
+ throw new Error(`Trying to generate merkle proof for a missing key "${key}"`);
100
+ }
101
+ });
102
+ const s = (0, core_1.beginCell)().storeDictDirect(dict).asSlice();
103
+ return doGenerateMerkleProof('', s, keyObject.bits, keys.map((key) => keyObject.serialize(key).toString(2).padStart(keyObject.bits, '0')));
104
+ }
105
+ function generateMerkleProof(dict, keys, keyObject) {
106
+ return convertToMerkleProof(generateMerkleProofDirect(dict, keys, keyObject));
107
+ }
@@ -0,0 +1,55 @@
1
+ import { Cell, Dictionary, Slice } from "@ton/core";
2
+ import { PriceData, RawPriceData } from "../types/Common";
3
+ import { Oracle, PoolAssetsConfig } from "../types/Master";
4
+ import 'promise.any';
5
+ type OutputData = {
6
+ metadata: {
7
+ blockId: string;
8
+ transactionId: string;
9
+ outputIndex: number;
10
+ isSpent: boolean;
11
+ milestoneIndexSpent: number;
12
+ milestoneTimestampSpent: number;
13
+ transactionIdSpent: string;
14
+ milestoneIndexBooked: number;
15
+ milestoneTimestampBooked: number;
16
+ ledgerIndex: number;
17
+ };
18
+ output: {
19
+ type: number;
20
+ amount: string;
21
+ nftId: string;
22
+ unlockConditions: {
23
+ type: number;
24
+ address: {
25
+ type: number;
26
+ pubKeyHash: string;
27
+ };
28
+ }[];
29
+ features: {
30
+ type: number;
31
+ data: string;
32
+ }[];
33
+ };
34
+ };
35
+ export type OraclePricesData = {
36
+ timestamp: number;
37
+ prices: Dictionary<bigint, bigint>;
38
+ };
39
+ export declare function loadPrices(oracleNftId: String, endpoints: String[], timeout: number): Promise<OutputData>;
40
+ export declare function parsePrices(outputData: OutputData, oracleId: number): Promise<RawPriceData>;
41
+ export declare function verifyPrices(assets: PoolAssetsConfig): (priceData: RawPriceData) => boolean;
42
+ export declare function getMedianPrice(pricesData: PriceData[], asset: bigint): bigint;
43
+ export declare function packAssetsData(assetsData: {
44
+ assetId: bigint;
45
+ medianPrice: bigint;
46
+ }[]): Cell;
47
+ export declare function packPrices(assetsDataCell: Cell, oraclesDataCell: Cell): Cell;
48
+ export declare function createOracleDataProof(oracle: Oracle, data: OraclePricesData, signature: Buffer, assets: Array<bigint>): Slice;
49
+ export declare function packOraclesData(oraclesData: {
50
+ oracle: Oracle;
51
+ data: OraclePricesData;
52
+ signature: Buffer;
53
+ }[], assets: Array<bigint>): Cell;
54
+ export declare function sumDicts(result: Dictionary<bigint, bigint>, addendum: Dictionary<bigint, bigint>): void;
55
+ export {};
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadPrices = loadPrices;
4
+ exports.parsePrices = parsePrices;
5
+ exports.verifyPrices = verifyPrices;
6
+ exports.getMedianPrice = getMedianPrice;
7
+ exports.packAssetsData = packAssetsData;
8
+ exports.packPrices = packPrices;
9
+ exports.createOracleDataProof = createOracleDataProof;
10
+ exports.packOraclesData = packOraclesData;
11
+ exports.sumDicts = sumDicts;
12
+ const core_1 = require("@ton/core");
13
+ const config_1 = require("../config");
14
+ const merkleProof_1 = require("./merkleProof");
15
+ require("promise.any");
16
+ async function loadPrices(oracleNftId, endpoints, timeout) {
17
+ return await Promise.any(endpoints.map(x => loadOracleData(oracleNftId, x, timeout)));
18
+ }
19
+ async function loadOracleData(oracleNftId, endpoint, timeout) {
20
+ let result = await fetch(`https://${endpoint}/api/indexer/v1/outputs/nft/${oracleNftId}`, {
21
+ headers: { accept: 'application/json' },
22
+ signal: AbortSignal.timeout(timeout)
23
+ });
24
+ let outputId = (await result.json());
25
+ result = await fetch(`https://${endpoint}/api/core/v2/outputs/${outputId.items[0]}`, {
26
+ headers: { accept: 'application/json' },
27
+ signal: AbortSignal.timeout(timeout)
28
+ });
29
+ return await result.json();
30
+ }
31
+ async function parsePrices(outputData, oracleId) {
32
+ const data = JSON.parse(decodeURIComponent(outputData.output.features[0].data.replace('0x', '').replace(/[0-9a-f]{2}/g, '%$&')));
33
+ try {
34
+ const pricesCell = core_1.Cell.fromBoc(Buffer.from(data['packedPrices'], 'hex'))[0];
35
+ const signature = Buffer.from(data['signature'], 'hex');
36
+ const publicKey = Buffer.from(data['publicKey'], 'hex');
37
+ const timestamp = Number(data['timestamp']);
38
+ return {
39
+ dict: pricesCell.beginParse().loadRef().beginParse().loadDictDirect(core_1.Dictionary.Keys.BigUint(256), core_1.Dictionary.Values.BigVarUint(4)),
40
+ dataCell: (0, core_1.beginCell)().storeRef(pricesCell).storeBuffer(signature).endCell(),
41
+ oracleId: oracleId,
42
+ signature: signature,
43
+ pubkey: publicKey,
44
+ timestamp: timestamp,
45
+ };
46
+ }
47
+ catch (error) {
48
+ console.log(oracleId, data, error);
49
+ throw Error();
50
+ }
51
+ }
52
+ function verifyPrices(assets) {
53
+ return function (priceData) {
54
+ const timestamp = Date.now() / 1000;
55
+ const pricesTime = priceData.timestamp;
56
+ for (const [key, asset] of Object.entries(assets)) {
57
+ if (!priceData.dict.has(asset.assetId)) {
58
+ return false;
59
+ }
60
+ }
61
+ // console.log('timestamp', timestamp, 'pricestime', pricesTime, timestamp - pricesTime);
62
+ return timestamp - pricesTime < config_1.TTL_ORACLE_DATA_SEC;
63
+ };
64
+ }
65
+ function getMedianPrice(pricesData, asset) {
66
+ const sorted = pricesData.map(x => x.dict.get(asset)).sort((a, b) => Number(a) - Number(b));
67
+ const mid = Math.floor(sorted.length / 2);
68
+ if (sorted.length % 2 === 0) {
69
+ return (sorted[mid - 1] + sorted[mid]) / 2n;
70
+ }
71
+ else {
72
+ return sorted[mid];
73
+ }
74
+ }
75
+ function packAssetsData(assetsData) {
76
+ if (assetsData.length == 0) {
77
+ throw new Error("No assets data to pack");
78
+ }
79
+ return assetsData.reduceRight((acc, { assetId, medianPrice }) => (0, core_1.beginCell)()
80
+ .storeUint(assetId, 256)
81
+ .storeCoins(medianPrice)
82
+ .storeMaybeRef(acc)
83
+ .endCell(), null);
84
+ }
85
+ function packPrices(assetsDataCell, oraclesDataCell) {
86
+ let pricesCell = (0, core_1.beginCell)()
87
+ .storeRef(assetsDataCell)
88
+ .storeRef(oraclesDataCell)
89
+ .endCell();
90
+ return pricesCell;
91
+ }
92
+ function createOracleDataProof(oracle, data, signature, assets) {
93
+ let prunedDict = (0, merkleProof_1.generateMerkleProofDirect)(data.prices, assets, core_1.Dictionary.Keys.BigUint(256));
94
+ let prunedData = (0, core_1.beginCell)().storeUint(data.timestamp, 32).storeMaybeRef(prunedDict).endCell();
95
+ let merkleProof = (0, merkleProof_1.convertToMerkleProof)(prunedData);
96
+ let oracleDataProof = (0, core_1.beginCell)().storeUint(oracle.id, 32).storeRef(merkleProof).storeBuffer(signature).asSlice();
97
+ return oracleDataProof;
98
+ }
99
+ function packOraclesData(oraclesData, assets) {
100
+ if (oraclesData.length == 0) {
101
+ throw new Error("no oracles data to pack");
102
+ }
103
+ let proofs = oraclesData.sort((d1, d2) => d1.oracle.id - d2.oracle.id).map(({ oracle, data, signature }) => createOracleDataProof(oracle, data, signature, assets));
104
+ return proofs.reduceRight((acc, val) => (0, core_1.beginCell)().storeSlice(val).storeMaybeRef(acc).endCell(), null);
105
+ }
106
+ // : String = "api.stardust-mainnet.iotaledger.net"
107
+ function sumDicts(result, addendum) {
108
+ for (const key of addendum.keys()) {
109
+ const current = result.get(key);
110
+ const value = addendum.get(key);
111
+ if (current === undefined) {
112
+ result.set(key, value);
113
+ continue;
114
+ }
115
+ result.set(key, current + value);
116
+ }
117
+ }
@@ -3,11 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.sha256Hash = void 0;
6
+ exports.sha256Hash = sha256Hash;
7
7
  const sha256_1 = __importDefault(require("crypto-js/sha256"));
8
8
  function sha256Hash(input) {
9
9
  const hash = (0, sha256_1.default)(input);
10
10
  const hashHex = hash.toString();
11
11
  return BigInt('0x' + hashHex);
12
12
  }
13
- exports.sha256Hash = sha256Hash;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getTonConnectSender = exports.getLastSentBoc = void 0;
3
+ exports.getLastSentBoc = getLastSentBoc;
4
+ exports.getTonConnectSender = getTonConnectSender;
4
5
  const core_1 = require("@ton/core");
5
6
  /*
6
7
  This is not the best solution to get the BOC of the sent external message, however the Sender
@@ -11,7 +12,6 @@ let lastSentBoc;
11
12
  function getLastSentBoc() {
12
13
  return lastSentBoc;
13
14
  }
14
- exports.getLastSentBoc = getLastSentBoc;
15
15
  function getTonConnectSender(connector) {
16
16
  return {
17
17
  get address() {
@@ -34,4 +34,3 @@ function getTonConnectSender(connector) {
34
34
  },
35
35
  };
36
36
  }
37
- exports.getTonConnectSender = getTonConnectSender;
@@ -1,3 +1,3 @@
1
1
  import { Address } from '@ton/core';
2
- import { PoolAssetConfig, PoolJettonAssetConfig } from '../types/Master';
3
- export declare function getUserJettonWallet(ownerAddress: Address, poolAssetConfig: PoolAssetConfig & PoolJettonAssetConfig): Address;
2
+ import { PoolAssetConfig } from '../types/Master';
3
+ export declare function getUserJettonWallet(ownerAddress: Address, poolAssetConfig: PoolAssetConfig): Address;
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getUserJettonWallet = void 0;
3
+ exports.getUserJettonWallet = getUserJettonWallet;
4
4
  const core_1 = require("@ton/core");
5
+ const assets_1 = require("../constants/assets");
5
6
  function getUserJettonWallet(ownerAddress, poolAssetConfig) {
7
+ if (poolAssetConfig.name == 'TON' || poolAssetConfig.assetId === assets_1.UNDEFINED_ASSET.assetId) {
8
+ throw new Error(`Cant getUserJettonWallet for ${poolAssetConfig.name} asset`);
9
+ }
6
10
  const jettonMasterAddress = poolAssetConfig.jettonMasterAddress;
7
11
  const jettonWalletCode = poolAssetConfig.jettonWalletCode;
8
12
  if (poolAssetConfig.name === 'USDT') {
@@ -55,4 +59,3 @@ function getUserJettonWallet(ownerAddress, poolAssetConfig) {
55
59
  .endCell();
56
60
  return new core_1.Address(0, stateInit.hash());
57
61
  }
58
- exports.getUserJettonWallet = getUserJettonWallet;
@@ -0,0 +1,2 @@
1
+ import { PoolAssetConfig } from "../types/Master";
2
+ export declare function isTonAsset(asset: PoolAssetConfig): boolean;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isTonAsset = isTonAsset;
4
+ function isTonAsset(asset) {
5
+ return asset.name === 'TON';
6
+ }
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@evaafi/sdk",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "SDK for EVAA contracts",
5
5
  "main": "dist/index.js",
6
- "types": "src/index.ts",
7
6
  "files": [
8
7
  "dist",
9
8
  "src"
@@ -21,7 +20,7 @@
21
20
  "devDependencies": {
22
21
  "@orbs-network/ton-access": "^2.3.3",
23
22
  "@ton/core": "0.56.0",
24
- "@tonconnect/sdk": "^3.0.0",
23
+ "@tonconnect/sdk": "^3.0.5",
25
24
  "@types/jest": "^29.5.12",
26
25
  "@types/node": "^20.10.4",
27
26
  "crypto-js": "^4.2.0",
package/src/api/math.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { AssetConfig, AssetData, AssetInterest, ExtendedAssetData, ExtendedAssetsConfig, ExtendedAssetsData, MasterConstants } from '../types/Master';
1
+ import { AgregatedBalances, AssetConfig, AssetData, AssetInterest, ExtendedAssetData, ExtendedAssetsConfig, ExtendedAssetsData, MasterConstants, PoolConfig } from '../types/Master';
2
2
  import { Dictionary } from '@ton/core';
3
3
  import { BalanceChangeType, BalanceType, LiquidationData, PredictHealthFactorArgs, UserBalance } from '../types/User';
4
4
  import { sha256Hash } from '../utils/sha256BigInt';
5
+ import { TON_MAINNET, UNDEFINED_ASSET } from '../constants/assets';
6
+ import { MAINNET_POOL_CONFIG, TESTNET_POOL_CONFIG } from '..';
5
7
 
6
8
  export function mulFactor(decimal: bigint, a: bigint, b: bigint): bigint {
7
9
  return (a * b) / decimal;
@@ -125,13 +127,111 @@ export function checkNotInDebtAtAll(principals: Dictionary<bigint, bigint>): boo
125
127
  return principals.values().every(x => x >= 0n);
126
128
  }
127
129
 
128
- export function calculateMaximumWithdrawAmount(
130
+ export function getAgregatedBalances (
131
+ assetsData: ExtendedAssetsData,
132
+ assetsConfig: ExtendedAssetsConfig,
133
+ principals: Dictionary<bigint, bigint>,
134
+ prices: Dictionary<bigint, bigint>,
135
+ masterConstants: MasterConstants,
136
+ ): AgregatedBalances {
137
+ let user_total_supply = 0n;
138
+ let user_total_borrow = 0n;
139
+
140
+ for (const [assetId, principal] of principals) {
141
+
142
+ if (principal) {
143
+
144
+ if (!prices.has(assetId)) {
145
+ return {totalSupply: 0n, totalBorrow: 0n};
146
+ }
147
+ const price = prices.get(assetId)!;
148
+ const assetData = assetsData.get(assetId)!;
149
+ const assetConfig = assetsConfig.get(assetId)!;
150
+
151
+ if (principal < 0) {
152
+ user_total_borrow += presentValue(assetData.sRate, assetData.bRate, principal, masterConstants).amount * price / 10n ** assetConfig.decimals;
153
+ } else {
154
+ user_total_supply += presentValue(assetData.sRate, assetData.bRate, principal, masterConstants).amount * price / 10n ** assetConfig.decimals;
155
+ }
156
+
157
+ }
158
+ }
159
+ return {totalSupply: user_total_supply, totalBorrow: user_total_borrow};
160
+ }
161
+
162
+ /**
163
+ * @deprecated The method should be used only for main contract v5
164
+ */
165
+ export function isV5MainPoolContract(poolConfig: PoolConfig): boolean {
166
+ if ((poolConfig.masterAddress == MAINNET_POOL_CONFIG.masterAddress && poolConfig.masterVersion == 5) ||
167
+ (poolConfig.masterAddress == TESTNET_POOL_CONFIG.masterAddress && poolConfig.masterVersion == 5)) {
168
+ return true;
169
+ } else {
170
+ return false;
171
+ }
172
+ }
173
+
174
+ /**
175
+ * @deprecated The method should be used only for main contract v5
176
+ */
177
+ export function calculateMaximumWithdrawAmountOld(
129
178
  assetsConfig: ExtendedAssetsConfig,
130
179
  assetsData: ExtendedAssetsData,
131
180
  principals: Dictionary<bigint, bigint>,
132
181
  prices: Dictionary<bigint, bigint>,
133
182
  masterConstants: MasterConstants,
134
- assetId: bigint
183
+ assetId: bigint,
184
+ ): bigint {
185
+ let withdrawAmountMax = 0n;
186
+
187
+ const assetConfig = assetsConfig.get(assetId) as AssetConfig;
188
+ const assetData = assetsData.get(assetId) as ExtendedAssetData;
189
+ const oldPrincipal = principals.get(assetId) as bigint;
190
+ const oldPresentValue = presentValue(assetData.sRate, assetData.bRate, oldPrincipal, masterConstants);
191
+
192
+ if (oldPresentValue.amount > assetConfig.dust) {
193
+ if(checkNotInDebtAtAll(principals)) {
194
+ withdrawAmountMax = oldPresentValue.amount;
195
+ } else {
196
+ if (!prices.has(assetId)) {
197
+ return 0n;
198
+ }
199
+
200
+ //const borrowable = getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants);
201
+ const price = prices.get(assetId) as bigint;
202
+ const agregatedBalances = getAgregatedBalances(assetsData, assetsConfig, principals, principals, masterConstants);
203
+ let maxAmountToReclaim =
204
+ mulDiv(
205
+ agregatedBalances.totalSupply - mulDivC(agregatedBalances.totalBorrow, masterConstants.ASSET_COEFFICIENT_SCALE, assetConfig.collateralFactor),
206
+ 10n**assetConfig.decimals,
207
+ price
208
+ );
209
+
210
+ withdrawAmountMax = bigIntMin(
211
+ maxAmountToReclaim,
212
+ oldPresentValue.amount
213
+ );
214
+ }
215
+ } else {
216
+ if (!prices.has(assetId)) {
217
+ return 0n;
218
+ }
219
+
220
+ const price = prices.get(assetId) as bigint;
221
+
222
+ return getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants) * (10n ** assetConfig.decimals) / price;
223
+ }
224
+
225
+ return withdrawAmountMax;
226
+ }
227
+
228
+ export function calculateMaximumWithdrawAmount( // todo v6 ifelse dust not debt at all is fixed?
229
+ assetsConfig: ExtendedAssetsConfig,
230
+ assetsData: ExtendedAssetsData,
231
+ principals: Dictionary<bigint, bigint>,
232
+ prices: Dictionary<bigint, bigint>,
233
+ masterConstants: MasterConstants,
234
+ assetId: bigint,
135
235
  ): bigint {
136
236
  let withdrawAmountMax = 0n;
137
237
 
@@ -152,6 +252,7 @@ export function calculateMaximumWithdrawAmount(
152
252
  const price = prices.get(assetId) as bigint;
153
253
 
154
254
  let maxAmountToReclaim = 0n;
255
+
155
256
  if (assetConfig.collateralFactor == 0n) {
156
257
  maxAmountToReclaim = oldPresentValue.amount;
157
258
  }
@@ -230,74 +331,86 @@ export function presentValue(sRate: bigint, bRate: bigint, principalValue: bigin
230
331
  }
231
332
  }
232
333
 
334
+ /**
335
+ *
336
+ * @param assetsConfig
337
+ * @param assetsData
338
+ * @param principals
339
+ * @param prices
340
+ * @param poolConfig
341
+ * @returns can return UNDEFINED_ASSET if there are no assets
342
+ */
233
343
  export function calculateLiquidationData(
234
344
  assetsConfig: ExtendedAssetsConfig,
235
345
  assetsData: ExtendedAssetsData,
236
346
  principals: Dictionary<bigint, bigint>,
237
347
  prices: Dictionary<bigint, bigint>,
238
- masterConstants: MasterConstants,
348
+ poolConfig: PoolConfig,
239
349
  ): LiquidationData {
240
- let gCollateralValue = 0n;
241
- let gCollateralAsset = 0n;
242
- let gLoanValue = 0n;
243
- let gLoanAsset = 0n;
350
+ let collateralValue = 0n;
351
+ let collateralAsset = UNDEFINED_ASSET;
352
+ let loanValue = 0n;
353
+ let loanAsset = UNDEFINED_ASSET;
244
354
  let totalDebt = 0n;
245
355
  let totalLimit = 0n;
246
356
 
247
- for (const key of principals.keys()) {
248
- const principal = principals.get(key)!;
249
- const assetConfig = assetsConfig.get(key)!;
250
- const assetData = assetsData.get(key)!;
357
+ for (const asset of poolConfig.poolAssetsConfig) {
358
+ if (!principals.has(asset.assetId)) {
359
+ continue;
360
+ }
361
+ const principal = principals.get(asset.assetId)!;
362
+ const assetConfig = assetsConfig.get(asset.assetId)!;
363
+ const assetData = assetsData.get(asset.assetId)!;
251
364
  const balance =
252
365
  principal > 0 ? (principal * assetData.sRate) / BigInt(1e12) : (principal * assetData.bRate) / BigInt(1e12);
253
366
  if (balance > 0) {
254
- const assetWorth = (balance * prices.get(key)!) / 10n ** assetConfig.decimals;
255
- totalLimit += (assetWorth * assetConfig.liquidationThreshold) / masterConstants.ASSET_COEFFICIENT_SCALE;
256
- if (assetWorth > gCollateralValue) {
257
- gCollateralValue = assetWorth;
258
- gCollateralAsset = key;
367
+ const assetWorth = (balance * prices.get(asset.assetId)!) / 10n ** assetConfig.decimals;
368
+ totalLimit += (assetWorth * assetConfig.liquidationThreshold) / poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE;
369
+ if (assetWorth > collateralValue) {
370
+ collateralValue = assetWorth;
371
+ collateralAsset = asset;
259
372
  }
260
373
  } else if (balance < 0) {
261
- const assetWorth = (-balance * prices.get(key)!) / 10n ** assetConfig.decimals;
374
+ const assetWorth = (-balance * prices.get(asset.assetId)!) / 10n ** assetConfig.decimals;
262
375
  totalDebt += assetWorth;
263
- if (assetWorth > gLoanValue) {
264
- gLoanValue = assetWorth;
265
- gLoanAsset = key;
376
+ if (assetWorth > loanValue) {
377
+ loanValue = assetWorth;
378
+ loanAsset = asset;
266
379
  }
267
380
  }
268
381
  }
269
382
 
270
- if (totalLimit < totalDebt) {
271
- const gLoanAssetPrice = prices.get(gLoanAsset)!;
383
+ if (collateralAsset.assetId !== UNDEFINED_ASSET.assetId && totalLimit < totalDebt) {
384
+ const loanAssetPrice = prices.get(loanAsset.assetId)!;
272
385
  const values: bigint[] = [];
273
- const gCollateralAssetConfig = assetsConfig.get(gCollateralAsset)!;
274
- const gLoanAssetConfig = assetsConfig.get(gLoanAsset)!;
275
- const liquidationBonus = gCollateralAssetConfig.liquidationBonus;
276
- const loanDecimal = 10n ** gLoanAssetConfig.decimals;
386
+ const collateralAssetConfig = assetsConfig.get(collateralAsset.assetId)!;
387
+ const loanAssetConfig = assetsConfig.get(loanAsset.assetId)!;
388
+ const liquidationBonus = collateralAssetConfig.liquidationBonus;
389
+ const loanScale = 10n ** loanAssetConfig.decimals;
277
390
  values.push(
278
- (bigIntMax(gCollateralValue / 2n, bigIntMin(gCollateralValue, 10_000_000_000n)) *
279
- loanDecimal *
280
- masterConstants.ASSET_COEFFICIENT_SCALE) /
391
+ (bigIntMax(collateralValue / 2n, bigIntMin(collateralValue, 100_000_000_000n)) *
392
+ loanScale *
393
+ poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE) /
281
394
  liquidationBonus /
282
- gLoanAssetPrice,
395
+ loanAssetPrice,
283
396
  );
284
- values.push((gLoanValue * loanDecimal) / gLoanAssetPrice);
397
+ values.push((loanValue * loanScale) / loanAssetPrice);
285
398
 
286
399
  const liquidationAmount = (bigIntMin(...values) as bigint) - 5n;
287
- const gCollateralAssetPrice: bigint = prices.get(gCollateralAsset)!;
288
- const collateralDecimal = 10n ** gCollateralAssetConfig.decimals;
400
+ const collateralAssetPrice: bigint = prices.get(collateralAsset.assetId)!;
401
+ const collateralDecimal = 10n ** collateralAssetConfig.decimals;
289
402
  let minCollateralAmount =
290
- (((liquidationAmount * gLoanAssetPrice * liquidationBonus) / 10000n) * collateralDecimal) /
291
- gCollateralAssetPrice /
292
- loanDecimal -
403
+ (((liquidationAmount * loanAssetPrice * liquidationBonus) / poolConfig.masterConstants.ASSET_LIQUIDATION_BONUS_SCALE) * collateralDecimal) /
404
+ collateralAssetPrice /
405
+ loanScale -
293
406
  10n;
294
407
  minCollateralAmount = (minCollateralAmount * 97n) / 100n;
295
408
  if (minCollateralAmount / collateralDecimal >= 1n) {
296
409
  return {
297
- greatestCollateralAsset: gCollateralAsset,
298
- greatestCollateralValue: gCollateralValue,
299
- greatestLoanAsset: gLoanAsset,
300
- greatestLoanValue: gLoanValue,
410
+ greatestCollateralAsset: collateralAsset,
411
+ greatestCollateralValue: collateralValue,
412
+ greatestLoanAsset: loanAsset,
413
+ greatestLoanValue: loanValue,
301
414
  totalDebt,
302
415
  totalLimit,
303
416
  liquidable: true,
@@ -308,10 +421,10 @@ export function calculateLiquidationData(
308
421
  }
309
422
 
310
423
  return {
311
- greatestCollateralAsset: gCollateralAsset,
312
- greatestCollateralValue: gCollateralValue,
313
- greatestLoanAsset: gLoanAsset,
314
- greatestLoanValue: gLoanValue,
424
+ greatestCollateralAsset: collateralAsset,
425
+ greatestCollateralValue: collateralValue,
426
+ greatestLoanAsset: loanAsset,
427
+ greatestLoanValue: loanValue,
315
428
  totalDebt,
316
429
  totalLimit,
317
430
  liquidable: false,
@@ -319,7 +432,7 @@ export function calculateLiquidationData(
319
432
  }
320
433
 
321
434
  export function predictHealthFactor(args: PredictHealthFactorArgs): number {
322
- const liquidationData = calculateLiquidationData(args.assetsConfig, args.assetsData, args.balances, args.prices, args.masterConstants);
435
+ const liquidationData = calculateLiquidationData(args.assetsConfig, args.assetsData, args.principals, args.prices, args.poolConfig);
323
436
  const tokenHash = sha256Hash(args.tokenSymbol);
324
437
 
325
438
  const assetConfig = args.assetsConfig.get(tokenHash)!;
@@ -337,13 +450,13 @@ export function predictHealthFactor(args: PredictHealthFactorArgs): number {
337
450
 
338
451
  if (currentAmount != null && currentAmount != 0n) {
339
452
  if (changeType == BalanceChangeType.Borrow) {
340
- totalBorrow += currentBalance * (1 + Number(assetConfig.originationFee) / Number(args.masterConstants.ASSET_ORIGINATION_FEE_SCALE));
453
+ totalBorrow += currentBalance * (1 + Number(assetConfig.originationFee) / Number(args.poolConfig.masterConstants.ASSET_ORIGINATION_FEE_SCALE));
341
454
  } else if (changeType == BalanceChangeType.Repay) {
342
455
  totalBorrow -= currentBalance;
343
456
  } else if (changeType == BalanceChangeType.Withdraw) {
344
- totalLimit -= currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.masterConstants.ASSET_COEFFICIENT_SCALE);
457
+ totalLimit -= currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
345
458
  } else if (changeType == BalanceChangeType.Supply) {
346
- totalLimit += currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.masterConstants.ASSET_COEFFICIENT_SCALE);
459
+ totalLimit += currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
347
460
  }
348
461
  }
349
462
  if (Number(totalLimit) == 0) {