@evaafi/sdk 0.5.4 → 0.5.6-a
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.
- package/dist/api/math.d.ts +24 -2
- package/dist/api/math.js +185 -50
- package/dist/api/parser.d.ts +3 -3
- package/dist/api/parser.js +32 -19
- package/dist/api/prices.d.ts +1 -1
- package/dist/api/prices.js +4 -3
- package/dist/config.d.ts +1 -0
- package/dist/config.js +4 -0
- package/dist/constants/assets.d.ts +2 -3
- package/dist/constants/assets.js +10 -2
- package/dist/constants/general.d.ts +3 -2
- package/dist/constants/general.js +6 -5
- package/dist/constants/pools.d.ts +0 -1
- package/dist/constants/pools.js +1 -14
- package/dist/constants.d.ts +4 -4
- package/dist/constants.js +15 -7
- package/dist/contracts/MasterContract.d.ts +13 -45
- package/dist/contracts/MasterContract.js +10 -12
- package/dist/contracts/UserContract.js +7 -7
- package/dist/index.d.ts +6 -5
- package/dist/index.js +5 -3
- package/dist/types/Master.d.ts +8 -7
- package/dist/types/User.d.ts +5 -5
- package/dist/utils/merkleProof.d.ts +4 -0
- package/dist/utils/merkleProof.js +108 -0
- package/dist/utils/priceUtils.d.ts +55 -0
- package/dist/utils/priceUtils.js +117 -0
- package/dist/utils/userJettonWallet.d.ts +2 -2
- package/dist/utils/userJettonWallet.js +4 -0
- package/dist/utils/utils.d.ts +2 -0
- package/dist/utils/utils.js +7 -0
- package/package.json +2 -2
- package/src/api/math.ts +234 -50
- package/src/api/parser.ts +52 -37
- package/src/api/prices.ts +2 -1
- package/src/constants/assets.ts +11 -3
- package/src/constants/general.ts +6 -4
- package/src/constants/pools.ts +1 -15
- package/src/contracts/MasterContract.ts +25 -67
- package/src/contracts/UserContract.ts +8 -10
- package/src/index.ts +4 -7
- package/src/types/Master.ts +9 -8
- package/src/types/User.ts +5 -5
- package/src/utils/userJettonWallet.ts +6 -2
- package/src/utils/utils.ts +6 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sumDicts = exports.packOraclesData = exports.createOracleDataProof = exports.packPrices = exports.packAssetsData = exports.getMedianPrice = exports.verifyPrices = exports.parsePrices = exports.loadPrices = void 0;
|
|
4
|
+
const core_1 = require("@ton/core");
|
|
5
|
+
const config_1 = require("../config");
|
|
6
|
+
const merkleProof_1 = require("./merkleProof");
|
|
7
|
+
async function loadPrices(oracleNftId, endpoints) {
|
|
8
|
+
return await Promise.any(endpoints.map(x => loadOracleData(oracleNftId, x)));
|
|
9
|
+
}
|
|
10
|
+
exports.loadPrices = loadPrices;
|
|
11
|
+
async function loadOracleData(oracleNftId, endpoint) {
|
|
12
|
+
let result = await fetch(`https://${endpoint}/api/indexer/v1/outputs/nft/${oracleNftId}`, {
|
|
13
|
+
headers: { accept: 'application/json' },
|
|
14
|
+
signal: AbortSignal.timeout(5000)
|
|
15
|
+
});
|
|
16
|
+
let outputId = (await result.json());
|
|
17
|
+
result = await fetch(`https://${endpoint}/api/core/v2/outputs/${outputId.items[0]}`, {
|
|
18
|
+
headers: { accept: 'application/json' },
|
|
19
|
+
signal: AbortSignal.timeout(5000)
|
|
20
|
+
});
|
|
21
|
+
return await result.json();
|
|
22
|
+
}
|
|
23
|
+
async function parsePrices(outputData, oracleId) {
|
|
24
|
+
const data = JSON.parse(decodeURIComponent(outputData.output.features[0].data.replace('0x', '').replace(/[0-9a-f]{2}/g, '%$&')));
|
|
25
|
+
try {
|
|
26
|
+
const pricesCell = core_1.Cell.fromBoc(Buffer.from(data['packedPrices'], 'hex'))[0];
|
|
27
|
+
const signature = Buffer.from(data['signature'], 'hex');
|
|
28
|
+
const publicKey = Buffer.from(data['publicKey'], 'hex');
|
|
29
|
+
const timestamp = Number(data['timestamp']);
|
|
30
|
+
return {
|
|
31
|
+
dict: pricesCell.beginParse().loadRef().beginParse().loadDictDirect(core_1.Dictionary.Keys.BigUint(256), core_1.Dictionary.Values.BigVarUint(4)),
|
|
32
|
+
dataCell: (0, core_1.beginCell)().storeRef(pricesCell).storeBuffer(signature).endCell(),
|
|
33
|
+
oracleId: oracleId,
|
|
34
|
+
signature: signature,
|
|
35
|
+
pubkey: publicKey,
|
|
36
|
+
timestamp: timestamp,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.log(oracleId, data, error);
|
|
41
|
+
throw Error();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.parsePrices = parsePrices;
|
|
45
|
+
function verifyPrices(assets) {
|
|
46
|
+
return function (priceData) {
|
|
47
|
+
const timestamp = Date.now() / 1000;
|
|
48
|
+
const pricesTime = priceData.timestamp;
|
|
49
|
+
for (const asset of assets) {
|
|
50
|
+
if (!priceData.dict.has(asset.assetId)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// console.log('timestamp', timestamp, 'pricestime', pricesTime, timestamp - pricesTime);
|
|
55
|
+
return timestamp - pricesTime < config_1.TTL_ORACLE_DATA_SEC;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
exports.verifyPrices = verifyPrices;
|
|
59
|
+
function getMedianPrice(pricesData, asset) {
|
|
60
|
+
const sorted = pricesData.map(x => x.dict.get(asset)).sort((a, b) => Number(a) - Number(b));
|
|
61
|
+
const mid = Math.floor(sorted.length / 2);
|
|
62
|
+
if (sorted.length % 2 === 0) {
|
|
63
|
+
return (sorted[mid - 1] + sorted[mid]) / 2n;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
return sorted[mid];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.getMedianPrice = getMedianPrice;
|
|
70
|
+
function packAssetsData(assetsData) {
|
|
71
|
+
if (assetsData.length == 0) {
|
|
72
|
+
throw new Error("No assets data to pack");
|
|
73
|
+
}
|
|
74
|
+
return assetsData.reduceRight((acc, { assetId, medianPrice }) => (0, core_1.beginCell)()
|
|
75
|
+
.storeUint(assetId, 256)
|
|
76
|
+
.storeCoins(medianPrice)
|
|
77
|
+
.storeMaybeRef(acc)
|
|
78
|
+
.endCell(), null);
|
|
79
|
+
}
|
|
80
|
+
exports.packAssetsData = packAssetsData;
|
|
81
|
+
function packPrices(assetsDataCell, oraclesDataCell) {
|
|
82
|
+
let pricesCell = (0, core_1.beginCell)()
|
|
83
|
+
.storeRef(assetsDataCell)
|
|
84
|
+
.storeRef(oraclesDataCell)
|
|
85
|
+
.endCell();
|
|
86
|
+
return pricesCell;
|
|
87
|
+
}
|
|
88
|
+
exports.packPrices = packPrices;
|
|
89
|
+
function createOracleDataProof(oracle, data, signature, assets) {
|
|
90
|
+
let prunedDict = (0, merkleProof_1.generateMerkleProofDirect)(data.prices, assets, core_1.Dictionary.Keys.BigUint(256));
|
|
91
|
+
let prunedData = (0, core_1.beginCell)().storeUint(data.timestamp, 32).storeMaybeRef(prunedDict).endCell();
|
|
92
|
+
let merkleProof = (0, merkleProof_1.convertToMerkleProof)(prunedData);
|
|
93
|
+
let oracleDataProof = (0, core_1.beginCell)().storeUint(oracle.id, 32).storeRef(merkleProof).storeBuffer(signature).asSlice();
|
|
94
|
+
return oracleDataProof;
|
|
95
|
+
}
|
|
96
|
+
exports.createOracleDataProof = createOracleDataProof;
|
|
97
|
+
function packOraclesData(oraclesData, assets) {
|
|
98
|
+
if (oraclesData.length == 0) {
|
|
99
|
+
throw new Error("no oracles data to pack");
|
|
100
|
+
}
|
|
101
|
+
let proofs = oraclesData.sort((d1, d2) => d1.oracle.id - d2.oracle.id).map(({ oracle, data, signature }) => createOracleDataProof(oracle, data, signature, assets));
|
|
102
|
+
return proofs.reduceRight((acc, val) => (0, core_1.beginCell)().storeSlice(val).storeMaybeRef(acc).endCell(), null);
|
|
103
|
+
}
|
|
104
|
+
exports.packOraclesData = packOraclesData;
|
|
105
|
+
// : String = "api.stardust-mainnet.iotaledger.net"
|
|
106
|
+
function sumDicts(result, addendum) {
|
|
107
|
+
for (const key of addendum.keys()) {
|
|
108
|
+
const current = result.get(key);
|
|
109
|
+
const value = addendum.get(key);
|
|
110
|
+
if (current === undefined) {
|
|
111
|
+
result.set(key, value);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
result.set(key, current + value);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.sumDicts = sumDicts;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Address } from '@ton/core';
|
|
2
|
-
import { PoolAssetConfig
|
|
3
|
-
export declare function getUserJettonWallet(ownerAddress: Address, poolAssetConfig: PoolAssetConfig
|
|
2
|
+
import { PoolAssetConfig } from '../types/Master';
|
|
3
|
+
export declare function getUserJettonWallet(ownerAddress: Address, poolAssetConfig: PoolAssetConfig): Address;
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getUserJettonWallet = void 0;
|
|
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') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evaafi/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6a",
|
|
4
4
|
"description": "SDK for EVAA contracts",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@orbs-network/ton-access": "^2.3.3",
|
|
22
22
|
"@ton/core": "0.56.0",
|
|
23
|
-
"@tonconnect/sdk": "^3.0.
|
|
23
|
+
"@tonconnect/sdk": "^3.0.5",
|
|
24
24
|
"@types/jest": "^29.5.12",
|
|
25
25
|
"@types/node": "^20.10.4",
|
|
26
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;
|
|
@@ -11,6 +13,12 @@ export function mulDiv(x: bigint, y: bigint, z: bigint): bigint {
|
|
|
11
13
|
return (x * y) / z;
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
export function mulDivC(x: bigint, y: bigint, z: bigint): bigint {
|
|
17
|
+
//const mul = x * y;
|
|
18
|
+
//return mul / z + (mul % z ? 1n : 0n);
|
|
19
|
+
return BigInt(Math.ceil(Number(x * y) / Number(z)));
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
export function bigIntMax(...args: bigint[]): bigint {
|
|
15
23
|
return args.reduce((m, e) => (e > m ? e : m));
|
|
16
24
|
}
|
|
@@ -116,6 +124,169 @@ export function calculateAssetInterest(assetConfig: AssetConfig, assetData: Asse
|
|
|
116
124
|
};
|
|
117
125
|
}
|
|
118
126
|
|
|
127
|
+
export function checkNotInDebtAtAll(principals: Dictionary<bigint, bigint>): boolean {
|
|
128
|
+
return principals.values().every(x => x >= 0n);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function getAgregatedBalances (
|
|
132
|
+
assetsData: ExtendedAssetsData,
|
|
133
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
134
|
+
principals: Dictionary<bigint, bigint>,
|
|
135
|
+
prices: Dictionary<bigint, bigint>,
|
|
136
|
+
masterConstants: MasterConstants,
|
|
137
|
+
): AgregatedBalances {
|
|
138
|
+
let user_total_supply = 0n;
|
|
139
|
+
let user_total_borrow = 0n;
|
|
140
|
+
|
|
141
|
+
for (const [assetId, principal] of principals) {
|
|
142
|
+
|
|
143
|
+
if (principal) {
|
|
144
|
+
|
|
145
|
+
if (!prices.has(assetId)) {
|
|
146
|
+
return {totalSupply: 0n, totalBorrow: 0n};
|
|
147
|
+
}
|
|
148
|
+
const price = prices.get(assetId)!;
|
|
149
|
+
const assetData = assetsData.get(assetId)!;
|
|
150
|
+
const assetConfig = assetsConfig.get(assetId)!;
|
|
151
|
+
// console.log('price', price);
|
|
152
|
+
if (principal < 0) {
|
|
153
|
+
user_total_borrow += presentValue(assetData.sRate, assetData.bRate, principal, masterConstants).amount * price / 10n ** assetConfig.decimals;
|
|
154
|
+
} else {
|
|
155
|
+
user_total_supply += presentValue(assetData.sRate, assetData.bRate, principal, masterConstants).amount * price / 10n ** assetConfig.decimals;
|
|
156
|
+
}
|
|
157
|
+
// console.log('aggregated', assetId, presentValue(assetData.sRate, assetData.bRate, principal, masterConstants).type, presentValue(assetData.sRate, assetData.bRate, principal, masterConstants).amount * price / 10n ** assetConfig.decimals)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {totalSupply: user_total_supply, totalBorrow: user_total_borrow};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @deprecated The method should be used only for main contract v5
|
|
165
|
+
*/
|
|
166
|
+
export function isV5MainPoolContract(poolConfig: PoolConfig): boolean {
|
|
167
|
+
if ((poolConfig.masterAddress == MAINNET_POOL_CONFIG.masterAddress && poolConfig.masterVersion == 5) ||
|
|
168
|
+
(poolConfig.masterAddress == TESTNET_POOL_CONFIG.masterAddress && poolConfig.masterVersion == 5)) {
|
|
169
|
+
return true;
|
|
170
|
+
} else {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @deprecated The method should be used only for main contract v5
|
|
177
|
+
*/
|
|
178
|
+
export function calculateMaximumWithdrawAmountOld(
|
|
179
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
180
|
+
assetsData: ExtendedAssetsData,
|
|
181
|
+
principals: Dictionary<bigint, bigint>,
|
|
182
|
+
prices: Dictionary<bigint, bigint>,
|
|
183
|
+
masterConstants: MasterConstants,
|
|
184
|
+
assetId: bigint,
|
|
185
|
+
): bigint {
|
|
186
|
+
let withdrawAmountMax = 0n;
|
|
187
|
+
|
|
188
|
+
const assetConfig = assetsConfig.get(assetId) as AssetConfig;
|
|
189
|
+
const assetData = assetsData.get(assetId) as ExtendedAssetData;
|
|
190
|
+
const oldPrincipal = principals.get(assetId) as bigint;
|
|
191
|
+
const oldPresentValue = presentValue(assetData.sRate, assetData.bRate, oldPrincipal, masterConstants);
|
|
192
|
+
|
|
193
|
+
if (oldPresentValue.amount > assetConfig.dust) {
|
|
194
|
+
if(checkNotInDebtAtAll(principals)) {
|
|
195
|
+
withdrawAmountMax = oldPresentValue.amount;
|
|
196
|
+
} else {
|
|
197
|
+
if (!prices.has(assetId)) {
|
|
198
|
+
return 0n;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//const borrowable = getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants);
|
|
202
|
+
const price = prices.get(assetId) as bigint;
|
|
203
|
+
const agregatedBalances = getAgregatedBalances(assetsData, assetsConfig, principals, prices, masterConstants);
|
|
204
|
+
let maxAmountToReclaim =
|
|
205
|
+
mulDiv(
|
|
206
|
+
agregatedBalances.totalSupply - mulDivC(agregatedBalances.totalBorrow, masterConstants.ASSET_COEFFICIENT_SCALE, assetConfig.collateralFactor),
|
|
207
|
+
10n**assetConfig.decimals,
|
|
208
|
+
price
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
withdrawAmountMax = bigIntMin(
|
|
212
|
+
maxAmountToReclaim,
|
|
213
|
+
oldPresentValue.amount
|
|
214
|
+
);
|
|
215
|
+
//console.log('agregatedBalances', agregatedBalances);
|
|
216
|
+
//console.log('masterConstants.ASSET_COEFFICIENT_SCALE', masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
217
|
+
//console.log('assetConfig.collateralFactor', assetConfig.collateralFactor);
|
|
218
|
+
//console.log('sekay', maxAmountToReclaim, oldPresentValue.amount);
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
if (!prices.has(assetId)) {
|
|
222
|
+
return 0n;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const price = prices.get(assetId) as bigint;
|
|
226
|
+
|
|
227
|
+
return getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants) * (10n ** assetConfig.decimals) / price;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return withdrawAmountMax;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function calculateMaximumWithdrawAmount( // todo v6 ifelse dust not debt at all is fixed?
|
|
234
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
235
|
+
assetsData: ExtendedAssetsData,
|
|
236
|
+
principals: Dictionary<bigint, bigint>,
|
|
237
|
+
prices: Dictionary<bigint, bigint>,
|
|
238
|
+
masterConstants: MasterConstants,
|
|
239
|
+
assetId: bigint,
|
|
240
|
+
): bigint {
|
|
241
|
+
let withdrawAmountMax = 0n;
|
|
242
|
+
|
|
243
|
+
const assetConfig = assetsConfig.get(assetId) as AssetConfig;
|
|
244
|
+
const assetData = assetsData.get(assetId) as ExtendedAssetData;
|
|
245
|
+
const oldPrincipal = principals.get(assetId) as bigint;
|
|
246
|
+
|
|
247
|
+
if (oldPrincipal > assetConfig.dust) {
|
|
248
|
+
const oldPresentValue = presentValue(assetData.sRate, assetData.bRate, oldPrincipal, masterConstants);
|
|
249
|
+
if(checkNotInDebtAtAll(principals)) {
|
|
250
|
+
withdrawAmountMax = oldPresentValue.amount;
|
|
251
|
+
} else {
|
|
252
|
+
if (!prices.has(assetId)) {
|
|
253
|
+
return 0n;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const borrowable = getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants);
|
|
257
|
+
const price = prices.get(assetId) as bigint;
|
|
258
|
+
|
|
259
|
+
let maxAmountToReclaim = 0n;
|
|
260
|
+
|
|
261
|
+
if (assetConfig.collateralFactor == 0n) {
|
|
262
|
+
maxAmountToReclaim = oldPresentValue.amount;
|
|
263
|
+
}
|
|
264
|
+
else if (price > 0) {
|
|
265
|
+
maxAmountToReclaim =
|
|
266
|
+
mulDiv(
|
|
267
|
+
mulDivC(borrowable, masterConstants.ASSET_COEFFICIENT_SCALE, assetConfig.collateralFactor),
|
|
268
|
+
10n ** assetConfig.decimals, price
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
withdrawAmountMax = bigIntMin(
|
|
273
|
+
maxAmountToReclaim,
|
|
274
|
+
oldPresentValue.amount
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
if (!prices.has(assetId)) {
|
|
279
|
+
return 0n;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const price = prices.get(assetId) as bigint;
|
|
283
|
+
|
|
284
|
+
return getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants) * (10n ** assetConfig.decimals) / price;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return withdrawAmountMax;
|
|
288
|
+
}
|
|
289
|
+
|
|
119
290
|
export function getAvailableToBorrow(
|
|
120
291
|
assetsConfig: ExtendedAssetsConfig,
|
|
121
292
|
assetsData: ExtendedAssetsData,
|
|
@@ -136,9 +307,10 @@ export function getAvailableToBorrow(
|
|
|
136
307
|
borrowAmount += (calculatePresentValue(assetData.bRate, -principal, masterConstants) * price) / 10n ** assetConfig.decimals;
|
|
137
308
|
} else if (principal > 0) {
|
|
138
309
|
borrowLimit +=
|
|
139
|
-
|
|
140
|
-
10n ** assetConfig.decimals
|
|
141
|
-
|
|
310
|
+
mulDivC(
|
|
311
|
+
mulDivC(calculatePresentValue(assetData.sRate, principal, masterConstants), price, 10n ** assetConfig.decimals),
|
|
312
|
+
assetConfig.collateralFactor,
|
|
313
|
+
masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
142
314
|
}
|
|
143
315
|
}
|
|
144
316
|
|
|
@@ -164,74 +336,86 @@ export function presentValue(sRate: bigint, bRate: bigint, principalValue: bigin
|
|
|
164
336
|
}
|
|
165
337
|
}
|
|
166
338
|
|
|
339
|
+
/**
|
|
340
|
+
*
|
|
341
|
+
* @param assetsConfig
|
|
342
|
+
* @param assetsData
|
|
343
|
+
* @param principals
|
|
344
|
+
* @param prices
|
|
345
|
+
* @param poolConfig
|
|
346
|
+
* @returns can return UNDEFINED_ASSET if there are no assets
|
|
347
|
+
*/
|
|
167
348
|
export function calculateLiquidationData(
|
|
168
349
|
assetsConfig: ExtendedAssetsConfig,
|
|
169
350
|
assetsData: ExtendedAssetsData,
|
|
170
351
|
principals: Dictionary<bigint, bigint>,
|
|
171
352
|
prices: Dictionary<bigint, bigint>,
|
|
172
|
-
|
|
353
|
+
poolConfig: PoolConfig,
|
|
173
354
|
): LiquidationData {
|
|
174
|
-
let
|
|
175
|
-
let
|
|
176
|
-
let
|
|
177
|
-
let
|
|
355
|
+
let collateralValue = 0n;
|
|
356
|
+
let collateralAsset = UNDEFINED_ASSET;
|
|
357
|
+
let loanValue = 0n;
|
|
358
|
+
let loanAsset = UNDEFINED_ASSET;
|
|
178
359
|
let totalDebt = 0n;
|
|
179
360
|
let totalLimit = 0n;
|
|
180
361
|
|
|
181
|
-
for (const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
362
|
+
for (const asset of poolConfig.poolAssetsConfig) {
|
|
363
|
+
if (!principals.has(asset.assetId)) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const principal = principals.get(asset.assetId)!;
|
|
367
|
+
const assetConfig = assetsConfig.get(asset.assetId)!;
|
|
368
|
+
const assetData = assetsData.get(asset.assetId)!;
|
|
185
369
|
const balance =
|
|
186
370
|
principal > 0 ? (principal * assetData.sRate) / BigInt(1e12) : (principal * assetData.bRate) / BigInt(1e12);
|
|
187
371
|
if (balance > 0) {
|
|
188
|
-
const assetWorth = (balance * prices.get(
|
|
189
|
-
totalLimit += (assetWorth * assetConfig.liquidationThreshold) / masterConstants.ASSET_COEFFICIENT_SCALE;
|
|
190
|
-
if (assetWorth >
|
|
191
|
-
|
|
192
|
-
|
|
372
|
+
const assetWorth = (balance * prices.get(asset.assetId)!) / 10n ** assetConfig.decimals;
|
|
373
|
+
totalLimit += (assetWorth * assetConfig.liquidationThreshold) / poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE;
|
|
374
|
+
if (assetWorth > collateralValue) {
|
|
375
|
+
collateralValue = assetWorth;
|
|
376
|
+
collateralAsset = asset;
|
|
193
377
|
}
|
|
194
378
|
} else if (balance < 0) {
|
|
195
|
-
const assetWorth = (-balance * prices.get(
|
|
379
|
+
const assetWorth = (-balance * prices.get(asset.assetId)!) / 10n ** assetConfig.decimals;
|
|
196
380
|
totalDebt += assetWorth;
|
|
197
|
-
if (assetWorth >
|
|
198
|
-
|
|
199
|
-
|
|
381
|
+
if (assetWorth > loanValue) {
|
|
382
|
+
loanValue = assetWorth;
|
|
383
|
+
loanAsset = asset;
|
|
200
384
|
}
|
|
201
385
|
}
|
|
202
386
|
}
|
|
203
387
|
|
|
204
|
-
if (totalLimit < totalDebt) {
|
|
205
|
-
const
|
|
388
|
+
if (collateralAsset.assetId !== UNDEFINED_ASSET.assetId && totalLimit < totalDebt) {
|
|
389
|
+
const loanAssetPrice = prices.get(loanAsset.assetId)!;
|
|
206
390
|
const values: bigint[] = [];
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
const liquidationBonus =
|
|
210
|
-
const
|
|
391
|
+
const collateralAssetConfig = assetsConfig.get(collateralAsset.assetId)!;
|
|
392
|
+
const loanAssetConfig = assetsConfig.get(loanAsset.assetId)!;
|
|
393
|
+
const liquidationBonus = collateralAssetConfig.liquidationBonus;
|
|
394
|
+
const loanScale = 10n ** loanAssetConfig.decimals;
|
|
211
395
|
values.push(
|
|
212
|
-
(bigIntMax(
|
|
213
|
-
|
|
214
|
-
masterConstants.ASSET_COEFFICIENT_SCALE) /
|
|
396
|
+
(bigIntMax(collateralValue / 2n, bigIntMin(collateralValue, 100_000_000_000n)) *
|
|
397
|
+
loanScale *
|
|
398
|
+
poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE) /
|
|
215
399
|
liquidationBonus /
|
|
216
|
-
|
|
400
|
+
loanAssetPrice,
|
|
217
401
|
);
|
|
218
|
-
values.push((
|
|
402
|
+
values.push((loanValue * loanScale) / loanAssetPrice);
|
|
219
403
|
|
|
220
404
|
const liquidationAmount = (bigIntMin(...values) as bigint) - 5n;
|
|
221
|
-
const
|
|
222
|
-
const collateralDecimal = 10n **
|
|
405
|
+
const collateralAssetPrice: bigint = prices.get(collateralAsset.assetId)!;
|
|
406
|
+
const collateralDecimal = 10n ** collateralAssetConfig.decimals;
|
|
223
407
|
let minCollateralAmount =
|
|
224
|
-
(((liquidationAmount *
|
|
225
|
-
|
|
226
|
-
|
|
408
|
+
(((liquidationAmount * loanAssetPrice * liquidationBonus) / poolConfig.masterConstants.ASSET_LIQUIDATION_BONUS_SCALE) * collateralDecimal) /
|
|
409
|
+
collateralAssetPrice /
|
|
410
|
+
loanScale -
|
|
227
411
|
10n;
|
|
228
412
|
minCollateralAmount = (minCollateralAmount * 97n) / 100n;
|
|
229
413
|
if (minCollateralAmount / collateralDecimal >= 1n) {
|
|
230
414
|
return {
|
|
231
|
-
greatestCollateralAsset:
|
|
232
|
-
greatestCollateralValue:
|
|
233
|
-
greatestLoanAsset:
|
|
234
|
-
greatestLoanValue:
|
|
415
|
+
greatestCollateralAsset: collateralAsset,
|
|
416
|
+
greatestCollateralValue: collateralValue,
|
|
417
|
+
greatestLoanAsset: loanAsset,
|
|
418
|
+
greatestLoanValue: loanValue,
|
|
235
419
|
totalDebt,
|
|
236
420
|
totalLimit,
|
|
237
421
|
liquidable: true,
|
|
@@ -242,10 +426,10 @@ export function calculateLiquidationData(
|
|
|
242
426
|
}
|
|
243
427
|
|
|
244
428
|
return {
|
|
245
|
-
greatestCollateralAsset:
|
|
246
|
-
greatestCollateralValue:
|
|
247
|
-
greatestLoanAsset:
|
|
248
|
-
greatestLoanValue:
|
|
429
|
+
greatestCollateralAsset: collateralAsset,
|
|
430
|
+
greatestCollateralValue: collateralValue,
|
|
431
|
+
greatestLoanAsset: loanAsset,
|
|
432
|
+
greatestLoanValue: loanValue,
|
|
249
433
|
totalDebt,
|
|
250
434
|
totalLimit,
|
|
251
435
|
liquidable: false,
|
|
@@ -253,7 +437,7 @@ export function calculateLiquidationData(
|
|
|
253
437
|
}
|
|
254
438
|
|
|
255
439
|
export function predictHealthFactor(args: PredictHealthFactorArgs): number {
|
|
256
|
-
const liquidationData = calculateLiquidationData(args.assetsConfig, args.assetsData, args.
|
|
440
|
+
const liquidationData = calculateLiquidationData(args.assetsConfig, args.assetsData, args.principals, args.prices, args.poolConfig);
|
|
257
441
|
const tokenHash = sha256Hash(args.tokenSymbol);
|
|
258
442
|
|
|
259
443
|
const assetConfig = args.assetsConfig.get(tokenHash)!;
|
|
@@ -271,13 +455,13 @@ export function predictHealthFactor(args: PredictHealthFactorArgs): number {
|
|
|
271
455
|
|
|
272
456
|
if (currentAmount != null && currentAmount != 0n) {
|
|
273
457
|
if (changeType == BalanceChangeType.Borrow) {
|
|
274
|
-
totalBorrow += currentBalance * (1 + Number(assetConfig.originationFee) / Number(args.masterConstants.ASSET_ORIGINATION_FEE_SCALE));
|
|
458
|
+
totalBorrow += currentBalance * (1 + Number(assetConfig.originationFee) / Number(args.poolConfig.masterConstants.ASSET_ORIGINATION_FEE_SCALE));
|
|
275
459
|
} else if (changeType == BalanceChangeType.Repay) {
|
|
276
460
|
totalBorrow -= currentBalance;
|
|
277
461
|
} else if (changeType == BalanceChangeType.Withdraw) {
|
|
278
|
-
totalLimit -= currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
462
|
+
totalLimit -= currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
279
463
|
} else if (changeType == BalanceChangeType.Supply) {
|
|
280
|
-
totalLimit += currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
464
|
+
totalLimit += currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
281
465
|
}
|
|
282
466
|
}
|
|
283
467
|
if (Number(totalLimit) == 0) {
|