@ensuro/core 2.0.0-beta8 → 2.0.0

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 (65) hide show
  1. package/README.md +31 -14
  2. package/build/contracts/AccessManager.sol/AccessManager.json +7 -2
  3. package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +28 -3
  4. package/build/contracts/EToken.sol/EToken.json +21 -15
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +21 -2
  6. package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +26 -1
  7. package/build/contracts/Policy.sol/Policy.json +2 -2
  8. package/build/contracts/PolicyPool.sol/PolicyPool.json +40 -2
  9. package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +19 -0
  10. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +21 -15
  11. package/build/contracts/Reserve.sol/Reserve.json +19 -13
  12. package/build/contracts/RiskModule.sol/RiskModule.json +19 -0
  13. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +1110 -0
  14. package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
  15. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +137 -2
  16. package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
  17. package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +26 -1
  18. package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +38 -0
  19. package/build/contracts/interfaces/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +19 -0
  20. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +2 -2
  21. package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +2 -2
  22. package/build/contracts/mocks/InterfaceIdCalculator.sol/InterfaceIdCalculator.json +180 -0
  23. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +28 -2
  24. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +21 -2
  25. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +115 -2
  26. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
  27. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +111 -2
  28. package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
  29. package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
  30. package/contracts/AccessManager.sol +31 -2
  31. package/contracts/ERC4626AssetManager.sol +8 -2
  32. package/contracts/EToken.sol +129 -60
  33. package/contracts/LPManualWhitelist.sol +18 -2
  34. package/contracts/LiquidityThresholdAssetManager.sol +28 -12
  35. package/contracts/Policy.sol +5 -3
  36. package/contracts/PolicyPool.sol +221 -29
  37. package/contracts/PolicyPoolComponent.sol +36 -25
  38. package/contracts/PremiumsAccount.sol +220 -32
  39. package/contracts/Reserve.sol +130 -19
  40. package/contracts/RiskModule.sol +53 -20
  41. package/contracts/SignedQuoteRiskModule.sol +310 -0
  42. package/contracts/TimeScaled.sol +19 -15
  43. package/contracts/TrustfulRiskModule.sol +107 -6
  44. package/contracts/dependencies/WadRayMath.sol +126 -0
  45. package/contracts/interfaces/IAccessManager.sol +68 -13
  46. package/contracts/interfaces/IAssetManager.sol +69 -2
  47. package/contracts/interfaces/IEToken.sol +4 -1
  48. package/contracts/interfaces/ILPWhitelist.sol +17 -0
  49. package/contracts/interfaces/IPolicyPool.sol +19 -1
  50. package/contracts/interfaces/IPolicyPoolComponent.sol +5 -1
  51. package/contracts/interfaces/IRiskModule.sol +67 -4
  52. package/contracts/mocks/InterfaceIdCalculator.sol +32 -0
  53. package/contracts/mocks/PolicyHolderMock.sol +21 -0
  54. package/contracts/mocks/PolicyPoolComponentMock.sol +10 -0
  55. package/contracts/mocks/PolicyPoolMock.sol +31 -0
  56. package/contracts/mocks/RiskModuleMock.sol +7 -1
  57. package/js/deploy.js +734 -0
  58. package/js/test-utils.js +184 -69
  59. package/package.json +4 -2
  60. package/scripts/change-pragma.sh +19 -0
  61. package/scripts/deploySmokeTest-fork.sh +53 -0
  62. package/scripts/deploySmokeTest.sh +51 -0
  63. package/scripts/storageLayout.js +33 -0
  64. package/scripts/utils.sh +65 -0
  65. package/contracts/WadRayMath.sol +0 -135
@@ -3,19 +3,20 @@ pragma solidity ^0.8.0;
3
3
 
4
4
  import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5
5
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
6
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
6
7
  import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
7
8
  import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
8
9
  import {IAccessManager} from "./interfaces/IAccessManager.sol";
9
10
  import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
10
- import {WadRayMath} from "./WadRayMath.sol";
11
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
11
12
 
12
13
  /**
13
14
  * @title Base class for PolicyPool components
14
15
  * @dev This is the base class of all the components of the protocol that are linked to the PolicyPool and created
15
16
  * after it.
16
17
  * Holds the reference to _policyPool as immutable, also provides access to common admin roles:
17
- * - LEVEL1_ROLE: High impact changes like upgrades or other critical operations
18
- * - LEVEL2_ROLE: Mid-impact changes like adding new risk modules or changing some parameters
18
+ * - LEVEL1_ROLE: High impact changes like upgrades, adding or removing components or other critical operations
19
+ * - LEVEL2_ROLE: Mid-impact changes like changing some parameters
19
20
  * - LEVEL3_ROLE: Low-impact changes like changing some parameters up to given percentage (tweaks)
20
21
  * - GUARDIAN_ROLE: For emergency operations oriented to protect the protocol in case of attacks or hacking.
21
22
  *
@@ -51,17 +52,17 @@ abstract contract PolicyPoolComponent is
51
52
  }
52
53
 
53
54
  modifier onlyComponentRole(bytes32 role) {
54
- _policyPool.access().checkComponentRole(address(this), role, msg.sender, false);
55
+ _policyPool.access().checkComponentRole(address(this), role, _msgSender(), false);
55
56
  _;
56
57
  }
57
58
 
58
59
  modifier onlyGlobalOrComponentRole(bytes32 role) {
59
- _policyPool.access().checkComponentRole(address(this), role, msg.sender, true);
60
+ _policyPool.access().checkComponentRole(address(this), role, _msgSender(), true);
60
61
  _;
61
62
  }
62
63
 
63
64
  modifier onlyGlobalOrComponentRole2(bytes32 role1, bytes32 role2) {
64
- _policyPool.access().checkComponentRole2(address(this), role1, role2, msg.sender, true);
65
+ _policyPool.access().checkComponentRole2(address(this), role1, role2, _msgSender(), true);
65
66
  _;
66
67
  }
67
68
 
@@ -71,19 +72,24 @@ abstract contract PolicyPoolComponent is
71
72
  bytes32 role3
72
73
  ) {
73
74
  IAccessManager access = _policyPool.access();
74
- if (!access.hasComponentRole(address(this), role1, msg.sender, true)) {
75
- _policyPool.access().checkComponentRole2(address(this), role2, role3, msg.sender, true);
75
+ if (!access.hasComponentRole(address(this), role1, _msgSender(), true)) {
76
+ _policyPool.access().checkComponentRole2(address(this), role2, role3, _msgSender(), true);
76
77
  }
77
78
  _;
78
79
  }
79
80
 
80
81
  /// @custom:oz-upgrades-unsafe-allow constructor
81
82
  constructor(IPolicyPool policyPool_) {
83
+ require(
84
+ address(policyPool_) != address(0),
85
+ "PolicyPoolComponent: policyPool cannot be zero address"
86
+ );
87
+ _disableInitializers();
82
88
  _policyPool = policyPool_;
83
89
  }
84
90
 
85
91
  // solhint-disable-next-line func-name-mixedcase
86
- function __PolicyPoolComponent_init() internal initializer {
92
+ function __PolicyPoolComponent_init() internal onlyInitializing {
87
93
  __UUPSUpgradeable_init();
88
94
  __Pausable_init();
89
95
  }
@@ -94,12 +100,25 @@ abstract contract PolicyPoolComponent is
94
100
  override
95
101
  onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
96
102
  {
103
+ _upgradeValidations(newImpl);
104
+ }
105
+
106
+ function _upgradeValidations(address newImpl) internal view virtual {
97
107
  require(
98
108
  IPolicyPoolComponent(newImpl).policyPool() == _policyPool,
99
109
  "Can't upgrade changing the PolicyPool!"
100
110
  );
101
111
  }
102
112
 
113
+ /**
114
+ * @dev See {IERC165-supportsInterface}.
115
+ */
116
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
117
+ return
118
+ interfaceId == type(IERC165).interfaceId ||
119
+ interfaceId == type(IPolicyPoolComponent).interfaceId;
120
+ }
121
+
103
122
  function pause() public onlyGlobalOrComponentRole(GUARDIAN_ROLE) {
104
123
  _pause();
105
124
  }
@@ -117,22 +136,7 @@ abstract contract PolicyPoolComponent is
117
136
  }
118
137
 
119
138
  function hasPoolRole(bytes32 role) internal view returns (bool) {
120
- return _policyPool.access().hasComponentRole(address(this), role, msg.sender, true);
121
- }
122
-
123
- function _isTweakRay(
124
- uint256 oldValue,
125
- uint256 newValue,
126
- uint256 maxTweak
127
- ) internal pure returns (bool) {
128
- if (oldValue == newValue) return true;
129
- if (oldValue == 0) return maxTweak >= WadRayMath.RAY;
130
- if (newValue == 0) return false;
131
- if (oldValue < newValue) {
132
- return (newValue.rayDiv(oldValue) - WadRayMath.RAY) <= maxTweak;
133
- } else {
134
- return (WadRayMath.RAY - newValue.rayDiv(oldValue)) <= maxTweak;
135
- }
139
+ return _policyPool.access().hasComponentRole(address(this), role, _msgSender(), true);
136
140
  }
137
141
 
138
142
  function _isTweakWad(
@@ -186,4 +190,11 @@ abstract contract PolicyPoolComponent is
186
190
  }
187
191
  }
188
192
  }
193
+
194
+ /**
195
+ * @dev This empty reserved space is put in place to allow future versions to add new
196
+ * variables without shifting down storage in the inheritance chain.
197
+ * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
198
+ */
199
+ uint256[49] private __gap;
189
200
  }
@@ -4,10 +4,12 @@ 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";
@@ -15,7 +17,14 @@ import {IAssetManager} from "./interfaces/IAssetManager.sol";
15
17
 
16
18
  /**
17
19
  * @title Ensuro Premiums Account
18
- * @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
+ *
19
28
  * @custom:security-contact security@ensuro.co
20
29
  * @author Ensuro
21
30
  */
@@ -23,15 +32,37 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
23
32
  using Policy for Policy.PolicyData;
24
33
  using WadRayMath for uint256;
25
34
  using SafeERC20 for IERC20Metadata;
35
+ using SafeCast for uint256;
26
36
 
27
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;
28
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
+ */
29
45
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
30
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
+ */
31
52
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
32
53
  IEToken internal immutable _seniorEtk;
33
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
+ */
34
59
  uint256 internal _activePurePremiums; // sum of pure-premiums of active policies - In Wad
60
+
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
+ */
35
66
  int256 internal _surplus;
36
67
 
37
68
  struct PackedParams {
@@ -41,14 +72,22 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
41
72
 
42
73
  PackedParams internal _params;
43
74
 
44
- /*
45
- * Premiums can come in (for free, without liability) with receiveGrant.
75
+ /**
76
+ * Premiums can come in (for "free", without liability) with receiveGrant.
46
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
47
81
  */
48
82
  event WonPremiumsInOut(bool moneyIn, uint256 value);
49
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
+ */
50
90
  /// @custom:oz-upgrades-unsafe-allow constructor
51
- // solhint-disable-next-line no-empty-blocks
52
91
  constructor(
53
92
  IPolicyPool policyPool_,
54
93
  IEToken juniorEtk_,
@@ -59,35 +98,54 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
59
98
  }
60
99
 
61
100
  /**
62
- * @dev Public initialize Initializes the PremiumsAccount
101
+ * @dev Initializes the PremiumsAccount
63
102
  */
64
103
  function initialize() public initializer {
65
104
  __PremiumsAccount_init();
66
105
  }
67
106
 
68
107
  /**
69
- * @dev Initializes the PremiumsAccount
108
+ * @dev Initializes the PremiumsAccount (to be called by subclasses)
70
109
  */
71
110
  // solhint-disable-next-line func-name-mixedcase
72
- function __PremiumsAccount_init() internal initializer {
73
- __PolicyPoolComponent_init();
111
+ function __PremiumsAccount_init() internal onlyInitializing {
112
+ __Reserve_init();
74
113
  __PremiumsAccount_init_unchained();
75
114
  }
76
115
 
77
116
  // solhint-disable-next-line func-name-mixedcase
78
- function __PremiumsAccount_init_unchained() internal initializer {
117
+ function __PremiumsAccount_init_unchained() internal onlyInitializing {
79
118
  /*
80
119
  _activePurePremiums = 0;
81
120
  */
82
- if (address(_juniorEtk) != address(0))
83
- currency().approve(address(_juniorEtk), type(uint256).max);
84
- if (address(_seniorEtk) != address(0))
85
- currency().approve(address(_seniorEtk), type(uint256).max);
86
-
87
- _params = PackedParams({deficitRatio: 1e4, assetManager: IAssetManager(address(0))});
121
+ _params = PackedParams({
122
+ deficitRatio: HUNDRED_PERCENT,
123
+ assetManager: IAssetManager(address(0))
124
+ });
88
125
  _validateParameters();
89
126
  }
90
127
 
128
+ function _upgradeValidations(address newImpl) internal view virtual override {
129
+ super._upgradeValidations(newImpl);
130
+ IPremiumsAccount newPA = IPremiumsAccount(newImpl);
131
+ require(
132
+ newPA.juniorEtk() == _juniorEtk || address(_juniorEtk) == address(0),
133
+ "Can't upgrade changing the Junior ETK unless to non-zero"
134
+ );
135
+ require(
136
+ newPA.seniorEtk() == _seniorEtk || address(_seniorEtk) == address(0),
137
+ "Can't upgrade changing the Senior ETK unless to non-zero"
138
+ );
139
+ }
140
+
141
+ /**
142
+ * @dev See {IERC165-supportsInterface}.
143
+ */
144
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
145
+ return
146
+ super.supportsInterface(interfaceId) || interfaceId == type(IPremiumsAccount).interfaceId;
147
+ }
148
+
91
149
  function assetManager() public view override returns (IAssetManager) {
92
150
  return _params.assetManager;
93
151
  }
@@ -96,6 +154,14 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
96
154
  _params.assetManager = newAM;
97
155
  }
98
156
 
157
+ /**
158
+ * @dev This is called by the {Reserve} base class to record the earnings generated by the asset management.
159
+ *
160
+ * @param earningsOrLosses Indicates the amount earned since last time earnings where recorded.
161
+ * - If positive, repays the loans and accumulates the rest in the surplus.
162
+ * - If negative (losses) substracts it from surplus. It never can exceed _maxDeficit and doesn't takes
163
+ * loans to cover asset losses.
164
+ */
99
165
  function _assetEarnings(int256 earningsOrLosses) internal override {
100
166
  if (earningsOrLosses > 0) {
101
167
  uint256 earnings = uint256(earningsOrLosses);
@@ -103,14 +169,13 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
103
169
  if (address(_juniorEtk) != address(0)) earnings = _repayLoan(earnings, _juniorEtk);
104
170
  _storePurePremiumWon(earnings);
105
171
  } else {
106
- _payFromPremiums(uint256(-earningsOrLosses));
172
+ require(_payFromPremiums(uint256(-earningsOrLosses)) == 0, "Losses can't exceed maxDeficit");
107
173
  }
108
174
  }
109
175
 
110
- // solhint-disable-next-line no-empty-blocks
111
176
  function _validateParameters() internal view override {
112
177
  require(
113
- _params.deficitRatio <= 1e4 && _params.deficitRatio >= 0,
178
+ _params.deficitRatio <= HUNDRED_PERCENT && _params.deficitRatio >= 0,
114
179
  "Validation: deficitRatio must be <= 1"
115
180
  );
116
181
  }
@@ -119,18 +184,35 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
119
184
  return uint256(int256(_activePurePremiums) + _surplus);
120
185
  }
121
186
 
187
+ /**
188
+ * @dev Returns the total amount of pure premiums that were collected by the active policies of the risk modules
189
+ * linked to this PremiumsAccount.
190
+ */
122
191
  function activePurePremiums() external view returns (uint256) {
123
192
  return _activePurePremiums;
124
193
  }
125
194
 
195
+ /**
196
+ * @dev Returns the surplus between pure premiums collected and payouts of finalized policies. Returns 0 if no surplus
197
+ * or deficit.
198
+ */
126
199
  function wonPurePremiums() external view returns (uint256) {
127
200
  return _surplus >= 0 ? uint256(_surplus) : 0;
128
201
  }
129
202
 
203
+ /**
204
+ * @dev Returns the amount of active pure premiums that was used to cover payouts of finalized policies (in excess of
205
+ * collected pure premiums). This is limited by `_maxDeficit()`
206
+ */
130
207
  function borrowedActivePP() external view returns (uint256) {
131
208
  return _surplus >= 0 ? 0 : uint256(-_surplus);
132
209
  }
133
210
 
211
+ /**
212
+ * @dev Returns the surplus between pure premiums collected and payouts of finalized policies. Losses where more than
213
+ * premiums collected, returns a negative number that indicates the amount of the active pure premiums that was used
214
+ * to cover finalized premiums.
215
+ */
134
216
  function surplus() external view returns (int256) {
135
217
  return _surplus;
136
218
  }
@@ -143,35 +225,83 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
143
225
  return _juniorEtk;
144
226
  }
145
227
 
228
+ /**
229
+ * @dev Returns the maximum deficit that's supported by the PremiumsAccount. If more money is needed, it must take
230
+ * loans from the eTokens. The value is calculated as a fraction of the active pure premiums. The fraction is
231
+ * regulated by the `deficitRatio` parameter that indicates the percentage of the active pure premiums that can be
232
+ * used to cover payouts of finalized policies. In many cases is fine to use the active pure premiums to cover the
233
+ * losses because in most cases the policies with payout are triggered long time before the policies without payout.
234
+ * But this also can be dangerous because it can be postponing the losses that should impact on liquidity providers.
235
+ *
236
+ * @param ratio The ratio used in the calculation of the deficit. It's the deficitRatio parameter (whether the current
237
+ * one or the new one when it's being modified).
238
+ */
146
239
  function _maxDeficit(uint256 ratio) internal view returns (int256) {
147
240
  return -int256(_activePurePremiums.wadMul(ratio));
148
241
  }
149
242
 
243
+ /**
244
+ * @dev Returns the percentage of the active pure premiums that can be used to cover losses of finalized policies.
245
+ */
150
246
  function deficitRatio() public view returns (uint256) {
151
- return uint256(_params.deficitRatio) * 1e14; // 4 -> 18 decimals
247
+ return uint256(_params.deficitRatio) * FOUR_DECIMAL_TO_WAD; // 4 -> 18 decimals
152
248
  }
153
249
 
250
+ /**
251
+ * @dev Changes the `deficitRatio` parameter.
252
+ *
253
+ * Requirements:
254
+ * - onlyGlobalOrComponentRole(LEVEL2_ROLE)
255
+ *
256
+ * Events:
257
+ * - Emits GovernanceAction with action = setDeficitRatio or setDeficitRatioWithAdjustment if an adjustment was made.
258
+ *
259
+ * @param adjustment If true and the new ratio leaves `_surplus < -_maxDeficit()`, it adjusts the _surplus to the new
260
+ * `_maxDeficit()` and borrows the difference from the eTokens.
261
+ * If false and the new ratio leaves `_surplus < -_maxDeficit()`, the operation is reverted.
262
+ */
154
263
  function setDeficitRatio(uint256 newRatio, bool adjustment)
155
264
  external
156
265
  onlyComponentRole(LEVEL2_ROLE)
157
266
  {
158
- require(newRatio <= 1e18 && newRatio >= 0, "Validation: deficitRatio must be <= 1");
267
+ require(newRatio <= 1e18, "Validation: deficitRatio must be <= 1");
268
+
269
+ uint16 truncatedRatio = (newRatio / FOUR_DECIMAL_TO_WAD).toUint16();
270
+ require(
271
+ uint256(truncatedRatio) * FOUR_DECIMAL_TO_WAD == newRatio,
272
+ "Validation: only up to 4 decimals allowed"
273
+ );
274
+
159
275
  int256 maxDeficit = _maxDeficit(newRatio);
160
276
  require(adjustment || _surplus >= maxDeficit, "Validation: surplus must be >= maxDeficit");
277
+
278
+ IAccessManager.GovernanceActions action = IAccessManager.GovernanceActions.setDeficitRatio;
161
279
  if (_surplus < maxDeficit) {
162
280
  // Do the adjustment
163
281
  uint256 borrow = uint256(-_surplus + maxDeficit);
164
282
  _surplus = maxDeficit;
165
283
  _borrowFromEtk(borrow, address(this), address(_juniorEtk) != address(0));
284
+ action = IAccessManager.GovernanceActions.setDeficitRatioWithAdjustment;
166
285
  }
167
- _params.deficitRatio = uint16(newRatio / 1e14);
286
+ _params.deficitRatio = truncatedRatio;
287
+ _parameterChanged(action, newRatio, false);
168
288
  }
169
289
 
290
+ /**
291
+ * @dev Internal function called when money in the PremiumsAccount is not enough and we need to borrow from the
292
+ * eTokens.
293
+ *
294
+ * @param borrow The amount to borrow.
295
+ * @param receiver The address that will receive the money of the loan. Usually is the policy holder if this is called
296
+ * in the context of a policy payout.
297
+ * @param jrEtk If true it indicates that the loan is asked first from the junior eToken.
298
+ */
170
299
  function _borrowFromEtk(
171
300
  uint256 borrow,
172
301
  address receiver,
173
302
  bool jrEtk
174
303
  ) internal {
304
+ require(receiver != address(0), "PremiumsAccount: receiver cannot be the zero address");
175
305
  uint256 left;
176
306
  if (jrEtk) {
177
307
  // Consume Junior Pool until exhausted
@@ -186,6 +316,13 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
186
316
  }
187
317
  }
188
318
 
319
+ /**
320
+ * @dev Updates the `_surplus` field with the payment made. Since the _surplus can never exceed `_maxDeficit()`,
321
+ * returns the remaining amount in case something can't be paid from the PremiumsAccount.
322
+ *
323
+ * @param toPay The amount to pay.
324
+ * @return The amount that couldn't be paid from the premiums account.
325
+ */
189
326
  function _payFromPremiums(uint256 toPay) internal returns (uint256) {
190
327
  int256 newSurplus = _surplus - int256(toPay);
191
328
  int256 maxDeficit = _maxDeficit(deficitRatio());
@@ -197,44 +334,61 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
197
334
  return uint256(-newSurplus + maxDeficit);
198
335
  }
199
336
 
337
+ /**
338
+ * @dev Stores an earned pure premium. Adds to the surplus, increasing the surplus if it was positive or reducing the
339
+ * deficit if it was negative.
340
+ *
341
+ * @param purePremiumWon The amount earned
342
+ */
200
343
  function _storePurePremiumWon(uint256 purePremiumWon) internal {
201
344
  if (purePremiumWon == 0) return;
202
345
  _surplus += int256(purePremiumWon);
203
346
  }
204
347
 
205
- // TODO: restore repayETokenLoan?
206
-
207
348
  /**
208
349
  *
209
350
  * Endpoint to receive "free money" and inject that money into the premium pool.
210
351
  *
211
352
  * Can be used for example if the PolicyPool subscribes an excess loss policy with other company.
212
353
  *
354
+ * Requirements:
355
+ * - The sender needs to approve the spending of `currency()` by this contract.
356
+ *
357
+ * Events:
358
+ * - Emits {WonPremiumsInOut} with moneyIn = true
359
+ *
360
+ * @param amount The amount to be transferred.
213
361
  */
214
362
  function receiveGrant(uint256 amount) external {
215
- currency().safeTransferFrom(msg.sender, address(this), amount);
216
363
  _storePurePremiumWon(amount);
217
364
  emit WonPremiumsInOut(true, amount);
365
+ currency().safeTransferFrom(_msgSender(), address(this), amount);
218
366
  }
219
367
 
220
368
  /**
221
369
  *
222
- * Withdraws excess premiums to PolicyPool's treasury.
370
+ * Withdraws excess premiums (surplus) to the destination.
371
+ *
223
372
  * This might be needed in some cases for example if we are deprecating the protocol or the excess premiums
224
373
  * are needed to compensate something. Shouldn't be used. Can be disabled revoking role WITHDRAW_WON_PREMIUMS_ROLE
225
374
  *
226
- * returns The amount withdrawed
227
- *
228
375
  * Requirements:
229
- *
230
376
  * - onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
231
- * - _wonPurePremiums > 0
377
+ * - _surplus > 0
378
+ *
379
+ * Events:
380
+ * - Emits {WonPremiumsInOut} with moneyIn = false
381
+ *
382
+ * @param amount The amount to withdraw
383
+ * @param destination The address that will receive the transferred funds.
384
+ * @return Returns the actual amount withdrawn.
232
385
  */
233
386
  function withdrawWonPremiums(uint256 amount, address destination)
234
387
  external
235
388
  onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
236
389
  returns (uint256)
237
390
  {
391
+ require(destination != address(0), "PremiumsAccount: destination cannot be the zero address");
238
392
  if (_surplus <= 0) {
239
393
  amount = 0;
240
394
  } else {
@@ -283,6 +437,11 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
283
437
  }
284
438
  }
285
439
 
440
+ /**
441
+ * @dev Internal function that calls the eTokens to lock the solvency capital when the policy is created.
442
+ *
443
+ * @param policy The policy created
444
+ */
286
445
  function _unlockScr(Policy.PolicyData memory policy) internal {
287
446
  if (policy.jrScr > 0) {
288
447
  _juniorEtk.unlockScr(
@@ -300,12 +459,34 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
300
459
  }
301
460
  }
302
461
 
462
+ /**
463
+ * @dev Internal function that repays a loan taken (if any outstanding) from the an eToken
464
+ *
465
+ * @param purePremiumWon The amount earned and available for loan repayment.
466
+ * @param etk The eToken with the potential debt
467
+ * @return The excess amount of the purePremiumWon that wasn't used for the loan repayment.
468
+ */
303
469
  function _repayLoan(uint256 purePremiumWon, IEToken etk) internal returns (uint256) {
304
470
  if (purePremiumWon < NEGLIGIBLE_AMOUNT) return purePremiumWon;
305
471
  uint256 borrowedFromEtk = etk.getLoan(address(this));
306
472
  if (borrowedFromEtk == 0) return purePremiumWon;
307
- uint256 repayAmount = borrowedFromEtk > purePremiumWon ? purePremiumWon : borrowedFromEtk;
308
- // TODO: make sure the balance is available or deinvest
473
+ uint256 repayAmount = Math.min(purePremiumWon, borrowedFromEtk);
474
+
475
+ // If not enough liquidity, it deinvests from the asset manager
476
+ if (currency().balanceOf(address(this)) < repayAmount) {
477
+ /**
478
+ * I send `repayAmount` because the IAssetManager expects the full amount that's needed, not the missing one.
479
+ * It uses the value of the full amount to optimize the deinvestment leaving more liquidity if possible to avoid
480
+ * future deinvestment. It will only fail if it can't refill `repayAmount - currency().balanceOf(address(this))`
481
+ */
482
+ _refillWallet(repayAmount);
483
+ }
484
+ // Checks the allowance before repayment
485
+ if (currency().allowance(address(this), address(etk)) < repayAmount) {
486
+ // If I have to approve, I approve for all the pending debt (not just repayAmount), this way I avoid some
487
+ // future approvals.
488
+ currency().approve(address(etk), borrowedFromEtk);
489
+ }
309
490
  etk.repayLoan(repayAmount, address(this));
310
491
  return purePremiumWon - repayAmount;
311
492
  }
@@ -334,4 +515,11 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
334
515
  _storePurePremiumWon(purePremiumWon);
335
516
  _unlockScr(policy);
336
517
  }
518
+
519
+ /**
520
+ * @dev This empty reserved space is put in place to allow future versions to add new
521
+ * variables without shifting down storage in the inheritance chain.
522
+ * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
523
+ */
524
+ uint256[47] private __gap;
337
525
  }