@gearbox-protocol/sdk 13.0.0-beta.1 → 13.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,9 +18,12 @@ module.exports = __toCommonJS(utils_exports);
18
18
  __reExport(utils_exports, require("./AddressMap.js"), module.exports);
19
19
  __reExport(utils_exports, require("./AddressSet.js"), module.exports);
20
20
  __reExport(utils_exports, require("./abi-decode.js"), module.exports);
21
+ __reExport(utils_exports, require("./assetsMath.js"), module.exports);
22
+ __reExport(utils_exports, require("./bigintMath.js"), module.exports);
21
23
  __reExport(utils_exports, require("./bytes32ToString.js"), module.exports);
22
24
  __reExport(utils_exports, require("./childLogger.js"), module.exports);
23
25
  __reExport(utils_exports, require("./createRawTx.js"), module.exports);
26
+ __reExport(utils_exports, require("./creditAccount.js"), module.exports);
24
27
  __reExport(utils_exports, require("./etherscan.js"), module.exports);
25
28
  __reExport(utils_exports, require("./filterDust.js"), module.exports);
26
29
  __reExport(utils_exports, require("./formatter.js"), module.exports);
@@ -28,6 +31,7 @@ __reExport(utils_exports, require("./hex.js"), module.exports);
28
31
  __reExport(utils_exports, require("./isDust.js"), module.exports);
29
32
  __reExport(utils_exports, require("./json.js"), module.exports);
30
33
  __reExport(utils_exports, require("./mappers.js"), module.exports);
34
+ __reExport(utils_exports, require("./priceMath.js"), module.exports);
31
35
  __reExport(utils_exports, require("./retry.js"), module.exports);
32
36
  __reExport(utils_exports, require("./toAddress.js"), module.exports);
33
37
  __reExport(utils_exports, require("./type-utils.js"), module.exports);
@@ -37,9 +41,12 @@ __reExport(utils_exports, require("./zod.js"), module.exports);
37
41
  ...require("./AddressMap.js"),
38
42
  ...require("./AddressSet.js"),
39
43
  ...require("./abi-decode.js"),
44
+ ...require("./assetsMath.js"),
45
+ ...require("./bigintMath.js"),
40
46
  ...require("./bytes32ToString.js"),
41
47
  ...require("./childLogger.js"),
42
48
  ...require("./createRawTx.js"),
49
+ ...require("./creditAccount.js"),
43
50
  ...require("./etherscan.js"),
44
51
  ...require("./filterDust.js"),
45
52
  ...require("./formatter.js"),
@@ -47,6 +54,7 @@ __reExport(utils_exports, require("./zod.js"), module.exports);
47
54
  ...require("./isDust.js"),
48
55
  ...require("./json.js"),
49
56
  ...require("./mappers.js"),
57
+ ...require("./priceMath.js"),
50
58
  ...require("./retry.js"),
51
59
  ...require("./toAddress.js"),
52
60
  ...require("./type-utils.js"),
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var priceMath_exports = {};
20
+ __export(priceMath_exports, {
21
+ PriceUtils: () => PriceUtils
22
+ });
23
+ module.exports = __toCommonJS(priceMath_exports);
24
+ var import__ = require("../index.js");
25
+ class PriceUtils {
26
+ static calcTotalPrice = (price, amount, decimals = 18) => amount * import__.WAD * price / 10n ** BigInt(decimals) / import__.PRICE_DECIMALS;
27
+ static convertByPrice(totalMoney, { price: targetPrice, decimals: targetDecimals = 18 }) {
28
+ if (targetPrice <= 0n) return 0n;
29
+ return totalMoney * 10n ** BigInt(targetDecimals) * import__.PRICE_DECIMALS / targetPrice / import__.WAD;
30
+ }
31
+ }
32
+ // Annotate the CommonJS export names for ESM import in node:
33
+ 0 && (module.exports = {
34
+ PriceUtils
35
+ });
@@ -0,0 +1,125 @@
1
+ import { BigIntMath, PriceUtils } from "../index.js";
2
+ import { CreditAccountDataUtils } from "./creditAccount.js";
3
+ class AssetUtils {
4
+ static nextAsset({
5
+ allowedTokens,
6
+ selectedAssets,
7
+ balances,
8
+ tokensList,
9
+ prices = {}
10
+ }) {
11
+ const selectedRecord = selectedAssets.reduce(
12
+ (acc, { token }) => {
13
+ acc[token.toLowerCase()] = true;
14
+ return acc;
15
+ },
16
+ {}
17
+ );
18
+ const notSelected = allowedTokens.filter((allowedToken) => {
19
+ const alreadySelected = selectedRecord[allowedToken.toLowerCase()];
20
+ return !alreadySelected;
21
+ });
22
+ const sorted = CreditAccountDataUtils.sortBalances(
23
+ AssetUtils.getBalances(notSelected, balances),
24
+ prices,
25
+ tokensList
26
+ );
27
+ const [address] = sorted[0] || [];
28
+ return address;
29
+ }
30
+ static getBalances(allowedTokens, externalBalances) {
31
+ return allowedTokens.reduce((acc, address) => {
32
+ const addressLc = address.toLowerCase();
33
+ return {
34
+ ...acc,
35
+ [addressLc]: externalBalances[addressLc] || 0n
36
+ };
37
+ }, {});
38
+ }
39
+ static constructAssetRecord(a) {
40
+ const record = a.reduce((acc, asset) => {
41
+ acc[asset.token] = asset;
42
+ return acc;
43
+ }, {});
44
+ return record;
45
+ }
46
+ static memoWrap = (unwrappedAddress, wrappedAddress, prices, tokensList) => function wrap(assets) {
47
+ const assetsRecord = AssetUtils.constructAssetRecord(assets);
48
+ const unwrapped = assetsRecord[unwrappedAddress];
49
+ const wrapped = assetsRecord[wrappedAddress];
50
+ const { balance: wrappedAmount = 0n } = wrapped || {};
51
+ if (unwrapped) {
52
+ const { balance: unwrappedAmount = 0n } = unwrapped || {};
53
+ const unwrappedToken = tokensList[unwrappedAddress];
54
+ const unwrappedPrice = prices[unwrappedAddress] || 0n;
55
+ const wrappedToken = tokensList[wrappedAddress];
56
+ const wrappedPrice = prices[wrappedAddress] || 0n;
57
+ const unwrappedInWrapped = PriceUtils.convertByPrice(
58
+ PriceUtils.calcTotalPrice(
59
+ unwrappedPrice,
60
+ BigIntMath.max(0n, unwrappedAmount),
61
+ unwrappedToken.decimals
62
+ ),
63
+ {
64
+ price: wrappedPrice,
65
+ decimals: wrappedToken.decimals
66
+ }
67
+ );
68
+ assetsRecord[wrappedAddress] = {
69
+ token: wrappedAddress,
70
+ balance: BigIntMath.max(0n, wrappedAmount) + unwrappedInWrapped
71
+ };
72
+ delete assetsRecord[unwrappedAddress];
73
+ return [Object.values(assetsRecord), unwrappedInWrapped, wrappedAmount];
74
+ }
75
+ return [Object.values(assetsRecord), 0n, wrappedAmount];
76
+ };
77
+ /**
78
+ * Sums the the second assets list into the first assets list
79
+ * Balances cant be negative; creates new assets.
80
+ */
81
+ static sumAssets(a, b) {
82
+ const aRecord = AssetUtils.constructAssetRecord(a);
83
+ const resRecord = b.reduce((acc, bAsset) => {
84
+ const aAsset = acc[bAsset.token];
85
+ const { balance: amount = 0n } = aAsset || {};
86
+ const amountSum = BigIntMath.max(0n, bAsset.balance) + BigIntMath.max(0n, amount);
87
+ const aOrB = aAsset || bAsset;
88
+ acc[bAsset.token] = {
89
+ ...aOrB,
90
+ balance: amountSum
91
+ };
92
+ return acc;
93
+ }, aRecord);
94
+ return Object.values(resRecord);
95
+ }
96
+ /**
97
+ * Sums the the second assets list into the first assets list
98
+ * Balances cant be negative; doesn't create new assets.
99
+ */
100
+ static addBalances(a, b) {
101
+ const bRecord = AssetUtils.constructAssetRecord(b);
102
+ return a.map((asset) => {
103
+ const bAsset = bRecord[asset.token];
104
+ const { balance: bAmount = 0n } = bAsset || {};
105
+ const amountSum = BigIntMath.max(0n, asset.balance) + BigIntMath.max(0n, bAmount);
106
+ return { ...asset, balance: amountSum };
107
+ });
108
+ }
109
+ /**
110
+ * Subtracts the the second assets list from the first assets list
111
+ * Balances cant be negative; doesn't create new assets.
112
+ */
113
+ static subAssets(a, b) {
114
+ const bRecord = AssetUtils.constructAssetRecord(b);
115
+ return a.map((asset) => {
116
+ const bAsset = bRecord[asset.token];
117
+ const { balance: bAmount = 0n } = bAsset || {};
118
+ const amountSub = BigIntMath.max(0n, asset.balance) - BigIntMath.max(0n, bAmount);
119
+ return { ...asset, balance: BigIntMath.max(0n, amountSub) };
120
+ });
121
+ }
122
+ }
123
+ export {
124
+ AssetUtils
125
+ };
@@ -0,0 +1,9 @@
1
+ class BigIntMath {
2
+ static abs = (x) => x < 0n ? -x : x;
3
+ static max = (a, b) => a > b ? a : b;
4
+ static min = (a, b) => a < b ? a : b;
5
+ static neg = (a) => a > 0 ? a * -1n : a;
6
+ }
7
+ export {
8
+ BigIntMath
9
+ };
@@ -0,0 +1,394 @@
1
+ import {
2
+ BigIntMath,
3
+ MIN_INT96,
4
+ PERCENTAGE_DECIMALS,
5
+ PERCENTAGE_FACTOR,
6
+ PRICE_DECIMALS,
7
+ PRICE_DECIMALS_POW,
8
+ PriceUtils,
9
+ SECONDS_PER_YEAR,
10
+ WAD,
11
+ WAD_DECIMALS_POW
12
+ } from "../index.js";
13
+ const MAX_UINT16 = 65535;
14
+ class CreditAccountDataUtils {
15
+ static sortBalances(balances, prices, tokens) {
16
+ return Object.entries(balances).sort(
17
+ ([addr1, amount1], [addr2, amount2]) => {
18
+ return CreditAccountDataUtils.assetComparator(
19
+ {
20
+ token: addr1,
21
+ balance: amount1
22
+ },
23
+ {
24
+ token: addr2,
25
+ balance: amount2
26
+ },
27
+ prices,
28
+ prices,
29
+ tokens,
30
+ tokens
31
+ );
32
+ }
33
+ );
34
+ }
35
+ static sortAssets(balances, prices, tokens) {
36
+ return [...balances].sort(
37
+ (t1, t2) => CreditAccountDataUtils.assetComparator(
38
+ t1,
39
+ t2,
40
+ prices,
41
+ prices,
42
+ tokens,
43
+ tokens
44
+ )
45
+ );
46
+ }
47
+ static assetComparator(t1, t2, prices1, prices2, tokens1, tokens2) {
48
+ const addr1Lc = t1.token.toLowerCase();
49
+ const addr2Lc = t2.token.toLowerCase();
50
+ const token1 = tokens1?.[addr1Lc];
51
+ const token2 = tokens2?.[addr2Lc];
52
+ const price1 = prices1?.[addr1Lc] || PRICE_DECIMALS;
53
+ const price2 = prices2?.[addr2Lc] || PRICE_DECIMALS;
54
+ const totalPrice1 = PriceUtils.calcTotalPrice(
55
+ price1,
56
+ t1.balance,
57
+ token1?.decimals
58
+ );
59
+ const totalPrice2 = PriceUtils.calcTotalPrice(
60
+ price2,
61
+ t2.balance,
62
+ token2?.decimals
63
+ );
64
+ if (totalPrice1 === totalPrice2) {
65
+ return t1.balance === t2.balance ? CreditAccountDataUtils.tokensAbcComparator(token1, token2) : CreditAccountDataUtils.amountAbcComparator(t1.balance, t2.balance);
66
+ }
67
+ return CreditAccountDataUtils.amountAbcComparator(totalPrice1, totalPrice2);
68
+ }
69
+ static tokensAbcComparator(t1, t2) {
70
+ const { symbol: symbol1 = "" } = t1 || {};
71
+ const { symbol: symbol2 = "" } = t2 || {};
72
+ const symbol1LC = symbol1.toLowerCase();
73
+ const symbol2LC = symbol2.toLowerCase();
74
+ if (symbol1LC === symbol2LC) return 0;
75
+ return symbol1LC > symbol2LC ? 1 : -1;
76
+ }
77
+ static amountAbcComparator(t1, t2) {
78
+ return t1 > t2 ? -1 : 1;
79
+ }
80
+ static calcMaxDebtIncrease(healthFactor, debt, underlyingLT, minHf = Number(PERCENTAGE_FACTOR)) {
81
+ const result = debt * BigInt(healthFactor - minHf) / BigInt(minHf - underlyingLT);
82
+ return BigIntMath.max(0n, result);
83
+ }
84
+ static calcMaxLendingDebt({
85
+ assets,
86
+ liquidationThresholds,
87
+ underlyingToken,
88
+ prices,
89
+ tokensList,
90
+ targetHF = PERCENTAGE_FACTOR
91
+ }) {
92
+ const assetsLTMoney = assets.reduce(
93
+ (acc, { token: tokenAddress, balance: amount }) => {
94
+ const tokenDecimals = tokensList[tokenAddress]?.decimals || 18;
95
+ const lt = liquidationThresholds[tokenAddress] || 0n;
96
+ const price = prices[tokenAddress] || 0n;
97
+ const tokenMoney = PriceUtils.calcTotalPrice(
98
+ price,
99
+ amount,
100
+ tokenDecimals
101
+ );
102
+ const tokenLtMoney = tokenMoney * lt;
103
+ return acc + tokenLtMoney;
104
+ },
105
+ 0n
106
+ );
107
+ const underlyingPrice = prices[underlyingToken] || 0n;
108
+ const underlyingDecimals = tokensList[underlyingToken]?.decimals || 18;
109
+ const max = underlyingPrice > 0 ? assetsLTMoney * 10n ** BigInt(underlyingDecimals) / underlyingPrice / targetHF / 10n ** BigInt(WAD_DECIMALS_POW - PRICE_DECIMALS_POW) : 0n;
110
+ return max;
111
+ }
112
+ // [
113
+ // Sum(amount_i * price_i * apy_i - quota_i * quotaPrice * quotaRate_i * (1 + feeInterest)) -
114
+ // debt * debtPrice * baseRateWithFee
115
+ // ] / (totalValue - debt) * debtPrice
116
+ static calcOverallAPY({
117
+ caAssets,
118
+ lpAPY,
119
+ prices,
120
+ quotas,
121
+ quotaRates,
122
+ feeInterest,
123
+ totalValue,
124
+ debt,
125
+ baseRateWithFee,
126
+ underlyingToken,
127
+ tokensList
128
+ }) {
129
+ if (!lpAPY || !totalValue || totalValue <= 0n || !debt || totalValue <= debt)
130
+ return void 0;
131
+ const underlyingTokenDecimals = tokensList[underlyingToken]?.decimals || 18;
132
+ const underlyingPrice = prices[underlyingToken];
133
+ const assetAPYMoney = caAssets.reduce(
134
+ (acc, { token: tokenAddress, balance: amount }) => {
135
+ const apy = lpAPY[tokenAddress] || 0;
136
+ const tokenDecimals = tokensList[tokenAddress]?.decimals || 18;
137
+ const price = prices[tokenAddress] || 0n;
138
+ const money = PriceUtils.calcTotalPrice(price, amount, tokenDecimals);
139
+ const apyMoney = money * BigInt(apy);
140
+ const { rate: quotaAPY = 0n, isActive = false } = quotaRates?.[tokenAddress] || {};
141
+ const { balance: quotaBalance = 0n } = quotas[tokenAddress] || {};
142
+ const quotaAmount = isActive ? quotaBalance : 0n;
143
+ const quotaMoney = PriceUtils.calcTotalPrice(
144
+ underlyingPrice || 0n,
145
+ quotaAmount,
146
+ underlyingTokenDecimals
147
+ );
148
+ const quotaRate = quotaAPY * (BigInt(feeInterest) + PERCENTAGE_FACTOR) / PERCENTAGE_FACTOR;
149
+ const quotaAPYMoney = quotaMoney * quotaRate;
150
+ return acc + apyMoney - quotaAPYMoney;
151
+ },
152
+ 0n
153
+ );
154
+ const debtMoney = PriceUtils.calcTotalPrice(
155
+ underlyingPrice || 0n,
156
+ debt,
157
+ underlyingTokenDecimals
158
+ );
159
+ const debtAPYMoney = debtMoney * BigInt(baseRateWithFee);
160
+ const yourAssetsMoney = PriceUtils.calcTotalPrice(
161
+ underlyingPrice || PRICE_DECIMALS,
162
+ totalValue - debt,
163
+ underlyingTokenDecimals
164
+ );
165
+ const apyInPercent = (assetAPYMoney - debtAPYMoney) / yourAssetsMoney;
166
+ return apyInPercent;
167
+ }
168
+ static calcHealthFactor({
169
+ assets,
170
+ quotas,
171
+ quotasInfo,
172
+ liquidationThresholds,
173
+ underlyingToken,
174
+ debt,
175
+ prices,
176
+ tokensList
177
+ }) {
178
+ if (debt === 0n) return MAX_UINT16;
179
+ const underlyingDecimals = tokensList[underlyingToken]?.decimals || 18;
180
+ const underlyingPrice = prices[underlyingToken] || 0n;
181
+ const assetMoney = assets.reduce(
182
+ (acc, { token: tokenAddress, balance: amount }) => {
183
+ const tokenDecimals = tokensList[tokenAddress]?.decimals || 18;
184
+ const lt = liquidationThresholds[tokenAddress] || 0n;
185
+ const price = prices[tokenAddress] || 0n;
186
+ const tokenMoney = PriceUtils.calcTotalPrice(
187
+ price,
188
+ amount,
189
+ tokenDecimals
190
+ );
191
+ const tokenLtMoney = tokenMoney * lt / PERCENTAGE_FACTOR;
192
+ const { isActive = false } = quotasInfo?.[tokenAddress] || {};
193
+ const quota = quotas[tokenAddress];
194
+ const quotaBalance = isActive ? quota?.balance || 0n : 0n;
195
+ const quotaMoney = PriceUtils.calcTotalPrice(
196
+ underlyingPrice,
197
+ quotaBalance,
198
+ underlyingDecimals
199
+ );
200
+ const money = quota ? BigIntMath.min(quotaMoney, tokenLtMoney) : tokenLtMoney;
201
+ return acc + money;
202
+ },
203
+ 0n
204
+ );
205
+ const borrowedMoney = PriceUtils.calcTotalPrice(
206
+ underlyingPrice || PRICE_DECIMALS,
207
+ debt,
208
+ underlyingDecimals
209
+ );
210
+ const hfInPercent = borrowedMoney > 0n ? assetMoney * PERCENTAGE_FACTOR / borrowedMoney : 0n;
211
+ return Number(hfInPercent);
212
+ }
213
+ static roundUpQuota(quotaChange) {
214
+ return quotaChange !== MIN_INT96 ? quotaChange / PERCENTAGE_FACTOR * PERCENTAGE_FACTOR : quotaChange;
215
+ }
216
+ static calcRecommendedQuota({
217
+ amount,
218
+ debt,
219
+ lt,
220
+ quotaReserve
221
+ }) {
222
+ const recommendedBaseQuota = BigIntMath.min(
223
+ debt,
224
+ amount * lt / PERCENTAGE_FACTOR
225
+ );
226
+ const recommendedQuota = recommendedBaseQuota * (PERCENTAGE_FACTOR + quotaReserve) / PERCENTAGE_FACTOR;
227
+ return CreditAccountDataUtils.roundUpQuota(recommendedQuota);
228
+ }
229
+ static calcDefaultQuota({ amount, lt, quotaReserve }) {
230
+ const recommendedBaseQuota = amount * lt / PERCENTAGE_FACTOR;
231
+ const recommendedQuota = recommendedBaseQuota * (PERCENTAGE_FACTOR + quotaReserve) / PERCENTAGE_FACTOR;
232
+ return CreditAccountDataUtils.roundUpQuota(recommendedQuota);
233
+ }
234
+ static calcQuotaUpdate(props) {
235
+ const { quotas, initialQuotas, maxDebt, allowedToSpend, allowedToObtain } = props;
236
+ const quotaDecrease = Object.keys(allowedToSpend).reduce((acc, token) => {
237
+ const ch = CreditAccountDataUtils.getSingleQuotaChange(
238
+ token,
239
+ 0n,
240
+ props
241
+ );
242
+ if (ch && ch.balance < 0) acc[ch.token] = ch;
243
+ return acc;
244
+ }, {});
245
+ const quotaCap = CreditAccountDataUtils.roundUpQuota(maxDebt * 2n);
246
+ const quotaBought = Object.values(initialQuotas).reduce(
247
+ (sum, q) => sum + CreditAccountDataUtils.roundUpQuota(q?.quota || 0n),
248
+ 0n
249
+ );
250
+ const quotaReduced = Object.values(quotaDecrease).reduce((sum, q) => {
251
+ const quotaBalance = q.balance || 0n;
252
+ const safeBalance = quotaBalance === MIN_INT96 ? BigIntMath.neg(
253
+ CreditAccountDataUtils.roundUpQuota(
254
+ initialQuotas[q.token]?.quota || 0n
255
+ )
256
+ ) : quotaBalance;
257
+ return sum + safeBalance;
258
+ }, 0n);
259
+ const maxQuotaIncrease = CreditAccountDataUtils.roundUpQuota(
260
+ BigIntMath.max(quotaCap - (quotaBought + quotaReduced), 0n)
261
+ );
262
+ const quotaIncrease = Object.keys(allowedToObtain).reduce((acc, token) => {
263
+ const ch = CreditAccountDataUtils.getSingleQuotaChange(
264
+ token,
265
+ maxQuotaIncrease,
266
+ props
267
+ );
268
+ if (ch && ch.balance > 0) acc[ch.token] = ch;
269
+ return acc;
270
+ }, {});
271
+ const quotaChange = {
272
+ ...quotaDecrease,
273
+ ...quotaIncrease
274
+ };
275
+ const desiredQuota = Object.values(quotas).reduce(
276
+ (acc, cmQuota) => {
277
+ const { token, isActive } = cmQuota;
278
+ const { quota: initialQuota = 0n } = initialQuotas[token] || {};
279
+ if (!isActive) {
280
+ acc[token] = {
281
+ balance: initialQuota,
282
+ token
283
+ };
284
+ } else {
285
+ const change = quotaChange[token]?.balance || 0n;
286
+ const quotaAfter = change === MIN_INT96 ? 0n : initialQuota + change;
287
+ acc[token] = {
288
+ balance: quotaAfter,
289
+ token
290
+ };
291
+ }
292
+ return acc;
293
+ },
294
+ {}
295
+ );
296
+ return {
297
+ desiredQuota,
298
+ quotaDecrease: Object.values(quotaDecrease),
299
+ quotaIncrease: Object.values(quotaIncrease)
300
+ };
301
+ }
302
+ static getSingleQuotaChange(token, unsafeMaxQuotaIncrease, props) {
303
+ const { isActive = false } = props.quotas[token] || {};
304
+ const { quota: unsafeInitialQuota = 0n } = props.initialQuotas[token] || {};
305
+ if (!isActive) {
306
+ return void 0;
307
+ }
308
+ const assetAfter = props.assetsAfterUpdate[token];
309
+ const { amountInTarget = 0n } = assetAfter || {};
310
+ const lt = props.liquidationThresholds[token] || 0n;
311
+ const maxQuotaIncrease = CreditAccountDataUtils.roundUpQuota(
312
+ unsafeMaxQuotaIncrease
313
+ );
314
+ const initialQuota = CreditAccountDataUtils.roundUpQuota(unsafeInitialQuota);
315
+ const defaultQuota = props.calcModification?.type === "recommendedQuota" && props.calcModification.debt > 0 ? CreditAccountDataUtils.calcRecommendedQuota({
316
+ lt,
317
+ quotaReserve: props.quotaReserve,
318
+ amount: amountInTarget,
319
+ debt: props.calcModification.debt
320
+ }) : CreditAccountDataUtils.calcDefaultQuota({
321
+ lt,
322
+ quotaReserve: props.quotaReserve,
323
+ amount: amountInTarget
324
+ });
325
+ const unsafeQuotaChange = CreditAccountDataUtils.roundUpQuota(
326
+ defaultQuota - initialQuota
327
+ );
328
+ const quotaChange = unsafeQuotaChange > 0 ? BigIntMath.min(maxQuotaIncrease, unsafeQuotaChange) : unsafeQuotaChange < 0 && BigIntMath.abs(unsafeQuotaChange) >= initialQuota ? MIN_INT96 : unsafeQuotaChange;
329
+ const correctIncrease = assetAfter && props.allowedToObtain[token] && quotaChange > 0;
330
+ const correctDecrease = assetAfter && props.allowedToSpend[token] && quotaChange < 0;
331
+ if (correctIncrease || correctDecrease) {
332
+ return {
333
+ balance: quotaChange,
334
+ token
335
+ };
336
+ }
337
+ return void 0;
338
+ }
339
+ static calcQuotaBorrowRate({ quotas, quotaRates }) {
340
+ const totalRateBalance = Object.values(quotas).reduce(
341
+ (acc, { token, balance }) => {
342
+ const { rate = 0, isActive = false } = quotaRates?.[token] || {};
343
+ const quotaBalance = isActive ? balance : 0n;
344
+ const rateBalance = quotaBalance * BigInt(rate);
345
+ return acc + rateBalance;
346
+ },
347
+ 0n
348
+ );
349
+ return totalRateBalance;
350
+ }
351
+ static calcRelativeBaseBorrowRate({
352
+ debt,
353
+ baseRateWithFee,
354
+ assetAmountInUnderlying
355
+ }) {
356
+ return debt * BigInt(baseRateWithFee) * assetAmountInUnderlying;
357
+ }
358
+ static liquidationPrice({
359
+ liquidationThresholds,
360
+ debt,
361
+ underlyingToken,
362
+ targetToken,
363
+ assets,
364
+ tokensList
365
+ }) {
366
+ const underlyingDecimals = tokensList[underlyingToken]?.decimals || 18;
367
+ const { balance: underlyingBalance = 0n } = assets[underlyingToken] || {};
368
+ const ltUnderlying = liquidationThresholds[underlyingToken] || 0n;
369
+ const effectiveDebt = (debt - underlyingBalance * ltUnderlying / PERCENTAGE_FACTOR) * WAD / 10n ** BigInt(underlyingDecimals);
370
+ const targetDecimals = tokensList[targetToken]?.decimals || 18;
371
+ const { balance: targetBalance = 0n } = assets[targetToken] || {};
372
+ const effectiveTargetBalance = targetBalance * WAD / 10n ** BigInt(targetDecimals);
373
+ const lpLT = liquidationThresholds[targetToken] || 0n;
374
+ if (targetBalance <= 0n || lpLT <= 0n) return 0n;
375
+ return effectiveDebt * PRICE_DECIMALS * PERCENTAGE_FACTOR / (effectiveTargetBalance * lpLT);
376
+ }
377
+ /**
378
+ * Calculates the time remaining until liquidation for a credit account.
379
+ * @returns The time remaining until liquidation in milliseconds.
380
+ */
381
+ static getTimeToLiquidation({
382
+ healthFactor,
383
+ totalBorrowRate_debt
384
+ }) {
385
+ if (healthFactor <= PERCENTAGE_FACTOR || totalBorrowRate_debt === 0n)
386
+ return null;
387
+ const HF_1 = BigInt(healthFactor) - PERCENTAGE_FACTOR;
388
+ const brPerYear = BigInt(SECONDS_PER_YEAR) * PERCENTAGE_FACTOR * PERCENTAGE_DECIMALS / totalBorrowRate_debt;
389
+ return HF_1 * brPerYear * 1000n / PERCENTAGE_FACTOR;
390
+ }
391
+ }
392
+ export {
393
+ CreditAccountDataUtils
394
+ };
@@ -1,4 +1,6 @@
1
1
  import { formatDuration as fmtDuration, intervalToDuration } from "date-fns";
2
+ import Decimal from "decimal.js-light";
3
+ import { LEVERAGE_DECIMALS, PERCENTAGE_FACTOR } from "../index.js";
2
4
  const toBigInt = (v) => {
3
5
  const value = typeof v === "object" && v.type === "BigNumber" ? v.hex : v.toString();
4
6
  return BigInt(value);
@@ -98,14 +100,48 @@ function formatTimestamp(timestamp, raw = true) {
98
100
  });
99
101
  return raw ? `${result} [${timestamp}]` : result;
100
102
  }
103
+ function rayToNumber(num) {
104
+ return Number(toBigInt(num) / 10n ** 21n) / 1e6;
105
+ }
106
+ function toSignificant(num, decimals, precision = 6) {
107
+ if (num === 1n) return "0";
108
+ const divider = new Decimal(10).toPower(decimals);
109
+ const number = new Decimal(num.toString()).div(divider);
110
+ return number.toSignificantDigits(precision, 4).toString();
111
+ }
112
+ function toBN(num, decimals) {
113
+ if (num === "") return 0n;
114
+ const multiplier = new Decimal(10).toPower(decimals);
115
+ const number = new Decimal(num).mul(multiplier);
116
+ return BigInt(number.toFixed(0));
117
+ }
118
+ function shortAddress(address) {
119
+ return address === void 0 ? "" : `${address.slice(0, 6)}...${address.slice(address.length - 4)}`;
120
+ }
121
+ function shortHash(address) {
122
+ return address === void 0 ? "" : `${address.slice(0, 5)}...`;
123
+ }
124
+ function formatPercentage(healthFactor, decimals = 2) {
125
+ return (healthFactor / Number(PERCENTAGE_FACTOR)).toFixed(decimals);
126
+ }
127
+ function formatLeverage(leverage, decimals = 2) {
128
+ return (leverage / Number(LEVERAGE_DECIMALS)).toFixed(decimals);
129
+ }
101
130
  export {
102
131
  fmtBinaryMask,
103
132
  formatBN,
104
133
  formatBNvalue,
105
134
  formatDuration,
135
+ formatLeverage,
106
136
  formatNumberToString_,
137
+ formatPercentage,
107
138
  formatTimestamp,
108
139
  numberWithCommas,
109
140
  percentFmt,
110
- toBigInt
141
+ rayToNumber,
142
+ shortAddress,
143
+ shortHash,
144
+ toBN,
145
+ toBigInt,
146
+ toSignificant
111
147
  };
@@ -1,9 +1,12 @@
1
1
  export * from "./AddressMap.js";
2
2
  export * from "./AddressSet.js";
3
3
  export * from "./abi-decode.js";
4
+ export * from "./assetsMath.js";
5
+ export * from "./bigintMath.js";
4
6
  export * from "./bytes32ToString.js";
5
7
  export * from "./childLogger.js";
6
8
  export * from "./createRawTx.js";
9
+ export * from "./creditAccount.js";
7
10
  export * from "./etherscan.js";
8
11
  export * from "./filterDust.js";
9
12
  export * from "./formatter.js";
@@ -11,6 +14,7 @@ export * from "./hex.js";
11
14
  export * from "./isDust.js";
12
15
  export * from "./json.js";
13
16
  export * from "./mappers.js";
17
+ export * from "./priceMath.js";
14
18
  export * from "./retry.js";
15
19
  export * from "./toAddress.js";
16
20
  export * from "./type-utils.js";
@@ -0,0 +1,11 @@
1
+ import { PRICE_DECIMALS, WAD } from "../index.js";
2
+ class PriceUtils {
3
+ static calcTotalPrice = (price, amount, decimals = 18) => amount * WAD * price / 10n ** BigInt(decimals) / PRICE_DECIMALS;
4
+ static convertByPrice(totalMoney, { price: targetPrice, decimals: targetDecimals = 18 }) {
5
+ if (targetPrice <= 0n) return 0n;
6
+ return totalMoney * 10n ** BigInt(targetDecimals) * PRICE_DECIMALS / targetPrice / WAD;
7
+ }
8
+ }
9
+ export {
10
+ PriceUtils
11
+ };