@carrot-protocol/clend-rpc 0.1.6 → 0.1.7-fe-math-dev-ce2629b

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/math.js DELETED
@@ -1,737 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.uiToAmount = uiToAmount;
4
- exports.amountToUi = amountToUi;
5
- exports.calculateUtilizationRate = calculateUtilizationRate;
6
- exports.calculateAssetQuantity = calculateAssetQuantity;
7
- exports.calculateLiabilityQuantity = calculateLiabilityQuantity;
8
- exports.calculateTotalAssetQuantity = calculateTotalAssetQuantity;
9
- exports.calculateTotalLiabilityQuantity = calculateTotalLiabilityQuantity;
10
- exports.calculateBankUtilizationRate = calculateBankUtilizationRate;
11
- exports.calculateBaseInterestRate = calculateBaseInterestRate;
12
- exports.calculateSupplyApy = calculateSupplyApy;
13
- exports.calculateBorrowApy = calculateBorrowApy;
14
- exports.computeAdjustLeverageAmounts = computeAdjustLeverageAmounts;
15
- exports.computeMaxLeverage = computeMaxLeverage;
16
- exports.computeLoopingAmounts = computeLoopingAmounts;
17
- exports.calculateWeightedValue = calculateWeightedValue;
18
- exports.calculateWeightedAmount = calculateWeightedAmount;
19
- exports.calculateWeightedLeverage = calculateWeightedLeverage;
20
- exports.computeWithdrawLeverageAmounts = computeWithdrawLeverageAmounts;
21
- exports.calculateHealthFactorAndLeverage = calculateHealthFactorAndLeverage;
22
- exports.calculateAccruedInterest = calculateAccruedInterest;
23
- exports.adjustBorrowForOriginationFee = adjustBorrowForOriginationFee;
24
- exports.adjustAmountForSlippage = adjustAmountForSlippage;
25
- exports.calculateLiquidationPrice = calculateLiquidationPrice;
26
- exports.calculateLtv = calculateLtv;
27
- exports.calculateLiquidationPriceChangePercentage = calculateLiquidationPriceChangePercentage;
28
- exports.calculateLiabilityInterest = calculateLiabilityInterest;
29
- exports.addInterestAccrualBuffer = addInterestAccrualBuffer;
30
- const anchor_1 = require("@coral-xyz/anchor");
31
- const decimal_js_1 = require("decimal.js");
32
- /**
33
- * Convert a UI number to a token amount BN
34
- * @param uiAmount Amount in UI format (e.g., 1.5 USDC)
35
- * @param decimals Token decimals
36
- * @returns BN representing the token amount in smallest units (lamports)
37
- */
38
- function uiToAmount(uiAmount, decimals) {
39
- return new anchor_1.BN(Math.floor(uiAmount * 10 ** decimals));
40
- }
41
- /**
42
- * Convert a token amount BN to a UI number
43
- * @param tokenAmount BN representing the token amount in smallest units (lamports)
44
- * @param decimals Token decimals
45
- * @returns Number in UI format (e.g., 1.5 USDC)
46
- */
47
- function amountToUi(amount, decimals) {
48
- if (typeof amount === "number") {
49
- return amount / 10 ** decimals;
50
- }
51
- return amount.toNumber() / 10 ** decimals;
52
- }
53
- /**
54
- * Calculate the utilization rate of a bank
55
- * @param totalSupply Total supply of tokens
56
- * @param totalBorrow Total borrowed tokens
57
- * @returns Utilization rate as a decimal (0-1)
58
- */
59
- function calculateUtilizationRate(totalSupply, totalBorrow) {
60
- if (totalSupply === 0 || totalBorrow === 0)
61
- return 0;
62
- return Math.min(totalBorrow / totalSupply, 1);
63
- }
64
- /**
65
- * Calculate asset quantity from asset shares
66
- * @param assetShares Asset shares amount
67
- * @param assetShareValue Value of each asset share
68
- * @returns Asset quantity
69
- */
70
- function calculateAssetQuantity(assetShares, assetShareValue) {
71
- const shares = typeof assetShares === "number"
72
- ? assetShares
73
- : Number(assetShares.toString());
74
- return shares * assetShareValue;
75
- }
76
- /**
77
- * Calculate liability quantity from liability shares
78
- * @param liabilityShares Liability shares amount
79
- * @param liabilityShareValue Value of each liability share
80
- * @returns Liability quantity
81
- */
82
- function calculateLiabilityQuantity(liabilityShares, liabilityShareValue) {
83
- const shares = typeof liabilityShares === "number"
84
- ? liabilityShares
85
- : Number(liabilityShares.toString());
86
- return shares * liabilityShareValue;
87
- }
88
- /**
89
- * Calculate total asset quantity
90
- * @param totalAssetShares Total asset shares in the bank
91
- * @param assetShareValue Value of each asset share
92
- * @returns Total asset quantity
93
- *
94
- * Note: You can get these values from a Bank object:
95
- * - totalAssetShares = bank.totalAssetShares
96
- * - assetShareValue = bank.assetShareValue
97
- */
98
- function calculateTotalAssetQuantity(totalAssetShares, assetShareValue) {
99
- return calculateAssetQuantity(totalAssetShares, assetShareValue);
100
- }
101
- /**
102
- * Calculate total liability quantity
103
- * @param totalLiabilityShares Total liability shares in the bank
104
- * @param liabilityShareValue Value of each liability share
105
- * @returns Total liability quantity
106
- *
107
- * Note: You can get these values from a Bank object:
108
- * - totalLiabilityShares = bank.totalLiabilityShares
109
- * - liabilityShareValue = bank.liabilityShareValue
110
- */
111
- function calculateTotalLiabilityQuantity(totalLiabilityShares, liabilityShareValue) {
112
- return calculateLiabilityQuantity(totalLiabilityShares, liabilityShareValue);
113
- }
114
- /**
115
- * Calculate the bank's utilization rate
116
- * @param totalAssetShares Total asset shares in the bank
117
- * @param assetShareValue Value of each asset share
118
- * @param totalLiabilityShares Total liability shares in the bank
119
- * @param liabilityShareValue Value of each liability share
120
- * @returns Utilization rate as a decimal (0-1)
121
- *
122
- * Note: You can get these values from a Bank object:
123
- * - totalAssetShares = bank.totalAssetShares
124
- * - assetShareValue = bank.assetShareValue
125
- * - totalLiabilityShares = bank.totalLiabilityShares
126
- * - liabilityShareValue = bank.liabilityShareValue
127
- */
128
- function calculateBankUtilizationRate(totalAssetShares, assetShareValue, totalLiabilityShares, liabilityShareValue) {
129
- const totalAssets = calculateTotalAssetQuantity(totalAssetShares, assetShareValue);
130
- const totalLiabilities = calculateTotalLiabilityQuantity(totalLiabilityShares, liabilityShareValue);
131
- return calculateUtilizationRate(totalAssets, totalLiabilities);
132
- }
133
- /**
134
- * Calculate the base interest rate based on utilization rate
135
- * @param utilizationRate Current utilization rate (0-1)
136
- * @param optimalUtilizationRate Optimal utilization rate target
137
- * @param plateauInterestRate Interest rate at optimal utilization
138
- * @param maxInterestRate Maximum interest rate at 100% utilization
139
- * @returns Base interest rate as a decimal
140
- *
141
- * Note: You can get these values from a Bank object:
142
- * - optimalUtilizationRate = bank.config.interestRateConfig.optimalUtilizationRate
143
- * - plateauInterestRate = bank.config.interestRateConfig.plateauInterestRate
144
- * - maxInterestRate = bank.config.interestRateConfig.maxInterestRate
145
- */
146
- function calculateBaseInterestRate(utilizationRate, optimalUtilizationRate, plateauInterestRate, maxInterestRate) {
147
- if (utilizationRate <= optimalUtilizationRate) {
148
- // Below optimal: linear increase from 0 to plateau rate
149
- return (utilizationRate / optimalUtilizationRate) * plateauInterestRate;
150
- }
151
- else {
152
- // Above optimal: linear increase from plateau to max rate
153
- const excessUtilization = utilizationRate - optimalUtilizationRate;
154
- const remainingUtilization = 1 - optimalUtilizationRate;
155
- const excessRate = maxInterestRate - plateauInterestRate;
156
- return (plateauInterestRate +
157
- (excessUtilization / remainingUtilization) * excessRate);
158
- }
159
- }
160
- /**
161
- * Calculate the supply APY
162
- * @param utilizationRate Current utilization rate (0-1)
163
- * @param baseInterestRate Base interest rate calculated from utilization
164
- * @param protocolIrFee Protocol interest rate fee
165
- * @param insuranceIrFee Insurance interest rate fee
166
- * @returns Supply APY as a decimal (e.g., 0.05 for 5%)
167
- *
168
- * Note: You can get these values from a Bank object:
169
- * - utilizationRate = calculateBankUtilizationRate(...)
170
- * - baseInterestRate = calculateBaseInterestRate(...)
171
- * - protocolIrFee = bank.config.interestRateConfig.protocolIrFee
172
- * - insuranceIrFee = bank.config.interestRateConfig.insuranceIrFee
173
- */
174
- function calculateSupplyApy(utilizationRate, baseInterestRate, protocolIrFee, insuranceIrFee) {
175
- // Supply rate = base rate * utilization rate * (1 - protocol fees)
176
- const totalFeeRate = protocolIrFee + insuranceIrFee;
177
- return baseInterestRate * utilizationRate * (1 - totalFeeRate);
178
- }
179
- /**
180
- * Calculate the borrow APY
181
- * @param baseInterestRate Base interest rate calculated from utilization
182
- * @param protocolFixedFeeApr Protocol fixed fee APR
183
- * @param insuranceFeeFixedApr Insurance fixed fee APR
184
- * @returns Borrow APY as a decimal (e.g., 0.08 for 8%)
185
- *
186
- * Note: You can get these values from a Bank object:
187
- * - baseInterestRate = calculateBaseInterestRate(...)
188
- * - protocolFixedFeeApr = bank.config.interestRateConfig.protocolFixedFeeApr
189
- * - insuranceFeeFixedApr = bank.config.interestRateConfig.insuranceFeeFixedApr
190
- */
191
- function calculateBorrowApy(baseInterestRate, protocolFixedFeeApr, insuranceFeeFixedApr) {
192
- // Borrow rate = base rate + fixed protocol fee + fixed insurance fee
193
- const totalFeeRate = protocolFixedFeeApr + insuranceFeeFixedApr;
194
- return baseInterestRate + totalFeeRate;
195
- }
196
- /**
197
- * Computes amounts needed to adjust leverage
198
- * @param currentCollateral - Current collateral amount (e.g., JLP)
199
- * @param currentDebt - Current debt amount (e.g., USDC)
200
- * @param collateralPrice - Current collateral price in USD
201
- * @param debtPrice - Current debt token price in USD
202
- * @param currentLeverage - Current leverage ratio
203
- * @param targetLeverage - Desired leverage ratio
204
- * @param collateralWeight - Weight of collateral for maintenance (0-1)
205
- * @param debtWeight - Weight of debt for maintenance (1+)
206
- */
207
- function computeAdjustLeverageAmounts(currentCollateral, currentDebt, collateralPrice, debtPrice, currentLeverage, targetLeverage, collateralWeight, debtWeight) {
208
- // Convert everything to weighted USD value
209
- const weightedCollateralValue = calculateWeightedValue(currentCollateral, collateralPrice, collateralWeight);
210
- const weightedDebtValue = calculateWeightedValue(currentDebt, debtPrice, debtWeight);
211
- const weightedEquity = weightedCollateralValue - weightedDebtValue;
212
- if (weightedEquity <= 0) {
213
- throw new Error("Weighted equity must be positive");
214
- }
215
- // Target weighted position size based on desired leverage
216
- const targetWeightedPositionValue = weightedEquity * targetLeverage;
217
- const targetWeightedDebtValue = targetWeightedPositionValue - weightedEquity;
218
- // Convert back to raw token amounts
219
- const targetCollateral = targetWeightedPositionValue / (collateralPrice * collateralWeight);
220
- const targetDebt = targetWeightedDebtValue / (debtPrice * debtWeight);
221
- // Calculate changes needed
222
- const collateralDelta = targetCollateral - currentCollateral;
223
- const debtDelta = targetDebt - currentDebt;
224
- return {
225
- isIncrease: targetLeverage > currentLeverage,
226
- collateralDelta: Math.abs(collateralDelta),
227
- debtDelta: Math.abs(debtDelta),
228
- };
229
- }
230
- /**
231
- * Computes the maximum leverage possible given deposit and borrow asset weights
232
- * @param depositAssetWeight - Initial risk weight of deposit asset (0-1). Higher value = more trusted as collateral
233
- * @param borrowLiabilityWeight - Initial risk weight of borrow asset (0-2). Higher value = more conservative borrowing
234
- * @returns Object containing:
235
- * - maxLeverage: Maximum leverage possible through recursive borrow-deposit
236
- * - ltv: Loan-to-Value ratio, representing what portion of collateral can be borrowed
237
- */
238
- function computeMaxLeverage(depositAssetWeight, borrowLiabilityWeight) {
239
- // Handle edge cases
240
- if (borrowLiabilityWeight <= 0) {
241
- return { maxLeverage: -1, ltv: -1 };
242
- }
243
- // LTV represents what portion of collateral value can be borrowed
244
- const ltv = depositAssetWeight / borrowLiabilityWeight;
245
- // Maximum leverage possible through recursive borrow-deposit cycles
246
- // If LTV > 1, leverage is infinite
247
- if (ltv > 1) {
248
- return { maxLeverage: -1, ltv };
249
- }
250
- if (ltv === 1) {
251
- return { maxLeverage: -1, ltv };
252
- }
253
- return {
254
- maxLeverage: 1 / (1 - ltv),
255
- ltv,
256
- };
257
- }
258
- /**
259
- * Computes the borrow and deposit amounts needed to achieve target leverage, accounting for weights
260
- * @param principal - Initial deposit amount
261
- * @param targetLeverage - Desired leverage multiplier
262
- * @param depositPrice - Price of deposit/collateral asset
263
- * @param borrowPrice - Price of borrow/debt asset
264
- * @param collateralWeight - Weight of collateral asset (0-1)
265
- * @param debtWeight - Weight of debt asset (1+)
266
- * @returns Object containing:
267
- * - borrowAmount: Amount to borrow in borrow asset units
268
- * - totalDepositAmount: Total final position size in deposit asset units
269
- */
270
- function computeLoopingAmounts(principal, targetLeverage, depositPrice, borrowPrice, collateralWeight, debtWeight) {
271
- // Validate inputs
272
- if (principal <= 0)
273
- throw new Error("Principal must be positive");
274
- if (targetLeverage <= 1)
275
- throw new Error("Target leverage must be greater than 1");
276
- if (depositPrice <= 0 || borrowPrice <= 0)
277
- throw new Error("Prices must be positive");
278
- if (collateralWeight <= 0 || collateralWeight > 1)
279
- throw new Error("Collateral weight must be between 0 and 1");
280
- if (debtWeight < 1)
281
- throw new Error("Debt weight must be greater than or equal to 1");
282
- const numerator = principal * depositPrice * collateralWeight * (targetLeverage - 1);
283
- const denominator = borrowPrice *
284
- (collateralWeight - targetLeverage * (collateralWeight - debtWeight));
285
- // If denominator is close to zero or negative, we need to cap the leverage
286
- if (denominator <= 0.000001) {
287
- throw new Error("Target leverage is too high for the given weights");
288
- }
289
- const borrowAmount = numerator / denominator;
290
- // Calculate total deposit amount
291
- const totalDepositAmount = principal + borrowAmount * (borrowPrice / depositPrice);
292
- return {
293
- borrowAmountUi: borrowAmount,
294
- totalDepositAmountUi: totalDepositAmount,
295
- };
296
- }
297
- /**
298
- * Calculates the weighted value of an amount based on its price and weight
299
- * @param amount - Raw amount of the token
300
- * @param price - Price of the token
301
- * @param weight - Weight to apply (0-1 for assets, 1+ for liabilities)
302
- * @returns Weighted value in USD terms
303
- */
304
- function calculateWeightedValue(amount, price, weight) {
305
- const weightedAmount = calculateWeightedAmount(amount, weight);
306
- return weightedAmount * price;
307
- }
308
- /**
309
- * Calculates the weighted value of an amount based on its price and weight
310
- * @param amount - Raw amount of the token
311
- * @param price - Price of the token
312
- * @param weight - Weight to apply (0-1 for assets, 1+ for liabilities)
313
- * @returns Weighted value in USD terms
314
- */
315
- function calculateWeightedAmount(amount, weight) {
316
- return amount * weight;
317
- }
318
- /**
319
- * Calculates leverage using weighted values
320
- * @param collateralAmount - Amount of collateral
321
- * @param collateralPrice - Price of collateral
322
- * @param collateralWeight - Weight of collateral (0-1)
323
- * @param debtAmount - Amount of debt
324
- * @param debtPrice - Price of debt
325
- * @param debtWeight - Weight of debt (1+)
326
- * @returns Leverage ratio
327
- */
328
- function calculateWeightedLeverage(collateralAmount, collateralPrice, collateralWeight, debtAmount, debtPrice, debtWeight) {
329
- const weightedCollateralValue = calculateWeightedValue(collateralAmount, collateralPrice, collateralWeight);
330
- const weightedDebtValue = calculateWeightedValue(debtAmount, debtPrice, debtWeight);
331
- const netValue = weightedCollateralValue - weightedDebtValue;
332
- if (netValue <= 0) {
333
- throw new Error("Net value must be positive");
334
- }
335
- return weightedCollateralValue / netValue;
336
- }
337
- /**
338
- * Calculates the debt to repay when withdrawing collateral to maintain the same leverage ratio
339
- * @param currentCollateral - Current collateral amount
340
- * @param currentDebt - Current debt amount
341
- * @param withdrawAmount - Amount of collateral to withdraw
342
- * @param collateralPrice - Price of collateral token
343
- * @param debtPrice - Price of debt token
344
- * @param collateralWeight - Weight of collateral (0-1)
345
- * @param debtWeight - Weight of debt (1+)
346
- * @returns Object containing the debt to repay and new leverage values
347
- */
348
- function computeWithdrawLeverageAmounts(currentCollateral, currentDebt, withdrawAmount, collateralPrice, debtPrice, collateralWeight, debtWeight) {
349
- // Calculate current leverage
350
- const currentLeverage = calculateWeightedLeverage(currentCollateral, collateralPrice, collateralWeight, currentDebt, debtPrice, debtWeight);
351
- // Ensure we don't withdraw more than available
352
- if (withdrawAmount > currentCollateral) {
353
- throw new Error("Withdrawal amount exceeds available collateral");
354
- }
355
- // Calculate new collateral amount
356
- const newCollateral = currentCollateral - withdrawAmount;
357
- // Calculate weighted collateral after withdrawal
358
- const newWeightedCollateral = newCollateral * collateralPrice * collateralWeight;
359
- // To maintain the same leverage, we need:
360
- // currentLeverage = newWeightedCollateral / (newWeightedCollateral - newWeightedDebt)
361
- //
362
- // Solving for newWeightedDebt:
363
- // currentLeverage * (newWeightedCollateral - newWeightedDebt) = newWeightedCollateral
364
- // currentLeverage * newWeightedCollateral - currentLeverage * newWeightedDebt = newWeightedCollateral
365
- // -currentLeverage * newWeightedDebt = newWeightedCollateral - currentLeverage * newWeightedCollateral
366
- // -currentLeverage * newWeightedDebt = newWeightedCollateral * (1 - currentLeverage)
367
- const newWeightedDebt = newWeightedCollateral * (1 - 1 / currentLeverage);
368
- // Convert weighted debt to actual debt amount
369
- const newDebt = newWeightedDebt / (debtPrice * debtWeight);
370
- // Calculate debt to repay
371
- const debtToRepay = currentDebt - newDebt;
372
- // Calculate what the new leverage will be (should be very close to currentLeverage)
373
- const newLeverage = calculateWeightedLeverage(newCollateral, collateralPrice, collateralWeight, newDebt, debtPrice, debtWeight);
374
- return {
375
- debtToRepayUi: debtToRepay,
376
- newCollateralUi: newCollateral,
377
- newDebtUi: newDebt,
378
- currentLeverage,
379
- newLeverage,
380
- };
381
- }
382
- /**
383
- * Calculates the health factor and leverage of a Clend account using weighted maintenance values.
384
- *
385
- * Standard Health Factor = Total Weighted Assets (Maintenance) / Total Weighted Liabilities (Maintenance)
386
- * Interpretation:
387
- * - HF > 1: Position is overcollateralized (safe).
388
- * - HF = 1: Position is at the liquidation threshold.
389
- * - HF < 1: Position is undercollateralized and liquidatable.
390
- * - HF = Infinity: Position has no debt (healthiest).
391
- *
392
- * Leverage = Total Weighted Assets (Maintenance) / (Total Weighted Assets (Maintenance) - Total Weighted Liabilities (Maintenance))
393
- *
394
- * @param weightedAssetValues - An array of the user's weighted asset values (maintenance).
395
- * @param weightedLiabilityValues - An array of the user's weighted liability values (maintenance).
396
- * @returns An object containing the healthFactor and leverage.
397
- */
398
- function calculateHealthFactorAndLeverage(weightedAssetValues, weightedLiabilityValues) {
399
- let totalWeightedAssets = 0;
400
- for (const weightedAssetValue of weightedAssetValues) {
401
- totalWeightedAssets += weightedAssetValue;
402
- }
403
- let totalWeightedLiabilities = 0;
404
- for (const weightedLiabilityValue of weightedLiabilityValues) {
405
- totalWeightedLiabilities += weightedLiabilityValue;
406
- }
407
- // Calculate health factor using standard convention (Assets / Liabilities)
408
- let healthFactor;
409
- if (totalWeightedLiabilities === 0) {
410
- // Handle division by zero: No liabilities means infinite health
411
- healthFactor = Infinity;
412
- }
413
- else if (totalWeightedAssets === 0) {
414
- // Handle edge case where assets are zero but liabilities are not (should imply HF=0)
415
- healthFactor = 0;
416
- }
417
- else {
418
- // Standard calculation
419
- healthFactor = totalWeightedAssets / totalWeightedLiabilities;
420
- }
421
- // Calculate leverage (same formula as before, using weighted values)
422
- let leverage = 0; // Default to 0 if equity is zero or negative
423
- const equity = totalWeightedAssets - totalWeightedLiabilities;
424
- if (equity > 0 && totalWeightedAssets > 0) {
425
- // Ensure equity is positive and avoid division by zero if assets are zero
426
- leverage = totalWeightedAssets / equity;
427
- }
428
- else if (totalWeightedAssets > 0 && totalWeightedLiabilities === 0) {
429
- // If only assets exist, leverage is 1x
430
- leverage = 1;
431
- }
432
- return { healthFactor, leverage };
433
- }
434
- /**
435
- * Calculates the interest that has accrued but not yet been applied to a bank
436
- * @param lastUpdate The timestamp of the last update
437
- * @param assetAmountUi The total asset amount in UI format
438
- * @param liabilityAmountUi The total liability amount in UI format
439
- * @param utilizationRate The current utilization rate
440
- * @param interestRateConfig The interest rate configuration
441
- * @param currentTimestamp Optional current timestamp (in seconds). Defaults to Math.floor(Date.now() / 1000)
442
- * @returns Object containing:
443
- * - assetInterestAccrued: Interest accrued for lenders (in token units)
444
- * - liabilityInterestAccrued: Interest accrued for borrowers (in token units)
445
- * - insuranceFeesAccrued: Insurance fees accrued (in token units)
446
- * - groupFeesAccrued: Group fees accrued (in token units)
447
- * - protocolFeesAccrued: Protocol fees accrued (in token units)
448
- */
449
- function calculateAccruedInterest(lastUpdate, assetAmountUi, liabilityAmountUi, utilizationRate, interestRateConfig, currentTimestamp) {
450
- const timeDelta = currentTimestamp - lastUpdate.toNumber();
451
- // If no time has passed, return zero values
452
- if (timeDelta <= 0) {
453
- return {
454
- assetInterestAccrued: 0,
455
- liabilityInterestAccrued: 0,
456
- insuranceFeesAccrued: 0,
457
- groupFeesAccrued: 0,
458
- protocolFeesAccrued: 0,
459
- };
460
- }
461
- // Get total assets and liabilities
462
- const totalAssets = assetAmountUi;
463
- const totalLiabilities = liabilityAmountUi;
464
- // If either total assets or liabilities are zero, return zero values
465
- if (totalAssets === 0 || totalLiabilities === 0) {
466
- return {
467
- assetInterestAccrued: 0,
468
- liabilityInterestAccrued: 0,
469
- insuranceFeesAccrued: 0,
470
- groupFeesAccrued: 0,
471
- protocolFeesAccrued: 0,
472
- };
473
- }
474
- // Get interest rate configuration
475
- const { optimalUtilizationRate, plateauInterestRate, maxInterestRate, insuranceFeeFixedApr, insuranceIrFee, protocolFixedFeeApr, protocolIrFee, } = interestRateConfig;
476
- // Calculate base interest rate
477
- const baseInterestRate = calculateBaseInterestRate(utilizationRate, optimalUtilizationRate, plateauInterestRate, maxInterestRate);
478
- // Calculate lending rate (supply rate)
479
- const lendingRate = baseInterestRate * utilizationRate * (1 - (protocolIrFee + insuranceIrFee));
480
- // Calculate borrowing rate
481
- const borrowingRate = baseInterestRate * (1 + protocolIrFee) +
482
- protocolFixedFeeApr +
483
- insuranceFeeFixedApr;
484
- // Calculate fee rates
485
- const insuranceFeeRate = baseInterestRate * insuranceIrFee + insuranceFeeFixedApr;
486
- const groupFeeRate = baseInterestRate * protocolIrFee + protocolFixedFeeApr;
487
- const protocolFeeRate = baseInterestRate * protocolIrFee + protocolFixedFeeApr;
488
- // Calculate interest accrued for the time period
489
- const secondsPerYear = 365 * 24 * 60 * 60;
490
- const timeDeltaFraction = timeDelta / secondsPerYear;
491
- // Calculate asset interest accrued (for lenders)
492
- const assetInterestAccrued = totalAssets * lendingRate * timeDeltaFraction;
493
- // Calculate liability interest accrued (for borrowers)
494
- const liabilityInterestAccrued = totalLiabilities * borrowingRate * timeDeltaFraction;
495
- // Calculate fees accrued
496
- const insuranceFeesAccrued = totalLiabilities * insuranceFeeRate * timeDeltaFraction;
497
- const groupFeesAccrued = totalLiabilities * groupFeeRate * timeDeltaFraction;
498
- const protocolFeesAccrued = totalLiabilities * protocolFeeRate * timeDeltaFraction;
499
- return {
500
- assetInterestAccrued,
501
- liabilityInterestAccrued,
502
- insuranceFeesAccrued,
503
- groupFeesAccrued,
504
- protocolFeesAccrued,
505
- };
506
- }
507
- /**
508
- * Adjusts an ideal borrow amount downwards solely to compensate for the
509
- * protocol's origination fee. This calculates the principal amount to borrow
510
- * such that after the fee is added, the liability matches the ideal amount.
511
- *
512
- * @param idealBorrowAmount - The target borrow amount before considering the fee (native units, BN).
513
- * @param originationFeeRate - The protocol origination fee rate (e.g., 0.001 for 0.1%).
514
- * @returns Adjusted borrow amount (native units, BN), slightly less than or equal to ideal.
515
- */
516
- function adjustBorrowForOriginationFee(idealBorrowAmount, originationFeeRate) {
517
- // Ensure the fee rate is valid
518
- if (originationFeeRate < 0) {
519
- return idealBorrowAmount;
520
- }
521
- const feeRateDecimal = new decimal_js_1.Decimal(originationFeeRate);
522
- // If there's no fee, no adjustment is needed
523
- if (feeRateDecimal.isZero()) {
524
- return idealBorrowAmount;
525
- }
526
- // Formula: B_actual ≈ B_ideal / (1 + FeeRate)
527
- // We borrow slightly less, anticipating the fee being added to the liability.
528
- const denominator = new decimal_js_1.Decimal(1).add(feeRateDecimal);
529
- // Safety check for the denominator (should not happen with non-negative fee)
530
- if (denominator.isZero() ||
531
- denominator.isNaN() ||
532
- !denominator.isFinite() ||
533
- denominator.isNegative()) {
534
- return idealBorrowAmount;
535
- }
536
- const idealBorrowDecimal = new decimal_js_1.Decimal(idealBorrowAmount.toString());
537
- const adjustedBorrowDecimal = idealBorrowDecimal.div(denominator);
538
- // Round down when reducing the borrow amount due to the fee cost.
539
- const adjustedBorrowAmount = new anchor_1.BN(adjustedBorrowDecimal.floor().toFixed());
540
- // Ensure we don't adjust below zero
541
- return anchor_1.BN.max(adjustedBorrowAmount, new anchor_1.BN(0));
542
- }
543
- /**
544
- * Adjusts an amount upwards to compensate for potential swap slippage,
545
- * assuming worst-case based on slippageBps.
546
- *
547
- * @param amountToAdjust - The amount to adjust (native units, BN), typically after cost adjustments.
548
- * @param slippageBps - The maximum slippage tolerance in basis points (e.g., 100 for 1%).
549
- * @returns Adjusted amount (native units, BN), slightly more than the input amount. Returns input amount if slippageBps is zero or invalid.
550
- */
551
- function adjustAmountForSlippage(amountToAdjust, slippageBps) {
552
- if (slippageBps <= 0) {
553
- return amountToAdjust; // No adjustment if no slippage tolerance
554
- }
555
- const S = new decimal_js_1.Decimal(slippageBps).div(10000); // Slippage factor S as decimal
556
- // Ensure S is less than 1 (100% slippage)
557
- if (S.gte(1)) {
558
- return amountToAdjust;
559
- }
560
- // Formula: B_actual ≈ B_ideal / (1 - S)
561
- // We borrow slightly more, anticipating the swap output will be worth less by factor S.
562
- const denominator = new decimal_js_1.Decimal(1).sub(S);
563
- // Should not happen with S < 1, but safety check
564
- if (denominator.isZero() ||
565
- denominator.isNaN() ||
566
- !denominator.isFinite() ||
567
- denominator.isNegative()) {
568
- return amountToAdjust;
569
- }
570
- const amountToAdjustDecimal = new decimal_js_1.Decimal(amountToAdjust.toString());
571
- const adjustedAmountDecimal = amountToAdjustDecimal.div(denominator);
572
- // Round up when increasing amount due to needing more input for slippage
573
- const adjustedAmount = new anchor_1.BN(adjustedAmountDecimal.ceil().toFixed());
574
- return adjustedAmount;
575
- }
576
- /**
577
- * Calculates the estimated liquidation price for a collateral asset.
578
- *
579
- * Liquidation is possible when the risk-adjusted value of debt meets or exceeds
580
- * the risk-adjusted value of the collateral, based on maintenance requirements.
581
- *
582
- * Formula:
583
- * CollateralLiquidationPrice = (DebtAmount * DebtPrice * DebtMaintenanceLiabilityWeight) / (CollateralAmount * CollateralMaintenanceAssetWeight)
584
- *
585
- * @param collateralAmount The amount of the collateral asset deposited (e.g., number of JLP tokens).
586
- * @param collateralMaintenanceAssetWeight The maintenance asset weight for the collateral asset (e.g., 0.85 for 85%). Found in Marginfi protocol config/UI.
587
- * @param debtAmount The amount of the debt asset borrowed (e.g., number of USDC tokens).
588
- * @param debtMaintenanceLiabilityWeight The maintenance liability weight for the debt asset (e.g., 1.1 for 110%). Found in Marginfi protocol config/UI.
589
- * @param debtPrice The current price of the debt asset.
590
- * @returns The estimated price of the collateral asset at which liquidation may occur, or Infinity if collateral amount/weight is zero.
591
- */
592
- function calculateLiquidationPrice(collateralAmountUi, collateralMaintenanceAssetWeight, debtAmountUi, debtMaintenanceLiabilityWeight, debtPrice) {
593
- // Ensure inputs are valid numbers
594
- if (isNaN(collateralAmountUi) ||
595
- isNaN(collateralMaintenanceAssetWeight) ||
596
- isNaN(debtAmountUi) ||
597
- isNaN(debtMaintenanceLiabilityWeight) ||
598
- isNaN(debtPrice)) {
599
- throw new Error("All input parameters must be valid numbers.");
600
- }
601
- // if 0 values just return 0
602
- if (collateralAmountUi === 0 ||
603
- debtAmountUi === 0 ||
604
- debtPrice === 0 ||
605
- collateralMaintenanceAssetWeight === 0 ||
606
- debtMaintenanceLiabilityWeight === 0) {
607
- return 0;
608
- }
609
- // Calculate the weighted value of the collateral side (denominator)
610
- const weightedCollateralValueFactor = collateralAmountUi * collateralMaintenanceAssetWeight;
611
- // Avoid division by zero if collateral amount or weight is zero
612
- if (weightedCollateralValueFactor <= 0) {
613
- throw new Error(`collateral amount is 0 or less`);
614
- }
615
- // Calculate the weighted value of the debt side (numerator)
616
- const weightedDebtValue = debtAmountUi * debtPrice * debtMaintenanceLiabilityWeight;
617
- // Calculate the liquidation price
618
- const liquidationPrice = weightedDebtValue / weightedCollateralValueFactor;
619
- return liquidationPrice;
620
- }
621
- /**
622
- * Calculates the Loan-to-Value (LTV) ratio.
623
- * LTV = Total Value of Liabilities / Total Value of Assets
624
- *
625
- * Note: The input values can be either weighted (initial or maintenance) or unweighted USD values,
626
- * as long as the same type of value is used consistently for both assets and liabilities.
627
- * The interpretation of the LTV depends on the type of values used.
628
- *
629
- * @param {number[]} assetValues - An array of the user's asset values (e.g., USD value, weighted or unweighted).
630
- * @param {number[]} liabilityValues - An array of the user's liability values (e.g., USD value, weighted or unweighted).
631
- * @returns {number} The LTV ratio. Returns 0 if there are no liabilities. Returns `NaN` if total asset value is zero or negative.
632
- */
633
- function calculateLtv(assetValues, liabilityValues) {
634
- let totalAssetValue = 0;
635
- for (const value of assetValues) {
636
- totalAssetValue += value;
637
- }
638
- let totalLiabilityValue = 0;
639
- for (const value of liabilityValues) {
640
- totalLiabilityValue += value;
641
- }
642
- // If there are no assets (or negative total value), LTV is undefined.
643
- if (totalAssetValue <= 0) {
644
- // Return NaN if liabilities exist but assets don't, or if assets have zero/negative value.
645
- // Return 0 if both are zero (as technically liabilities are 0).
646
- return totalLiabilityValue === 0 ? 0 : NaN;
647
- }
648
- // If there are no liabilities, LTV is 0.
649
- if (totalLiabilityValue === 0) {
650
- return 0;
651
- }
652
- // Standard LTV calculation
653
- const ltv = totalLiabilityValue / totalAssetValue;
654
- return ltv;
655
- }
656
- /**
657
- * Calculates the percentage change required for the current price to reach the liquidation price.
658
- *
659
- * @param currentPrice The current price of the asset.
660
- * @param liquidationPrice The price at which the asset would be liquidated.
661
- * @returns The percentage change needed to reach the liquidation price (e.g., -10.5 for a 10.5% drop). Returns Infinity if currentPrice is 0.
662
- */
663
- function calculateLiquidationPriceChangePercentage(currentPrice, liquidationPrice) {
664
- if (currentPrice <= 0) {
665
- return 0;
666
- }
667
- const priceChange = liquidationPrice - currentPrice;
668
- const percentageChange = (priceChange / currentPrice) * 100;
669
- return percentageChange;
670
- }
671
- /**
672
- * Calculates the current liability interest for a specific liability position,
673
- * accounting for interest accrued since the bank's last update.
674
- *
675
- * @param userLiabilitySharesBN - The user's liability shares for the debt asset (native units, BN).
676
- * @param bankLiabilityShareValue - The bank's liability share value as of last update (number).
677
- * @param bankBorrowApy - The current estimated borrow APY of the debt asset (e.g., 0.025 for 2.5%).
678
- * @param bankLastUpdateSeconds - The Unix timestamp (seconds) when the bank's interest was last updated on-chain.
679
- * @param currentTimeSeconds - The current Unix timestamp (seconds).
680
- * @returns The estimated current debt amount for the user (native units, BN).
681
- */
682
- function calculateLiabilityInterest(userLiabilityShares, bankLiabilityShareValue, bankBorrowApy, bankLastUpdateSeconds, currentTimeSeconds) {
683
- // If user has no liability shares for this bank, debt is zero.
684
- if (userLiabilityShares <= 0) {
685
- return new anchor_1.BN(0);
686
- }
687
- // Calculate time delta
688
- const secondsSinceLastUpdate = currentTimeSeconds - bankLastUpdateSeconds;
689
- let currentLiabilityShareValue;
690
- // hardcode for now
691
- const secondsPerYear = 31536000;
692
- // Calculate the current share value if time has passed and interest rate is positive
693
- if (secondsSinceLastUpdate > 0) {
694
- const interestRatePerSecond = bankBorrowApy / secondsPerYear;
695
- const growthFactor = interestRatePerSecond * secondsSinceLastUpdate + 1;
696
- currentLiabilityShareValue = bankLiabilityShareValue * growthFactor;
697
- }
698
- else {
699
- // Otherwise, use the share value as of the last update
700
- currentLiabilityShareValue = bankLiabilityShareValue;
701
- }
702
- // Calculate estimated debt = user_shares * current_share_value
703
- const debt = new anchor_1.BN(userLiabilityShares * currentLiabilityShareValue);
704
- return debt;
705
- }
706
- /**
707
- * Calculates the final debt repayment target amount by adding buffers
708
- * for future interest accrual and minor discrepancies to an estimated current debt.
709
- * this is used to calculate the final debt to repay amount taking into account tx execution time from when its calculated
710
- *
711
- * @param currentDebt - The current debt amount including interest accrued up to the present moment
712
- * @param borrowApy - The current estimated borrow APY of the debt asset (e.g., 0.025 for 2.5%).
713
- * @param futureInterestBufferSec - The estimated number of seconds of future interest to buffer for transaction execution time. As in, if it takes
714
- * 10 seconds to execute the tx from when this is called, we will add 10 seconds of interest to the debt
715
- * @returns The final debt repayment target amount (native units, BN).
716
- */
717
- function addInterestAccrualBuffer(currentDebt, borrowApy, futureInterestBufferSec) {
718
- // A small fixed buffer amount (in lamports/native units) to add for minor rounding or timing variations (default: 100).
719
- const fixedBufferLamports = new anchor_1.BN(1000);
720
- const SECONDS_PER_YEAR = 31536000;
721
- let finalDebtToRepay = currentDebt;
722
- // Add buffer for future interest during execution, if applicable
723
- if (borrowApy > 0 && futureInterestBufferSec > 0 && !currentDebt.isZero()) {
724
- const interestRatePerSecond = new decimal_js_1.Decimal(borrowApy).div(SECONDS_PER_YEAR);
725
- // Calculate buffer based on the estimated current debt amount
726
- const futureInterestDecimal = new decimal_js_1.Decimal(currentDebt.toString())
727
- .mul(interestRatePerSecond)
728
- .mul(futureInterestBufferSec);
729
- // Round UP the future interest amount to be conservative
730
- const futureInterestBN = new anchor_1.BN(futureInterestDecimal.ceil().toFixed());
731
- finalDebtToRepay = finalDebtToRepay.add(futureInterestBN);
732
- }
733
- // Add the small fixed buffer
734
- finalDebtToRepay = finalDebtToRepay.add(fixedBufferLamports);
735
- return finalDebtToRepay;
736
- }
737
- //# sourceMappingURL=math.js.map