/
CommonHealthCheck.sol
159 lines (137 loc) · 4.24 KB
/
CommonHealthCheck.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
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0;
pragma experimental ABIEncoderV2;
interface CustomHealthCheck {
function check(
uint256 profit,
uint256 loss,
uint256 debtPayment,
uint256 debtOutstanding,
address callerStrategy
) external view returns (bool);
}
// LEGACY INTERFACES PRE 0.3.2
struct LegacyStrategyParams {
uint256 performanceFee;
uint256 activation;
uint256 debtRatio;
uint256 rateLimit;
uint256 lastReport;
uint256 totalDebt;
uint256 totalGain;
uint256 totalLoss;
}
struct Limits {
uint256 profitLimitRatio;
uint256 lossLimitRatio;
bool exists;
}
contract CommonHealthCheck {
// Default Settings for all strategies
uint256 constant MAX_BPS = 10_000;
uint256 public profitLimitRatio;
uint256 public lossLimitRatio;
// profit & loss for specific strategy
mapping(address => Limits) public strategiesLimits;
address public governance;
address public management;
mapping(address => address) public checks;
modifier onlyGovernance() {
require(msg.sender == governance, "!authorized");
_;
}
modifier onlyAuthorized() {
require(
msg.sender == governance || msg.sender == management,
"!authorized"
);
_;
}
constructor() public {
governance = msg.sender;
management = msg.sender;
profitLimitRatio = 300;
lossLimitRatio = 100;
}
function setGovernance(address _governance) external onlyGovernance {
require(_governance != address(0));
governance = _governance;
}
function setManagement(address _management) external onlyGovernance {
require(_management != address(0));
management = _management;
}
function setProfitLimitRatio(uint256 _profitLimitRatio) external onlyAuthorized {
require(_profitLimitRatio < MAX_BPS);
profitLimitRatio = _profitLimitRatio;
}
function setlossLimitRatio(uint256 _lossLimitRatio) external onlyAuthorized {
require(_lossLimitRatio < MAX_BPS);
lossLimitRatio = _lossLimitRatio;
}
function setStrategyLimits(address _strategy, uint256 _profitLimitRatio, uint256 _lossLimitRatio) external onlyAuthorized {
require(_lossLimitRatio < MAX_BPS);
require(_profitLimitRatio < MAX_BPS);
strategiesLimits[_strategy] = Limits(_profitLimitRatio, _lossLimitRatio, true);
}
function setCheck(address _strategy, address _check)
external
onlyAuthorized
{
checks[_strategy] = _check;
}
function check(
uint256 profit,
uint256 loss,
uint256 debtPayment,
uint256 debtOutstanding,
uint256 totalDebt
) external view returns (bool) {
return
_runChecks(profit, loss, debtPayment, debtOutstanding, totalDebt);
}
function _runChecks(
uint256 profit,
uint256 loss,
uint256 debtPayment,
uint256 debtOutstanding,
uint256 totalDebt
) internal view returns (bool) {
address customCheck = checks[msg.sender];
if (customCheck == address(0)) {
return _executeDefaultCheck(profit, loss, totalDebt);
}
return
CustomHealthCheck(customCheck).check(
profit,
loss,
debtPayment,
debtOutstanding,
msg.sender
);
}
function _executeDefaultCheck(
uint256 _profit,
uint256 _loss,
uint256 _totalDebt
) internal view returns (bool) {
Limits memory limits = strategiesLimits[msg.sender];
uint256 _profitLimitRatio;
uint256 _lossLimitRatio;
if(limits.exists) {
_profitLimitRatio = limits.profitLimitRatio;
_lossLimitRatio = limits.lossLimitRatio;
} else {
_profitLimitRatio = profitLimitRatio;
_lossLimitRatio = lossLimitRatio;
}
if (_profit > ((_totalDebt * _profitLimitRatio) / MAX_BPS)) {
return false;
}
if (_loss > ((_totalDebt * _lossLimitRatio) / MAX_BPS)) {
return false;
}
// health checks pass
return true;
}
}