@ensuro/core 2.0.0-beta6 → 2.0.0-beta8

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.
Files changed (44) hide show
  1. package/build/contracts/AccessManager.sol/AccessManager.json +74 -2
  2. package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +213 -0
  3. package/build/contracts/EToken.sol/EToken.json +114 -45
  4. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
  5. package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +197 -0
  6. package/build/contracts/Policy.sol/Policy.json +2 -2
  7. package/build/contracts/PolicyPool.sol/PolicyPool.json +324 -100
  8. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +122 -2
  9. package/build/contracts/Reserve.sol/Reserve.json +71 -0
  10. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +3 -3
  11. package/build/contracts/interfaces/IAccessManager.sol/IAccessManager.json +96 -0
  12. package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +103 -0
  13. package/build/contracts/interfaces/IEToken.sol/IEToken.json +13 -0
  14. package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +6 -14
  15. package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
  16. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
  17. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
  18. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +7 -15
  19. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
  20. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +674 -0
  21. package/contracts/AccessManager.sol +39 -8
  22. package/contracts/ERC4626AssetManager.sol +49 -0
  23. package/contracts/EToken.sol +72 -52
  24. package/contracts/LiquidityThresholdAssetManager.sol +159 -0
  25. package/contracts/PolicyPool.sol +155 -135
  26. package/contracts/PolicyPoolComponent.sol +29 -19
  27. package/contracts/PremiumsAccount.sol +125 -62
  28. package/contracts/Reserve.sol +86 -2
  29. package/contracts/RiskModule.sol +17 -6
  30. package/contracts/TrustfulRiskModule.sol +17 -2
  31. package/contracts/interfaces/IAccessManager.sol +23 -5
  32. package/contracts/interfaces/IAssetManager.sol +22 -0
  33. package/contracts/interfaces/IEToken.sol +21 -1
  34. package/contracts/interfaces/IPolicyPool.sol +6 -11
  35. package/contracts/interfaces/IPremiumsAccount.sol +5 -0
  36. package/contracts/mocks/FixedRateVault.sol +85 -0
  37. package/contracts/mocks/PolicyPoolMock.sol +2 -5
  38. package/contracts/mocks/RiskModuleMock.sol +69 -0
  39. package/js/test-utils.js +35 -26
  40. package/package.json +1 -1
  41. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
  42. package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
  43. package/contracts/PolicyNFT.sol +0 -96
  44. 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
- uint256 internal _borrowedActivePP; // amount borrowed from active pure premiums to pay defaulted policies
35
- uint256 internal _wonPurePremiums; // amount of pure premiums won from non-defaulted policies
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() public view returns (uint256) {
88
- return _activePurePremiums + _wonPurePremiums - _borrowedActivePP;
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 _wonPurePremiums;
127
+ return _surplus >= 0 ? uint256(_surplus) : 0;
97
128
  }
98
129
 
99
130
  function borrowedActivePP() external view returns (uint256) {
100
- return _borrowedActivePP;
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 _payFromPool(uint256 toPay) internal returns (uint256) {
112
- // 1. take from won_pure_premiums
113
- if (toPay <= _wonPurePremiums) {
114
- _wonPurePremiums -= toPay;
115
- return 0;
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
- toPay -= _wonPurePremiums;
118
- _wonPurePremiums = 0;
119
- // 2. borrow from active pure premiums
120
- if (_activePurePremiums > _borrowedActivePP) {
121
- if (toPay <= (_activePurePremiums - _borrowedActivePP)) {
122
- _borrowedActivePP += toPay;
123
- return 0;
124
- } else {
125
- toPay -= _activePurePremiums - _borrowedActivePP;
126
- _borrowedActivePP = _activePurePremiums;
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
- return toPay;
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
- if (_borrowedActivePP >= purePremiumWon) {
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
- * - onlyPoolRole(WITHDRAW_WON_PREMIUMS_ROLE)
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
- onlyPoolRole(WITHDRAW_WON_PREMIUMS_ROLE)
235
+ onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
175
236
  returns (uint256)
176
237
  {
177
- if (amount > _wonPurePremiums) amount = _wonPurePremiums;
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
- _wonPurePremiums -= amount;
180
- _transferTo(_policyPool.treasury(), amount); // TODO: discuss if destination shoud be msg.sender
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) external override onlyPolicyPool {
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 = _payFromPool(payout - policy.purePremium);
277
+ uint256 borrowFromScr = _payFromPremiums(payout - policy.purePremium);
209
278
  _unlockScr(policy);
210
279
  if (borrowFromScr > 0) {
211
- uint256 left;
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) external override onlyPolicyPool {
256
- uint256 aux;
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
- if (_borrowedActivePP > _activePurePremiums) {
262
- aux = Math.min(_borrowedActivePP - _activePurePremiums, purePremiumWon);
263
- _borrowedActivePP -= aux;
264
- purePremiumWon -= aux;
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);
@@ -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 && (amount - balance) < NEGLIGIBLE_AMOUNT) amount = balance;
31
- // TODO: asset management
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
+ onlyGlobalOrComponentRole(LEVEL2_ROLE)
113
+ returns (bytes memory)
114
+ {
115
+ address am = address(assetManager());
116
+ return am.functionDelegateCall(functionCall);
117
+ }
34
118
  }
@@ -189,7 +189,7 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
189
189
 
190
190
  function setParam(Parameter param, uint256 newValue)
191
191
  external
192
- onlyPoolRole3(LEVEL1_ROLE, LEVEL2_ROLE, LEVEL3_ROLE)
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 customer The policy holder
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 customer,
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(customer != address(0), "Customer can't be zero address");
326
+ require(onBehalfOf != address(0), "Customer can't be zero address");
320
327
  require(
321
- _policyPool.currency().allowance(customer, address(_policyPool)) >= premium,
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, customer, internalId);
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 customer,
62
+ address onBehalfOf,
63
63
  uint96 internalId
64
64
  ) external onlyComponentRole(PRICER_ROLE) returns (uint256) {
65
- return _newPolicy(payout, premium, lossProb, expiration, customer, internalId).id;
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, // Changes PolicyPool AssetManager
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
- cfgFiller1, // Reserve space for future PolicyPoolConfig actions
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 checkRole2(
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(int256 earnings);
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 {PoolBorrowerAdded}
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
- * - `customer` approved the spending of `currency()` for at least `policy.premium`
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 customer address to the different receivers of the premium
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 customer The address of the policy holder and the payer of the premiums
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 customer,
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
  }