-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathRiskEngine.sol
198 lines (177 loc) · 6.79 KB
/
RiskEngine.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {Errors} from "../utils/Errors.sol";
import {Ownable} from "../utils/Ownable.sol";
import {IOracle} from "oracle/core/IOracle.sol";
import {IERC20} from "../interface/tokens/IERC20.sol";
import {ILToken} from "../interface/tokens/ILToken.sol";
import {IAccount} from "../interface/core/IAccount.sol";
import {IRegistry} from "../interface/core/IRegistry.sol";
import {IRiskEngine} from "../interface/core/IRiskEngine.sol";
import {IAccountManager} from "../interface/core/IAccountManager.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
/**
@title Risk Engine
@notice Risk engine is a sentiment utility contract used by the protocol to
analyze the health factor of a given account.
*/
contract RiskEngine is Ownable, IRiskEngine {
using FixedPointMathLib for uint;
/* -------------------------------------------------------------------------- */
/* STATE VARIABLES */
/* -------------------------------------------------------------------------- */
/// @notice Registry
IRegistry public immutable registry;
/// @notice Oracle Facade
IOracle public oracle;
/// @notice Account Manager
IAccountManager public accountManager;
/// @notice Balance:Borrow, Default = 1.2
uint public constant balanceToBorrowThreshold = 1.2e18;
/* -------------------------------------------------------------------------- */
/* CONSTRUCTOR */
/* -------------------------------------------------------------------------- */
/**
@notice Contract constructor
@param _registry Address of registry contract
*/
constructor(IRegistry _registry) {
initOwnable(msg.sender);
registry = _registry;
}
/* -------------------------------------------------------------------------- */
/* EXTERNAL FUNCTIONS */
/* -------------------------------------------------------------------------- */
/// @notice Initializes external dependencies
function initDep() external adminOnly {
oracle = IOracle(registry.getAddress('ORACLE'));
accountManager = IAccountManager(registry.getAddress('ACCOUNT_MANAGER'));
}
/**
@notice Utility function to determine if an account can borrow a
specified amount of a token
isBorrowAllowed = (currentAccountBalance + borrowValue) /
(currentAccountBorrows + borrowValue) > balanceToBorrowThreshold
@param account Address of account
@param token Address of token
@param amt Amount of token to borrow
@return isBorrowAllowed Returns whether a borrow is allowed or not
*/
function isBorrowAllowed(
address account,
address token,
uint amt
)
external
view
returns (bool)
{
uint borrowValue = _valueInWei(token, amt);
return _isAccountHealthy(
_getBalance(account) + borrowValue,
_getBorrows(account) + borrowValue
);
}
/**
@notice Utility function to determine if an account can withdraw a
specified amount of a token
isWithdrawAllowed = (currentAccountBalance - withdrawValue) /
currentAccountBorrows > balanceToBorrowThreshold
@param account Address of account
@param token Address of token
@param amt Amount of token to withdraw
@return isWithdrawAllowed Returns whether a withdraw is allowed or not
*/
function isWithdrawAllowed(
address account,
address token,
uint amt
)
external
view
returns (bool)
{
if (IAccount(account).hasNoDebt()) return true;
return _isAccountHealthy(
_getBalance(account) - _valueInWei(token, amt),
_getBorrows(account)
);
}
/**
@notice Utility function to determine if an account is healthy or not
isAccountHealthy = currentAccountBalance / currentAccountBorrows >
balanceToBorrowThreshold
@param account Address of account
@return isAccountHealthy Returns whether an account is healthy or not.
*/
function isAccountHealthy(address account) external view returns (bool) {
return _isAccountHealthy(
_getBalance(account),
_getBorrows(account)
);
}
/**
@notice Returns total account Balance
@param account Address of account
@return balance Total account balance
*/
function getBalance(address account) external view returns (uint) {
return _getBalance(account);
}
/**
@notice Returns total account Borrows
@param account Address of account
@return borrows Total account borrows
*/
function getBorrows(address account) external view returns (uint) {
return _getBorrows(account);
}
/* -------------------------------------------------------------------------- */
/* Internal Functions */
/* -------------------------------------------------------------------------- */
function _getBalance(address account) internal view returns (uint) {
address[] memory assets = IAccount(account).getAssets();
uint assetsLen = assets.length;
uint totalBalance;
for(uint i; i < assetsLen; ++i) {
totalBalance += _valueInWei(
assets[i],
IERC20(assets[i]).balanceOf(account)
);
}
return totalBalance + account.balance;
}
function _getBorrows(address account) internal view returns (uint) {
if (IAccount(account).hasNoDebt()) return 0;
address[] memory borrows = IAccount(account).getBorrows();
uint borrowsLen = borrows.length;
uint totalBorrows;
for(uint i; i < borrowsLen; ++i) {
address LTokenAddr = registry.LTokenFor(borrows[i]);
totalBorrows += _valueInWei(
borrows[i],
ILToken(LTokenAddr).getBorrowBalance(account)
);
}
return totalBorrows;
}
function _valueInWei(address token, uint amt)
internal
view
returns (uint)
{
return oracle.getPrice(token)
.mulDivDown(
amt,
10 ** ((token == address(0)) ? 18 : IERC20(token).decimals())
);
}
function _isAccountHealthy(uint accountBalance, uint accountBorrows)
internal
pure
returns (bool)
{
return (accountBorrows == 0) ? true :
(accountBalance.divWadDown(accountBorrows) > balanceToBorrowThreshold);
}
}