@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
@@ -1,8 +1,9 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  pragma solidity ^0.8.0;
3
3
 
4
- import {WadRayMath} from "./WadRayMath.sol";
4
+ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.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 {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
7
8
  import {IAssetManager} from "./interfaces/IAssetManager.sol";
8
9
  import {IAccessManager} from "./interfaces/IAccessManager.sol";
@@ -17,9 +18,11 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
17
18
  * cash at liquidityMiddle.
18
19
  * @custom:security-contact security@ensuro.co
19
20
  * @author Ensuro
21
+ *
22
+ * @notice This contracts uses Diamond Storage and should not define state variables outside of that. See the diamondStorage method for more details.
20
23
  */
21
24
  abstract contract LiquidityThresholdAssetManager is IAssetManager {
22
- using WadRayMath for uint256;
25
+ using SafeCast for uint256;
23
26
 
24
27
  IERC20Metadata internal immutable _asset;
25
28
  event GovernanceAction(IAccessManager.GovernanceActions indexed action, uint256 value);
@@ -37,6 +40,10 @@ abstract contract LiquidityThresholdAssetManager is IAssetManager {
37
40
  }
38
41
 
39
42
  constructor(IERC20Metadata asset_) {
43
+ require(
44
+ address(asset_) != address(0),
45
+ "LiquidityThresholdAssetManager: asset cannot be zero address"
46
+ );
40
47
  _asset = asset_;
41
48
  }
42
49
 
@@ -70,7 +77,7 @@ abstract contract LiquidityThresholdAssetManager is IAssetManager {
70
77
  uint256 investmentValue = getInvestmentValue();
71
78
  DiamondStorage storage ds = diamondStorage();
72
79
  int256 earnings = int256(investmentValue) - int256(uint256(ds.lastInvestmentValue));
73
- ds.lastInvestmentValue = uint128(investmentValue);
80
+ ds.lastInvestmentValue = investmentValue.toUint128();
74
81
  emit EarningsRecorded(earnings);
75
82
  return earnings;
76
83
  }
@@ -96,31 +103,32 @@ abstract contract LiquidityThresholdAssetManager is IAssetManager {
96
103
  }
97
104
 
98
105
  /**
99
- * @dev This is called from PolicyPool when doesn't have enought money for payment.
100
- * After the call, there should be enought money in PolicyPool.currency().balanceOf(this) to
106
+ * @dev This is called from PolicyPool when doesn't have enough money for payment.
107
+ * After the call, there should be enough money in PolicyPool.currency().balanceOf(this) to
101
108
  * do the payment
102
109
  * @param paymentAmount The amount of the payment
103
110
  */
104
- function refillWallet(uint256 paymentAmount) external override {
111
+ function refillWallet(uint256 paymentAmount) external override returns (uint256 deinvest) {
105
112
  uint256 cash = _asset.balanceOf(address(this));
106
113
  require(cash < paymentAmount, "No need to refill the wallet for this payment");
107
114
  uint256 investmentValue = getInvestmentValue();
108
115
  // try to leave the pool balance at liquidity_middle after the payment
109
- uint256 deinvest = paymentAmount + liquidityMiddle() - cash;
116
+ deinvest = paymentAmount + liquidityMiddle() - cash;
110
117
  if (deinvest > investmentValue) deinvest = investmentValue;
111
118
  _deinvest(deinvest);
119
+ return deinvest;
112
120
  }
113
121
 
114
122
  function _invest(uint256 amount) internal virtual {
115
123
  DiamondStorage storage ds = diamondStorage();
116
- ds.lastInvestmentValue += uint128(amount);
124
+ ds.lastInvestmentValue += amount.toUint128();
117
125
  emit MoneyInvested(amount);
118
126
  // must be reimplemented do the actual cash movement
119
127
  }
120
128
 
121
129
  function _deinvest(uint256 amount) internal virtual {
122
130
  DiamondStorage storage ds = diamondStorage();
123
- ds.lastInvestmentValue -= uint128(Math.min(uint256(ds.lastInvestmentValue), amount));
131
+ ds.lastInvestmentValue -= Math.min(uint256(ds.lastInvestmentValue), amount).toUint128();
124
132
  emit MoneyDeinvested(amount);
125
133
  // must be reimplemented do the actual cash movement
126
134
  }
@@ -144,16 +152,24 @@ abstract contract LiquidityThresholdAssetManager is IAssetManager {
144
152
  ) external validateParamsAfterChange {
145
153
  DiamondStorage storage ds = diamondStorage();
146
154
  if (min != type(uint256).max) {
147
- ds.liquidityMin = uint32(min / 10**_asset.decimals());
155
+ ds.liquidityMin = (min / 10**_asset.decimals()).toUint32();
148
156
  emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMin, min);
149
157
  }
150
158
  if (middle != type(uint256).max) {
151
- ds.liquidityMiddle = uint32(middle / 10**_asset.decimals());
159
+ ds.liquidityMiddle = (middle / 10**_asset.decimals()).toUint32();
152
160
  emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMiddle, middle);
153
161
  }
154
162
  if (max != type(uint256).max) {
155
- ds.liquidityMax = uint32(max / 10**_asset.decimals());
163
+ ds.liquidityMax = (max / 10**_asset.decimals()).toUint32();
156
164
  emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMax, max);
157
165
  }
158
166
  }
167
+
168
+ /**
169
+ * @dev See {IERC165-supportsInterface}.
170
+ */
171
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
172
+ return
173
+ interfaceId == type(IERC165).interfaceId || interfaceId == type(IAssetManager).interfaceId;
174
+ }
159
175
  }
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  pragma solidity ^0.8.0;
3
- import {WadRayMath} from "./WadRayMath.sol";
3
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
4
4
  import {IRiskModule} from "./interfaces/IRiskModule.sol";
5
5
 
6
6
  /**
@@ -102,7 +102,9 @@ library Policy {
102
102
  return (policy.srCoc * (block.timestamp - policy.start)) / (policy.expiration - policy.start);
103
103
  }
104
104
 
105
- function hash(PolicyData memory policy) internal pure returns (bytes32) {
106
- return keccak256(abi.encode(policy));
105
+ function hash(PolicyData memory policy) internal pure returns (bytes32 retHash) {
106
+ retHash = keccak256(abi.encode(policy));
107
+ require(retHash != bytes32(0), "Policy: hash cannot be 0");
108
+ return retHash;
107
109
  }
108
110
  }
@@ -8,7 +8,6 @@ import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC72
8
8
  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
9
9
  import {IAccessManager} from "./interfaces/IAccessManager.sol";
10
10
  import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
11
- import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
12
11
  import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
13
12
  import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
14
13
  import {IRiskModule} from "./interfaces/IRiskModule.sol";
@@ -16,21 +15,22 @@ import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
16
15
  import {IEToken} from "./interfaces/IEToken.sol";
17
16
  import {IPolicyHolder} from "./interfaces/IPolicyHolder.sol";
18
17
  import {Policy} from "./Policy.sol";
19
- import {WadRayMath} from "./WadRayMath.sol";
18
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
20
19
 
21
20
  /**
22
21
  * @title Ensuro PolicyPool contract
23
22
  * @dev This is the main contract of the protocol, it stores the eTokens (liquidity pools) and has the operations
24
- * to interact with them. This is also the contract that receives and sends the underlying asset.
23
+ * to interact with them. This is also the contract that receives and sends the underlying asset (currency).
25
24
  * Also this contract keeps track of accumulated premiums in different stages:
26
25
  * - activePurePremiums
27
26
  * - wonPurePremiums (surplus)
28
27
  * - borrowedActivePP (deficit borrowed from activePurePremiums)
28
+ * This contract also implements the ERC721 standard, because it mints and NFT for each policy created. The
29
+ * property of the NFT represents the one that will receive the payout.
29
30
  * @custom:security-contact security@ensuro.co
30
31
  * @author Ensuro
31
32
  */
32
33
  contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721Upgradeable {
33
- using EnumerableSet for EnumerableSet.AddressSet;
34
34
  using WadRayMath for uint256;
35
35
  using Policy for Policy.PolicyData;
36
36
  using SafeERC20 for IERC20Metadata;
@@ -40,13 +40,26 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
40
40
  bytes32 public constant LEVEL2_ROLE = keccak256("LEVEL2_ROLE");
41
41
  bytes32 public constant LEVEL3_ROLE = keccak256("LEVEL3_ROLE");
42
42
 
43
+ /**
44
+ * @dev {AccessManager} that handles the access permissions for the PolicyPool and its components.
45
+ */
43
46
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
44
47
  IAccessManager internal immutable _access;
48
+
49
+ /**
50
+ * @dev {ERC20} token used in PolicyPool as currency. Usually it will be a stablecoin such as USDC.
51
+ */
45
52
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
46
53
  IERC20Metadata internal immutable _currency;
47
54
 
55
+ /**
56
+ * @dev Address of Ensuro's treasury that receives the protocol fees.
57
+ */
48
58
  address internal _treasury; // address of Ensuro treasury
49
59
 
60
+ /**
61
+ * @dev This enum tracks the different status that a component ({PremiumsAccount}, {EToken} or {RiskModule} can have.
62
+ */
50
63
  enum ComponentStatus {
51
64
  /**
52
65
  * @dev inactive status = 0 means the component doesn't exists - All operations rejected
@@ -73,6 +86,10 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
73
86
  suspended
74
87
  }
75
88
 
89
+ /**
90
+ * @dev Enum of the different kind of top level components that can be plugged into the pool. Each one corresponds
91
+ * with the {EToken}, {RiskModule} and {PremiumsAccount} respectively.
92
+ */
76
93
  enum ComponentKind {
77
94
  unknown,
78
95
  eToken,
@@ -80,6 +97,10 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
80
97
  premiumsAccount
81
98
  }
82
99
 
100
+ /**
101
+ * @dev Struct to keep the state and type of the components installed. The `kind` never changes. The `status`
102
+ * initially is `active` and can be changes with {PolicyPool-changeComponentStatus} and {PolicyPool-removeComponent}.
103
+ */
83
104
  struct Component {
84
105
  ComponentStatus status;
85
106
  ComponentKind kind;
@@ -87,7 +108,6 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
87
108
 
88
109
  /**
89
110
  * @dev Mapping of installed components (see {EToken}, {RiskModule}, {PremiumsAccount}) in the PolicyPool.
90
- * For each one it keep an state {ComponentStatus}.
91
111
  */
92
112
  mapping(IPolicyPoolComponent => Component) private _components;
93
113
 
@@ -100,17 +120,27 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
100
120
  /**
101
121
  * @dev Event emitted every time a new policy is added to the pool. Contains all the data about the policy that is
102
122
  * later required for doing operations with the policy like resolution or expiration.
123
+ *
124
+ * @param riskModule The risk module that created the policy
125
+ * @param policy The {Policy-PolicyData} struct with all the immutable fields of the policy.
103
126
  */
104
127
  event NewPolicy(IRiskModule indexed riskModule, Policy.PolicyData policy);
105
128
 
106
129
  /**
107
130
  * @dev Event emitted every time a policy is removed from the pool. If the policy expired, the `payout` is 0,
108
131
  * otherwise is the amount transferred to the policyholder.
132
+ *
133
+ * @param riskModule The risk module where that created the policy initially.
134
+ * @param policyId The unique id of the policy
135
+ * @param payout The payout that has been paid to the policy holder. 0 when the policy expired.
109
136
  */
110
137
  event PolicyResolved(IRiskModule indexed riskModule, uint256 indexed policyId, uint256 payout);
111
138
 
112
139
  /**
113
140
  * @dev Event emitted when the treasury changes
141
+ *
142
+ * @param action The type of governance action (just setTreasury in this contract for now)
143
+ * @param value The address of the new treasury
114
144
  */
115
145
  event ComponentChanged(IAccessManager.GovernanceActions indexed action, address value);
116
146
 
@@ -127,45 +157,98 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
127
157
  ComponentStatus newStatus
128
158
  );
129
159
 
160
+ /**
161
+ * @dev Modifier that checks the caller has a given role
162
+ */
130
163
  modifier onlyRole(bytes32 role) {
131
- _access.checkRole(role, msg.sender);
164
+ _access.checkRole(role, _msgSender());
132
165
  _;
133
166
  }
134
167
 
168
+ /**
169
+ * @dev Modifier that checks the caller has any of the given roles
170
+ */
135
171
  modifier onlyRole2(bytes32 role1, bytes32 role2) {
136
- _access.checkRole2(role1, role2, msg.sender);
172
+ _access.checkRole2(role1, role2, _msgSender());
137
173
  _;
138
174
  }
139
175
 
176
+ /**
177
+ * @dev Instantiates a Policy Pool. Sets immutable fields.
178
+ *
179
+ * @param access_ The address of the {AccessManager} that manages the access permissions for the pool governance
180
+ * operations.
181
+ * @param currency_ The {ERC20} token that's used as a currency in the protocol. Usually a stablecoin such as USDC.
182
+ */
140
183
  /// @custom:oz-upgrades-unsafe-allow constructor
141
184
  constructor(IAccessManager access_, IERC20Metadata currency_) {
185
+ require(address(access_) != address(0), "PolicyPool: access cannot be zero address");
186
+ require(address(currency_) != address(0), "PolicyPool: currency cannot be zero address");
187
+ _disableInitializers();
142
188
  _access = access_;
143
189
  _currency = currency_;
144
190
  }
145
191
 
192
+ /**
193
+ * @dev Initializes a Policy Pool
194
+ *
195
+ * @param name_ The name of the ERC721 token.
196
+ * @param symbol_ The symbol of the ERC721 token.
197
+ * @param treasury_ The address of the treasury that will receive the protocol fees.
198
+ */
146
199
  function initialize(
147
200
  string memory name_,
148
201
  string memory symbol_,
149
202
  address treasury_
150
203
  ) public initializer {
204
+ require(bytes(name_).length > 0, "PolicyPool: name cannot be empty");
205
+ require(bytes(symbol_).length > 0, "PolicyPool: symbol cannot be empty");
151
206
  __UUPSUpgradeable_init();
152
207
  __ERC721_init(name_, symbol_);
153
208
  __Pausable_init();
154
209
  __PolicyPool_init_unchained(treasury_);
155
210
  }
156
211
 
212
+ /**
213
+ * @dev See {IERC165-supportsInterface}.
214
+ */
215
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
216
+ return super.supportsInterface(interfaceId) || interfaceId == type(IPolicyPool).interfaceId;
217
+ }
218
+
157
219
  // solhint-disable-next-line func-name-mixedcase
158
- function __PolicyPool_init_unchained(address treasury_) internal initializer {
159
- _treasury = treasury_;
220
+ function __PolicyPool_init_unchained(address treasury_) internal onlyInitializing {
221
+ _setTreasury(treasury_);
160
222
  }
161
223
 
162
- // solhint-disable-next-line no-empty-blocks
163
- function _authorizeUpgrade(address) internal override onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {}
224
+ function _authorizeUpgrade(address newImpl)
225
+ internal
226
+ view
227
+ override
228
+ onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
229
+ {
230
+ IPolicyPool newPool = IPolicyPool(newImpl);
231
+ require(newPool.access() == _access, "Can't upgrade changing the access manager");
232
+ require(newPool.currency() == _currency, "Can't upgrade changing the currency");
233
+ }
164
234
 
235
+ /**
236
+ * @dev Pauses the contract. When the contract is paused, several operations are rejected: deposits, withdrawals, new
237
+ * policies, policy resolution and expiration, nft transfers.
238
+ *
239
+ * Requirements:
240
+ * - Must be executed by a user with the {GUARDIAN_ROLE}.
241
+ */
165
242
  function pause() public onlyRole(GUARDIAN_ROLE) {
166
243
  _pause();
167
244
  }
168
245
 
246
+ /**
247
+ * @dev Unpauses the contract. All the operations disabled when the contract was paused are re-enabled.
248
+ *
249
+ * Requirements:
250
+ * - Must be called by a user with either the {GUARDIAN_ROLE} or a {LEVEL1_ROLE}.
251
+ */
169
252
  function unpause() public onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {
170
253
  _unpause();
171
254
  }
@@ -178,15 +261,47 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
178
261
  return _currency;
179
262
  }
180
263
 
181
- function setTreasury(address treasury_) external onlyRole(LEVEL1_ROLE) {
264
+ function _setTreasury(address treasury_) internal {
265
+ require(treasury_ != address(0), "PolicyPool: treasury cannot be the zero address");
182
266
  _treasury = treasury_;
183
267
  emit ComponentChanged(IAccessManager.GovernanceActions.setTreasury, _treasury);
184
268
  }
185
269
 
270
+ /**
271
+ * @dev Changes the address of the treasury, the one that receives the protocol fees.
272
+ *
273
+ * Requirements:
274
+ * - Must be called by a user with the {LEVEL1_ROLE}.
275
+ *
276
+ * Events:
277
+ * - Emits {ComponentChanged} with action = setTreasury and the address of the new treasury.
278
+ */
279
+ function setTreasury(address treasury_) external onlyRole(LEVEL1_ROLE) {
280
+ _setTreasury(treasury_);
281
+ }
282
+
283
+ /**
284
+ * @dev Returns the address of the treasury, the one that receives the protocol fees.
285
+ */
186
286
  function treasury() external view override returns (address) {
187
287
  return _treasury;
188
288
  }
189
289
 
290
+ /**
291
+ * @dev Adds a new component (either an {EToken}, {RiskModule} or {PremiumsAccount}) to the protocol. The component
292
+ * status will be `active`.
293
+ *
294
+ * Requirements:
295
+ * - Must be called by a user with the {LEVEL1_ROLE}
296
+ * - The component wasn't added before.
297
+ *
298
+ * Events:
299
+ * - Emits {ComponentStatusChanged} with status active.
300
+ *
301
+ * @param component The address of component contract. Must be an {EToken}, {RiskModule} or {PremiumsAccount} linked
302
+ * to this specific {PolicyPool} and matching the `kind` specified in the next paramter.
303
+ * @param kind The type of component to be added.
304
+ */
190
305
  function addComponent(IPolicyPoolComponent component, ComponentKind kind)
191
306
  external
192
307
  onlyRole(LEVEL1_ROLE)
@@ -195,6 +310,15 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
195
310
  require(comp.status == ComponentStatus.inactive, "Component already in the pool");
196
311
  require(component.policyPool() == this, "Component not linked to this pool");
197
312
 
313
+ require(
314
+ (kind == ComponentKind.eToken && component.supportsInterface(type(IEToken).interfaceId)) ||
315
+ (kind == ComponentKind.premiumsAccount &&
316
+ component.supportsInterface(type(IPremiumsAccount).interfaceId)) ||
317
+ (kind == ComponentKind.riskModule &&
318
+ component.supportsInterface(type(IRiskModule).interfaceId)),
319
+ "PolicyPool: Not the right kind"
320
+ );
321
+
198
322
  comp.status = ComponentStatus.active;
199
323
  comp.kind = kind;
200
324
  if (kind == ComponentKind.premiumsAccount) {
@@ -211,6 +335,19 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
211
335
  emit ComponentStatusChanged(component, kind, ComponentStatus.active);
212
336
  }
213
337
 
338
+ /**
339
+ * @dev Removes a component from the protocol. The component needs to be in `deprecated` status before doing this
340
+ * operation.
341
+ *
342
+ * Requirements:
343
+ * - Must be called by a user with the {LEVEL1_ROLE}
344
+ * - The component status is `deprecated`.
345
+ *
346
+ * Events:
347
+ * - Emits {ComponentStatusChanged} with status inactive.
348
+ *
349
+ * @param component The address of component contract. Must be a component added before.
350
+ */
214
351
  function removeComponent(IPolicyPoolComponent component) external onlyRole(LEVEL1_ROLE) {
215
352
  Component storage comp = _components[component];
216
353
  require(comp.status == ComponentStatus.deprecated, "Component not deprecated");
@@ -240,6 +377,19 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
240
377
  delete _components[component];
241
378
  }
242
379
 
380
+ /**
381
+ * @dev Changes the status of a component.
382
+ *
383
+ * Requirements:
384
+ * - Must be called by a user with the {LEVEL1_ROLE} if the new status is `active` or `deprecated`.
385
+ * - Must be called by a user with the {GUARDIAN_ROLE} if the new status is `suspended`.
386
+ *
387
+ * Events:
388
+ * - Emits {ComponentStatusChanged} with the new status.
389
+ *
390
+ * @param component The address of component contract. Must be a component added before.
391
+ * @param newStatus The new status, must be either `active`, `deprecated` or `suspended`.
392
+ */
243
393
  function changeComponentStatus(IPolicyPoolComponent component, ComponentStatus newStatus)
244
394
  external
245
395
  onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
@@ -247,15 +397,21 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
247
397
  Component storage comp = _components[component];
248
398
  require(comp.status != ComponentStatus.inactive, "Component not found");
249
399
  require(
250
- (newStatus == ComponentStatus.active && _access.hasRole(LEVEL1_ROLE, msg.sender)) ||
251
- (newStatus == ComponentStatus.suspended && _access.hasRole(GUARDIAN_ROLE, msg.sender)) ||
252
- (newStatus == ComponentStatus.deprecated && _access.hasRole(LEVEL1_ROLE, msg.sender)),
400
+ (newStatus == ComponentStatus.active && _access.hasRole(LEVEL1_ROLE, _msgSender())) ||
401
+ (newStatus == ComponentStatus.suspended && _access.hasRole(GUARDIAN_ROLE, _msgSender())) ||
402
+ (newStatus == ComponentStatus.deprecated && _access.hasRole(LEVEL1_ROLE, _msgSender())),
253
403
  "Only GUARDIAN can suspend / Only LEVEL1 can activate/deprecate"
254
404
  );
255
405
  comp.status = newStatus;
256
406
  emit ComponentStatusChanged(component, comp.kind, newStatus);
257
407
  }
258
408
 
409
+ /**
410
+ * @dev Returns the status of a component.
411
+ *
412
+ * @param component The address of the component
413
+ * @return The status of the component. See {ComponentStatus}
414
+ */
259
415
  function getComponentStatus(IPolicyPoolComponent component)
260
416
  external
261
417
  view
@@ -284,8 +440,9 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
284
440
 
285
441
  function deposit(IEToken eToken, uint256 amount) external override whenNotPaused {
286
442
  require(_etkStatus(eToken) == ComponentStatus.active, "eToken is not active");
287
- _currency.safeTransferFrom(msg.sender, address(eToken), amount);
288
- eToken.deposit(msg.sender, amount);
443
+ uint256 balanceBefore = _currency.balanceOf(address(eToken));
444
+ _currency.safeTransferFrom(_msgSender(), address(eToken), amount);
445
+ eToken.deposit(_msgSender(), _currency.balanceOf(address(eToken)) - balanceBefore);
289
446
  }
290
447
 
291
448
  function withdraw(IEToken eToken, uint256 amount)
@@ -299,7 +456,7 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
299
456
  etkStatus == ComponentStatus.active || etkStatus == ComponentStatus.deprecated,
300
457
  "eToken not found or withdraws not allowed"
301
458
  );
302
- address provider = msg.sender;
459
+ address provider = _msgSender();
303
460
  return eToken.withdraw(provider, amount);
304
461
  }
305
462
 
@@ -309,15 +466,20 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
309
466
  address policyHolder,
310
467
  uint96 internalId
311
468
  ) external override whenNotPaused returns (uint256) {
469
+ // Checks
312
470
  IRiskModule rm = policy.riskModule;
313
- require(address(rm) == msg.sender, "Only the RM can create new policies");
471
+ require(address(rm) == _msgSender(), "Only the RM can create new policies");
314
472
  require(_rmStatus(rm) == ComponentStatus.active, "RM module not found or not active");
315
- policy.id = (uint256(uint160(address(rm))) << 96) + internalId;
316
- _policies[policy.id] = policy.hash();
317
473
  IPremiumsAccount pa = rm.premiumsAccount();
318
474
  require(_paStatus(pa) == ComponentStatus.active, "PremiumsAccount not found or not active");
475
+
476
+ // Effects
477
+ policy.id = (uint256(uint160(address(rm))) << 96) + internalId;
478
+ require(_policies[policy.id] == bytes32(0), "Policy already exists");
479
+ _policies[policy.id] = policy.hash();
480
+
481
+ // Interactions
319
482
  pa.policyCreated(policy);
320
- _safeMint(policyHolder, policy.id, "");
321
483
 
322
484
  // Distribute the premium
323
485
  _currency.safeTransferFrom(payer, address(pa), policy.purePremium);
@@ -326,16 +488,17 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
326
488
  _currency.safeTransferFrom(payer, _treasury, policy.ensuroCommission);
327
489
  if (policy.partnerCommission > 0 && payer != rm.wallet())
328
490
  _currency.safeTransferFrom(payer, rm.wallet(), policy.partnerCommission);
329
- // TODO: this code does up to 5 ERC20 transfers. How we can avoid this? Delayed transfers?
491
+ /**
492
+ * This code does up to 5 ERC20 transfers. This can be avoided to reduce the gas cost, by implementing delayed
493
+ * transfers. This might be considered in the future, but to avoid increasing the complexity and since so far we
494
+ * operate on low gas-cost blockchains, we keep it as it is.
495
+ */
496
+ _safeMint(policyHolder, policy.id, "");
330
497
 
331
498
  emit NewPolicy(rm, policy);
332
499
  return policy.id;
333
500
  }
334
501
 
335
- function _balance() internal view returns (uint256) {
336
- return _currency.balanceOf(address(this));
337
- }
338
-
339
502
  function _validatePolicy(Policy.PolicyData memory policy) internal view {
340
503
  require(policy.id != 0 && policy.hash() == _policies[policy.id], "Policy not found");
341
504
  }
@@ -361,10 +524,21 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
361
524
  return _resolvePolicy(policy, customerWon ? policy.payout : 0, false);
362
525
  }
363
526
 
527
+ function isActive(uint256 policyId) external view override returns (bool) {
528
+ return _policies[policyId] != bytes32(0);
529
+ }
530
+
531
+ function getPolicyHash(uint256 policyId) external view override returns (bytes32) {
532
+ return _policies[policyId];
533
+ }
534
+
364
535
  /**
365
536
  * @dev Internal function that handles the different alternative resolutions for a policy, with or without payout and
366
537
  * expiration.
367
538
  *
539
+ * Events:
540
+ * - Emits {PolicyResolved} with the payout
541
+ *
368
542
  * @param policy A policy created with {Policy-initialize}
369
543
  * @param payout The amount to paid to the policyholder
370
544
  * @param expired True for expiration resolution (`payout` must be 0)
@@ -374,9 +548,10 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
374
548
  uint256 payout,
375
549
  bool expired
376
550
  ) internal {
551
+ // Checks
377
552
  _validatePolicy(policy);
378
553
  IRiskModule rm = policy.riskModule;
379
- require(expired || address(rm) == msg.sender, "Only the RM can resolve policies");
554
+ require(expired || address(rm) == _msgSender(), "Only the RM can resolve policies");
380
555
  require(payout == 0 || policy.expiration > block.timestamp, "Can't pay expired policy");
381
556
  ComponentStatus compStatus = _rmStatus(rm);
382
557
  require(
@@ -393,6 +568,9 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
393
568
  compStatus == ComponentStatus.active || compStatus == ComponentStatus.deprecated,
394
569
  "PremiumsAccount must be active or deprecated to process resolutions"
395
570
  );
571
+ // Effects
572
+ delete _policies[policy.id];
573
+ // Interactions
396
574
  if (customerWon) {
397
575
  address policyOwner = ownerOf(policy.id);
398
576
  pa.policyResolvedWithPayout(policyOwner, policy, payout);
@@ -403,7 +581,6 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
403
581
  rm.releaseExposure(policy.payout);
404
582
 
405
583
  emit PolicyResolved(policy.riskModule, policy.id, payout);
406
- delete _policies[policy.id];
407
584
  if (payout > 0) {
408
585
  _notifyPayout(policy.id, payout);
409
586
  } else {
@@ -451,4 +628,19 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
451
628
  return;
452
629
  }
453
630
  }
631
+
632
+ function _beforeTokenTransfer(
633
+ address from,
634
+ address to,
635
+ uint256 tokenId
636
+ ) internal override whenNotPaused {
637
+ super._beforeTokenTransfer(from, to, tokenId);
638
+ }
639
+
640
+ /**
641
+ * @dev This empty reserved space is put in place to allow future versions to add new
642
+ * variables without shifting down storage in the inheritance chain.
643
+ * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
644
+ */
645
+ uint256[47] private __gap;
454
646
  }