1
1
// SPDX-License-Identifier: UNLICENSED
2
2
pragma solidity 0.8.20 ;
3
3
4
+ import {IIrm} from "src/interfaces/IIrm.sol " ;
4
5
import {IERC20 } from "src/interfaces/IERC20.sol " ;
5
6
import {IOracle} from "src/interfaces/IOracle.sol " ;
6
7
7
8
import {MathLib} from "src/libraries/MathLib.sol " ;
8
9
import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol " ;
9
10
10
11
uint constant WAD = 1e18 ;
11
-
12
- uint constant alpha = 0.5e18 ;
12
+ uint constant ALPHA = 0.5e18 ;
13
13
14
14
// Market id.
15
15
type Id is bytes32 ;
@@ -20,26 +20,24 @@ struct Market {
20
20
IERC20 collateralAsset;
21
21
IOracle borrowableOracle;
22
22
IOracle collateralOracle;
23
- uint lLTV;
23
+ IIrm irm;
24
+ uint lltv;
24
25
}
25
26
26
27
using {toId} for Market;
28
+
27
29
function toId (Market calldata market ) pure returns (Id) {
28
30
return Id.wrap (keccak256 (abi.encode (market)));
29
31
}
30
32
31
- function irm (uint utilization ) pure returns (uint ) {
32
- // Divide by the number of seconds in a year.
33
- // This is a very simple model (to refine later) where x% utilization corresponds to x% APR.
34
- return utilization / 365 days ;
35
- }
36
-
37
33
contract Blue {
38
34
using MathLib for uint ;
39
35
using SafeTransferLib for IERC20 ;
40
36
41
37
// Storage.
42
38
39
+ // Owner.
40
+ address public owner;
43
41
// User' supply balances.
44
42
mapping (Id => mapping (address => uint )) public supplyShare;
45
43
// User' borrow balances.
@@ -56,24 +54,58 @@ contract Blue {
56
54
mapping (Id => uint ) public totalBorrowShares;
57
55
// Interests last update (used to check if a market has been created).
58
56
mapping (Id => uint ) public lastUpdate;
57
+ // Enabled IRMs.
58
+ mapping (IIrm => bool ) public isIrmEnabled;
59
+ // Enabled LLTVs.
60
+ mapping (uint => bool ) public isLltvEnabled;
61
+
62
+ // Constructor.
63
+
64
+ constructor (address newOwner ) {
65
+ owner = newOwner;
66
+ }
67
+
68
+ // Modifiers.
69
+
70
+ modifier onlyOwner () {
71
+ require (msg .sender == owner, "not owner " );
72
+ _;
73
+ }
74
+
75
+ // Only owner functions.
76
+
77
+ function transferOwnership (address newOwner ) external onlyOwner {
78
+ owner = newOwner;
79
+ }
80
+
81
+ function enableIrm (IIrm irm ) external onlyOwner {
82
+ isIrmEnabled[irm] = true ;
83
+ }
84
+
85
+ function enableLltv (uint lltv ) external onlyOwner {
86
+ require (lltv < WAD, "LLTV too high " );
87
+ isLltvEnabled[lltv] = true ;
88
+ }
59
89
60
90
// Markets management.
61
91
62
92
function createMarket (Market calldata market ) external {
63
93
Id id = market.toId ();
94
+ require (isIrmEnabled[market.irm], "IRM not enabled " );
95
+ require (isLltvEnabled[market.lltv], "LLTV not enabled " );
64
96
require (lastUpdate[id] == 0 , "market already exists " );
65
97
66
- accrueInterests (id);
98
+ accrueInterests (market, id);
67
99
}
68
100
69
101
// Supply management.
70
102
71
103
function supply (Market calldata market , uint amount ) external {
72
104
Id id = market.toId ();
73
105
require (lastUpdate[id] != 0 , "unknown market " );
74
- require (amount > 0 , "zero amount " );
106
+ require (amount != 0 , "zero amount " );
75
107
76
- accrueInterests (id);
108
+ accrueInterests (market, id);
77
109
78
110
if (totalSupply[id] == 0 ) {
79
111
supplyShare[id][msg .sender ] = WAD;
@@ -92,9 +124,9 @@ contract Blue {
92
124
function withdraw (Market calldata market , uint amount ) external {
93
125
Id id = market.toId ();
94
126
require (lastUpdate[id] != 0 , "unknown market " );
95
- require (amount > 0 , "zero amount " );
127
+ require (amount != 0 , "zero amount " );
96
128
97
- accrueInterests (id);
129
+ accrueInterests (market, id);
98
130
99
131
uint shares = amount.wMul (totalSupplyShares[id]).wDiv (totalSupply[id]);
100
132
supplyShare[id][msg .sender ] -= shares;
@@ -112,9 +144,9 @@ contract Blue {
112
144
function borrow (Market calldata market , uint amount ) external {
113
145
Id id = market.toId ();
114
146
require (lastUpdate[id] != 0 , "unknown market " );
115
- require (amount > 0 , "zero amount " );
147
+ require (amount != 0 , "zero amount " );
116
148
117
- accrueInterests (id);
149
+ accrueInterests (market, id);
118
150
119
151
if (totalBorrow[id] == 0 ) {
120
152
borrowShare[id][msg .sender ] = WAD;
@@ -136,9 +168,9 @@ contract Blue {
136
168
function repay (Market calldata market , uint amount ) external {
137
169
Id id = market.toId ();
138
170
require (lastUpdate[id] != 0 , "unknown market " );
139
- require (amount > 0 , "zero amount " );
171
+ require (amount != 0 , "zero amount " );
140
172
141
- accrueInterests (id);
173
+ accrueInterests (market, id);
142
174
143
175
uint shares = amount.wMul (totalBorrowShares[id]).wDiv (totalBorrow[id]);
144
176
borrowShare[id][msg .sender ] -= shares;
@@ -151,12 +183,13 @@ contract Blue {
151
183
152
184
// Collateral management.
153
185
186
+ /// @dev Don't accrue interests because it's not required and it saves gas.
154
187
function supplyCollateral (Market calldata market , uint amount ) external {
155
188
Id id = market.toId ();
156
189
require (lastUpdate[id] != 0 , "unknown market " );
157
- require (amount > 0 , "zero amount " );
190
+ require (amount != 0 , "zero amount " );
158
191
159
- accrueInterests (id);
192
+ // Don't accrue interests because it's not required and it saves gas.
160
193
161
194
collateral[id][msg .sender ] += amount;
162
195
@@ -166,9 +199,9 @@ contract Blue {
166
199
function withdrawCollateral (Market calldata market , uint amount ) external {
167
200
Id id = market.toId ();
168
201
require (lastUpdate[id] != 0 , "unknown market " );
169
- require (amount > 0 , "zero amount " );
202
+ require (amount != 0 , "zero amount " );
170
203
171
- accrueInterests (id);
204
+ accrueInterests (market, id);
172
205
173
206
collateral[id][msg .sender ] -= amount;
174
207
@@ -182,14 +215,14 @@ contract Blue {
182
215
function liquidate (Market calldata market , address borrower , uint seized ) external {
183
216
Id id = market.toId ();
184
217
require (lastUpdate[id] != 0 , "unknown market " );
185
- require (seized > 0 , "zero amount " );
218
+ require (seized != 0 , "zero amount " );
186
219
187
- accrueInterests (id);
220
+ accrueInterests (market, id);
188
221
189
222
require (! isHealthy (market, id, borrower), "cannot liquidate a healthy position " );
190
223
191
- // The liquidation incentive is 1 + alpha * (1 / LLTV - 1).
192
- uint incentive = WAD + alpha .wMul (WAD.wDiv (market.lLTV ) - WAD);
224
+ // The liquidation incentive is 1 + ALPHA * (1 / LLTV - 1).
225
+ uint incentive = WAD + ALPHA .wMul (WAD.wDiv (market.lltv ) - WAD);
193
226
uint repaid = seized.wMul (market.collateralOracle.price ()).wDiv (incentive).wDiv (market.borrowableOracle.price ());
194
227
uint repaidShares = repaid.wMul (totalBorrowShares[id]).wDiv (totalBorrow[id]);
195
228
@@ -212,13 +245,12 @@ contract Blue {
212
245
213
246
// Interests management.
214
247
215
- function accrueInterests (Id id ) private {
248
+ function accrueInterests (Market calldata market , Id id ) private {
216
249
uint marketTotalSupply = totalSupply[id];
217
250
218
251
if (marketTotalSupply != 0 ) {
219
252
uint marketTotalBorrow = totalBorrow[id];
220
- uint utilization = marketTotalBorrow.wDiv (marketTotalSupply);
221
- uint borrowRate = irm (utilization);
253
+ uint borrowRate = market.irm.borrowRate (market);
222
254
uint accruedInterests = marketTotalBorrow.wMul (borrowRate).wMul (block .timestamp - lastUpdate[id]);
223
255
totalSupply[id] = marketTotalSupply + accruedInterests;
224
256
totalBorrow[id] = marketTotalBorrow + accruedInterests;
@@ -231,11 +263,11 @@ contract Blue {
231
263
232
264
function isHealthy (Market calldata market , Id id , address user ) private view returns (bool ) {
233
265
uint borrowShares = borrowShare[id][user];
266
+ if (borrowShares == 0 ) return true ;
234
267
// totalBorrowShares[id] > 0 when borrowShares > 0.
235
- uint borrowValue = borrowShares > 0
236
- ? borrowShares.wMul (totalBorrow[id]).wDiv (totalBorrowShares[id]).wMul (market.borrowableOracle.price ())
237
- : 0 ;
268
+ uint borrowValue =
269
+ borrowShares.wMul (totalBorrow[id]).wDiv (totalBorrowShares[id]).wMul (market.borrowableOracle.price ());
238
270
uint collateralValue = collateral[id][user].wMul (market.collateralOracle.price ());
239
- return collateralValue.wMul (market.lLTV ) >= borrowValue;
271
+ return collateralValue.wMul (market.lltv ) >= borrowValue;
240
272
}
241
273
}
0 commit comments