@ensuro/core 2.0.0-beta6 → 2.0.0-beta7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/contracts/AccessManager.sol/AccessManager.json +74 -2
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +219 -0
- package/build/contracts/EToken.sol/EToken.json +114 -45
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
- package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +203 -0
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +324 -100
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +122 -2
- package/build/contracts/Reserve.sol/Reserve.json +71 -0
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +3 -3
- package/build/contracts/interfaces/IAccessManager.sol/IAccessManager.json +96 -0
- package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +109 -0
- package/build/contracts/interfaces/IEToken.sol/IEToken.json +13 -0
- package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +6 -14
- package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
- package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +7 -15
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +674 -0
- package/contracts/AccessManager.sol +39 -8
- package/contracts/ERC4626AssetManager.sol +50 -0
- package/contracts/EToken.sol +72 -52
- package/contracts/LiquidityThresholdAssetManager.sol +158 -0
- package/contracts/PolicyPool.sol +155 -135
- package/contracts/PolicyPoolComponent.sol +29 -19
- package/contracts/PremiumsAccount.sol +125 -62
- package/contracts/Reserve.sol +86 -2
- package/contracts/RiskModule.sol +17 -6
- package/contracts/TrustfulRiskModule.sol +17 -2
- package/contracts/interfaces/IAccessManager.sol +23 -5
- package/contracts/interfaces/IAssetManager.sol +22 -0
- package/contracts/interfaces/IEToken.sol +21 -1
- package/contracts/interfaces/IPolicyPool.sol +6 -11
- package/contracts/interfaces/IPremiumsAccount.sol +5 -0
- package/contracts/mocks/FixedRateVault.sol +85 -0
- package/contracts/mocks/PolicyPoolMock.sol +2 -5
- package/contracts/mocks/RiskModuleMock.sol +69 -0
- package/js/test-utils.js +35 -26
- package/package.json +1 -1
- package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
- package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
- package/contracts/PolicyNFT.sol +0 -96
- package/contracts/interfaces/IPolicyNFT.sol +0 -15
|
@@ -11,6 +11,7 @@ import {Reserve} from "./Reserve.sol";
|
|
|
11
11
|
import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
|
|
12
12
|
import {Policy} from "./Policy.sol";
|
|
13
13
|
import {IEToken} from "./interfaces/IEToken.sol";
|
|
14
|
+
import {IAssetManager} from "./interfaces/IAssetManager.sol";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* @title Ensuro Premiums Account
|
|
@@ -31,8 +32,14 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
31
32
|
IEToken internal immutable _seniorEtk;
|
|
32
33
|
|
|
33
34
|
uint256 internal _activePurePremiums; // sum of pure-premiums of active policies - In Wad
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
int256 internal _surplus;
|
|
36
|
+
|
|
37
|
+
struct PackedParams {
|
|
38
|
+
uint16 deficitRatio;
|
|
39
|
+
IAssetManager assetManager;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
PackedParams internal _params;
|
|
36
43
|
|
|
37
44
|
/*
|
|
38
45
|
* Premiums can come in (for free, without liability) with receiveGrant.
|
|
@@ -71,21 +78,45 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
71
78
|
function __PremiumsAccount_init_unchained() internal initializer {
|
|
72
79
|
/*
|
|
73
80
|
_activePurePremiums = 0;
|
|
74
|
-
_borrowedActivePP = 0;
|
|
75
|
-
_wonPurePremiums = 0;
|
|
76
81
|
*/
|
|
77
82
|
if (address(_juniorEtk) != address(0))
|
|
78
83
|
currency().approve(address(_juniorEtk), type(uint256).max);
|
|
79
84
|
if (address(_seniorEtk) != address(0))
|
|
80
85
|
currency().approve(address(_seniorEtk), type(uint256).max);
|
|
86
|
+
|
|
87
|
+
_params = PackedParams({deficitRatio: 1e4, assetManager: IAssetManager(address(0))});
|
|
81
88
|
_validateParameters();
|
|
82
89
|
}
|
|
83
90
|
|
|
91
|
+
function assetManager() public view override returns (IAssetManager) {
|
|
92
|
+
return _params.assetManager;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function _setAssetManager(IAssetManager newAM) internal override {
|
|
96
|
+
_params.assetManager = newAM;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function _assetEarnings(int256 earningsOrLosses) internal override {
|
|
100
|
+
if (earningsOrLosses > 0) {
|
|
101
|
+
uint256 earnings = uint256(earningsOrLosses);
|
|
102
|
+
if (address(_seniorEtk) != address(0)) earnings = _repayLoan(earnings, _seniorEtk);
|
|
103
|
+
if (address(_juniorEtk) != address(0)) earnings = _repayLoan(earnings, _juniorEtk);
|
|
104
|
+
_storePurePremiumWon(earnings);
|
|
105
|
+
} else {
|
|
106
|
+
_payFromPremiums(uint256(-earningsOrLosses));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
84
110
|
// solhint-disable-next-line no-empty-blocks
|
|
85
|
-
function _validateParameters() internal view override {
|
|
111
|
+
function _validateParameters() internal view override {
|
|
112
|
+
require(
|
|
113
|
+
_params.deficitRatio <= 1e4 && _params.deficitRatio >= 0,
|
|
114
|
+
"Validation: deficitRatio must be <= 1"
|
|
115
|
+
);
|
|
116
|
+
}
|
|
86
117
|
|
|
87
|
-
function purePremiums()
|
|
88
|
-
return _activePurePremiums +
|
|
118
|
+
function purePremiums() external view override returns (uint256) {
|
|
119
|
+
return uint256(int256(_activePurePremiums) + _surplus);
|
|
89
120
|
}
|
|
90
121
|
|
|
91
122
|
function activePurePremiums() external view returns (uint256) {
|
|
@@ -93,11 +124,15 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
93
124
|
}
|
|
94
125
|
|
|
95
126
|
function wonPurePremiums() external view returns (uint256) {
|
|
96
|
-
return
|
|
127
|
+
return _surplus >= 0 ? uint256(_surplus) : 0;
|
|
97
128
|
}
|
|
98
129
|
|
|
99
130
|
function borrowedActivePP() external view returns (uint256) {
|
|
100
|
-
return
|
|
131
|
+
return _surplus >= 0 ? 0 : uint256(-_surplus);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function surplus() external view returns (int256) {
|
|
135
|
+
return _surplus;
|
|
101
136
|
}
|
|
102
137
|
|
|
103
138
|
function seniorEtk() external view override returns (IEToken) {
|
|
@@ -108,37 +143,63 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
108
143
|
return _juniorEtk;
|
|
109
144
|
}
|
|
110
145
|
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
146
|
+
function _maxDeficit(uint256 ratio) internal view returns (int256) {
|
|
147
|
+
return -int256(_activePurePremiums.wadMul(ratio));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function deficitRatio() public view returns (uint256) {
|
|
151
|
+
return uint256(_params.deficitRatio) * 1e14; // 4 -> 18 decimals
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function setDeficitRatio(uint256 newRatio, bool adjustment)
|
|
155
|
+
external
|
|
156
|
+
onlyComponentRole(LEVEL2_ROLE)
|
|
157
|
+
{
|
|
158
|
+
require(newRatio <= 1e18 && newRatio >= 0, "Validation: deficitRatio must be <= 1");
|
|
159
|
+
int256 maxDeficit = _maxDeficit(newRatio);
|
|
160
|
+
require(adjustment || _surplus >= maxDeficit, "Validation: surplus must be >= maxDeficit");
|
|
161
|
+
if (_surplus < maxDeficit) {
|
|
162
|
+
// Do the adjustment
|
|
163
|
+
uint256 borrow = uint256(-_surplus + maxDeficit);
|
|
164
|
+
_surplus = maxDeficit;
|
|
165
|
+
_borrowFromEtk(borrow, address(this), address(_juniorEtk) != address(0));
|
|
116
166
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
167
|
+
_params.deficitRatio = uint16(newRatio / 1e14);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function _borrowFromEtk(
|
|
171
|
+
uint256 borrow,
|
|
172
|
+
address receiver,
|
|
173
|
+
bool jrEtk
|
|
174
|
+
) internal {
|
|
175
|
+
uint256 left;
|
|
176
|
+
if (jrEtk) {
|
|
177
|
+
// Consume Junior Pool until exhausted
|
|
178
|
+
left = _juniorEtk.internalLoan(borrow, receiver, false);
|
|
179
|
+
} else {
|
|
180
|
+
left = borrow;
|
|
181
|
+
}
|
|
182
|
+
if (left > NEGLIGIBLE_AMOUNT) {
|
|
183
|
+
// Consume Senior Pool only up to SCR
|
|
184
|
+
left = _seniorEtk.internalLoan(left, receiver, true);
|
|
185
|
+
require(left <= NEGLIGIBLE_AMOUNT, "Don't know where to take the rest of the money");
|
|
128
186
|
}
|
|
129
|
-
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function _payFromPremiums(uint256 toPay) internal returns (uint256) {
|
|
190
|
+
int256 newSurplus = _surplus - int256(toPay);
|
|
191
|
+
int256 maxDeficit = _maxDeficit(deficitRatio());
|
|
192
|
+
if (newSurplus >= maxDeficit) {
|
|
193
|
+
_surplus = newSurplus;
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
_surplus = maxDeficit;
|
|
197
|
+
return uint256(-newSurplus + maxDeficit);
|
|
130
198
|
}
|
|
131
199
|
|
|
132
200
|
function _storePurePremiumWon(uint256 purePremiumWon) internal {
|
|
133
|
-
// TODO: merge _wonPurePremiums and _borrowedActivePP into single int256 variable
|
|
134
|
-
// and this will be just `_wonPurePremiums += purePremiumWon;`
|
|
135
201
|
if (purePremiumWon == 0) return;
|
|
136
|
-
|
|
137
|
-
_borrowedActivePP -= purePremiumWon;
|
|
138
|
-
} else {
|
|
139
|
-
_wonPurePremiums += (purePremiumWon - _borrowedActivePP);
|
|
140
|
-
_borrowedActivePP = 0;
|
|
141
|
-
}
|
|
202
|
+
_surplus += int256(purePremiumWon);
|
|
142
203
|
}
|
|
143
204
|
|
|
144
205
|
// TODO: restore repayETokenLoan?
|
|
@@ -166,24 +227,32 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
166
227
|
*
|
|
167
228
|
* Requirements:
|
|
168
229
|
*
|
|
169
|
-
* -
|
|
230
|
+
* - onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
|
|
170
231
|
* - _wonPurePremiums > 0
|
|
171
232
|
*/
|
|
172
|
-
function withdrawWonPremiums(uint256 amount)
|
|
233
|
+
function withdrawWonPremiums(uint256 amount, address destination)
|
|
173
234
|
external
|
|
174
|
-
|
|
235
|
+
onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
|
|
175
236
|
returns (uint256)
|
|
176
237
|
{
|
|
177
|
-
if (
|
|
238
|
+
if (_surplus <= 0) {
|
|
239
|
+
amount = 0;
|
|
240
|
+
} else {
|
|
241
|
+
amount = Math.min(amount, uint256(_surplus));
|
|
242
|
+
}
|
|
178
243
|
require(amount > 0, "No premiums to withdraw");
|
|
179
|
-
|
|
180
|
-
_transferTo(
|
|
181
|
-
// TODO: see if this will be a component role
|
|
244
|
+
_surplus -= int256(amount);
|
|
245
|
+
_transferTo(destination, amount);
|
|
182
246
|
emit WonPremiumsInOut(false, amount);
|
|
183
247
|
return amount;
|
|
184
248
|
}
|
|
185
249
|
|
|
186
|
-
function policyCreated(Policy.PolicyData memory policy)
|
|
250
|
+
function policyCreated(Policy.PolicyData memory policy)
|
|
251
|
+
external
|
|
252
|
+
override
|
|
253
|
+
onlyPolicyPool
|
|
254
|
+
whenNotPaused
|
|
255
|
+
{
|
|
187
256
|
_activePurePremiums += policy.purePremium;
|
|
188
257
|
if (policy.jrScr > 0) _juniorEtk.lockScr(policy.jrScr, policy.jrInterestRate());
|
|
189
258
|
if (policy.srScr > 0) _seniorEtk.lockScr(policy.srScr, policy.srInterestRate());
|
|
@@ -193,7 +262,7 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
193
262
|
address policyHolder,
|
|
194
263
|
Policy.PolicyData memory policy,
|
|
195
264
|
uint256 payout
|
|
196
|
-
) external override onlyPolicyPool {
|
|
265
|
+
) external override onlyPolicyPool whenNotPaused {
|
|
197
266
|
_activePurePremiums -= policy.purePremium;
|
|
198
267
|
if (policy.purePremium >= payout) {
|
|
199
268
|
uint256 purePremiumWon = policy.purePremium - payout;
|
|
@@ -205,21 +274,10 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
205
274
|
_unlockScr(policy);
|
|
206
275
|
_transferTo(policyHolder, payout);
|
|
207
276
|
} else {
|
|
208
|
-
uint256 borrowFromScr =
|
|
277
|
+
uint256 borrowFromScr = _payFromPremiums(payout - policy.purePremium);
|
|
209
278
|
_unlockScr(policy);
|
|
210
279
|
if (borrowFromScr > 0) {
|
|
211
|
-
|
|
212
|
-
if (policy.jrScr > 0) {
|
|
213
|
-
// Consume Junior Pool until exhausted
|
|
214
|
-
left = _juniorEtk.internalLoan(borrowFromScr, policyHolder, false);
|
|
215
|
-
} else {
|
|
216
|
-
left = borrowFromScr;
|
|
217
|
-
}
|
|
218
|
-
if (left > NEGLIGIBLE_AMOUNT) {
|
|
219
|
-
// Consume Senior Pool only up to SCR
|
|
220
|
-
left = _seniorEtk.internalLoan(left, policyHolder, true);
|
|
221
|
-
require(left <= NEGLIGIBLE_AMOUNT, "Don't know where to take the rest of the money");
|
|
222
|
-
}
|
|
280
|
+
_borrowFromEtk(borrowFromScr, policyHolder, policy.jrScr > 0);
|
|
223
281
|
}
|
|
224
282
|
_transferTo(policyHolder, payout - borrowFromScr);
|
|
225
283
|
}
|
|
@@ -252,16 +310,21 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
252
310
|
return purePremiumWon - repayAmount;
|
|
253
311
|
}
|
|
254
312
|
|
|
255
|
-
function policyExpired(Policy.PolicyData memory policy)
|
|
256
|
-
|
|
313
|
+
function policyExpired(Policy.PolicyData memory policy)
|
|
314
|
+
external
|
|
315
|
+
override
|
|
316
|
+
onlyPolicyPool
|
|
317
|
+
whenNotPaused
|
|
318
|
+
{
|
|
257
319
|
uint256 purePremiumWon = policy.purePremium;
|
|
258
320
|
_activePurePremiums -= purePremiumWon;
|
|
259
321
|
|
|
260
322
|
// If negative _activePurePremiums, repay this first (shouldn't happen)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
purePremiumWon -=
|
|
323
|
+
int256 maxDeficit = _maxDeficit(deficitRatio());
|
|
324
|
+
if (_surplus < maxDeficit) {
|
|
325
|
+
// Covers the excess of deficit first
|
|
326
|
+
purePremiumWon -= uint256(-_surplus + maxDeficit);
|
|
327
|
+
_surplus = maxDeficit;
|
|
265
328
|
}
|
|
266
329
|
|
|
267
330
|
if (address(_seniorEtk) != address(0)) purePremiumWon = _repayLoan(purePremiumWon, _seniorEtk);
|
package/contracts/Reserve.sol
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
4
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
5
|
+
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
5
6
|
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
6
7
|
import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
|
|
8
|
+
import {IAssetManager} from "./interfaces/IAssetManager.sol";
|
|
9
|
+
import {IAccessManager} from "./interfaces/IAccessManager.sol";
|
|
7
10
|
import {PolicyPoolComponent} from "./PolicyPoolComponent.sol";
|
|
8
11
|
|
|
9
12
|
/**
|
|
@@ -14,6 +17,7 @@ import {PolicyPoolComponent} from "./PolicyPoolComponent.sol";
|
|
|
14
17
|
*/
|
|
15
18
|
abstract contract Reserve is PolicyPoolComponent {
|
|
16
19
|
using SafeERC20 for IERC20Metadata;
|
|
20
|
+
using Address for address;
|
|
17
21
|
|
|
18
22
|
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
|
19
23
|
// solhint-disable-next-line var-name-mixedcase
|
|
@@ -27,8 +31,88 @@ abstract contract Reserve is PolicyPoolComponent {
|
|
|
27
31
|
function _transferTo(address destination, uint256 amount) internal {
|
|
28
32
|
if (amount == 0) return;
|
|
29
33
|
uint256 balance = currency().balanceOf(address(this));
|
|
30
|
-
if (balance < amount
|
|
31
|
-
|
|
34
|
+
if (balance < amount) {
|
|
35
|
+
address am = address(assetManager());
|
|
36
|
+
if (am != address(0)) {
|
|
37
|
+
am.functionDelegateCall(
|
|
38
|
+
abi.encodeWithSelector(IAssetManager.refillWallet.selector, amount),
|
|
39
|
+
"Error refilling wallet"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if ((amount - balance) < NEGLIGIBLE_AMOUNT) amount = balance;
|
|
43
|
+
}
|
|
32
44
|
currency().safeTransfer(destination, amount);
|
|
33
45
|
}
|
|
46
|
+
|
|
47
|
+
function assetManager() public view virtual returns (IAssetManager);
|
|
48
|
+
|
|
49
|
+
function _setAssetManager(IAssetManager newAM) internal virtual;
|
|
50
|
+
|
|
51
|
+
function _assetEarnings(int256 earnings) internal virtual;
|
|
52
|
+
|
|
53
|
+
function setAssetManager(IAssetManager newAM, bool force)
|
|
54
|
+
external
|
|
55
|
+
onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
|
|
56
|
+
{
|
|
57
|
+
require(
|
|
58
|
+
address(newAM) == address(0) || address(newAM).isContract(),
|
|
59
|
+
"The assetManager is not a contract!"
|
|
60
|
+
);
|
|
61
|
+
address am = address(assetManager());
|
|
62
|
+
IAccessManager.GovernanceActions action = IAccessManager.GovernanceActions.setAssetManager;
|
|
63
|
+
if (am != address(0)) {
|
|
64
|
+
if (force) {
|
|
65
|
+
// Ignores success or not
|
|
66
|
+
// solhint-disable-next-line avoid-low-level-calls
|
|
67
|
+
(bool success, bytes memory result) = am.delegatecall(
|
|
68
|
+
abi.encodeWithSelector(IAssetManager.deinvestAll.selector)
|
|
69
|
+
);
|
|
70
|
+
if (!success) {
|
|
71
|
+
action = IAccessManager.GovernanceActions.setAssetManagerForced;
|
|
72
|
+
} else {
|
|
73
|
+
_assetEarnings(abi.decode(result, (int256)));
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
bytes memory result = am.functionDelegateCall(
|
|
77
|
+
abi.encodeWithSelector(IAssetManager.deinvestAll.selector)
|
|
78
|
+
);
|
|
79
|
+
_assetEarnings(abi.decode(result, (int256)));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
_setAssetManager(newAM);
|
|
83
|
+
am = address(assetManager());
|
|
84
|
+
if (am != address(0)) {
|
|
85
|
+
am.functionDelegateCall(abi.encodeWithSelector(IAssetManager.connect.selector));
|
|
86
|
+
}
|
|
87
|
+
_componentChanged(action, address(newAM));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function rebalance() public whenNotPaused {
|
|
91
|
+
address am = address(assetManager());
|
|
92
|
+
require(am != address(0), "No asset manager");
|
|
93
|
+
am.functionDelegateCall(abi.encodeWithSelector(IAssetManager.rebalance.selector));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function recordEarnings() public whenNotPaused {
|
|
97
|
+
address am = address(assetManager());
|
|
98
|
+
require(am != address(0), "No asset manager");
|
|
99
|
+
bytes memory result = am.functionDelegateCall(
|
|
100
|
+
abi.encodeWithSelector(IAssetManager.recordEarnings.selector)
|
|
101
|
+
);
|
|
102
|
+
_assetEarnings(abi.decode(result, (int256)));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function checkpoint() external whenNotPaused {
|
|
106
|
+
recordEarnings();
|
|
107
|
+
rebalance();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function forwardToAssetManager(bytes memory functionCall)
|
|
111
|
+
external
|
|
112
|
+
onlyComponentRole(LEVEL2_ROLE)
|
|
113
|
+
returns (bytes memory)
|
|
114
|
+
{
|
|
115
|
+
address am = address(assetManager());
|
|
116
|
+
return am.functionDelegateCall(functionCall);
|
|
117
|
+
}
|
|
34
118
|
}
|
package/contracts/RiskModule.sol
CHANGED
|
@@ -189,7 +189,7 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
189
189
|
|
|
190
190
|
function setParam(Parameter param, uint256 newValue)
|
|
191
191
|
external
|
|
192
|
-
|
|
192
|
+
onlyGlobalOrComponentRole3(LEVEL1_ROLE, LEVEL2_ROLE, LEVEL3_ROLE)
|
|
193
193
|
{
|
|
194
194
|
bool tweak = !hasPoolRole(LEVEL2_ROLE) && !hasPoolRole(LEVEL1_ROLE);
|
|
195
195
|
if (param == Parameter.moc) {
|
|
@@ -236,6 +236,8 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
236
236
|
} else if (param == Parameter.maxDuration) {
|
|
237
237
|
require(!tweak, "Tweak exceeded");
|
|
238
238
|
_params.maxDuration = uint16(newValue);
|
|
239
|
+
} else {
|
|
240
|
+
revert("Invalid param!");
|
|
239
241
|
}
|
|
240
242
|
_parameterChanged(
|
|
241
243
|
IAccessManager.GovernanceActions(
|
|
@@ -300,8 +302,9 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
300
302
|
* @param payout The exposure (maximum payout) of the policy
|
|
301
303
|
* @param premium The premium that will be paid by the policyHolder
|
|
302
304
|
* @param lossProb The probability of having to pay the maximum payout (wad)
|
|
305
|
+
* @param payer The account that pays for the premium
|
|
303
306
|
* @param expiration The expiration of the policy (timestamp)
|
|
304
|
-
* @param
|
|
307
|
+
* @param onBehalfOf The policy holder
|
|
305
308
|
* @param internalId An id that's unique within this module and it will be used to identify the policy
|
|
306
309
|
*/
|
|
307
310
|
function _newPolicy(
|
|
@@ -309,18 +312,26 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
309
312
|
uint256 premium,
|
|
310
313
|
uint256 lossProb,
|
|
311
314
|
uint40 expiration,
|
|
312
|
-
address
|
|
315
|
+
address payer,
|
|
316
|
+
address onBehalfOf,
|
|
313
317
|
uint96 internalId
|
|
314
318
|
) internal whenNotPaused returns (Policy.PolicyData memory) {
|
|
319
|
+
if (premium == type(uint256).max) {
|
|
320
|
+
premium = getMinimumPremium(payout, lossProb, expiration);
|
|
321
|
+
}
|
|
315
322
|
require(premium < payout, "Premium must be less than payout");
|
|
316
323
|
uint40 now_ = uint40(block.timestamp);
|
|
317
324
|
require(expiration > now_, "Expiration must be in the future");
|
|
318
325
|
require(((expiration - now_) / 3600) < _params.maxDuration, "Policy exceeds max duration");
|
|
319
|
-
require(
|
|
326
|
+
require(onBehalfOf != address(0), "Customer can't be zero address");
|
|
320
327
|
require(
|
|
321
|
-
_policyPool.currency().allowance(
|
|
328
|
+
_policyPool.currency().allowance(payer, address(_policyPool)) >= premium,
|
|
322
329
|
"You must allow ENSURO to transfer the premium"
|
|
323
330
|
);
|
|
331
|
+
require(
|
|
332
|
+
payer == msg.sender || _policyPool.currency().allowance(payer, msg.sender) >= premium,
|
|
333
|
+
"Payer must allow caller to transfer the premium"
|
|
334
|
+
);
|
|
324
335
|
require(payout <= maxPayoutPerPolicy(), "RiskModule: Payout is more than maximum per policy");
|
|
325
336
|
Policy.PolicyData memory policy = Policy.initialize(
|
|
326
337
|
this,
|
|
@@ -332,7 +343,7 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
332
343
|
);
|
|
333
344
|
_activeExposure += policy.payout;
|
|
334
345
|
require(_activeExposure <= exposureLimit(), "RiskModule: SCR limit exceeded");
|
|
335
|
-
uint256 policyId = _policyPool.newPolicy(policy,
|
|
346
|
+
uint256 policyId = _policyPool.newPolicy(policy, payer, onBehalfOf, internalId);
|
|
336
347
|
policy.id = policyId;
|
|
337
348
|
return policy;
|
|
338
349
|
}
|
|
@@ -59,10 +59,25 @@ contract TrustfulRiskModule is RiskModule {
|
|
|
59
59
|
uint256 premium,
|
|
60
60
|
uint256 lossProb,
|
|
61
61
|
uint40 expiration,
|
|
62
|
-
address
|
|
62
|
+
address onBehalfOf,
|
|
63
63
|
uint96 internalId
|
|
64
64
|
) external onlyComponentRole(PRICER_ROLE) returns (uint256) {
|
|
65
|
-
|
|
65
|
+
address payer = onBehalfOf;
|
|
66
|
+
if (payer != msg.sender && _policyPool.currency().allowance(payer, msg.sender) < premium)
|
|
67
|
+
/**
|
|
68
|
+
* The standard is the payer should be the msg.sender but usually, in this type of module,
|
|
69
|
+
* the sender is an operative account managed by software, where the onBehalfOf is a more
|
|
70
|
+
* secure account (hardware wallet) that does the cash movements.
|
|
71
|
+
* This non standard behaviour allows for a more secure setup, where the sender never manages
|
|
72
|
+
* cash.
|
|
73
|
+
* We leverage the currency's allowance mechanism to allow the sender access to the payer's
|
|
74
|
+
* funds.
|
|
75
|
+
* Note that this allowance won't be spent, so it can be set as the maximum amount of a single
|
|
76
|
+
* premium even for multiple policies.
|
|
77
|
+
*/
|
|
78
|
+
payer = msg.sender;
|
|
79
|
+
|
|
80
|
+
return _newPolicy(payout, premium, lossProb, expiration, payer, onBehalfOf, internalId).id;
|
|
66
81
|
}
|
|
67
82
|
|
|
68
83
|
function resolvePolicy(Policy.PolicyData calldata policy, uint256 payout)
|
|
@@ -12,12 +12,12 @@ interface IAccessManager is IAccessControlUpgradeable {
|
|
|
12
12
|
enum GovernanceActions {
|
|
13
13
|
none,
|
|
14
14
|
setTreasury, // Changes PolicyPool treasury address
|
|
15
|
-
setAssetManager, //
|
|
15
|
+
setAssetManager, // Change in the asset manager strategy of a reserve
|
|
16
16
|
setInsolvencyHook, // Changes PolicyPool InsolvencyHook
|
|
17
17
|
setLPWhitelist, // Changes PolicyPool Liquidity Providers Whitelist
|
|
18
18
|
addRiskModule,
|
|
19
19
|
removeRiskModule,
|
|
20
|
-
|
|
20
|
+
setAssetManagerForced, // Change in the asset manager strategy of a reserve, forced (deinvest failed)
|
|
21
21
|
cfgFiller2, // Reserve space for future PolicyPoolConfig actions
|
|
22
22
|
cfgFiller3, // Reserve space for future PolicyPoolConfig actions
|
|
23
23
|
cfgFiller4, // Reserve space for future PolicyPoolConfig actions
|
|
@@ -59,17 +59,35 @@ interface IAccessManager is IAccessControlUpgradeable {
|
|
|
59
59
|
last
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function getComponentRole(address component, bytes32 role) external view returns (bytes32);
|
|
63
|
+
|
|
64
|
+
function hasComponentRole(
|
|
65
|
+
address component,
|
|
66
|
+
bytes32 role,
|
|
67
|
+
address account,
|
|
68
|
+
bool alsoGlobal
|
|
69
|
+
) external view returns (bool);
|
|
70
|
+
|
|
62
71
|
function checkRole(bytes32 role, address account) external view;
|
|
63
72
|
|
|
73
|
+
function checkRole2(
|
|
74
|
+
bytes32 role1,
|
|
75
|
+
bytes32 role2,
|
|
76
|
+
address account
|
|
77
|
+
) external view;
|
|
78
|
+
|
|
64
79
|
function checkComponentRole(
|
|
65
80
|
address component,
|
|
66
81
|
bytes32 role,
|
|
67
|
-
address account
|
|
82
|
+
address account,
|
|
83
|
+
bool alsoGlobal
|
|
68
84
|
) external view;
|
|
69
85
|
|
|
70
|
-
function
|
|
86
|
+
function checkComponentRole2(
|
|
87
|
+
address component,
|
|
71
88
|
bytes32 role1,
|
|
72
89
|
bytes32 role2,
|
|
73
|
-
address account
|
|
90
|
+
address account,
|
|
91
|
+
bool alsoGlobal
|
|
74
92
|
) external view;
|
|
75
93
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title IAssetManager - Interface of the asset management strategy that's plugged into the reserves
|
|
6
|
+
* @author Ensuro
|
|
7
|
+
*/
|
|
8
|
+
interface IAssetManager {
|
|
9
|
+
event MoneyInvested(uint256 amount);
|
|
10
|
+
event MoneyDeinvested(uint256 amount);
|
|
11
|
+
event EarningsRecorded(bool positive, uint256 amount);
|
|
12
|
+
|
|
13
|
+
function connect() external;
|
|
14
|
+
|
|
15
|
+
function rebalance() external;
|
|
16
|
+
|
|
17
|
+
function recordEarnings() external returns (int256);
|
|
18
|
+
|
|
19
|
+
function refillWallet(uint256 paymentAmount) external;
|
|
20
|
+
|
|
21
|
+
function deinvestAll() external returns (int256);
|
|
22
|
+
}
|
|
@@ -9,6 +9,13 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
9
9
|
* @author Ensuro
|
|
10
10
|
*/
|
|
11
11
|
interface IEToken is IERC20 {
|
|
12
|
+
enum Parameter {
|
|
13
|
+
liquidityRequirement,
|
|
14
|
+
minUtilizationRate,
|
|
15
|
+
maxUtilizationRate,
|
|
16
|
+
internalLoanInterestRate
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
/**
|
|
13
20
|
* @dev Event emitted when part of the funds of the eToken are locked as solvency capital.
|
|
14
21
|
* @param interestRate The annualized interestRate paid for the capital (wad)
|
|
@@ -110,12 +117,25 @@ interface IEToken is IERC20 {
|
|
|
110
117
|
* - Must be called by `policyPool()`
|
|
111
118
|
*
|
|
112
119
|
* Events:
|
|
113
|
-
* - Emits {
|
|
120
|
+
* - Emits {InternalBorrowerAdded}
|
|
114
121
|
*
|
|
115
122
|
* @param borrower The address of the _borrower_, a PremiumsAccount that has this eToken as senior or junior eToken.
|
|
116
123
|
*/
|
|
117
124
|
function addBorrower(address borrower) external;
|
|
118
125
|
|
|
126
|
+
/**
|
|
127
|
+
* @dev Removes an authorized _borrower_ to the eToken. The _borrower_ can't no longer lock funds or take loans.
|
|
128
|
+
*
|
|
129
|
+
* Requirements:
|
|
130
|
+
* - Must be called by `policyPool()`
|
|
131
|
+
*
|
|
132
|
+
* Events:
|
|
133
|
+
* - Emits {InternalBorrowerRemoved} with the defaulted debt
|
|
134
|
+
*
|
|
135
|
+
* @param borrower The address of the _borrower_, a PremiumsAccount that has this eToken as senior or junior eToken.
|
|
136
|
+
*/
|
|
137
|
+
function removeBorrower(address borrower) external;
|
|
138
|
+
|
|
119
139
|
/**
|
|
120
140
|
* @dev Lends `amount` to the borrower (msg.sender), transferring the money to `receiver`. This reduces the
|
|
121
141
|
* `totalSupply()` of the eToken, and stores a debt that will be repaid (hopefully) with `repayLoan`.
|
|
@@ -19,13 +19,6 @@ interface IPolicyPool {
|
|
|
19
19
|
*/
|
|
20
20
|
function access() external view returns (IAccessManager);
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* @dev Reference to the {PolicyNFT} contract. This contract it's an ERC721 Token that handles the ownership of the
|
|
24
|
-
* policies.
|
|
25
|
-
* @return The address of the PolicyNFT contract
|
|
26
|
-
*/
|
|
27
|
-
function policyNFT() external view returns (address);
|
|
28
|
-
|
|
29
22
|
/**
|
|
30
23
|
* @dev Address of the treasury, that receives protocol fees.
|
|
31
24
|
* @return The address of the treasury
|
|
@@ -37,22 +30,24 @@ interface IPolicyPool {
|
|
|
37
30
|
*
|
|
38
31
|
* Requirements:
|
|
39
32
|
* - `msg.sender` must be an active RiskModule
|
|
40
|
-
* - `
|
|
33
|
+
* - `caller` approved the spending of `currency()` for at least `policy.premium`
|
|
41
34
|
* - `internalId` must be unique within `policy.riskModule` and not used before
|
|
42
35
|
*
|
|
43
36
|
* Events:
|
|
44
37
|
* - {PolicyPool-NewPolicy}: with all the details about the policy
|
|
45
|
-
* - {ERC20-Transfer}: does several transfers from
|
|
38
|
+
* - {ERC20-Transfer}: does several transfers from caller address to the different receivers of the premium
|
|
46
39
|
* (see Premium Split in the docs)
|
|
47
40
|
*
|
|
48
41
|
* @param policy A policy created with {Policy-initialize}
|
|
49
|
-
* @param
|
|
42
|
+
* @param caller The pricer that's creating the policy and will pay for the premium
|
|
43
|
+
* @param policyHolder The address of the policy holder and the payer of the premiums
|
|
50
44
|
* @param internalId A unique id within the RiskModule, that will be used to compute the policy id
|
|
51
45
|
* @return The policy id, identifying the NFT and the policy
|
|
52
46
|
*/
|
|
53
47
|
function newPolicy(
|
|
54
48
|
Policy.PolicyData memory policy,
|
|
55
|
-
address
|
|
49
|
+
address caller,
|
|
50
|
+
address policyHolder,
|
|
56
51
|
uint96 internalId
|
|
57
52
|
) external returns (uint256);
|
|
58
53
|
|
|
@@ -68,4 +68,9 @@ interface IPremiumsAccount {
|
|
|
68
68
|
* @dev The junior eToken, the primary source of solvency, used if the premiums account is exhausted.
|
|
69
69
|
*/
|
|
70
70
|
function juniorEtk() external view returns (IEToken);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @dev The total amount of premiums hold by this PremiumsAccount
|
|
74
|
+
*/
|
|
75
|
+
function purePremiums() external view returns (uint256);
|
|
71
76
|
}
|