Skip to content

Commit 93a14d6

Browse files
authored
ADR: Revise interest curve utilization rate definition
1 parent 435891a commit 93a14d6

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Interest Curve Utilization Rate Definition
2+
**Status**: Proposed
3+
4+
**Date**: 2026-03-31
5+
6+
**Authors**: Jordan Schalm
7+
8+
**Component**: ALP
9+
10+
### References
11+
- [Slack discussion in Flow Foundation](https://flow-foundation.slack.com/archives/C08QF29F7TK/p1773783670640229)
12+
- [Automated Meeting Transcript & Notes](https://docs.google.com/document/d/1x5X8Dynlbs1IDO46NjckWnW3NaNw0nP9696W7MYQN60/edit?usp=sharing)
13+
14+
## Context
15+
16+
The protocol's interest curve requires a utilization rate to determine borrowing and lending rates. The current implementation computes utilization as `totalCredit / totalDebit`. This differs from the industry-standard approach (e.g. AAVE), and the way these values are computed is faulty in some circumstances, because they do not account for interest accumulation.
17+
18+
`totalDebt` and `totalCredit` are computed as the accumulation of inflows/outflows to/from debit/credit-direction positions. They are stored as absolute (not interest-adjusted) unsigned values, which can cause updates to the values to clamp to zero and become permanently inaccurate.
19+
20+
### Minimal Example
21+
- User A deposits 100 units of X
22+
- User B withdraws 100 units of X -> totalDebit(X) = 100
23+
- Suppose debit rate on X is 10%, and 1 year passes
24+
- User B deposits 105 units of X -> totalDebit(X) = 0
25+
- User B still owes 5 units of X, but totalDebit(X) = 0
26+
27+
## Decision
28+
29+
### 1. Retain the interest curve utilization rate:
30+
31+
```
32+
utilization = totalTrueCreditBalance / totalTrueDebitBalance
33+
```
34+
35+
### 2. Re-define `totalTrueCreditBalance`/`totalTrueDebitBalance` to account for interest accrual.
36+
37+
Let's describe the definition wlog for `totalTrueDebitBalance`:
38+
- Let `X` be some token
39+
- Let `debitPositions(X)` be the set of positions which have a debit balance for token `X`
40+
- Let `totalTrueDebitBalance(X)` be the sum of token X debit balances for all positions in `debitPositions(X)`
41+
42+
### 3. Change the protocol fee definition
43+
44+
Prior to this ADR, protocol fees were computed as a percentage of estimated debit income, when extracted. Instead, we will compute protocol fees (insurance fee, stability fee) explicitly as the excess funds available in reserves.
45+
46+
```
47+
protocolFee = reserveBalance + totalTrueDebitBalance - totalTrueCreditBalance
48+
```
49+
50+
## Rationale
51+
52+
1. **Eliminates inaccuracy from clamping.** The current `totalCredit / totalDebit` approach can permanently accumulate inaccuracies from clamping. The proposed formula avoids this by accounting for interest accumulation.
53+
2. **Clean fee handling.** Defining protocol fees in terms of actual reserve balances and credit/debit accounting values is easier to reason about.
54+
55+
## Related Changes
56+
57+
This ADR focuses on the utilization rate definition. The following related changes were discussed and should be addressed separately:
58+
59+
- **Protocol fee redefinition:** Change from a percentage of debit income to a basis-point spread between the debit rate and credit rate. In general, it would be good to consolidate how the fee definition is defined: currently it is a spread for fixed curves, and a percentage of debit income for other curves. (Possible overlap with https://github.com/onflow/FlowALP/pull/288).
60+
- **Sanity checks:** Derive and enforce an invariant relating debit/credit interest indices, total true debit/credit balances, position true credit/debit balances, and protocol fees, either on-chain or off-chain.
61+
- **Bad debt handling:** Currently manual; future work will automate liquidation using an insurance fund to cover shortfalls.
62+
63+
## Implementation Notes
64+
65+
- Track `totalTrueCreditBalance`/`totalTrueDebitBalance` as **scaled values**, updating via scaled diffs on each position change.
66+
- Add logic to convert scaled totals to true balances (scaled balance × interest index) for use in the utilization calculation.
67+
- Protocol fee can be derived as: `reserveBalance + totalTrueDebitBalance - totalTrueCreditBalance`.
68+
- Interest rate update logic incorrectly uses `totalDebitBalance`:
69+
```
70+
let debitIncome = self.totalDebitBalance * debitRate
71+
```

0 commit comments

Comments
 (0)