@ensuro/core 2.0.0-beta1 → 2.0.0-beta11

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 (84) hide show
  1. package/README.md +36 -14
  2. package/build/contracts/AccessManager.sol/AccessManager.json +564 -0
  3. package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +238 -0
  4. package/build/contracts/EToken.sol/EToken.json +147 -46
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +23 -4
  6. package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +222 -0
  7. package/build/contracts/Policy.sol/Policy.json +2 -2
  8. package/build/contracts/PolicyPool.sol/PolicyPool.json +437 -61
  9. package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +21 -2
  10. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +142 -16
  11. package/build/contracts/Reserve.sol/Reserve.json +85 -8
  12. package/build/contracts/RiskModule.sol/RiskModule.json +21 -2
  13. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +979 -0
  14. package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
  15. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +24 -5
  16. package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
  17. package/build/contracts/interfaces/{IPolicyPoolConfig.sol/IPolicyPoolConfig.json → IAccessManager.sol/IAccessManager.json} +77 -59
  18. package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +128 -0
  19. package/build/contracts/interfaces/IEToken.sol/IEToken.json +13 -0
  20. package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +40 -16
  21. package/build/contracts/interfaces/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +19 -0
  22. package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
  23. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
  24. package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +2 -2
  25. package/build/contracts/mocks/InterfaceIdCalculator.sol/InterfaceIdCalculator.json +180 -0
  26. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +28 -2
  27. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +21 -2
  28. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +118 -19
  29. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +6 -6
  30. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +783 -0
  31. package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
  32. package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
  33. package/contracts/AccessManager.sol +132 -0
  34. package/contracts/ERC4626AssetManager.sol +55 -0
  35. package/contracts/EToken.sol +208 -102
  36. package/contracts/LPManualWhitelist.sol +19 -3
  37. package/contracts/LiquidityThresholdAssetManager.sol +175 -0
  38. package/contracts/Policy.sol +5 -3
  39. package/contracts/PolicyPool.sol +381 -116
  40. package/contracts/PolicyPoolComponent.sol +61 -44
  41. package/contracts/PremiumsAccount.sol +324 -86
  42. package/contracts/Reserve.sol +199 -4
  43. package/contracts/RiskModule.sol +62 -31
  44. package/contracts/SignedQuoteRiskModule.sol +248 -0
  45. package/contracts/TimeScaled.sol +19 -15
  46. package/contracts/TrustfulRiskModule.sol +69 -2
  47. package/contracts/dependencies/WadRayMath.sol +126 -0
  48. package/contracts/interfaces/IAccessManager.sol +148 -0
  49. package/contracts/interfaces/IAssetManager.sol +89 -0
  50. package/contracts/interfaces/IEToken.sol +25 -2
  51. package/contracts/interfaces/ILPWhitelist.sol +17 -0
  52. package/contracts/interfaces/IPolicyPool.sol +21 -14
  53. package/contracts/interfaces/IPolicyPoolComponent.sol +5 -1
  54. package/contracts/interfaces/IPremiumsAccount.sol +5 -0
  55. package/contracts/interfaces/IRiskModule.sol +67 -4
  56. package/contracts/mocks/FixedRateVault.sol +85 -0
  57. package/contracts/mocks/InterfaceIdCalculator.sol +32 -0
  58. package/contracts/mocks/PolicyHolderMock.sol +21 -0
  59. package/contracts/mocks/PolicyPoolComponentMock.sol +10 -0
  60. package/contracts/mocks/PolicyPoolMock.sol +41 -15
  61. package/contracts/mocks/RiskModuleMock.sol +75 -0
  62. package/js/deploy.js +704 -0
  63. package/js/test-utils.js +339 -159
  64. package/package.json +1 -1
  65. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -890
  66. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
  67. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -691
  68. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -63
  69. package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
  70. package/build/contracts/mocks/LinkTokenMock.sol/LinkTokenMock.json +0 -370
  71. package/build/contracts/mocks/TestCurrencyUUPS.sol/TestCurrencyUUPS.json +0 -353
  72. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
  73. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -584
  74. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -615
  75. package/contracts/FlightDelayRiskModule.sol +0 -217
  76. package/contracts/PolicyNFT.sol +0 -96
  77. package/contracts/PolicyPoolConfig.sol +0 -204
  78. package/contracts/WadRayMath.sol +0 -135
  79. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
  80. package/contracts/interfaces/IPolicyNFT.sol +0 -15
  81. package/contracts/interfaces/IPolicyPoolConfig.sol +0 -96
  82. package/contracts/mocks/LinkTokenMock.sol +0 -30
  83. package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
  84. package/contracts/upgraded/PolicyNFTv1.sol +0 -126
@@ -4,17 +4,27 @@ pragma solidity ^0.8.0;
4
4
  import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
5
5
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
6
6
  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7
- import {WadRayMath} from "./WadRayMath.sol";
7
+ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
8
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
8
9
  import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
9
10
  import {IEToken} from "./interfaces/IEToken.sol";
10
11
  import {Reserve} from "./Reserve.sol";
12
+ import {IAccessManager} from "./interfaces/IAccessManager.sol";
11
13
  import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
12
14
  import {Policy} from "./Policy.sol";
13
15
  import {IEToken} from "./interfaces/IEToken.sol";
16
+ import {IAssetManager} from "./interfaces/IAssetManager.sol";
14
17
 
15
18
  /**
16
19
  * @title Ensuro Premiums Account
17
- * @dev This contract holds the premiums of a set of risk modules
20
+ * @dev This contract holds the pure premiums of a set of risk modules. The pure premiums is the part of the premium
21
+ * that is expected to cover the losses. The contract keeps track of the pure premiums of the active policies
22
+ * (_activePurePremiums) and the surplus or deficit generated by the finalized policies (pure premiums collected -
23
+ * losses).
24
+ *
25
+ * Collaborates with a junior {EToken} and a senior {EToken} that act as lenders when the premiums aren't enough to
26
+ * cover the losses.
27
+ *
18
28
  * @custom:security-contact security@ensuro.co
19
29
  * @author Ensuro
20
30
  */
@@ -22,26 +32,62 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
22
32
  using Policy for Policy.PolicyData;
23
33
  using WadRayMath for uint256;
24
34
  using SafeERC20 for IERC20Metadata;
35
+ using SafeCast for uint256;
25
36
 
26
37
  bytes32 public constant WITHDRAW_WON_PREMIUMS_ROLE = keccak256("WITHDRAW_WON_PREMIUMS_ROLE");
38
+ uint256 internal constant FOUR_DECIMAL_TO_WAD = 1e14;
39
+ uint16 internal constant HUNDRED_PERCENT = 1e4;
27
40
 
41
+ /**
42
+ * @dev The Junior eToken is the first {EToken} to which the PremiumsAccount will go for credit when it runs out of
43
+ * money. Optional (address(0)).
44
+ */
28
45
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
29
46
  IEToken internal immutable _juniorEtk;
47
+
48
+ /**
49
+ * @dev The Senior eToken is the second {EToken} to which the PremiumsAccount will go for credit, after trying before
50
+ * with the junior eToken, when it runs out of money. Optional (address(0)).
51
+ */
30
52
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
31
53
  IEToken internal immutable _seniorEtk;
32
54
 
55
+ /**
56
+ * @dev The active pure premiums field keeps track of the pure premiums collected by the active policies of risk
57
+ * modules linked with this PremiumsAccount.
58
+ */
33
59
  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
36
60
 
37
- /*
38
- * Premiums can come in (for free, without liability) with receiveGrant.
61
+ /**
62
+ * @dev The surplus field keeps track of the surplus or deficit (when negative) of the actual payouts made by the
63
+ * PremiumsAccount versus the collected pure premiums. On the negative side, it has a limit defined by `_maxDeficit()`,
64
+ * after that limit, internal loans are taken from the eTokens.
65
+ */
66
+ int256 internal _surplus;
67
+
68
+ struct PackedParams {
69
+ uint16 deficitRatio;
70
+ IAssetManager assetManager;
71
+ }
72
+
73
+ PackedParams internal _params;
74
+
75
+ /**
76
+ * Premiums can come in (for "free", without liability) with receiveGrant.
39
77
  * And can come out (withdrawed to treasury) with withdrawWonPremiums
78
+ *
79
+ * @param moneyIn Indicates if money came in or out (false).
80
+ * @param value The amount of money received or given
40
81
  */
41
82
  event WonPremiumsInOut(bool moneyIn, uint256 value);
42
83
 
84
+ /**
85
+ * @dev Constructor of the contract, sets the immutable fields.
86
+ *
87
+ * @param juniorEtk_ Address of the Junior EToken (first loss lender). `address(0)` if not present.
88
+ * @param seniorEtk_ Address of the Senior EToken (2nd loss lender). `address(0)` if not present.
89
+ */
43
90
  /// @custom:oz-upgrades-unsafe-allow constructor
44
- // solhint-disable-next-line no-empty-blocks
45
91
  constructor(
46
92
  IPolicyPool policyPool_,
47
93
  IEToken juniorEtk_,
@@ -52,52 +98,110 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
52
98
  }
53
99
 
54
100
  /**
55
- * @dev Public initialize Initializes the PremiumsAccount
101
+ * @dev Initializes the PremiumsAccount
56
102
  */
57
103
  function initialize() public initializer {
58
104
  __PremiumsAccount_init();
59
105
  }
60
106
 
61
107
  /**
62
- * @dev Initializes the PremiumsAccount
108
+ * @dev Initializes the PremiumsAccount (to be called by subclasses)
63
109
  */
64
110
  // solhint-disable-next-line func-name-mixedcase
65
- function __PremiumsAccount_init() internal initializer {
66
- __PolicyPoolComponent_init();
111
+ function __PremiumsAccount_init() internal onlyInitializing {
112
+ __Reserve_init();
67
113
  __PremiumsAccount_init_unchained();
68
114
  }
69
115
 
70
116
  // solhint-disable-next-line func-name-mixedcase
71
- function __PremiumsAccount_init_unchained() internal initializer {
117
+ function __PremiumsAccount_init_unchained() internal onlyInitializing {
72
118
  /*
73
119
  _activePurePremiums = 0;
74
- _borrowedActivePP = 0;
75
- _wonPurePremiums = 0;
76
120
  */
77
- if (address(_juniorEtk) != address(0))
78
- currency().approve(address(_juniorEtk), type(uint256).max);
79
- if (address(_seniorEtk) != address(0))
80
- currency().approve(address(_seniorEtk), type(uint256).max);
121
+ _params = PackedParams({
122
+ deficitRatio: HUNDRED_PERCENT,
123
+ assetManager: IAssetManager(address(0))
124
+ });
81
125
  _validateParameters();
82
126
  }
83
127
 
84
- // solhint-disable-next-line no-empty-blocks
85
- function _validateParameters() internal view override {}
128
+ /**
129
+ * @dev See {IERC165-supportsInterface}.
130
+ */
131
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
132
+ return
133
+ super.supportsInterface(interfaceId) || interfaceId == type(IPremiumsAccount).interfaceId;
134
+ }
135
+
136
+ function assetManager() public view override returns (IAssetManager) {
137
+ return _params.assetManager;
138
+ }
139
+
140
+ function _setAssetManager(IAssetManager newAM) internal override {
141
+ _params.assetManager = newAM;
142
+ }
143
+
144
+ /**
145
+ * @dev This is called by the {Reserve} base class to record the earnings generated by the asset management.
146
+ *
147
+ * @param earningsOrLosses Indicates the amount earned since last time earnings where recorded.
148
+ * - If positive, repays the loans and accumulates the rest in the surplus.
149
+ * - If negative (losses) substracts it from surplus. It never can exceed _maxDeficit and doesn't takes
150
+ * loans to cover asset losses.
151
+ */
152
+ function _assetEarnings(int256 earningsOrLosses) internal override {
153
+ if (earningsOrLosses > 0) {
154
+ uint256 earnings = uint256(earningsOrLosses);
155
+ if (address(_seniorEtk) != address(0)) earnings = _repayLoan(earnings, _seniorEtk);
156
+ if (address(_juniorEtk) != address(0)) earnings = _repayLoan(earnings, _juniorEtk);
157
+ _storePurePremiumWon(earnings);
158
+ } else {
159
+ require(_payFromPremiums(uint256(-earningsOrLosses)) == 0, "Losses can't exceed maxDeficit");
160
+ }
161
+ }
162
+
163
+ function _validateParameters() internal view override {
164
+ require(
165
+ _params.deficitRatio <= HUNDRED_PERCENT && _params.deficitRatio >= 0,
166
+ "Validation: deficitRatio must be <= 1"
167
+ );
168
+ }
86
169
 
87
- function purePremiums() public view returns (uint256) {
88
- return _activePurePremiums + _wonPurePremiums - _borrowedActivePP;
170
+ function purePremiums() external view override returns (uint256) {
171
+ return uint256(int256(_activePurePremiums) + _surplus);
89
172
  }
90
173
 
174
+ /**
175
+ * @dev Returns the total amount of pure premiums that were collected by the active policies of the risk modules
176
+ * linked to this PremiumsAccount.
177
+ */
91
178
  function activePurePremiums() external view returns (uint256) {
92
179
  return _activePurePremiums;
93
180
  }
94
181
 
182
+ /**
183
+ * @dev Returns the surplus between pure premiums collected and payouts of finalized policies. Returns 0 if no surplus
184
+ * or deficit.
185
+ */
95
186
  function wonPurePremiums() external view returns (uint256) {
96
- return _wonPurePremiums;
187
+ return _surplus >= 0 ? uint256(_surplus) : 0;
97
188
  }
98
189
 
190
+ /**
191
+ * @dev Returns the amount of active pure premiums that was used to cover payouts of finalized policies (in excess of
192
+ * collected pure premiums). This is limited by `_maxDeficit()`
193
+ */
99
194
  function borrowedActivePP() external view returns (uint256) {
100
- return _borrowedActivePP;
195
+ return _surplus >= 0 ? 0 : uint256(-_surplus);
196
+ }
197
+
198
+ /**
199
+ * @dev Returns the surplus between pure premiums collected and payouts of finalized policies. Losses where more than
200
+ * premiums collected, returns a negative number that indicates the amount of the active pure premiums that was used
201
+ * to cover finalized premiums.
202
+ */
203
+ function surplus() external view returns (int256) {
204
+ return _surplus;
101
205
  }
102
206
 
103
207
  function seniorEtk() external view override returns (IEToken) {
@@ -108,40 +212,125 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
108
212
  return _juniorEtk;
109
213
  }
110
214
 
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;
116
- }
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
- }
215
+ /**
216
+ * @dev Returns the maximum deficit that's supported by the PremiumsAccount. If more money is needed, it must take
217
+ * loans from the eTokens. The value is calculated as a fraction of the active pure premiums. The fraction is
218
+ * regulated by the `deficitRatio` parameter that indicates the percentage of the active pure premiums that can be
219
+ * used to cover payouts of finalized policies. In many cases is fine to use the active pure premiums to cover the
220
+ * losses because in most cases the policies with payout are triggered long time before the policies without payout.
221
+ * But this also can be dangerous because it can be postponing the losses that should impact on liquidity providers.
222
+ *
223
+ * @param ratio The ratio used in the calculation of the deficit. It's the deficitRatio parameter (whether the current
224
+ * one or the new one when it's being modified).
225
+ */
226
+ function _maxDeficit(uint256 ratio) internal view returns (int256) {
227
+ return -int256(_activePurePremiums.wadMul(ratio));
228
+ }
229
+
230
+ /**
231
+ * @dev Returns the percentage of the active pure premiums that can be used to cover losses of finalized policies.
232
+ */
233
+ function deficitRatio() public view returns (uint256) {
234
+ return uint256(_params.deficitRatio) * FOUR_DECIMAL_TO_WAD; // 4 -> 18 decimals
235
+ }
236
+
237
+ /**
238
+ * @dev Changes the `deficitRatio` parameter.
239
+ *
240
+ * Requirements:
241
+ * - onlyGlobalOrComponentRole(LEVEL2_ROLE)
242
+ *
243
+ * Events:
244
+ * - Emits GovernanceAction with action = setDeficitRatio or setDeficitRatioWithAdjustment if an adjustment was made.
245
+ *
246
+ * @param adjustment If true and the new ratio leaves `_surplus < -_maxDeficit()`, it adjusts the _surplus to the new
247
+ * `_maxDeficit()` and borrows the difference from the eTokens.
248
+ * If false and the new ratio leaves `_surplus < -_maxDeficit()`, the operation is reverted.
249
+ */
250
+ function setDeficitRatio(uint256 newRatio, bool adjustment)
251
+ external
252
+ onlyComponentRole(LEVEL2_ROLE)
253
+ {
254
+ require(newRatio <= 1e18, "Validation: deficitRatio must be <= 1");
255
+
256
+ uint16 truncatedRatio = (newRatio / FOUR_DECIMAL_TO_WAD).toUint16();
257
+ require(
258
+ uint256(truncatedRatio) * FOUR_DECIMAL_TO_WAD == newRatio,
259
+ "Validation: only up to 4 decimals allowed"
260
+ );
261
+
262
+ int256 maxDeficit = _maxDeficit(newRatio);
263
+ require(adjustment || _surplus >= maxDeficit, "Validation: surplus must be >= maxDeficit");
264
+
265
+ IAccessManager.GovernanceActions action = IAccessManager.GovernanceActions.setDeficitRatio;
266
+ if (_surplus < maxDeficit) {
267
+ // Do the adjustment
268
+ uint256 borrow = uint256(-_surplus + maxDeficit);
269
+ _surplus = maxDeficit;
270
+ _borrowFromEtk(borrow, address(this), address(_juniorEtk) != address(0));
271
+ action = IAccessManager.GovernanceActions.setDeficitRatioWithAdjustment;
128
272
  }
129
- return toPay;
273
+ _params.deficitRatio = truncatedRatio;
274
+ _parameterChanged(action, newRatio, false);
130
275
  }
131
276
 
132
- function _storePurePremiumWon(uint256 purePremiumWon) internal {
133
- // TODO: merge _wonPurePremiums and _borrowedActivePP into single int256 variable
134
- // and this will be just `_wonPurePremiums += purePremiumWon;`
135
- if (purePremiumWon == 0) return;
136
- if (_borrowedActivePP >= purePremiumWon) {
137
- _borrowedActivePP -= purePremiumWon;
277
+ /**
278
+ * @dev Internal function called when money in the PremiumsAccount is not enough and we need to borrow from the
279
+ * eTokens.
280
+ *
281
+ * @param borrow The amount to borrow.
282
+ * @param receiver The address that will receive the money of the loan. Usually is the policy holder if this is called
283
+ * in the context of a policy payout.
284
+ * @param jrEtk If true it indicates that the loan is asked first from the junior eToken.
285
+ */
286
+ function _borrowFromEtk(
287
+ uint256 borrow,
288
+ address receiver,
289
+ bool jrEtk
290
+ ) internal {
291
+ require(receiver != address(0), "PremiumsAccount: receiver cannot be the zero address");
292
+ uint256 left;
293
+ if (jrEtk) {
294
+ // Consume Junior Pool until exhausted
295
+ left = _juniorEtk.internalLoan(borrow, receiver, false);
138
296
  } else {
139
- _wonPurePremiums += (purePremiumWon - _borrowedActivePP);
140
- _borrowedActivePP = 0;
297
+ left = borrow;
298
+ }
299
+ if (left > NEGLIGIBLE_AMOUNT) {
300
+ // Consume Senior Pool only up to SCR
301
+ left = _seniorEtk.internalLoan(left, receiver, true);
302
+ require(left <= NEGLIGIBLE_AMOUNT, "Don't know where to take the rest of the money");
303
+ }
304
+ }
305
+
306
+ /**
307
+ * @dev Updates the `_surplus` field with the payment made. Since the _surplus can never exceed `_maxDeficit()`,
308
+ * returns the remaining amount in case something can't be paid from the PremiumsAccount.
309
+ *
310
+ * @param toPay The amount to pay.
311
+ * @return The amount that couldn't be paid from the premiums account.
312
+ */
313
+ function _payFromPremiums(uint256 toPay) internal returns (uint256) {
314
+ int256 newSurplus = _surplus - int256(toPay);
315
+ int256 maxDeficit = _maxDeficit(deficitRatio());
316
+ if (newSurplus >= maxDeficit) {
317
+ _surplus = newSurplus;
318
+ return 0;
141
319
  }
320
+ _surplus = maxDeficit;
321
+ return uint256(-newSurplus + maxDeficit);
142
322
  }
143
323
 
144
- // TODO: restore repayETokenLoan?
324
+ /**
325
+ * @dev Stores an earned pure premium. Adds to the surplus, increasing the surplus if it was positive or reducing the
326
+ * deficit if it was negative.
327
+ *
328
+ * @param purePremiumWon The amount earned
329
+ */
330
+ function _storePurePremiumWon(uint256 purePremiumWon) internal {
331
+ if (purePremiumWon == 0) return;
332
+ _surplus += int256(purePremiumWon);
333
+ }
145
334
 
146
335
  /**
147
336
  *
@@ -149,41 +338,62 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
149
338
  *
150
339
  * Can be used for example if the PolicyPool subscribes an excess loss policy with other company.
151
340
  *
341
+ * Requirements:
342
+ * - The sender needs to approve the spending of `currency()` by this contract.
343
+ *
344
+ * Events:
345
+ * - Emits {WonPremiumsInOut} with moneyIn = true
346
+ *
347
+ * @param amount The amount to be transferred.
152
348
  */
153
349
  function receiveGrant(uint256 amount) external {
154
- currency().safeTransferFrom(msg.sender, address(this), amount);
155
350
  _storePurePremiumWon(amount);
156
351
  emit WonPremiumsInOut(true, amount);
352
+ currency().safeTransferFrom(_msgSender(), address(this), amount);
157
353
  }
158
354
 
159
355
  /**
160
356
  *
161
- * Withdraws excess premiums to PolicyPool's treasury.
357
+ * Withdraws excess premiums (surplus) to the destination.
358
+ *
162
359
  * This might be needed in some cases for example if we are deprecating the protocol or the excess premiums
163
360
  * are needed to compensate something. Shouldn't be used. Can be disabled revoking role WITHDRAW_WON_PREMIUMS_ROLE
164
361
  *
165
- * returns The amount withdrawed
166
- *
167
362
  * Requirements:
363
+ * - onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
364
+ * - _surplus > 0
168
365
  *
169
- * - onlyPoolRole(WITHDRAW_WON_PREMIUMS_ROLE)
170
- * - _wonPurePremiums > 0
366
+ * Events:
367
+ * - Emits {WonPremiumsInOut} with moneyIn = false
368
+ *
369
+ * @param amount The amount to withdraw
370
+ * @param destination The address that will receive the transferred funds.
371
+ * @return Returns the actual amount withdrawn.
171
372
  */
172
- function withdrawWonPremiums(uint256 amount)
373
+ function withdrawWonPremiums(uint256 amount, address destination)
173
374
  external
174
- onlyPoolRole(WITHDRAW_WON_PREMIUMS_ROLE)
375
+ onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
175
376
  returns (uint256)
176
377
  {
177
- if (amount > _wonPurePremiums) amount = _wonPurePremiums;
378
+ require(destination != address(0), "PremiumsAccount: destination cannot be the zero address");
379
+ if (_surplus <= 0) {
380
+ amount = 0;
381
+ } else {
382
+ amount = Math.min(amount, uint256(_surplus));
383
+ }
178
384
  require(amount > 0, "No premiums to withdraw");
179
- _wonPurePremiums -= amount;
180
- _transferTo(_policyPool.config().treasury(), amount); // TODO: discuss if destination shoud be msg.sender
181
- // TODO: see if this will be a component role
385
+ _surplus -= int256(amount);
386
+ _transferTo(destination, amount);
182
387
  emit WonPremiumsInOut(false, amount);
183
388
  return amount;
184
389
  }
185
390
 
186
- function policyCreated(Policy.PolicyData memory policy) external override onlyPolicyPool {
391
+ function policyCreated(Policy.PolicyData memory policy)
392
+ external
393
+ override
394
+ onlyPolicyPool
395
+ whenNotPaused
396
+ {
187
397
  _activePurePremiums += policy.purePremium;
188
398
  if (policy.jrScr > 0) _juniorEtk.lockScr(policy.jrScr, policy.jrInterestRate());
189
399
  if (policy.srScr > 0) _seniorEtk.lockScr(policy.srScr, policy.srInterestRate());
@@ -193,7 +403,7 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
193
403
  address policyHolder,
194
404
  Policy.PolicyData memory policy,
195
405
  uint256 payout
196
- ) external override onlyPolicyPool {
406
+ ) external override onlyPolicyPool whenNotPaused {
197
407
  _activePurePremiums -= policy.purePremium;
198
408
  if (policy.purePremium >= payout) {
199
409
  uint256 purePremiumWon = policy.purePremium - payout;
@@ -205,26 +415,20 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
205
415
  _unlockScr(policy);
206
416
  _transferTo(policyHolder, payout);
207
417
  } else {
208
- uint256 borrowFromScr = _payFromPool(payout - policy.purePremium);
418
+ uint256 borrowFromScr = _payFromPremiums(payout - policy.purePremium);
209
419
  _unlockScr(policy);
210
420
  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
- }
421
+ _borrowFromEtk(borrowFromScr, policyHolder, policy.jrScr > 0);
223
422
  }
224
423
  _transferTo(policyHolder, payout - borrowFromScr);
225
424
  }
226
425
  }
227
426
 
427
+ /**
428
+ * @dev Internal function that calls the eTokens to lock the solvency capital when the policy is created.
429
+ *
430
+ * @param policy The policy created
431
+ */
228
432
  function _unlockScr(Policy.PolicyData memory policy) internal {
229
433
  if (policy.jrScr > 0) {
230
434
  _juniorEtk.unlockScr(
@@ -242,26 +446,53 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
242
446
  }
243
447
  }
244
448
 
449
+ /**
450
+ * @dev Internal function that repays a loan taken (if any outstanding) from the an eToken
451
+ *
452
+ * @param purePremiumWon The amount earned and available for loan repayment.
453
+ * @param etk The eToken with the potential debt
454
+ * @return The excess amount of the purePremiumWon that wasn't used for the loan repayment.
455
+ */
245
456
  function _repayLoan(uint256 purePremiumWon, IEToken etk) internal returns (uint256) {
246
457
  if (purePremiumWon < NEGLIGIBLE_AMOUNT) return purePremiumWon;
247
458
  uint256 borrowedFromEtk = etk.getLoan(address(this));
248
459
  if (borrowedFromEtk == 0) return purePremiumWon;
249
- uint256 repayAmount = borrowedFromEtk > purePremiumWon ? purePremiumWon : borrowedFromEtk;
250
- // TODO: make sure the balance is available or deinvest
460
+ uint256 repayAmount = Math.min(purePremiumWon, borrowedFromEtk);
461
+
462
+ // If not enough liquidity, it deinvests from the asset manager
463
+ if (currency().balanceOf(address(this)) < repayAmount) {
464
+ /**
465
+ * I send `repayAmount` because the IAssetManager expects the full amount that's needed, not the missing one.
466
+ * It uses the value of the full amount to optimize the deinvestment leaving more liquidity if possible to avoid
467
+ * future deinvestment. It will only fail if it can't refill `repayAmount - currency().balanceOf(address(this))`
468
+ */
469
+ _refillWallet(repayAmount);
470
+ }
471
+ // Checks the allowance before repayment
472
+ if (currency().allowance(address(this), address(etk)) < repayAmount) {
473
+ // If I have to approve, I approve for all the pending debt (not just repayAmount), this way I avoid some
474
+ // future approvals.
475
+ currency().approve(address(etk), borrowedFromEtk);
476
+ }
251
477
  etk.repayLoan(repayAmount, address(this));
252
478
  return purePremiumWon - repayAmount;
253
479
  }
254
480
 
255
- function policyExpired(Policy.PolicyData memory policy) external override onlyPolicyPool {
256
- uint256 aux;
481
+ function policyExpired(Policy.PolicyData memory policy)
482
+ external
483
+ override
484
+ onlyPolicyPool
485
+ whenNotPaused
486
+ {
257
487
  uint256 purePremiumWon = policy.purePremium;
258
488
  _activePurePremiums -= purePremiumWon;
259
489
 
260
490
  // 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;
491
+ int256 maxDeficit = _maxDeficit(deficitRatio());
492
+ if (_surplus < maxDeficit) {
493
+ // Covers the excess of deficit first
494
+ purePremiumWon -= uint256(-_surplus + maxDeficit);
495
+ _surplus = maxDeficit;
265
496
  }
266
497
 
267
498
  if (address(_seniorEtk) != address(0)) purePremiumWon = _repayLoan(purePremiumWon, _seniorEtk);
@@ -271,4 +502,11 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
271
502
  _storePurePremiumWon(purePremiumWon);
272
503
  _unlockScr(policy);
273
504
  }
505
+
506
+ /**
507
+ * @dev This empty reserved space is put in place to allow future versions to add new
508
+ * variables without shifting down storage in the inheritance chain.
509
+ * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
510
+ */
511
+ uint256[47] private __gap;
274
512
  }