@ensuro/core 2.0.0-beta1 → 2.0.0-beta10

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 (79) hide show
  1. package/README.md +23 -14
  2. package/build/contracts/AccessManager.sol/AccessManager.json +559 -0
  3. package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +219 -0
  4. package/build/contracts/EToken.sol/EToken.json +134 -52
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +4 -4
  6. package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +203 -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 +2 -2
  10. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +123 -16
  11. package/build/contracts/Reserve.sol/Reserve.json +66 -8
  12. package/build/contracts/RiskModule.sol/RiskModule.json +2 -2
  13. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +960 -0
  14. package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
  15. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +5 -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 +109 -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/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
  22. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
  23. package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +2 -2
  24. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +28 -2
  25. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
  26. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +118 -19
  27. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +6 -6
  28. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +764 -0
  29. package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
  30. package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
  31. package/contracts/AccessManager.sol +103 -0
  32. package/contracts/ERC4626AssetManager.sol +52 -0
  33. package/contracts/EToken.sol +123 -87
  34. package/contracts/LPManualWhitelist.sol +1 -1
  35. package/contracts/LiquidityThresholdAssetManager.sol +160 -0
  36. package/contracts/Policy.sol +1 -1
  37. package/contracts/PolicyPool.sol +333 -115
  38. package/contracts/PolicyPoolComponent.sol +36 -41
  39. package/contracts/PremiumsAccount.sol +285 -79
  40. package/contracts/Reserve.sol +183 -4
  41. package/contracts/RiskModule.sol +26 -19
  42. package/contracts/SignedQuoteRiskModule.sol +241 -0
  43. package/contracts/TimeScaled.sol +19 -15
  44. package/contracts/TrustfulRiskModule.sol +62 -2
  45. package/contracts/dependencies/WadRayMath.sol +126 -0
  46. package/contracts/interfaces/IAccessManager.sol +148 -0
  47. package/contracts/interfaces/IAssetManager.sol +84 -0
  48. package/contracts/interfaces/IEToken.sol +25 -2
  49. package/contracts/interfaces/ILPWhitelist.sol +17 -0
  50. package/contracts/interfaces/IPolicyPool.sol +20 -13
  51. package/contracts/interfaces/IPolicyPoolComponent.sol +3 -0
  52. package/contracts/interfaces/IPremiumsAccount.sol +5 -0
  53. package/contracts/interfaces/IRiskModule.sol +67 -4
  54. package/contracts/mocks/FixedRateVault.sol +85 -0
  55. package/contracts/mocks/PolicyHolderMock.sol +21 -0
  56. package/contracts/mocks/PolicyPoolMock.sol +41 -15
  57. package/contracts/mocks/RiskModuleMock.sol +75 -0
  58. package/js/test-utils.js +227 -149
  59. package/package.json +1 -1
  60. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -890
  61. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
  62. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -691
  63. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -63
  64. package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
  65. package/build/contracts/mocks/LinkTokenMock.sol/LinkTokenMock.json +0 -370
  66. package/build/contracts/mocks/TestCurrencyUUPS.sol/TestCurrencyUUPS.json +0 -353
  67. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
  68. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -584
  69. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -615
  70. package/contracts/FlightDelayRiskModule.sol +0 -217
  71. package/contracts/PolicyNFT.sol +0 -96
  72. package/contracts/PolicyPoolConfig.sol +0 -204
  73. package/contracts/WadRayMath.sol +0 -135
  74. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
  75. package/contracts/interfaces/IPolicyNFT.sol +0 -15
  76. package/contracts/interfaces/IPolicyPoolConfig.sol +0 -96
  77. package/contracts/mocks/LinkTokenMock.sol +0 -30
  78. package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
  79. package/contracts/upgraded/PolicyNFTv1.sol +0 -126
@@ -5,9 +5,9 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U
5
5
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
6
6
  import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
7
7
  import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
8
- import {IPolicyPoolConfig} from "./interfaces/IPolicyPoolConfig.sol";
8
+ import {IAccessManager} from "./interfaces/IAccessManager.sol";
9
9
  import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
10
- import {WadRayMath} from "./WadRayMath.sol";
10
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
11
11
 
12
12
  /**
13
13
  * @title Base class for PolicyPool components
@@ -42,37 +42,38 @@ abstract contract PolicyPoolComponent is
42
42
  uint40 internal _lastTweakTimestamp;
43
43
  uint56 internal _lastTweakActions; // bitwise map of applied actions
44
44
 
45
- event GovernanceAction(IPolicyPoolConfig.GovernanceActions indexed action, uint256 value);
46
- event ComponentChanged(IPolicyPoolConfig.GovernanceActions indexed action, address value);
45
+ event GovernanceAction(IAccessManager.GovernanceActions indexed action, uint256 value);
46
+ event ComponentChanged(IAccessManager.GovernanceActions indexed action, address value);
47
47
 
48
48
  modifier onlyPolicyPool() {
49
49
  require(_msgSender() == address(_policyPool), "The caller must be the PolicyPool");
50
50
  _;
51
51
  }
52
52
 
53
- modifier onlyPoolRole3(
54
- bytes32 role1,
55
- bytes32 role2,
56
- bytes32 role3
57
- ) {
58
- if (!hasPoolRole(role1)) {
59
- _policyPool.config().checkRole2(role2, role3, msg.sender);
60
- }
53
+ modifier onlyComponentRole(bytes32 role) {
54
+ _policyPool.access().checkComponentRole(address(this), role, _msgSender(), false);
61
55
  _;
62
56
  }
63
57
 
64
- modifier onlyPoolRole2(bytes32 role1, bytes32 role2) {
65
- _policyPool.config().checkRole2(role1, role2, msg.sender);
58
+ modifier onlyGlobalOrComponentRole(bytes32 role) {
59
+ _policyPool.access().checkComponentRole(address(this), role, _msgSender(), true);
66
60
  _;
67
61
  }
68
62
 
69
- modifier onlyPoolRole(bytes32 role) {
70
- _policyPool.config().checkRole(role, msg.sender);
63
+ modifier onlyGlobalOrComponentRole2(bytes32 role1, bytes32 role2) {
64
+ _policyPool.access().checkComponentRole2(address(this), role1, role2, _msgSender(), true);
71
65
  _;
72
66
  }
73
67
 
74
- modifier onlyComponentRole(bytes32 role) {
75
- _policyPool.config().checkComponentRole(address(this), role, msg.sender);
68
+ modifier onlyGlobalOrComponentRole3(
69
+ bytes32 role1,
70
+ bytes32 role2,
71
+ bytes32 role3
72
+ ) {
73
+ IAccessManager access = _policyPool.access();
74
+ if (!access.hasComponentRole(address(this), role1, _msgSender(), true)) {
75
+ _policyPool.access().checkComponentRole2(address(this), role2, role3, _msgSender(), true);
76
+ }
76
77
  _;
77
78
  }
78
79
 
@@ -87,14 +88,23 @@ abstract contract PolicyPoolComponent is
87
88
  __Pausable_init();
88
89
  }
89
90
 
90
- // solhint-disable-next-line no-empty-blocks
91
- function _authorizeUpgrade(address) internal override onlyPoolRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {}
91
+ function _authorizeUpgrade(address newImpl)
92
+ internal
93
+ view
94
+ override
95
+ onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
96
+ {
97
+ require(
98
+ IPolicyPoolComponent(newImpl).policyPool() == _policyPool,
99
+ "Can't upgrade changing the PolicyPool!"
100
+ );
101
+ }
92
102
 
93
- function pause() public onlyPoolRole(GUARDIAN_ROLE) {
103
+ function pause() public onlyGlobalOrComponentRole(GUARDIAN_ROLE) {
94
104
  _pause();
95
105
  }
96
106
 
97
- function unpause() public onlyPoolRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {
107
+ function unpause() public onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {
98
108
  _unpause();
99
109
  }
100
110
 
@@ -107,22 +117,7 @@ abstract contract PolicyPoolComponent is
107
117
  }
108
118
 
109
119
  function hasPoolRole(bytes32 role) internal view returns (bool) {
110
- return _policyPool.config().hasRole(role, msg.sender);
111
- }
112
-
113
- function _isTweakRay(
114
- uint256 oldValue,
115
- uint256 newValue,
116
- uint256 maxTweak
117
- ) internal pure returns (bool) {
118
- if (oldValue == newValue) return true;
119
- if (oldValue == 0) return maxTweak >= WadRayMath.RAY;
120
- if (newValue == 0) return false;
121
- if (oldValue < newValue) {
122
- return (newValue.rayDiv(oldValue) - WadRayMath.RAY) <= maxTweak;
123
- } else {
124
- return (WadRayMath.RAY - newValue.rayDiv(oldValue)) <= maxTweak;
125
- }
120
+ return _policyPool.access().hasComponentRole(address(this), role, _msgSender(), true);
126
121
  }
127
122
 
128
123
  function _isTweakWad(
@@ -144,7 +139,7 @@ abstract contract PolicyPoolComponent is
144
139
  function _validateParameters() internal view virtual {} // Must be reimplemented with specific validations
145
140
 
146
141
  function _parameterChanged(
147
- IPolicyPoolConfig.GovernanceActions action,
142
+ IAccessManager.GovernanceActions action,
148
143
  uint256 value,
149
144
  bool tweak
150
145
  ) internal {
@@ -153,7 +148,7 @@ abstract contract PolicyPoolComponent is
153
148
  emit GovernanceAction(action, value);
154
149
  }
155
150
 
156
- function _componentChanged(IPolicyPoolConfig.GovernanceActions action, address value) internal {
151
+ function _componentChanged(IAccessManager.GovernanceActions action, address value) internal {
157
152
  _validateParameters();
158
153
  emit ComponentChanged(action, value);
159
154
  }
@@ -162,7 +157,7 @@ abstract contract PolicyPoolComponent is
162
157
  return (_lastTweakTimestamp, _lastTweakActions);
163
158
  }
164
159
 
165
- function _registerTweak(IPolicyPoolConfig.GovernanceActions action) internal {
160
+ function _registerTweak(IAccessManager.GovernanceActions action) internal {
166
161
  uint56 actionBitMap = uint56(1 << (uint8(action) - 1));
167
162
  if ((uint40(block.timestamp) - _lastTweakTimestamp) > TWEAK_EXPIRATION) {
168
163
  _lastTweakTimestamp = uint40(block.timestamp);
@@ -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 enought to
26
+ * cover the losses.
27
+ *
18
28
  * @custom:security-contact security@ensuro.co
19
29
  * @author Ensuro
20
30
  */
@@ -22,26 +32,60 @@ 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");
27
38
 
39
+ /**
40
+ * @dev The Junior eToken is the first {EToken} to which the PremiumsAccount will go for credit when it runs out of
41
+ * money. Optional (address(0)).
42
+ */
28
43
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
29
44
  IEToken internal immutable _juniorEtk;
45
+
46
+ /**
47
+ * @dev The Senior eToken is the second {EToken} to which the PremiumsAccount will go for credit, after trying before
48
+ * with the junior eToken, when it runs out of money. Optional (address(0)).
49
+ */
30
50
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
31
51
  IEToken internal immutable _seniorEtk;
32
52
 
53
+ /**
54
+ * @dev The active pure premiums field keeps track of the pure premiums collected by the active policies of risk
55
+ * modules linked with this PremiumsAccount.
56
+ */
33
57
  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
58
 
37
- /*
38
- * Premiums can come in (for free, without liability) with receiveGrant.
59
+ /**
60
+ * @dev The surplus field keeps track of the surplus or deficit (when negative) of the actual payouts made by the
61
+ * PremiumsAccount versus the collected pure premiums. On the negative side, it has a limit defined by `_maxDeficit()`,
62
+ * after that limit, internal loans are taken from the eTokens.
63
+ */
64
+ int256 internal _surplus;
65
+
66
+ struct PackedParams {
67
+ uint16 deficitRatio;
68
+ IAssetManager assetManager;
69
+ }
70
+
71
+ PackedParams internal _params;
72
+
73
+ /**
74
+ * Premiums can come in (for "free", without liability) with receiveGrant.
39
75
  * And can come out (withdrawed to treasury) with withdrawWonPremiums
76
+ *
77
+ * @param moneyIn Indicates if money came in or out (false).
78
+ * @param value The amount of money received or given
40
79
  */
41
80
  event WonPremiumsInOut(bool moneyIn, uint256 value);
42
81
 
82
+ /**
83
+ * @dev Constructor of the contract, sets the immutable fields.
84
+ *
85
+ * @param juniorEtk_ Address of the Junior EToken (first loss lender). `address(0)` if not present.
86
+ * @param seniorEtk_ Address of the Senior EToken (2nd loss lender). `address(0)` if not present.
87
+ */
43
88
  /// @custom:oz-upgrades-unsafe-allow constructor
44
- // solhint-disable-next-line no-empty-blocks
45
89
  constructor(
46
90
  IPolicyPool policyPool_,
47
91
  IEToken juniorEtk_,
@@ -52,14 +96,14 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
52
96
  }
53
97
 
54
98
  /**
55
- * @dev Public initialize Initializes the PremiumsAccount
99
+ * @dev Initializes the PremiumsAccount
56
100
  */
57
101
  function initialize() public initializer {
58
102
  __PremiumsAccount_init();
59
103
  }
60
104
 
61
105
  /**
62
- * @dev Initializes the PremiumsAccount
106
+ * @dev Initializes the PremiumsAccount (to be called by subclasses)
63
107
  */
64
108
  // solhint-disable-next-line func-name-mixedcase
65
109
  function __PremiumsAccount_init() internal initializer {
@@ -67,37 +111,93 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
67
111
  __PremiumsAccount_init_unchained();
68
112
  }
69
113
 
114
+ /**
115
+ * @dev In the initialization, besides settings the parameters, we approve the spending of funds by the eTokens
116
+ * so we don't need to do it on every repayment operation
117
+ */
70
118
  // solhint-disable-next-line func-name-mixedcase
71
119
  function __PremiumsAccount_init_unchained() internal initializer {
72
120
  /*
73
121
  _activePurePremiums = 0;
74
- _borrowedActivePP = 0;
75
- _wonPurePremiums = 0;
76
122
  */
77
123
  if (address(_juniorEtk) != address(0))
78
124
  currency().approve(address(_juniorEtk), type(uint256).max);
79
125
  if (address(_seniorEtk) != address(0))
80
126
  currency().approve(address(_seniorEtk), type(uint256).max);
127
+
128
+ _params = PackedParams({deficitRatio: 1e4, assetManager: IAssetManager(address(0))});
81
129
  _validateParameters();
82
130
  }
83
131
 
84
- // solhint-disable-next-line no-empty-blocks
85
- function _validateParameters() internal view override {}
132
+ function assetManager() public view override returns (IAssetManager) {
133
+ return _params.assetManager;
134
+ }
135
+
136
+ function _setAssetManager(IAssetManager newAM) internal override {
137
+ _params.assetManager = newAM;
138
+ }
139
+
140
+ /**
141
+ * @dev This is called by the {Reserve} base class to record the earnings generated by the asset management.
142
+ *
143
+ * @param earningsOrLosses Indicates the amount earned since last time earnings where recorded.
144
+ * - If positive, repays the lons and accumulates the rest in the surplus.
145
+ * - If negative (losses) substracts it from surplus. It never can exceed _maxDeficit and doesn't takes
146
+ * loans to cover asset losses.
147
+ */
148
+ function _assetEarnings(int256 earningsOrLosses) internal override {
149
+ if (earningsOrLosses > 0) {
150
+ uint256 earnings = uint256(earningsOrLosses);
151
+ if (address(_seniorEtk) != address(0)) earnings = _repayLoan(earnings, _seniorEtk);
152
+ if (address(_juniorEtk) != address(0)) earnings = _repayLoan(earnings, _juniorEtk);
153
+ _storePurePremiumWon(earnings);
154
+ } else {
155
+ require(_payFromPremiums(uint256(-earningsOrLosses)) == 0, "Losses can't exceed maxDeficit");
156
+ }
157
+ }
86
158
 
87
- function purePremiums() public view returns (uint256) {
88
- return _activePurePremiums + _wonPurePremiums - _borrowedActivePP;
159
+ function _validateParameters() internal view override {
160
+ require(
161
+ _params.deficitRatio <= 1e4 && _params.deficitRatio >= 0,
162
+ "Validation: deficitRatio must be <= 1"
163
+ );
89
164
  }
90
165
 
166
+ function purePremiums() external view override returns (uint256) {
167
+ return uint256(int256(_activePurePremiums) + _surplus);
168
+ }
169
+
170
+ /**
171
+ * @dev Returns the total amount of pure premiums that were collected by the active policies of the risk modules
172
+ * linked to this PremiumsAccount.
173
+ */
91
174
  function activePurePremiums() external view returns (uint256) {
92
175
  return _activePurePremiums;
93
176
  }
94
177
 
178
+ /**
179
+ * @dev Returns the surplus between pure premiums collected and payouts of finalized policies. Returns 0 if no surplus
180
+ * or deficit.
181
+ */
95
182
  function wonPurePremiums() external view returns (uint256) {
96
- return _wonPurePremiums;
183
+ return _surplus >= 0 ? uint256(_surplus) : 0;
97
184
  }
98
185
 
186
+ /**
187
+ * @dev Returns the amount of active pure premiums that was used to cover payouts of finalized policies (in excess of
188
+ * collected pure premiums). This is limited by `_maxDeficit()`
189
+ */
99
190
  function borrowedActivePP() external view returns (uint256) {
100
- return _borrowedActivePP;
191
+ return _surplus >= 0 ? 0 : uint256(-_surplus);
192
+ }
193
+
194
+ /**
195
+ * @dev Returns the surplus between pure premiums collected and payouts of finalized policies. Losses where more than
196
+ * premiums collected, returns a negative number that indicates the amount of the active pure premiums that was used
197
+ * to cover finalized premiums.
198
+ */
199
+ function surplus() external view returns (int256) {
200
+ return _surplus;
101
201
  }
102
202
 
103
203
  function seniorEtk() external view override returns (IEToken) {
@@ -108,40 +208,116 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
108
208
  return _juniorEtk;
109
209
  }
110
210
 
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
- }
211
+ /**
212
+ * @dev Returns the maximum deficit that's supported by the PremiumsAccount. If more money is needed, it must take
213
+ * loans from the eTokens. The value is calculated as a fraction of the active pure premiums. The fraction is
214
+ * regulated by the `deficitRatio` parameter that indicates the percentage of the active pure premiums that can be
215
+ * used to cover payouts of finalized policies. In many cases is fine to use the active pure premiums to cover the
216
+ * losses because in most cases the policies with payout are triggered long time before the policies without payout.
217
+ * But this also can be dangerous because it can be postponing the losses that should impact on liquidity providers.
218
+ *
219
+ * @param ratio The ratio used in the calculation of the deficit. It's the deficitRatio parameter (whether the current
220
+ * one or the new one when it's being modified).
221
+ */
222
+ function _maxDeficit(uint256 ratio) internal view returns (int256) {
223
+ return -int256(_activePurePremiums.wadMul(ratio));
224
+ }
225
+
226
+ /**
227
+ * @dev Returns the percentage of the active pure premiums that can be used to cover losses of finalized policies.
228
+ */
229
+ function deficitRatio() public view returns (uint256) {
230
+ return uint256(_params.deficitRatio) * 1e14; // 4 -> 18 decimals
231
+ }
232
+
233
+ /**
234
+ * @dev Changes the `deficitRatio` parameter.
235
+ *
236
+ * Requirements:
237
+ * - onlyGlobalOrComponentRole(LEVEL2_ROLE)
238
+ *
239
+ * Events:
240
+ * - Emits GovernanceAction with action = setDeficitRatio or setDeficitRatioWithAdjustment if an adjustment was made.
241
+ *
242
+ * @param adjustment If true and the new ratio leaves `_surplus < -_maxDeficit()`, if adjusts the _surplus to the new
243
+ * `_maxDeficit()` and borrows the difference from the eTokens.
244
+ * If false and the new ratio leaves `_surplus < -_maxDeficit()`, the operation is reverted.
245
+ */
246
+ function setDeficitRatio(uint256 newRatio, bool adjustment)
247
+ external
248
+ onlyComponentRole(LEVEL2_ROLE)
249
+ {
250
+ require(newRatio <= 1e18 && newRatio >= 0, "Validation: deficitRatio must be <= 1");
251
+ int256 maxDeficit = _maxDeficit(newRatio);
252
+ require(adjustment || _surplus >= maxDeficit, "Validation: surplus must be >= maxDeficit");
253
+ IAccessManager.GovernanceActions action = IAccessManager.GovernanceActions.setDeficitRatio;
254
+ if (_surplus < maxDeficit) {
255
+ // Do the adjustment
256
+ uint256 borrow = uint256(-_surplus + maxDeficit);
257
+ _surplus = maxDeficit;
258
+ _borrowFromEtk(borrow, address(this), address(_juniorEtk) != address(0));
259
+ action = IAccessManager.GovernanceActions.setDeficitRatioWithAdjustment;
128
260
  }
129
- return toPay;
261
+ _params.deficitRatio = (newRatio / 1e14).toUint16();
262
+ _parameterChanged(action, newRatio, false);
130
263
  }
131
264
 
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;
265
+ /**
266
+ * @dev Internal function called when money in the PremiumsAccount is not enought and we need to borrow from the
267
+ * eTokens.
268
+ *
269
+ * @param borrow The amount to borrow.
270
+ * @param receiver The address that will receive the money of the loan. Usually is the policy holder if this is called
271
+ * in the context of a policy payout.
272
+ * @param jrEtk If true it indicates that the loan is asked first from the junior eToken.
273
+ */
274
+ function _borrowFromEtk(
275
+ uint256 borrow,
276
+ address receiver,
277
+ bool jrEtk
278
+ ) internal {
279
+ uint256 left;
280
+ if (jrEtk) {
281
+ // Consume Junior Pool until exhausted
282
+ left = _juniorEtk.internalLoan(borrow, receiver, false);
138
283
  } else {
139
- _wonPurePremiums += (purePremiumWon - _borrowedActivePP);
140
- _borrowedActivePP = 0;
284
+ left = borrow;
285
+ }
286
+ if (left > NEGLIGIBLE_AMOUNT) {
287
+ // Consume Senior Pool only up to SCR
288
+ left = _seniorEtk.internalLoan(left, receiver, true);
289
+ require(left <= NEGLIGIBLE_AMOUNT, "Don't know where to take the rest of the money");
290
+ }
291
+ }
292
+
293
+ /**
294
+ * @dev Updates the `_surplus` field with the payment made. Since the _surplus can never exceed `_maxDeficit()`,
295
+ * returns the remaining amount in case something can't be paid from the PremiumsAccount.
296
+ *
297
+ * @param toPay The amount to pay.
298
+ * @return The amount that couldn't be paid from the premiums account.
299
+ */
300
+ function _payFromPremiums(uint256 toPay) internal returns (uint256) {
301
+ int256 newSurplus = _surplus - int256(toPay);
302
+ int256 maxDeficit = _maxDeficit(deficitRatio());
303
+ if (newSurplus >= maxDeficit) {
304
+ _surplus = newSurplus;
305
+ return 0;
141
306
  }
307
+ _surplus = maxDeficit;
308
+ return uint256(-newSurplus + maxDeficit);
142
309
  }
143
310
 
144
- // TODO: restore repayETokenLoan?
311
+ /**
312
+ * @dev Stores an earned pure premium. Adds to the surplus, increasing the surplus if it was positive or reducing the
313
+ * deficit if it was negative.
314
+ *
315
+ * @param purePremiumWon The amount earned
316
+ */
317
+ function _storePurePremiumWon(uint256 purePremiumWon) internal {
318
+ if (purePremiumWon == 0) return;
319
+ _surplus += int256(purePremiumWon);
320
+ }
145
321
 
146
322
  /**
147
323
  *
@@ -149,41 +325,61 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
149
325
  *
150
326
  * Can be used for example if the PolicyPool subscribes an excess loss policy with other company.
151
327
  *
328
+ * Requirements:
329
+ * - The sender needs to approve the spending of `currency()` by this contract.
330
+ *
331
+ * Events:
332
+ * - Emits {WonPremiumsInOut} with moneyIn = true
333
+ *
334
+ * @param amount The amount to be transferred.
152
335
  */
153
336
  function receiveGrant(uint256 amount) external {
154
- currency().safeTransferFrom(msg.sender, address(this), amount);
337
+ currency().safeTransferFrom(_msgSender(), address(this), amount);
155
338
  _storePurePremiumWon(amount);
156
339
  emit WonPremiumsInOut(true, amount);
157
340
  }
158
341
 
159
342
  /**
160
343
  *
161
- * Withdraws excess premiums to PolicyPool's treasury.
344
+ * Withdraws excess premiums (surplus) to the destination.
345
+ *
162
346
  * This might be needed in some cases for example if we are deprecating the protocol or the excess premiums
163
347
  * are needed to compensate something. Shouldn't be used. Can be disabled revoking role WITHDRAW_WON_PREMIUMS_ROLE
164
348
  *
165
- * returns The amount withdrawed
166
- *
167
349
  * Requirements:
350
+ * - onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
351
+ * - _surplus > 0
352
+ *
353
+ * Events:
354
+ * - Emits {WonPremiumsInOut} with moneyIn = false
168
355
  *
169
- * - onlyPoolRole(WITHDRAW_WON_PREMIUMS_ROLE)
170
- * - _wonPurePremiums > 0
356
+ * @param amount The amount to withdraw
357
+ * @param destination The address that will receive the transferred funds.
358
+ * @return Returns the actual amount withdrawn.
171
359
  */
172
- function withdrawWonPremiums(uint256 amount)
360
+ function withdrawWonPremiums(uint256 amount, address destination)
173
361
  external
174
- onlyPoolRole(WITHDRAW_WON_PREMIUMS_ROLE)
362
+ onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
175
363
  returns (uint256)
176
364
  {
177
- if (amount > _wonPurePremiums) amount = _wonPurePremiums;
365
+ if (_surplus <= 0) {
366
+ amount = 0;
367
+ } else {
368
+ amount = Math.min(amount, uint256(_surplus));
369
+ }
178
370
  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
371
+ _surplus -= int256(amount);
372
+ _transferTo(destination, amount);
182
373
  emit WonPremiumsInOut(false, amount);
183
374
  return amount;
184
375
  }
185
376
 
186
- function policyCreated(Policy.PolicyData memory policy) external override onlyPolicyPool {
377
+ function policyCreated(Policy.PolicyData memory policy)
378
+ external
379
+ override
380
+ onlyPolicyPool
381
+ whenNotPaused
382
+ {
187
383
  _activePurePremiums += policy.purePremium;
188
384
  if (policy.jrScr > 0) _juniorEtk.lockScr(policy.jrScr, policy.jrInterestRate());
189
385
  if (policy.srScr > 0) _seniorEtk.lockScr(policy.srScr, policy.srInterestRate());
@@ -193,7 +389,7 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
193
389
  address policyHolder,
194
390
  Policy.PolicyData memory policy,
195
391
  uint256 payout
196
- ) external override onlyPolicyPool {
392
+ ) external override onlyPolicyPool whenNotPaused {
197
393
  _activePurePremiums -= policy.purePremium;
198
394
  if (policy.purePremium >= payout) {
199
395
  uint256 purePremiumWon = policy.purePremium - payout;
@@ -205,26 +401,20 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
205
401
  _unlockScr(policy);
206
402
  _transferTo(policyHolder, payout);
207
403
  } else {
208
- uint256 borrowFromScr = _payFromPool(payout - policy.purePremium);
404
+ uint256 borrowFromScr = _payFromPremiums(payout - policy.purePremium);
209
405
  _unlockScr(policy);
210
406
  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
- }
407
+ _borrowFromEtk(borrowFromScr, policyHolder, policy.jrScr > 0);
223
408
  }
224
409
  _transferTo(policyHolder, payout - borrowFromScr);
225
410
  }
226
411
  }
227
412
 
413
+ /**
414
+ * @dev Internal function that calls the eTokens to lock the solvency capital when the policy is created.
415
+ *
416
+ * @param policy The policy created
417
+ */
228
418
  function _unlockScr(Policy.PolicyData memory policy) internal {
229
419
  if (policy.jrScr > 0) {
230
420
  _juniorEtk.unlockScr(
@@ -242,26 +432,42 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
242
432
  }
243
433
  }
244
434
 
435
+ /**
436
+ * @dev Internal function that repays a loan taken (if any outstanding) from the an eToken
437
+ *
438
+ * @param purePremiumWon The amount earned and available for loan repayment.
439
+ * @param etk The eToken with the potential debt
440
+ * @return The excess amount of the purePremiumWon that wasn't used for the loan repayment.
441
+ */
245
442
  function _repayLoan(uint256 purePremiumWon, IEToken etk) internal returns (uint256) {
246
443
  if (purePremiumWon < NEGLIGIBLE_AMOUNT) return purePremiumWon;
247
444
  uint256 borrowedFromEtk = etk.getLoan(address(this));
248
445
  if (borrowedFromEtk == 0) return purePremiumWon;
249
- uint256 repayAmount = borrowedFromEtk > purePremiumWon ? purePremiumWon : borrowedFromEtk;
250
- // TODO: make sure the balance is available or deinvest
446
+ uint256 repayAmount = Math.min(purePremiumWon, borrowedFromEtk);
447
+
448
+ // If not enought liquidity, it deinvests from the asset manager
449
+ if (currency().balanceOf(address(this)) < repayAmount) {
450
+ _refillWallet(repayAmount);
451
+ }
251
452
  etk.repayLoan(repayAmount, address(this));
252
453
  return purePremiumWon - repayAmount;
253
454
  }
254
455
 
255
- function policyExpired(Policy.PolicyData memory policy) external override onlyPolicyPool {
256
- uint256 aux;
456
+ function policyExpired(Policy.PolicyData memory policy)
457
+ external
458
+ override
459
+ onlyPolicyPool
460
+ whenNotPaused
461
+ {
257
462
  uint256 purePremiumWon = policy.purePremium;
258
463
  _activePurePremiums -= purePremiumWon;
259
464
 
260
465
  // 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;
466
+ int256 maxDeficit = _maxDeficit(deficitRatio());
467
+ if (_surplus < maxDeficit) {
468
+ // Covers the excess of deficit first
469
+ purePremiumWon -= uint256(-_surplus + maxDeficit);
470
+ _surplus = maxDeficit;
265
471
  }
266
472
 
267
473
  if (address(_seniorEtk) != address(0)) purePremiumWon = _repayLoan(purePremiumWon, _seniorEtk);