@ensuro/core 1.2.7 → 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 (109) hide show
  1. package/README.md +63 -27
  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 +217 -190
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +17 -4
  6. package/build/contracts/{extras/IPriceRiskModule.sol/IPriceRiskModule.json → LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json} +81 -131
  7. package/build/contracts/Policy.sol/Policy.json +2 -2
  8. package/build/contracts/PolicyPool.sol/PolicyPool.json +425 -295
  9. package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +15 -2
  10. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +868 -0
  11. package/build/contracts/{BaseAssetManager.sol/BaseAssetManager.json → Reserve.sol/Reserve.json} +37 -166
  12. package/build/contracts/RiskModule.sol/RiskModule.json +85 -312
  13. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +960 -0
  14. package/build/contracts/{DataTypes.sol/DataTypes.json → TimeScaled.sol/TimeScaled.json} +4 -4
  15. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +134 -336
  16. package/build/contracts/dependencies/ERC1967Proxy.sol/ERC1967Proxy.json +64 -0
  17. package/build/{interfaces/IExchange.sol/IExchange.json → contracts/dependencies/IERC20Metadata.sol/IERC20Metadata.json} +83 -79
  18. package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
  19. package/build/{interfaces/IPolicyPoolConfig.sol/IPolicyPoolConfig.json → contracts/interfaces/IAccessManager.sol/IAccessManager.json} +79 -77
  20. package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +109 -0
  21. package/build/{interfaces → contracts/interfaces}/IEToken.sol/IEToken.json +39 -91
  22. package/build/{interfaces → contracts/interfaces}/ILPWhitelist.sol/ILPWhitelist.json +1 -1
  23. package/build/{interfaces → contracts/interfaces}/IPolicyHolder.sol/IPolicyHolder.json +1 -1
  24. package/build/{interfaces → contracts/interfaces}/IPolicyPool.sol/IPolicyPool.json +137 -100
  25. package/build/{interfaces → contracts/interfaces}/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +1 -1
  26. package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +315 -0
  27. package/build/{interfaces → contracts/interfaces}/IRiskModule.sol/IRiskModule.json +58 -34
  28. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
  29. package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +43 -0
  30. package/build/{interfaces/IInsolvencyHook.sol/IInsolvencyHook.json → contracts/mocks/IMintableERC20.sol/IMintableERC20.json} +13 -8
  31. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +228 -0
  32. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +35 -0
  33. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +702 -0
  34. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +92 -0
  35. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +764 -0
  36. package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +343 -0
  37. package/build/contracts/mocks/TestNFT.sol/TestNFT.json +388 -0
  38. package/contracts/AccessManager.sol +103 -0
  39. package/contracts/ERC4626AssetManager.sol +52 -0
  40. package/contracts/EToken.sol +254 -299
  41. package/contracts/LPManualWhitelist.sol +4 -4
  42. package/contracts/LiquidityThresholdAssetManager.sol +160 -0
  43. package/contracts/Policy.sol +49 -45
  44. package/contracts/PolicyPool.sol +379 -514
  45. package/contracts/PolicyPoolComponent.sol +41 -41
  46. package/contracts/PremiumsAccount.sol +480 -0
  47. package/contracts/Reserve.sol +213 -0
  48. package/contracts/RiskModule.sol +233 -174
  49. package/contracts/SignedQuoteRiskModule.sol +241 -0
  50. package/contracts/TimeScaled.sol +138 -0
  51. package/contracts/TrustfulRiskModule.sol +85 -23
  52. package/contracts/dependencies/ERC1967Proxy.sol +19 -0
  53. package/contracts/dependencies/IERC20Metadata.sol +19 -0
  54. package/contracts/dependencies/WadRayMath.sol +126 -0
  55. package/contracts/interfaces/IAccessManager.sol +148 -0
  56. package/contracts/interfaces/IAssetManager.sol +84 -0
  57. package/contracts/interfaces/IEToken.sol +195 -0
  58. package/contracts/interfaces/ILPWhitelist.sol +40 -0
  59. package/{interfaces → contracts/interfaces}/IPolicyHolder.sol +0 -0
  60. package/contracts/interfaces/IPolicyPool.sol +145 -0
  61. package/{interfaces → contracts/interfaces}/IPolicyPoolComponent.sol +3 -0
  62. package/contracts/interfaces/IPremiumsAccount.sol +76 -0
  63. package/contracts/interfaces/IRiskModule.sol +123 -0
  64. package/contracts/mocks/FixedRateVault.sol +85 -0
  65. package/contracts/mocks/ForwardProxy.sol +66 -0
  66. package/contracts/mocks/IMintableERC20.sol +13 -0
  67. package/contracts/mocks/PolicyHolderMock.sol +89 -0
  68. package/contracts/mocks/PolicyPoolComponentMock.sol +17 -0
  69. package/contracts/mocks/PolicyPoolMock.sol +148 -0
  70. package/contracts/mocks/RiskModuleMock.sol +75 -0
  71. package/contracts/mocks/TestCurrency.sol +35 -0
  72. package/contracts/mocks/TestNFT.sol +23 -0
  73. package/js/test-utils.js +356 -0
  74. package/package.json +2 -2
  75. package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +0 -742
  76. package/build/contracts/Exchange.sol/Exchange.json +0 -591
  77. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -1112
  78. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -570
  79. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -686
  80. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -50
  81. package/build/contracts/extras/PriceRiskModule.sol/PriceRiskModule.json +0 -1055
  82. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
  83. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -571
  84. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -602
  85. package/build/interfaces/IAssetManager.sol/IAssetManager.json +0 -31
  86. package/build/interfaces/IExchange.sol/IPriceOracle.json +0 -30
  87. package/build/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
  88. package/contracts/AaveAssetManager.sol +0 -298
  89. package/contracts/BaseAssetManager.sol +0 -301
  90. package/contracts/DataTypes.sol +0 -287
  91. package/contracts/Exchange.sol +0 -189
  92. package/contracts/FlightDelayRiskModule.sol +0 -208
  93. package/contracts/PolicyNFT.sol +0 -96
  94. package/contracts/PolicyPoolConfig.sol +0 -247
  95. package/contracts/WadRayMath.sol +0 -135
  96. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
  97. package/contracts/extras/IPriceRiskModule.sol +0 -41
  98. package/contracts/extras/PriceRiskModule.sol +0 -243
  99. package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
  100. package/contracts/upgraded/PolicyNFTv1.sol +0 -126
  101. package/interfaces/IAssetManager.sol +0 -23
  102. package/interfaces/IEToken.sol +0 -48
  103. package/interfaces/IExchange.sol +0 -59
  104. package/interfaces/IInsolvencyHook.sol +0 -27
  105. package/interfaces/ILPWhitelist.sol +0 -23
  106. package/interfaces/IPolicyNFT.sol +0 -15
  107. package/interfaces/IPolicyPool.sol +0 -44
  108. package/interfaces/IPolicyPoolConfig.sol +0 -87
  109. package/interfaces/IRiskModule.sol +0 -29
@@ -4,231 +4,410 @@ pragma solidity ^0.8.0;
4
4
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
5
5
  import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
6
6
  import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
7
+ import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
7
8
  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
8
- import {IPolicyPoolConfig} from "../interfaces/IPolicyPoolConfig.sol";
9
+ import {IAccessManager} from "./interfaces/IAccessManager.sol";
9
10
  import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
10
- import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
11
- import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
12
- import {IRiskModule} from "../interfaces/IRiskModule.sol";
13
- import {IInsolvencyHook} from "../interfaces/IInsolvencyHook.sol";
14
- import {IPolicyPoolComponent} from "../interfaces/IPolicyPoolComponent.sol";
15
- import {IEToken} from "../interfaces/IEToken.sol";
16
- import {IPolicyNFT} from "../interfaces/IPolicyNFT.sol";
17
- import {IPolicyHolder} from "../interfaces/IPolicyHolder.sol";
18
- import {IAssetManager} from "../interfaces/IAssetManager.sol";
11
+ import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
12
+ import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
13
+ import {IRiskModule} from "./interfaces/IRiskModule.sol";
14
+ import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
15
+ import {IEToken} from "./interfaces/IEToken.sol";
16
+ import {IPolicyHolder} from "./interfaces/IPolicyHolder.sol";
19
17
  import {Policy} from "./Policy.sol";
20
- import {WadRayMath} from "./WadRayMath.sol";
21
- import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
22
- import {DataTypes} from "./DataTypes.sol";
18
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
23
19
 
24
20
  /**
25
21
  * @title Ensuro PolicyPool contract
26
22
  * @dev This is the main contract of the protocol, it stores the eTokens (liquidity pools) and has the operations
27
- * 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).
28
24
  * Also this contract keeps track of accumulated premiums in different stages:
29
25
  * - activePurePremiums
30
26
  * - wonPurePremiums (surplus)
31
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.
32
30
  * @custom:security-contact security@ensuro.co
33
31
  * @author Ensuro
34
32
  */
35
- // #invariant_disabled {:msg "Borrow up to activePurePremiums"} _borrowedActivePP <= _activePurePremiums;
36
- // #invariant_disabled {:msg "Can't borrow if not exhausted before won"} (_borrowedActivePP > 0) ==> _wonPurePremiums == 0;
37
- contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
38
- using EnumerableSet for EnumerableSet.AddressSet;
33
+ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721Upgradeable {
39
34
  using WadRayMath for uint256;
40
35
  using Policy for Policy.PolicyData;
41
- using DataTypes for DataTypes.ETokenToWadMap;
42
- using DataTypes for DataTypes.ETokenStatusMap;
43
36
  using SafeERC20 for IERC20Metadata;
44
37
 
45
- /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
46
- // solhint-disable-next-line var-name-mixedcase
47
- uint256 public immutable NEGLIGIBLE_AMOUNT; // init as 10**(decimals-3) == 0.001 USD
48
-
49
- bytes32 public constant WITHDRAW_WON_PREMIUMS_ROLE = keccak256("WITHDRAW_WON_PREMIUMS_ROLE");
50
-
51
38
  bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
52
39
  bytes32 public constant LEVEL1_ROLE = keccak256("LEVEL1_ROLE");
53
40
  bytes32 public constant LEVEL2_ROLE = keccak256("LEVEL2_ROLE");
54
41
  bytes32 public constant LEVEL3_ROLE = keccak256("LEVEL3_ROLE");
55
42
 
56
- uint256 public constant MAX_ETOKENS = 10;
57
-
43
+ /**
44
+ * @dev {AccessManager} that handles the access permissions for the PolicyPool and its components.
45
+ */
58
46
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
59
- IPolicyPoolConfig internal immutable _config;
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
+ */
60
52
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
61
53
  IERC20Metadata internal immutable _currency;
62
- /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
63
- IPolicyNFT internal immutable _policyNFT;
64
54
 
65
- DataTypes.ETokenStatusMap internal _eTokens;
55
+ /**
56
+ * @dev Address of Ensuro's treasury that receives the protocol fees.
57
+ */
58
+ address internal _treasury; // address of Ensuro treasury
66
59
 
67
- mapping(uint256 => bytes32) internal _policies;
68
- mapping(uint256 => DataTypes.ETokenToWadMap) internal _policiesFunds;
60
+ /**
61
+ * @dev This enum tracks the different status that a component ({PremiumsAccount}, {EToken} or {RiskModule} can have.
62
+ */
63
+ enum ComponentStatus {
64
+ /**
65
+ * @dev inactive status = 0 means the component doesn't exists - All operations rejected
66
+ */
67
+ inactive,
68
+ /**
69
+ * @dev active means the component is fully functional, all the component operations are allowed.
70
+ * deposit / withdraw for eTokens
71
+ * newPolicy / resolvePolicy for riskModules
72
+ * policyCreated / policyExpired / policyResolvedWithPayout for premiumsAccount
73
+ */
74
+ active,
75
+ /**
76
+ * @dev deprecated means the component is in process of being deactivated. Only some operations are allowed:
77
+ * withdraw for eTokens
78
+ * resolvePolicy / expirePolicy for riskModules
79
+ * policyExpired / policyResolvedWithPayout for premiumsAccount
80
+ */
81
+ deprecated,
82
+ /**
83
+ * @dev suspended means the component is temporarily deactivated. All the operations are rejected. Only GUARDIAN
84
+ * can suspend.
85
+ */
86
+ suspended
87
+ }
69
88
 
70
- uint256 internal _activePurePremiums; // sum of pure-premiums of active policies - In Wad
71
- uint256 internal _borrowedActivePP; // amount borrowed from active pure premiums to pay defaulted policies
72
- uint256 internal _wonPurePremiums; // amount of pure premiums won from non-defaulted policies
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
+ */
93
+ enum ComponentKind {
94
+ unknown,
95
+ eToken,
96
+ riskModule,
97
+ premiumsAccount
98
+ }
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
+ */
104
+ struct Component {
105
+ ComponentStatus status;
106
+ ComponentKind kind;
107
+ }
108
+
109
+ /**
110
+ * @dev Mapping of installed components (see {EToken}, {RiskModule}, {PremiumsAccount}) in the PolicyPool.
111
+ */
112
+ mapping(IPolicyPoolComponent => Component) private _components;
113
+
114
+ /**
115
+ * @dev Mapping that stores the active policies (the policyId is the key). It just saves the hash of the policies,
116
+ * the full {Policy-PolicyData} struct has to be sent for each operation (hash is used to verify).
117
+ */
118
+ mapping(uint256 => bytes32) internal _policies;
73
119
 
120
+ /**
121
+ * @dev Event emitted every time a new policy is added to the pool. Contains all the data about the policy that is
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.
126
+ */
74
127
  event NewPolicy(IRiskModule indexed riskModule, Policy.PolicyData policy);
75
- event PolicyRebalanced(IRiskModule indexed riskModule, uint256 indexed policyId);
76
- event PolicyResolved(IRiskModule indexed riskModule, uint256 indexed policyId, uint256 payout);
77
128
 
78
- event ETokenStatusChanged(IEToken indexed eToken, DataTypes.ETokenStatus newStatus);
129
+ /**
130
+ * @dev Event emitted every time a policy is removed from the pool. If the policy expired, the `payout` is 0,
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.
136
+ */
137
+ event PolicyResolved(IRiskModule indexed riskModule, uint256 indexed policyId, uint256 payout);
79
138
 
80
- /*
81
- * Premiums can come in (for free, without liability) with receiveGrant.
82
- * And can come out (withdrawed to treasury) with withdrawWonPremiums
139
+ /**
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
83
144
  */
84
- event WonPremiumsInOut(bool moneyIn, uint256 value);
145
+ event ComponentChanged(IAccessManager.GovernanceActions indexed action, address value);
85
146
 
86
- modifier onlyAssetManager() {
87
- require(
88
- msg.sender == address(_config.assetManager()),
89
- "Only assetManager can call this function"
90
- );
91
- _;
92
- }
147
+ /**
148
+ * @dev Event emitted when a new component added/removed to the pool or the status changes.
149
+ *
150
+ * @param component The address of the component, it can be an {EToken}, {RiskModule} or {PremiumsAccount}
151
+ * @param kind Value indicating the kind of component. See {ComponentKind}
152
+ * @param newStatus The status of the component after the operation. See {ComponentStatus}
153
+ */
154
+ event ComponentStatusChanged(
155
+ IPolicyPoolComponent indexed component,
156
+ ComponentKind kind,
157
+ ComponentStatus newStatus
158
+ );
93
159
 
160
+ /**
161
+ * @dev Modifier that checks the caller has a given role
162
+ */
94
163
  modifier onlyRole(bytes32 role) {
95
- _config.checkRole(role, msg.sender);
164
+ _access.checkRole(role, _msgSender());
96
165
  _;
97
166
  }
98
167
 
168
+ /**
169
+ * @dev Modifier that checks the caller has any of the given rolea
170
+ */
99
171
  modifier onlyRole2(bytes32 role1, bytes32 role2) {
100
- _config.checkRole2(role1, role2, msg.sender);
172
+ _access.checkRole2(role1, role2, _msgSender());
101
173
  _;
102
174
  }
103
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
+ */
104
183
  /// @custom:oz-upgrades-unsafe-allow constructor
105
- constructor(
106
- IPolicyPoolConfig config_,
107
- IPolicyNFT policyNFT_,
108
- IERC20Metadata currency_
109
- ) {
110
- _config = config_;
111
- _policyNFT = policyNFT_;
184
+ constructor(IAccessManager access_, IERC20Metadata currency_) {
185
+ _access = access_;
112
186
  _currency = currency_;
113
- NEGLIGIBLE_AMOUNT = 10**(currency_.decimals() - 3);
114
187
  }
115
188
 
116
- function initialize() public initializer {
189
+ /**
190
+ * @dev Initializes a Policy Pool
191
+ *
192
+ * @param name_ The name of the ERC721 token.
193
+ * @param symbol_ The symbol of the ERC721 token.
194
+ * @param treasury_ The address of the treasury that will receive the protocol fees.
195
+ */
196
+ function initialize(
197
+ string memory name_,
198
+ string memory symbol_,
199
+ address treasury_
200
+ ) public initializer {
117
201
  __UUPSUpgradeable_init();
202
+ __ERC721_init(name_, symbol_);
118
203
  __Pausable_init();
119
- __PolicyPool_init_unchained();
204
+ __PolicyPool_init_unchained(treasury_);
120
205
  }
121
206
 
122
207
  // solhint-disable-next-line func-name-mixedcase
123
- function __PolicyPool_init_unchained() internal initializer {
124
- _config.connect();
125
- require(
126
- _config.assetManager() == IAssetManager(address(0)),
127
- "AssetManager can't be set before PolicyPool initialization"
128
- );
129
- _policyNFT.connect();
130
- /*
131
- _activePurePremiums = 0;
132
- _borrowedActivePP = 0;
133
- _wonPurePremiums = 0;
134
- */
208
+ function __PolicyPool_init_unchained(address treasury_) internal initializer {
209
+ _treasury = treasury_;
135
210
  }
136
211
 
137
212
  // solhint-disable-next-line no-empty-blocks
138
213
  function _authorizeUpgrade(address) internal override onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {}
139
214
 
215
+ /**
216
+ * @dev Pauses the contract. When the contract is paused, several operations are rejected: deposits, withdrawals, new
217
+ * policies, policy resolution and expiration, nft transfers.
218
+ *
219
+ * Requirements:
220
+ * - Must be executed by a user with the {GUARDIAN_ROLE}.
221
+ */
140
222
  function pause() public onlyRole(GUARDIAN_ROLE) {
141
223
  _pause();
142
224
  }
143
225
 
226
+ /**
227
+ * @dev Unpauses the contract. All the operations disabled when the contract was paused are re-enabled.
228
+ *
229
+ * Requirements:
230
+ * - Must be called by a user with either the {GUARDIAN_ROLE} or a {LEVEL1_ROLE}.
231
+ */
144
232
  function unpause() public onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {
145
233
  _unpause();
146
234
  }
147
235
 
148
- function config() external view virtual override returns (IPolicyPoolConfig) {
149
- return _config;
236
+ function access() external view virtual override returns (IAccessManager) {
237
+ return _access;
150
238
  }
151
239
 
152
240
  function currency() external view virtual override returns (IERC20Metadata) {
153
241
  return _currency;
154
242
  }
155
243
 
156
- function policyNFT() external view virtual override returns (address) {
157
- return address(_policyNFT);
158
- }
159
-
160
- function purePremiums() public view returns (uint256) {
161
- return _activePurePremiums + _wonPurePremiums - _borrowedActivePP;
244
+ /**
245
+ * @dev Changes the address of the treasury, the one that receives the protocol fees.
246
+ *
247
+ * Requirements:
248
+ * - Must be called by a user with the {LEVEL1_ROLE}.
249
+ *
250
+ * Events:
251
+ * - Emits {ComponentChanged} with action = setTreasury and the address of the new treasury.
252
+ */
253
+ function setTreasury(address treasury_) external onlyRole(LEVEL1_ROLE) {
254
+ _treasury = treasury_;
255
+ emit ComponentChanged(IAccessManager.GovernanceActions.setTreasury, _treasury);
162
256
  }
163
257
 
164
- function activePurePremiums() external view returns (uint256) {
165
- return _activePurePremiums;
258
+ /**
259
+ * @dev Returns the address of the treasury, the one that receives the protocol fees.
260
+ */
261
+ function treasury() external view override returns (address) {
262
+ return _treasury;
166
263
  }
167
264
 
168
- function wonPurePremiums() external view returns (uint256) {
169
- return _wonPurePremiums;
265
+ /**
266
+ * @dev Adds a new component (either an {EToken}, {RiskModule} or {PremiumsAccount}) to the protocol. The component
267
+ * status will be `active`.
268
+ *
269
+ * Requirements:
270
+ * - Must be called by a user with the {LEVEL1_ROLE}
271
+ * - The component wasn't added before.
272
+ *
273
+ * Events:
274
+ * - Emits {ComponentStatusChanged} with status active.
275
+ *
276
+ * @param component The address of component contract. Must be an {EToken}, {RiskModule} or {PremiumsAccount} linked
277
+ * to this specific {PolicyPool} and matching the `kind` specified in the next paramter.
278
+ * @param kind The type of component to be added.
279
+ */
280
+ function addComponent(IPolicyPoolComponent component, ComponentKind kind)
281
+ external
282
+ onlyRole(LEVEL1_ROLE)
283
+ {
284
+ Component storage comp = _components[component];
285
+ require(comp.status == ComponentStatus.inactive, "Component already in the pool");
286
+ require(component.policyPool() == this, "Component not linked to this pool");
287
+
288
+ comp.status = ComponentStatus.active;
289
+ comp.kind = kind;
290
+ if (kind == ComponentKind.premiumsAccount) {
291
+ IPremiumsAccount pa = IPremiumsAccount(address(component));
292
+ IEToken etk = pa.juniorEtk();
293
+ if (address(etk) != address(0)) {
294
+ etk.addBorrower(address(pa));
295
+ }
296
+ etk = pa.seniorEtk();
297
+ if (address(etk) != address(0)) {
298
+ etk.addBorrower(address(pa));
299
+ }
300
+ }
301
+ emit ComponentStatusChanged(component, kind, ComponentStatus.active);
170
302
  }
171
303
 
172
- function borrowedActivePP() external view returns (uint256) {
173
- return _borrowedActivePP;
304
+ /**
305
+ * @dev Removes a component from the protocol. The component needs to be in `deprecated` status before doing this
306
+ * operation.
307
+ *
308
+ * Requirements:
309
+ * - Must be called by a user with the {LEVEL1_ROLE}
310
+ * - The component status is `deprecated`.
311
+ *
312
+ * Events:
313
+ * - Emits {ComponentStatusChanged} with status inactive.
314
+ *
315
+ * @param component The address of component contract. Must be a component added before.
316
+ */
317
+ function removeComponent(IPolicyPoolComponent component) external onlyRole(LEVEL1_ROLE) {
318
+ Component storage comp = _components[component];
319
+ require(comp.status == ComponentStatus.deprecated, "Component not deprecated");
320
+ if (comp.kind == ComponentKind.eToken) {
321
+ require(
322
+ IEToken(address(component)).totalSupply() == 0,
323
+ "EToken has liquidity, can't be removed"
324
+ );
325
+ } else if (comp.kind == ComponentKind.riskModule) {
326
+ require(
327
+ IRiskModule(address(component)).activeExposure() == 0,
328
+ "Can't remove a module with active policies"
329
+ );
330
+ } else if (comp.kind == ComponentKind.premiumsAccount) {
331
+ IPremiumsAccount pa = IPremiumsAccount(address(component));
332
+ require(pa.purePremiums() == 0, "Can't remove a PremiumsAccount with premiums");
333
+ IEToken etk = pa.juniorEtk();
334
+ if (address(etk) != address(0)) {
335
+ etk.removeBorrower(address(pa));
336
+ }
337
+ etk = pa.seniorEtk();
338
+ if (address(etk) != address(0)) {
339
+ etk.removeBorrower(address(pa));
340
+ }
341
+ }
342
+ emit ComponentStatusChanged(component, comp.kind, ComponentStatus.inactive);
343
+ delete _components[component];
174
344
  }
175
345
 
176
- // #if_succeeds_disabled {:msg "eToken added as active"} _eTokens.get(eToken) == DataTypes.ETokenStatus.active;
177
- function addEToken(IEToken eToken) external onlyRole(LEVEL1_ROLE) {
178
- require(_eTokens.length() < MAX_ETOKENS, "Maximum number of ETokens reached");
179
- require(!_eTokens.contains(eToken), "eToken already in the pool");
180
- require(address(eToken) != address(0), "eToken can't be zero");
346
+ /**
347
+ * @dev Changes the status of a component.
348
+ *
349
+ * Requirements:
350
+ * - Must be called by a user with the {LEVEL1_ROLE} if the new status is `active` or `deprecated`.
351
+ * - Must be called by a user with the {GUARDIAN_ROLE} if the new status is `suspended`.
352
+ *
353
+ * Events:
354
+ * - Emits {ComponentStatusChanged} with the new status.
355
+ *
356
+ * @param component The address of component contract. Must be a component added before.
357
+ * @param newStatus The new status, must be either `active`, `deprecated` or `suspended`.
358
+ */
359
+ function changeComponentStatus(IPolicyPoolComponent component, ComponentStatus newStatus)
360
+ external
361
+ onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
362
+ {
363
+ Component storage comp = _components[component];
364
+ require(comp.status != ComponentStatus.inactive, "Component not found");
181
365
  require(
182
- IPolicyPoolComponent(address(eToken)).policyPool() == this,
183
- "EToken not linked to this pool"
366
+ (newStatus == ComponentStatus.active && _access.hasRole(LEVEL1_ROLE, _msgSender())) ||
367
+ (newStatus == ComponentStatus.suspended && _access.hasRole(GUARDIAN_ROLE, _msgSender())) ||
368
+ (newStatus == ComponentStatus.deprecated && _access.hasRole(LEVEL1_ROLE, _msgSender())),
369
+ "Only GUARDIAN can suspend / Only LEVEL1 can activate/deprecate"
184
370
  );
185
-
186
- _eTokens.set(eToken, DataTypes.ETokenStatus.active);
187
- emit ETokenStatusChanged(eToken, DataTypes.ETokenStatus.active);
371
+ comp.status = newStatus;
372
+ emit ComponentStatusChanged(component, comp.kind, newStatus);
188
373
  }
189
374
 
190
- function removeEToken(IEToken eToken) external onlyRole(LEVEL3_ROLE) {
191
- require(_eTokens.get(eToken) == DataTypes.ETokenStatus.deprecated, "EToken not deprecated");
192
- require(eToken.totalSupply() == 0, "EToken has liquidity, can't be removed");
193
- emit ETokenStatusChanged(eToken, DataTypes.ETokenStatus.inactive);
194
- }
195
-
196
- function changeETokenStatus(IEToken eToken, DataTypes.ETokenStatus newStatus)
375
+ /**
376
+ * @dev Returns the status of a component.
377
+ *
378
+ * @param component The address of the component
379
+ * @return The status of the component. See {ComponentStatus}
380
+ */
381
+ function getComponentStatus(IPolicyPoolComponent component)
197
382
  external
198
- onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
383
+ view
384
+ returns (ComponentStatus)
199
385
  {
200
- require(_eTokens.contains(eToken), "Risk Module not found");
201
- require(
202
- newStatus != DataTypes.ETokenStatus.suspended || _config.hasRole(GUARDIAN_ROLE, msg.sender),
203
- "Only GUARDIAN can suspend eTokens"
204
- );
205
- _eTokens.set(eToken, newStatus);
206
- emit ETokenStatusChanged(eToken, newStatus);
386
+ return _components[component].status;
207
387
  }
208
388
 
209
- function getETokenStatus(IEToken eToken) external view returns (DataTypes.ETokenStatus) {
210
- return _eTokens.get(eToken);
389
+ function _etkStatus(IEToken eToken) internal view returns (ComponentStatus) {
390
+ Component storage comp = _components[IPolicyPoolComponent(address(eToken))];
391
+ require(comp.kind == ComponentKind.eToken, "Component is not an eToken");
392
+ return comp.status;
211
393
  }
212
394
 
213
- function setAssetManager(IAssetManager newAssetManager) external override {
214
- require(msg.sender == address(_config), "Only the PolicyPoolConfig can change assetManager");
215
- if (address(_config.assetManager()) != address(0)) {
216
- _config.assetManager().deinvestAll(); // deInvest all assets
217
- _currency.approve(address(_config.assetManager()), 0); // revoke currency management approval
218
- }
219
- if (address(newAssetManager) != address(0)) {
220
- _currency.approve(address(newAssetManager), type(uint256).max);
221
- }
395
+ function _rmStatus(IRiskModule riskModule) internal view returns (ComponentStatus) {
396
+ Component storage comp = _components[IPolicyPoolComponent(address(riskModule))];
397
+ require(comp.kind == ComponentKind.riskModule, "Component is not a RiskModule");
398
+ return comp.status;
399
+ }
400
+
401
+ function _paStatus(IPremiumsAccount premiumsAccount) internal view returns (ComponentStatus) {
402
+ Component storage comp = _components[IPolicyPoolComponent(address(premiumsAccount))];
403
+ require(comp.kind == ComponentKind.premiumsAccount, "Component is not a PremiumsAccount");
404
+ return comp.status;
222
405
  }
223
406
 
224
- /// #if_succeeds
225
- /// {:msg "must take balance from sender"}
226
- /// _currency.balanceOf(msg.sender) == old(_currency.balanceOf(msg.sender) - amount);
227
407
  function deposit(IEToken eToken, uint256 amount) external override whenNotPaused {
228
- (bool found, DataTypes.ETokenStatus etkStatus) = _eTokens.tryGet(eToken);
229
- require(found && etkStatus == DataTypes.ETokenStatus.active, "eToken is not active");
230
- _currency.safeTransferFrom(msg.sender, address(this), amount);
231
- eToken.deposit(msg.sender, amount);
408
+ require(_etkStatus(eToken) == ComponentStatus.active, "eToken is not active");
409
+ _currency.safeTransferFrom(_msgSender(), address(eToken), amount);
410
+ eToken.deposit(_msgSender(), amount);
232
411
  }
233
412
 
234
413
  function withdraw(IEToken eToken, uint256 amount)
@@ -237,173 +416,49 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
237
416
  whenNotPaused
238
417
  returns (uint256)
239
418
  {
240
- (bool found, DataTypes.ETokenStatus etkStatus) = _eTokens.tryGet(eToken);
419
+ ComponentStatus etkStatus = _etkStatus(eToken);
241
420
  require(
242
- found &&
243
- (
244
- (etkStatus == DataTypes.ETokenStatus.active ||
245
- etkStatus == DataTypes.ETokenStatus.deprecated)
246
- ),
421
+ etkStatus == ComponentStatus.active || etkStatus == ComponentStatus.deprecated,
247
422
  "eToken not found or withdraws not allowed"
248
423
  );
249
- address provider = msg.sender;
250
- uint256 withdrawed = eToken.withdraw(provider, amount);
251
- if (withdrawed > 0) _transferTo(provider, withdrawed);
252
- return withdrawed;
424
+ address provider = _msgSender();
425
+ return eToken.withdraw(provider, amount);
253
426
  }
254
427
 
255
428
  function newPolicy(
256
429
  Policy.PolicyData memory policy,
257
- address customer,
430
+ address payer,
431
+ address policyHolder,
258
432
  uint96 internalId
259
433
  ) external override whenNotPaused returns (uint256) {
260
434
  IRiskModule rm = policy.riskModule;
261
- require(address(rm) == msg.sender, "Only the RM can create new policies");
262
- _config.checkAcceptsNewPolicy(rm);
435
+ require(address(rm) == _msgSender(), "Only the RM can create new policies");
436
+ require(_rmStatus(rm) == ComponentStatus.active, "RM module not found or not active");
263
437
  policy.id = (uint256(uint160(address(rm))) << 96) + internalId;
264
438
  _policies[policy.id] = policy.hash();
265
- _activePurePremiums += policy.purePremium;
266
- _lockScr(policy);
267
- _policyNFT.safeMint(customer, policy.id);
268
- _currency.safeTransferFrom(customer, address(this), policy.purePremium + policy.premiumForLps);
269
- _currency.safeTransferFrom(customer, _config.treasury(), policy.premiumForEnsuro);
270
- if (policy.premiumForRm > 0 && customer != rm.wallet())
271
- _currency.safeTransferFrom(customer, rm.wallet(), policy.premiumForRm);
439
+ IPremiumsAccount pa = rm.premiumsAccount();
440
+ require(_paStatus(pa) == ComponentStatus.active, "PremiumsAccount not found or not active");
441
+ pa.policyCreated(policy);
442
+ _safeMint(policyHolder, policy.id, "");
443
+
444
+ // Distribute the premium
445
+ _currency.safeTransferFrom(payer, address(pa), policy.purePremium);
446
+ if (policy.srCoc > 0) _currency.safeTransferFrom(payer, address(pa.seniorEtk()), policy.srCoc);
447
+ if (policy.jrCoc > 0) _currency.safeTransferFrom(payer, address(pa.juniorEtk()), policy.jrCoc);
448
+ _currency.safeTransferFrom(payer, _treasury, policy.ensuroCommission);
449
+ if (policy.partnerCommission > 0 && payer != rm.wallet())
450
+ _currency.safeTransferFrom(payer, rm.wallet(), policy.partnerCommission);
451
+ // TODO: this code does up to 5 ERC20 transfers. How we can avoid this? Delayed transfers?
452
+
272
453
  emit NewPolicy(rm, policy);
273
454
  return policy.id;
274
455
  }
275
456
 
276
- function _lockScr(Policy.PolicyData memory policy) internal {
277
- uint256 ocean = 0;
278
- DataTypes.ETokenToWadMap storage policyFunds = _policiesFunds[policy.id];
279
-
280
- // Initially I iterate over all eTokens and accumulate ocean of eligible ones
281
- // saves the ocean in policyFunds, later will _distributeScr
282
- for (uint256 i = 0; i < _eTokens.length(); i++) {
283
- (IEToken etk, DataTypes.ETokenStatus etkStatus) = _eTokens.at(i);
284
- if (etkStatus != DataTypes.ETokenStatus.active) continue;
285
- if (!etk.accepts(address(policy.riskModule), policy.expiration)) continue;
286
- uint256 etkOcean = etk.oceanForNewScr();
287
- if (etkOcean == 0) continue;
288
- ocean += etkOcean;
289
- policyFunds.set(etk, etkOcean);
290
- }
291
- _distributeScr(policy.scr, policy.interestRate(), ocean, policyFunds);
292
- }
293
-
294
- /**
295
- * @dev Distributes SCR amount in policyFunds according to ocean per token
296
- * @param scr SCR to distribute
297
- * @param ocean Total ocean available in the ETokens for this SCR
298
- * @param policyFunds Input: loaded with ocean available for this SCR (sum=ocean)
299
- Ouput: loaded with locked SRC (sum=scr)
300
- */
301
- function _distributeScr(
302
- uint256 scr,
303
- uint256 interestRate,
304
- uint256 ocean,
305
- DataTypes.ETokenToWadMap storage policyFunds
306
- ) internal {
307
- require(ocean >= scr, "Not enought ocean to cover the policy");
308
- uint256 scrNotLocked = scr;
309
-
310
- for (uint256 i = 0; i < policyFunds.length(); i++) {
311
- uint256 etkScr;
312
- (IEToken etk, uint256 etkOcean) = policyFunds.at(i);
313
- if (i < policyFunds.length() - 1) etkScr = scr.wadMul(etkOcean).wadDiv(ocean);
314
- else etkScr = scrNotLocked;
315
- etk.lockScr(interestRate, etkScr);
316
- policyFunds.set(etk, etkScr);
317
- scrNotLocked -= etkScr;
318
- }
319
- }
320
-
321
- function _balance() internal view returns (uint256) {
322
- return _currency.balanceOf(address(this));
323
- }
324
-
325
- function _transferTo(address destination, uint256 amount) internal {
326
- if (amount == 0) return;
327
- if (_config.assetManager() != IAssetManager(address(0)) && _balance() < amount) {
328
- _config.assetManager().refillWallet(amount);
329
- }
330
- // Calculate again the balance and check if enought, if not call unsolvency_hook
331
- if (_config.insolvencyHook() != IInsolvencyHook(address(0)) && _balance() < amount) {
332
- _config.insolvencyHook().outOfCash(amount - _balance());
333
- }
334
- _currency.safeTransfer(destination, amount);
335
- }
336
-
337
- function _payFromPool(uint256 toPay) internal returns (uint256) {
338
- // 1. take from won_pure_premiums
339
- if (toPay <= _wonPurePremiums) {
340
- _wonPurePremiums -= toPay;
341
- return 0;
342
- }
343
- if (_wonPurePremiums > 0) {
344
- toPay -= _wonPurePremiums;
345
- _wonPurePremiums = 0;
346
- }
347
- // 2. borrow from active pure premiums
348
- if (_activePurePremiums > _borrowedActivePP) {
349
- if (toPay <= (_activePurePremiums - _borrowedActivePP)) {
350
- _borrowedActivePP += toPay;
351
- return 0;
352
- } else {
353
- toPay -= _activePurePremiums - _borrowedActivePP;
354
- _borrowedActivePP = _activePurePremiums;
355
- }
356
- }
357
- return toPay;
358
- }
359
-
360
- function _storePurePremiumWon(uint256 purePremiumWon) internal {
361
- if (purePremiumWon == 0) return;
362
- if (_borrowedActivePP >= purePremiumWon) {
363
- _borrowedActivePP -= purePremiumWon;
364
- } else {
365
- if (_borrowedActivePP > 0) {
366
- purePremiumWon -= _borrowedActivePP;
367
- _borrowedActivePP = 0;
368
- }
369
- _wonPurePremiums += purePremiumWon;
370
- }
371
- }
372
-
373
- function _processResolution(
374
- Policy.PolicyData memory policy,
375
- bool customerWon,
376
- uint256 payout
377
- ) internal returns (uint256, uint256) {
378
- uint256 borrowFromScr; // = 0
379
- uint256 purePremiumWon; // = 0
380
- uint256 aux;
381
-
382
- if (customerWon) {
383
- _transferTo(_policyNFT.ownerOf(policy.id), payout);
384
- if (policy.purePremium > payout) {
385
- purePremiumWon = policy.purePremium - payout;
386
- } else {
387
- borrowFromScr = _payFromPool(payout - policy.purePremium);
388
- }
389
- } else {
390
- // Pay RM and Ensuro
391
- purePremiumWon = policy.purePremium;
392
- // cover first _borrowedActivePP
393
- if (_borrowedActivePP > _activePurePremiums) {
394
- aux = Math.min(_borrowedActivePP - _activePurePremiums, purePremiumWon);
395
- _borrowedActivePP -= aux;
396
- purePremiumWon -= aux;
397
- }
398
- }
399
- return (borrowFromScr, purePremiumWon);
400
- }
401
-
402
457
  function _validatePolicy(Policy.PolicyData memory policy) internal view {
403
458
  require(policy.id != 0 && policy.hash() == _policies[policy.id], "Policy not found");
404
459
  }
405
460
 
406
- function expirePolicy(Policy.PolicyData calldata policy) external whenNotPaused {
461
+ function expirePolicy(Policy.PolicyData calldata policy) external override whenNotPaused {
407
462
  require(policy.expiration <= block.timestamp, "Policy not expired yet");
408
463
  return _resolvePolicy(policy, 0, true);
409
464
  }
@@ -424,6 +479,21 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
424
479
  return _resolvePolicy(policy, customerWon ? policy.payout : 0, false);
425
480
  }
426
481
 
482
+ function isActive(uint256 policyId) external view override returns (bool) {
483
+ return _policies[policyId] != bytes32(0);
484
+ }
485
+
486
+ /**
487
+ * @dev Internal function that handles the different alternative resolutions for a policy, with or without payout and
488
+ * expiration.
489
+ *
490
+ * Events:
491
+ * - Emits {PolicyResolved} with the payout
492
+ *
493
+ * @param policy A policy created with {Policy-initialize}
494
+ * @param payout The amount to paid to the policyholder
495
+ * @param expired True for expiration resolution (`payout` must be 0)
496
+ */
427
497
  function _resolvePolicy(
428
498
  Policy.PolicyData memory policy,
429
499
  uint256 payout,
@@ -431,40 +501,34 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
431
501
  ) internal {
432
502
  _validatePolicy(policy);
433
503
  IRiskModule rm = policy.riskModule;
434
- require(expired || address(rm) == msg.sender, "Only the RM can resolve policies");
504
+ require(expired || address(rm) == _msgSender(), "Only the RM can resolve policies");
435
505
  require(payout == 0 || policy.expiration > block.timestamp, "Can't pay expired policy");
436
- _config.checkAcceptsResolvePolicy(rm);
506
+ ComponentStatus compStatus = _rmStatus(rm);
507
+ require(
508
+ compStatus == ComponentStatus.active || compStatus == ComponentStatus.deprecated,
509
+ "Module must be active or deprecated to process resolutions"
510
+ );
437
511
  require(payout <= policy.payout, "payout > policy.payout");
438
512
 
439
513
  bool customerWon = payout > 0;
440
514
 
441
- _activePurePremiums -= policy.purePremium;
442
-
443
- (uint256 borrowFromScr, uint256 purePremiumWon) = _processResolution(
444
- policy,
445
- customerWon,
446
- payout
515
+ IPremiumsAccount pa = rm.premiumsAccount();
516
+ compStatus = _paStatus(pa);
517
+ require(
518
+ compStatus == ComponentStatus.active || compStatus == ComponentStatus.deprecated,
519
+ "PremiumsAccount must be active or deprecated to process resolutions"
447
520
  );
448
-
449
521
  if (customerWon) {
450
- uint256 borrowFromScrLeft;
451
- borrowFromScrLeft = _updatePolicyFundsCustWon(policy, borrowFromScr);
452
- if (borrowFromScrLeft > NEGLIGIBLE_AMOUNT)
453
- borrowFromScrLeft = _takeLoanFromAnyEtk(borrowFromScrLeft);
454
- require(
455
- borrowFromScrLeft <= NEGLIGIBLE_AMOUNT,
456
- "Don't know where to take the rest of the money"
457
- );
522
+ address policyOwner = ownerOf(policy.id);
523
+ pa.policyResolvedWithPayout(policyOwner, policy, payout);
458
524
  } else {
459
- purePremiumWon = _updatePolicyFundsCustLost(policy, purePremiumWon);
525
+ pa.policyExpired(policy);
460
526
  }
461
527
 
462
- _storePurePremiumWon(purePremiumWon); // it's possible in some cases purePremiumWon > 0 && customerWon
463
- rm.releaseScr(policy.scr);
528
+ rm.releaseExposure(policy.payout);
464
529
 
465
530
  emit PolicyResolved(policy.riskModule, policy.id, payout);
466
531
  delete _policies[policy.id];
467
- delete _policiesFunds[policy.id];
468
532
  if (payout > 0) {
469
533
  _notifyPayout(policy.id, payout);
470
534
  } else {
@@ -472,8 +536,12 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
472
536
  }
473
537
  }
474
538
 
539
+ /**
540
+ * @dev Notifies the payout with a callback if the policyholder is a contract. Only reverts if the policyholder
541
+ * contract explicitly reverts. Doesn't reverts is the callback is not implemented.
542
+ */
475
543
  function _notifyPayout(uint256 policyId, uint256 payout) internal {
476
- address customer = _policyNFT.ownerOf(policyId);
544
+ address customer = ownerOf(policyId);
477
545
  if (!AddressUpgradeable.isContract(customer)) return;
478
546
  try
479
547
  IPolicyHolder(customer).onPayoutReceived(_msgSender(), address(this), policyId, payout)
@@ -494,8 +562,11 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
494
562
  }
495
563
  }
496
564
 
565
+ /**
566
+ * @dev Notifies the expiration with a callback if the policyholder is a contract. Never reverts.
567
+ */
497
568
  function _notifyExpiration(uint256 policyId) internal {
498
- address customer = _policyNFT.ownerOf(policyId);
569
+ address customer = ownerOf(policyId);
499
570
  if (!AddressUpgradeable.isContract(customer)) return;
500
571
  try IPolicyHolder(customer).onPolicyExpired(_msgSender(), address(this), policyId) returns (
501
572
  bytes4
@@ -506,217 +577,11 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
506
577
  }
507
578
  }
508
579
 
509
- function _interestAdjustment(Policy.PolicyData memory policy)
510
- internal
511
- view
512
- returns (bool, uint256)
513
- {
514
- // Calculate interest accrual adjustment
515
- uint256 aux = policy.accruedInterest();
516
- if (policy.premiumForLps >= aux) return (true, policy.premiumForLps - aux);
517
- else return (false, aux - policy.premiumForLps);
518
- }
519
-
520
- function _updatePolicyFundsCustWon(Policy.PolicyData memory policy, uint256 borrowFromScr)
521
- internal
522
- returns (uint256)
523
- {
524
- uint256 borrowFromScrLeft = 0;
525
- uint256 interestRate = policy.interestRate();
526
- (bool positive, uint256 adjustment) = _interestAdjustment(policy);
527
-
528
- // Iterate policyFunds - unlockScr / adjust / take loan
529
- DataTypes.ETokenToWadMap storage policyFunds = _policiesFunds[policy.id];
530
- for (uint256 i = 0; i < policyFunds.length(); i++) {
531
- (IEToken etk, uint256 etkScr) = policyFunds.at(i);
532
- etk.unlockScr(interestRate, etkScr);
533
- etkScr = etkScr.wadDiv(policy.scr);
534
- // etkScr now represents the share of SCR that's covered by this etk (variable reuse)
535
- etk.discreteEarning(adjustment.wadMul(etkScr), positive);
536
- if (borrowFromScr > 0) {
537
- uint256 aux;
538
- aux = borrowFromScr.wadMul(etkScr);
539
- borrowFromScrLeft += aux - etk.lendToPool(aux, true);
540
- }
541
- }
542
- return borrowFromScrLeft;
543
- }
544
-
545
- // Almost duplicated code from _updatePolicyFundsCustWon but separated to avoid stack depth error
546
- function _updatePolicyFundsCustLost(Policy.PolicyData memory policy, uint256 purePremiumWon)
547
- internal
548
- returns (uint256)
549
- {
550
- uint256 interestRate = policy.interestRate();
551
- (bool positive, uint256 adjustment) = _interestAdjustment(policy);
552
-
553
- // Iterate policyFunds - unlockScr / adjust / repay loan
554
- DataTypes.ETokenToWadMap storage policyFunds = _policiesFunds[policy.id];
555
- for (uint256 i = 0; i < policyFunds.length(); i++) {
556
- (IEToken etk, uint256 etkScr) = policyFunds.at(i);
557
- etk.unlockScr(interestRate, etkScr);
558
- etkScr = etkScr.wadDiv(policy.scr);
559
- // etkScr now represents the share of SCR that's covered by this etk (variable reuse)
560
- etk.discreteEarning(adjustment.wadMul(etkScr), positive);
561
- if (purePremiumWon > 0 && etk.getPoolLoan() > 0) {
562
- uint256 aux;
563
- // if debt with token, repay from purePremium
564
- aux = policy.purePremium.wadMul(etkScr);
565
- aux = Math.min(purePremiumWon, Math.min(etk.getPoolLoan(), aux));
566
- etk.repayPoolLoan(aux);
567
- purePremiumWon -= aux;
568
- }
569
- }
570
- return purePremiumWon;
571
- }
572
-
573
- /*
574
- * Called when the payout to be taken from policyFunds wasn't enought.
575
- * Then I take loan from the others tokens
576
- */
577
- function _takeLoanFromAnyEtk(uint256 loanLeft) internal returns (uint256) {
578
- for (uint256 i = 0; i < _eTokens.length(); i++) {
579
- (IEToken etk, DataTypes.ETokenStatus etkStatus) = _eTokens.at(i);
580
- if (etkStatus != DataTypes.ETokenStatus.active) continue;
581
- loanLeft -= etk.lendToPool(loanLeft, false);
582
- if (loanLeft <= NEGLIGIBLE_AMOUNT) break;
583
- }
584
- return loanLeft;
585
- }
586
-
587
- /**
588
- *
589
- * Repays a loan taken with the eToken with the money in the premium pool.
590
- * The repayment should happen without calling this method when customer losses and eToken is one of the
591
- * policyFunds. But sometimes we need to take loans from tokens not linked to the policy.
592
- *
593
- * returns The amount repaid
594
- *
595
- * Requirements:
596
- *
597
- * - `eToken` must be `active` or `deprecated`
598
- */
599
- function repayETokenLoan(IEToken eToken) external whenNotPaused returns (uint256) {
600
- (bool found, DataTypes.ETokenStatus etkStatus) = _eTokens.tryGet(eToken);
601
- require(
602
- found &&
603
- (etkStatus == DataTypes.ETokenStatus.active ||
604
- etkStatus == DataTypes.ETokenStatus.deprecated),
605
- "eToken is not active"
606
- );
607
- uint256 poolLoan = eToken.getPoolLoan();
608
- uint256 toPayLater = _payFromPool(poolLoan);
609
- eToken.repayPoolLoan(poolLoan - toPayLater);
610
- return poolLoan - toPayLater;
611
- }
612
-
613
- /**
614
- *
615
- * Endpoint to receive "free money" and inject that money into the premium pool.
616
- *
617
- * Can be used for example if the PolicyPool subscribes an excess loss policy with other company.
618
- *
619
- */
620
- function receiveGrant(uint256 amount) external override {
621
- _currency.safeTransferFrom(msg.sender, address(this), amount);
622
- _storePurePremiumWon(amount);
623
- emit WonPremiumsInOut(true, amount);
624
- }
625
-
626
- /**
627
- *
628
- * Withdraws excess premiums to PolicyPool's treasury.
629
- * This might be needed in some cases for example if we are deprecating the protocol or the excess premiums
630
- * are needed to compensate something. Shouldn't be used. Can be disabled revoking role WITHDRAW_WON_PREMIUMS_ROLE
631
- *
632
- * returns The amount withdrawed
633
- *
634
- * Requirements:
635
- *
636
- * - onlyRole(WITHDRAW_WON_PREMIUMS_ROLE)
637
- * - _wonPurePremiums > 0
638
- */
639
- function withdrawWonPremiums(uint256 amount)
640
- external
641
- onlyRole(WITHDRAW_WON_PREMIUMS_ROLE)
642
- returns (uint256)
643
- {
644
- if (amount > _wonPurePremiums) amount = _wonPurePremiums;
645
- require(amount > 0, "No premiums to withdraw");
646
- _wonPurePremiums -= amount;
647
- _transferTo(_config.treasury(), amount);
648
- emit WonPremiumsInOut(false, amount);
649
- return amount;
650
- }
651
-
652
- function getInvestable() external view override returns (uint256) {
653
- uint256 borrowedFromEtk = 0;
654
- for (uint256 i = 0; i < _eTokens.length(); i++) {
655
- (
656
- IEToken etk, /* DataTypes.ETokenStatus etkStatus */
657
-
658
- ) = _eTokens.at(i);
659
- // TODO: define if not active are investable or not
660
- borrowedFromEtk += etk.getPoolLoan();
661
- }
662
- uint256 premiums = purePremiums();
663
- if (premiums > borrowedFromEtk) return premiums - borrowedFromEtk;
664
- else return 0;
665
- }
666
-
667
- function totalETokenSupply() public view override returns (uint256) {
668
- uint256 ret = 0;
669
- for (uint256 i = 0; i < _eTokens.length(); i++) {
670
- (
671
- IEToken etk, /* DataTypes.ETokenStatus etkStatus */
672
-
673
- ) = _eTokens.at(i);
674
- // TODO: define if not active are investable or not
675
- ret += etk.totalSupply();
676
- }
677
- return ret;
678
- }
679
-
680
- function assetEarnings(uint256 amount, bool positive)
681
- external
682
- override
683
- onlyAssetManager
684
- whenNotPaused
685
- {
686
- if (positive) {
687
- // earnings
688
- _storePurePremiumWon(amount);
689
- } else {
690
- // losses
691
- _payFromPool(amount); // return value should be 0 if not, losses are more than capital available
692
- }
693
- }
694
-
695
- function getPolicyFundCount(uint256 policyId) external view returns (uint256) {
696
- return _policiesFunds[policyId].length();
697
- }
698
-
699
- function getPolicyFundAt(uint256 policyId, uint256 index)
700
- external
701
- view
702
- returns (IEToken, uint256)
703
- {
704
- return _policiesFunds[policyId].at(index);
705
- }
706
-
707
- function getPolicyFund(uint256 policyId, IEToken etoken) external view returns (uint256) {
708
- (bool success, uint256 amount) = _policiesFunds[policyId].tryGet(etoken);
709
- if (success) return amount;
710
- else return 0;
711
- }
712
-
713
- function getETokenCount() external view override returns (uint256) {
714
- return _eTokens.length();
715
- }
716
-
717
- function getETokenAt(uint256 index) external view override returns (IEToken) {
718
- (IEToken etk, DataTypes.ETokenStatus etkStatus) = _eTokens.at(index);
719
- if (etkStatus != DataTypes.ETokenStatus.inactive) return etk;
720
- else return IEToken(address(0));
580
+ function _beforeTokenTransfer(
581
+ address from,
582
+ address to,
583
+ uint256 tokenId
584
+ ) internal override whenNotPaused {
585
+ super._beforeTokenTransfer(from, to, tokenId);
721
586
  }
722
587
  }