Skip to content

Commit 91be4d5

Browse files
committed
Reapply "Merge pull request #160 from onflow/jord/split-contracts"
This reverts commit 47d3ed0.
1 parent 3f464d7 commit 91be4d5

75 files changed

Lines changed: 3704 additions & 2486 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cadence/contracts/FlowALPEvents.cdc

Lines changed: 358 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import "FlowALPMath"
2+
3+
access(all) contract FlowALPInterestRates {
4+
5+
/// InterestCurve
6+
///
7+
/// A simple interface to calculate interest rate for a token type.
8+
access(all) struct interface InterestCurve {
9+
/// Returns the annual interest rate for the given credit and debit balance, for some token T.
10+
/// @param creditBalance The credit (deposit) balance of token T
11+
/// @param debitBalance The debit (withdrawal) balance of token T
12+
access(all) fun interestRate(creditBalance: UFix128, debitBalance: UFix128): UFix128 {
13+
post {
14+
// Max rate is 400% (4.0) to accommodate high-utilization scenarios
15+
// with kink-based curves like Aave v3's interest rate strategy
16+
result <= 4.0:
17+
"Interest rate can't exceed 400%"
18+
}
19+
}
20+
}
21+
22+
/// FixedCurve
23+
///
24+
/// A fixed-rate interest curve implementation that returns a constant yearly interest rate
25+
/// regardless of utilization. This is suitable for stable assets like MOET where predictable
26+
/// rates are desired.
27+
/// @param yearlyRate The fixed yearly interest rate as a UFix128 (e.g., 0.05 for 5% APY)
28+
access(all) struct FixedCurve: InterestCurve {
29+
30+
access(all) let yearlyRate: UFix128
31+
32+
init(yearlyRate: UFix128) {
33+
pre {
34+
yearlyRate <= 1.0: "Yearly rate cannot exceed 100%, got \(yearlyRate)"
35+
}
36+
self.yearlyRate = yearlyRate
37+
}
38+
39+
access(all) fun interestRate(creditBalance: UFix128, debitBalance: UFix128): UFix128 {
40+
return self.yearlyRate
41+
}
42+
}
43+
44+
/// KinkCurve
45+
///
46+
/// A kink-based interest rate curve implementation. The curve has two linear segments:
47+
/// - Before the optimal utilization ratio (the "kink"): a gentle slope
48+
/// - After the optimal utilization ratio: a steep slope to discourage over-utilization
49+
///
50+
/// This creates a "kinked" curve that incentivizes maintaining utilization near the
51+
/// optimal point while heavily penalizing over-utilization to protect protocol liquidity.
52+
///
53+
/// Formula:
54+
/// - utilization = debitBalance / (creditBalance + debitBalance)
55+
/// - Before kink (utilization <= optimalUtilization):
56+
/// rate = baseRate + (slope1 × utilization / optimalUtilization)
57+
/// - After kink (utilization > optimalUtilization):
58+
/// rate = baseRate + slope1 + (slope2 × excessUtilization)
59+
/// where excessUtilization = (utilization - optimalUtilization) / (1 - optimalUtilization)
60+
///
61+
/// @param optimalUtilization The target utilization ratio (e.g., 0.80 for 80%)
62+
/// @param baseRate The minimum yearly interest rate (e.g., 0.01 for 1% APY)
63+
/// @param slope1 The total rate increase from 0% to optimal utilization (e.g., 0.04 for 4%)
64+
/// @param slope2 The total rate increase from optimal to 100% utilization (e.g., 0.60 for 60%)
65+
access(all) struct KinkCurve: InterestCurve {
66+
67+
/// The optimal utilization ratio (the "kink" point), e.g., 0.80 = 80%
68+
access(all) let optimalUtilization: UFix128
69+
70+
/// The base yearly interest rate applied at 0% utilization
71+
access(all) let baseRate: UFix128
72+
73+
/// The slope of the interest curve before the optimal point (gentle slope)
74+
access(all) let slope1: UFix128
75+
76+
/// The slope of the interest curve after the optimal point (steep slope)
77+
access(all) let slope2: UFix128
78+
79+
init(
80+
optimalUtilization: UFix128,
81+
baseRate: UFix128,
82+
slope1: UFix128,
83+
slope2: UFix128
84+
) {
85+
pre {
86+
optimalUtilization >= 0.01:
87+
"Optimal utilization must be at least 1%, got \(optimalUtilization)"
88+
optimalUtilization <= 0.99:
89+
"Optimal utilization must be at most 99%, got \(optimalUtilization)"
90+
slope2 >= slope1:
91+
"Slope2 (\(slope2)) must be >= slope1 (\(slope1))"
92+
baseRate + slope1 + slope2 <= 4.0:
93+
"Maximum rate cannot exceed 400%, got \(baseRate + slope1 + slope2)"
94+
}
95+
self.optimalUtilization = optimalUtilization
96+
self.baseRate = baseRate
97+
self.slope1 = slope1
98+
self.slope2 = slope2
99+
}
100+
101+
access(all) fun interestRate(creditBalance: UFix128, debitBalance: UFix128): UFix128 {
102+
// If no debt, return base rate
103+
if debitBalance == 0.0 {
104+
return self.baseRate
105+
}
106+
107+
// Calculate utilization ratio: debitBalance / (creditBalance + debitBalance)
108+
// Note: totalBalance > 0 is guaranteed since debitBalance > 0 and creditBalance >= 0
109+
let totalBalance = creditBalance + debitBalance
110+
let utilization = debitBalance / totalBalance
111+
112+
// If utilization is below or at the optimal point, use slope1
113+
if utilization <= self.optimalUtilization {
114+
// rate = baseRate + (slope1 × utilization / optimalUtilization)
115+
let utilizationFactor = utilization / self.optimalUtilization
116+
let slope1Component = self.slope1 * utilizationFactor
117+
return self.baseRate + slope1Component
118+
} else {
119+
// If utilization is above the optimal point, use slope2 for excess
120+
// excessUtilization = (utilization - optimalUtilization) / (1 - optimalUtilization)
121+
let excessUtilization = utilization - self.optimalUtilization
122+
let maxExcess = FlowALPMath.one - self.optimalUtilization
123+
let excessFactor = excessUtilization / maxExcess
124+
125+
// rate = baseRate + slope1 + (slope2 × excessFactor)
126+
let slope2Component = self.slope2 * excessFactor
127+
return self.baseRate + self.slope1 + slope2Component
128+
}
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)