Skip to content

Commit be3cdd7

Browse files
committed
Refactor loan repayment calculations in Moolah SDK to use cumulative borrow index in RAY format (27 decimals) instead of WAD format (18 decimals). Update related tests and documentation to reflect changes, ensuring improved accuracy in dynamic loan repayment computations.
1 parent 563a7c1 commit be3cdd7

8 files changed

Lines changed: 49 additions & 41 deletions

File tree

packages/moolah-lending-sdk/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# @lista-dao/moolah-lending-sdk
22

3+
## 1.0.11
4+
5+
### Patch Changes
6+
7+
- Refactor loan repayment calculations in Moolah SDK to use cumulative borrow index in RAY format (27 decimals) instead of WAD format (18 decimals). Update related tests and documentation to reflect changes, ensuring improved accuracy in dynamic loan repayment computations.
8+
- Updated dependencies
9+
- @lista-dao/moolah-sdk-core@1.0.11
10+
311
## 1.0.10
412

513
### Patch Changes

packages/moolah-lending-sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lista-dao/moolah-lending-sdk",
3-
"version": "1.0.10",
3+
"version": "1.0.11",
44
"type": "module",
55
"main": "./dist/index.js",
66
"module": "./dist/index.js",

packages/moolah-lending-sdk/src/__tests__/read/broker/getBrokerUserPositions.test.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,21 @@ const BROKER = "0x1111111111111111111111111111111111111111" as Address;
1313
const RATE_CALCULATOR =
1414
"0x2222222222222222222222222222222222222222" as Address;
1515
const USER = "0x3333333333333333333333333333333333333333" as Address;
16-
const WAD = 10n ** 18n;
1716

1817
describe("getBrokerUserPositions - dynamic debt", () => {
1918
beforeEach(() => {
2019
vi.clearAllMocks();
2120
});
2221

23-
it("computes dynamic outstanding with legacy decimal-normalized dynamic rate", async () => {
22+
it("computes dynamic outstanding with cumulative borrow index rate", async () => {
2423
const principal = 17061806491632102441n;
2524
const normalizedDebt = 16878807815476167930n;
26-
const rate = 1011624315990879851775155575n;
25+
const rate = 1011624315990879851775155575n; // Cumulative borrow index in RAY format (27 decimals)
2726
const { totalRepay } = calculateDynamicLoanRepayment(
2827
{
2928
principal,
3029
normalizedDebt,
31-
rate: rate / WAD,
30+
rate, // Rate is passed directly in RAY format
3231
},
3332
18,
3433
);

packages/moolah-lending-sdk/src/read/broker/getBrokerUserPositions.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313

1414
const ONE_E27 = 10n ** 27n;
1515
const SECONDS_PER_WEEK = 604800n;
16-
const RATE_SCALE_18 = 10n ** 18n;
1716
const FLEXIBLE_RATE_NUMERATOR = 100n;
1817
const FLEXIBLE_RATE_DENOMINATOR = 95n;
1918

@@ -25,14 +24,6 @@ function normalizeAprRate(apr: bigint): bigint {
2524
return apr > ONE_E27 ? apr - ONE_E27 : apr;
2625
}
2726

28-
/**
29-
* Convert broker dynamic rate (27 decimals) to WAD (18 decimals)
30-
* expected by calculateDynamicLoanRepayment.
31-
*/
32-
function normalizeDynamicRateToWad(dynamicRate: bigint): bigint {
33-
return dynamicRate / RATE_SCALE_18;
34-
}
35-
3627
function buildTermRateData(terms: readonly RawFixedTerm[]) {
3728
const termRateByDuration = new Map<string, Decimal>();
3829
let currentFlexibleRate = Decimal.ZERO;
@@ -69,7 +60,7 @@ function calculateDynamicOutstanding(
6960
{
7061
principal: dynamicPosition.principal,
7162
normalizedDebt: dynamicPosition.normalizedDebt,
72-
rate: normalizeDynamicRateToWad(dynamicRate),
63+
rate: dynamicRate, // Rate is cumulative borrow index in RAY format (27 decimals)
7364
},
7465
loanDecimals,
7566
);

packages/moolah-sdk-core/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @lista-dao/moolah-sdk-core
22

3+
## 1.0.11
4+
5+
### Patch Changes
6+
7+
- Refactor loan repayment calculations in Moolah SDK to use cumulative borrow index in RAY format (27 decimals) instead of WAD format (18 decimals). Update related tests and documentation to reflect changes, ensuring improved accuracy in dynamic loan repayment computations.
8+
39
## 1.0.10
410

511
### Patch Changes

packages/moolah-sdk-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lista-dao/moolah-sdk-core",
3-
"version": "1.0.10",
3+
"version": "1.0.11",
44
"type": "module",
55
"main": "./dist/index.js",
66
"module": "./dist/index.js",

packages/moolah-sdk-core/src/__tests__/loan.test.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,14 @@ describe("getCurrentRoundedTimestamp", () => {
7272
});
7373

7474
describe("calculateDynamicLoanRepayment", () => {
75+
// Rate is now a cumulative borrow index in RAY format (27 decimals)
76+
// 1e27 = index of 1.0 (no interest), 1.05e27 = 5% interest accumulated
77+
const RATE_INDEX_5_PERCENT = RATE_SCALE_27 + (RATE_SCALE_27 * 5n) / 100n; // 1.05e27
78+
7579
it("should calculate repayment for dynamic loan", () => {
7680
const result = calculateDynamicLoanRepayment({
7781
principal: 1000n * DECIMAL_SCALE,
78-
rate: 158548959n, // ~5% APY per second rate
82+
rate: RATE_INDEX_5_PERCENT, // 5% accumulated interest (borrow index = 1.05)
7983
});
8084

8185
expect(result.principal.numerator).toBe(1000n * DECIMAL_SCALE);
@@ -87,35 +91,35 @@ describe("calculateDynamicLoanRepayment", () => {
8791
const result = calculateDynamicLoanRepayment({
8892
principal: 1000n * DECIMAL_SCALE,
8993
normalizedDebt: 1100n * DECIMAL_SCALE, // With accrued interest
90-
rate: 158548959n,
94+
rate: RATE_INDEX_5_PERCENT,
9195
});
9296

9397
expect(result.principal.numerator).toBe(1100n * DECIMAL_SCALE);
9498
});
9599

96-
it("should handle zero rate", () => {
100+
it("should handle zero rate (index = 1.0)", () => {
97101
const result = calculateDynamicLoanRepayment({
98102
principal: 1000n * DECIMAL_SCALE,
99-
rate: 0n,
103+
rate: RATE_SCALE_27, // Index = 1.0, no interest accumulated
100104
});
101105

102-
// With zero rate, total should equal principal (plus minimal 10-min buffer)
106+
// With index = 1.0, total should equal principal (no interest, no buffer)
103107
expect(result.totalRepay.valueOf()).toBeCloseTo(1000, 0);
104108
});
105109

106110
it("should respect loan decimals", () => {
107111
const result6 = calculateDynamicLoanRepayment(
108112
{
109113
principal: 1000n * 10n ** 6n,
110-
rate: 158548959n,
114+
rate: RATE_INDEX_5_PERCENT,
111115
},
112116
6,
113117
);
114118

115119
const result18 = calculateDynamicLoanRepayment(
116120
{
117121
principal: 1000n * DECIMAL_SCALE,
118-
rate: 158548959n,
122+
rate: RATE_INDEX_5_PERCENT,
119123
},
120124
18,
121125
);

packages/moolah-sdk-core/src/calculations/loan.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Decimal } from "../utils/decimal.js";
2-
import { MathLib } from "@morpho-org/blue-sdk";
32

43
import type {
54
DynamicLoanPosition,
@@ -45,7 +44,7 @@ export function getCurrentRoundedTimestamp(bufferMinutes = 10): bigint {
4544
* @param position - The dynamic loan position data
4645
* @param position.principal - Principal amount borrowed
4746
* @param position.normalizedDebt - Normalized debt amount (optional, defaults to principal)
48-
* @param position.rate - Current interest rate per second
47+
* @param position.rate - Cumulative borrow index in RAY format (27 decimals)
4948
* @param loanDecimals - The decimals of the loan token (default: 18)
5049
* @returns Object containing total repay amount and breakdown of principal and interest
5150
*/
@@ -58,32 +57,33 @@ export function calculateDynamicLoanRepayment(
5857
loanDecimals,
5958
);
6059

61-
// position.rate is already a per-second rate in 18 decimals
62-
const rawRate = MathLib.wTaylorCompounded(position.rate, ONE_YEAR_SECONDS);
63-
const rateDecimal = new Decimal(rawRate, 18);
60+
// rate is the cumulative borrow index in RAY format (27 decimals)
61+
const rateIndex = new Decimal(position.rate, 27);
6462

65-
// Total outstanding = normalized debt × (1 + rate)
66-
const currentTotalRepay = normalizedDebt.mul(rateDecimal.add(Decimal.ONE));
63+
// Current debt = normalized debt × rate index
64+
const currentDebt = normalizedDebt.mul(rateIndex);
6765

66+
// Principal is the normalized debt
6867
const principal = normalizedDebt;
69-
const tenMinutesRawRate = MathLib.wTaylorCompounded(
70-
position.rate,
71-
TEN_MINUTES_SECONDS,
72-
);
73-
const tenMinutesRateDecimal = new Decimal(tenMinutesRawRate, 18);
7468

75-
const tenMinutesInterest = principal.mul(tenMinutesRateDecimal.toString(18));
69+
// Interest = current debt - principal
70+
const currentInterest = currentDebt.sub(principal);
71+
72+
// Buffer = 10% of current interest
73+
const bufferRate = new Decimal(10n ** 17n, 18); // 0.1 in 18 decimals
74+
const buffer = currentInterest.mul(bufferRate);
7675

77-
// add additional 10 minutes interest to the current total repay
78-
const totalRepay = currentTotalRepay.add(tenMinutesInterest);
76+
// Total repay = current debt + buffer
77+
const totalRepay = currentDebt.add(buffer);
7978

80-
const interest = totalRepay.sub(normalizedDebt);
79+
// Total interest including buffer
80+
const interest = totalRepay.sub(principal);
8181

8282
return {
8383
totalRepay,
84-
principal: normalizedDebt,
84+
principal,
8585
interest,
86-
rate: rateDecimal,
86+
rate: rateIndex,
8787
};
8888
}
8989

0 commit comments

Comments
 (0)