@ensuro/core 2.0.0-beta1 → 2.0.0-beta11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -14
- package/build/contracts/AccessManager.sol/AccessManager.json +564 -0
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +238 -0
- package/build/contracts/EToken.sol/EToken.json +147 -46
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +23 -4
- package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +222 -0
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +437 -61
- package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +21 -2
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +142 -16
- package/build/contracts/Reserve.sol/Reserve.json +85 -8
- package/build/contracts/RiskModule.sol/RiskModule.json +21 -2
- package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +979 -0
- package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +24 -5
- package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
- package/build/contracts/interfaces/{IPolicyPoolConfig.sol/IPolicyPoolConfig.json → IAccessManager.sol/IAccessManager.json} +77 -59
- package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +128 -0
- package/build/contracts/interfaces/IEToken.sol/IEToken.json +13 -0
- package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +40 -16
- package/build/contracts/interfaces/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +19 -0
- package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
- package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
- package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +2 -2
- package/build/contracts/mocks/InterfaceIdCalculator.sol/InterfaceIdCalculator.json +180 -0
- package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +28 -2
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +21 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +118 -19
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +6 -6
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +783 -0
- package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
- package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
- package/contracts/AccessManager.sol +132 -0
- package/contracts/ERC4626AssetManager.sol +55 -0
- package/contracts/EToken.sol +208 -102
- package/contracts/LPManualWhitelist.sol +19 -3
- package/contracts/LiquidityThresholdAssetManager.sol +175 -0
- package/contracts/Policy.sol +5 -3
- package/contracts/PolicyPool.sol +381 -116
- package/contracts/PolicyPoolComponent.sol +61 -44
- package/contracts/PremiumsAccount.sol +324 -86
- package/contracts/Reserve.sol +199 -4
- package/contracts/RiskModule.sol +62 -31
- package/contracts/SignedQuoteRiskModule.sol +248 -0
- package/contracts/TimeScaled.sol +19 -15
- package/contracts/TrustfulRiskModule.sol +69 -2
- package/contracts/dependencies/WadRayMath.sol +126 -0
- package/contracts/interfaces/IAccessManager.sol +148 -0
- package/contracts/interfaces/IAssetManager.sol +89 -0
- package/contracts/interfaces/IEToken.sol +25 -2
- package/contracts/interfaces/ILPWhitelist.sol +17 -0
- package/contracts/interfaces/IPolicyPool.sol +21 -14
- package/contracts/interfaces/IPolicyPoolComponent.sol +5 -1
- package/contracts/interfaces/IPremiumsAccount.sol +5 -0
- package/contracts/interfaces/IRiskModule.sol +67 -4
- package/contracts/mocks/FixedRateVault.sol +85 -0
- package/contracts/mocks/InterfaceIdCalculator.sol +32 -0
- package/contracts/mocks/PolicyHolderMock.sol +21 -0
- package/contracts/mocks/PolicyPoolComponentMock.sol +10 -0
- package/contracts/mocks/PolicyPoolMock.sol +41 -15
- package/contracts/mocks/RiskModuleMock.sol +75 -0
- package/js/deploy.js +704 -0
- package/js/test-utils.js +339 -159
- package/package.json +1 -1
- package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -890
- package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
- package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -691
- package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -63
- package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
- package/build/contracts/mocks/LinkTokenMock.sol/LinkTokenMock.json +0 -370
- package/build/contracts/mocks/TestCurrencyUUPS.sol/TestCurrencyUUPS.json +0 -353
- package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
- package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -584
- package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -615
- package/contracts/FlightDelayRiskModule.sol +0 -217
- package/contracts/PolicyNFT.sol +0 -96
- package/contracts/PolicyPoolConfig.sol +0 -204
- package/contracts/WadRayMath.sol +0 -135
- package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
- package/contracts/interfaces/IPolicyNFT.sol +0 -15
- package/contracts/interfaces/IPolicyPoolConfig.sol +0 -96
- package/contracts/mocks/LinkTokenMock.sol +0 -30
- package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
- package/contracts/upgraded/PolicyNFTv1.sol +0 -126
package/contracts/PolicyPool.sol
CHANGED
|
@@ -4,33 +4,33 @@ 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 {
|
|
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
11
|
import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
|
|
12
12
|
import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
|
|
13
13
|
import {IRiskModule} from "./interfaces/IRiskModule.sol";
|
|
14
14
|
import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
|
|
15
15
|
import {IEToken} from "./interfaces/IEToken.sol";
|
|
16
|
-
import {IPolicyNFT} from "./interfaces/IPolicyNFT.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
|
-
contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
33
|
-
using EnumerableSet for EnumerableSet.AddressSet;
|
|
33
|
+
contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721Upgradeable {
|
|
34
34
|
using WadRayMath for uint256;
|
|
35
35
|
using Policy for Policy.PolicyData;
|
|
36
36
|
using SafeERC20 for IERC20Metadata;
|
|
@@ -40,25 +40,76 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
|
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
|
-
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
|
48
|
-
IPolicyNFT internal immutable _policyNFT;
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
/**
|
|
56
|
+
* @dev Address of Ensuro's treasury that receives the protocol fees.
|
|
57
|
+
*/
|
|
58
|
+
address internal _treasury; // address of Ensuro treasury
|
|
59
|
+
|
|
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
|
|
55
87
|
}
|
|
56
88
|
|
|
57
89
|
/**
|
|
58
|
-
* @dev
|
|
59
|
-
* {
|
|
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.
|
|
60
92
|
*/
|
|
61
|
-
|
|
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;
|
|
62
113
|
|
|
63
114
|
/**
|
|
64
115
|
* @dev Mapping that stores the active policies (the policyId is the key). It just saves the hash of the policies,
|
|
@@ -69,148 +120,321 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
|
69
120
|
/**
|
|
70
121
|
* @dev Event emitted every time a new policy is added to the pool. Contains all the data about the policy that is
|
|
71
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.
|
|
72
126
|
*/
|
|
73
127
|
event NewPolicy(IRiskModule indexed riskModule, Policy.PolicyData policy);
|
|
74
128
|
|
|
75
129
|
/**
|
|
76
130
|
* @dev Event emitted every time a policy is removed from the pool. If the policy expired, the `payout` is 0,
|
|
77
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.
|
|
78
136
|
*/
|
|
79
137
|
event PolicyResolved(IRiskModule indexed riskModule, uint256 indexed policyId, uint256 payout);
|
|
80
138
|
|
|
81
139
|
/**
|
|
82
|
-
* @dev Event emitted when
|
|
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
|
|
145
|
+
event ComponentChanged(IAccessManager.GovernanceActions indexed action, address value);
|
|
85
146
|
|
|
86
147
|
/**
|
|
87
|
-
* @dev Event emitted when a new
|
|
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}
|
|
88
153
|
*/
|
|
89
|
-
event
|
|
90
|
-
|
|
91
|
-
|
|
154
|
+
event ComponentStatusChanged(
|
|
155
|
+
IPolicyPoolComponent indexed component,
|
|
156
|
+
ComponentKind kind,
|
|
157
|
+
ComponentStatus newStatus
|
|
92
158
|
);
|
|
93
159
|
|
|
160
|
+
/**
|
|
161
|
+
* @dev Modifier that checks the caller has a given role
|
|
162
|
+
*/
|
|
94
163
|
modifier onlyRole(bytes32 role) {
|
|
95
|
-
|
|
164
|
+
_access.checkRole(role, _msgSender());
|
|
96
165
|
_;
|
|
97
166
|
}
|
|
98
167
|
|
|
168
|
+
/**
|
|
169
|
+
* @dev Modifier that checks the caller has any of the given roles
|
|
170
|
+
*/
|
|
99
171
|
modifier onlyRole2(bytes32 role1, bytes32 role2) {
|
|
100
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
_config = config_;
|
|
111
|
-
_policyNFT = policyNFT_;
|
|
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();
|
|
188
|
+
_access = access_;
|
|
112
189
|
_currency = currency_;
|
|
113
190
|
}
|
|
114
191
|
|
|
115
|
-
|
|
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
|
+
*/
|
|
199
|
+
function initialize(
|
|
200
|
+
string memory name_,
|
|
201
|
+
string memory symbol_,
|
|
202
|
+
address treasury_
|
|
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");
|
|
116
206
|
__UUPSUpgradeable_init();
|
|
207
|
+
__ERC721_init(name_, symbol_);
|
|
117
208
|
__Pausable_init();
|
|
118
|
-
__PolicyPool_init_unchained();
|
|
209
|
+
__PolicyPool_init_unchained(treasury_);
|
|
210
|
+
}
|
|
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;
|
|
119
217
|
}
|
|
120
218
|
|
|
121
219
|
// solhint-disable-next-line func-name-mixedcase
|
|
122
|
-
function __PolicyPool_init_unchained() internal
|
|
123
|
-
|
|
124
|
-
_policyNFT.connect();
|
|
220
|
+
function __PolicyPool_init_unchained(address treasury_) internal onlyInitializing {
|
|
221
|
+
_setTreasury(treasury_);
|
|
125
222
|
}
|
|
126
223
|
|
|
127
224
|
// solhint-disable-next-line no-empty-blocks
|
|
128
225
|
function _authorizeUpgrade(address) internal override onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {}
|
|
129
226
|
|
|
227
|
+
/**
|
|
228
|
+
* @dev Pauses the contract. When the contract is paused, several operations are rejected: deposits, withdrawals, new
|
|
229
|
+
* policies, policy resolution and expiration, nft transfers.
|
|
230
|
+
*
|
|
231
|
+
* Requirements:
|
|
232
|
+
* - Must be executed by a user with the {GUARDIAN_ROLE}.
|
|
233
|
+
*/
|
|
130
234
|
function pause() public onlyRole(GUARDIAN_ROLE) {
|
|
131
235
|
_pause();
|
|
132
236
|
}
|
|
133
237
|
|
|
238
|
+
/**
|
|
239
|
+
* @dev Unpauses the contract. All the operations disabled when the contract was paused are re-enabled.
|
|
240
|
+
*
|
|
241
|
+
* Requirements:
|
|
242
|
+
* - Must be called by a user with either the {GUARDIAN_ROLE} or a {LEVEL1_ROLE}.
|
|
243
|
+
*/
|
|
134
244
|
function unpause() public onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {
|
|
135
245
|
_unpause();
|
|
136
246
|
}
|
|
137
247
|
|
|
138
|
-
function
|
|
139
|
-
return
|
|
248
|
+
function access() external view virtual override returns (IAccessManager) {
|
|
249
|
+
return _access;
|
|
140
250
|
}
|
|
141
251
|
|
|
142
252
|
function currency() external view virtual override returns (IERC20Metadata) {
|
|
143
253
|
return _currency;
|
|
144
254
|
}
|
|
145
255
|
|
|
146
|
-
function
|
|
147
|
-
|
|
256
|
+
function _setTreasury(address treasury_) internal {
|
|
257
|
+
require(treasury_ != address(0), "PolicyPool: treasury cannot be the zero address");
|
|
258
|
+
_treasury = treasury_;
|
|
259
|
+
emit ComponentChanged(IAccessManager.GovernanceActions.setTreasury, _treasury);
|
|
148
260
|
}
|
|
149
261
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
262
|
+
/**
|
|
263
|
+
* @dev Changes the address of the treasury, the one that receives the protocol fees.
|
|
264
|
+
*
|
|
265
|
+
* Requirements:
|
|
266
|
+
* - Must be called by a user with the {LEVEL1_ROLE}.
|
|
267
|
+
*
|
|
268
|
+
* Events:
|
|
269
|
+
* - Emits {ComponentChanged} with action = setTreasury and the address of the new treasury.
|
|
270
|
+
*/
|
|
271
|
+
function setTreasury(address treasury_) external onlyRole(LEVEL1_ROLE) {
|
|
272
|
+
_setTreasury(treasury_);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @dev Returns the address of the treasury, the one that receives the protocol fees.
|
|
277
|
+
*/
|
|
278
|
+
function treasury() external view override returns (address) {
|
|
279
|
+
return _treasury;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @dev Adds a new component (either an {EToken}, {RiskModule} or {PremiumsAccount}) to the protocol. The component
|
|
284
|
+
* status will be `active`.
|
|
285
|
+
*
|
|
286
|
+
* Requirements:
|
|
287
|
+
* - Must be called by a user with the {LEVEL1_ROLE}
|
|
288
|
+
* - The component wasn't added before.
|
|
289
|
+
*
|
|
290
|
+
* Events:
|
|
291
|
+
* - Emits {ComponentStatusChanged} with status active.
|
|
292
|
+
*
|
|
293
|
+
* @param component The address of component contract. Must be an {EToken}, {RiskModule} or {PremiumsAccount} linked
|
|
294
|
+
* to this specific {PolicyPool} and matching the `kind` specified in the next paramter.
|
|
295
|
+
* @param kind The type of component to be added.
|
|
296
|
+
*/
|
|
297
|
+
function addComponent(IPolicyPoolComponent component, ComponentKind kind)
|
|
298
|
+
external
|
|
299
|
+
onlyRole(LEVEL1_ROLE)
|
|
300
|
+
{
|
|
301
|
+
Component storage comp = _components[component];
|
|
302
|
+
require(comp.status == ComponentStatus.inactive, "Component already in the pool");
|
|
303
|
+
require(component.policyPool() == this, "Component not linked to this pool");
|
|
304
|
+
|
|
154
305
|
require(
|
|
155
|
-
|
|
156
|
-
|
|
306
|
+
(kind == ComponentKind.eToken && component.supportsInterface(type(IEToken).interfaceId)) ||
|
|
307
|
+
(kind == ComponentKind.premiumsAccount &&
|
|
308
|
+
component.supportsInterface(type(IPremiumsAccount).interfaceId)) ||
|
|
309
|
+
(kind == ComponentKind.riskModule &&
|
|
310
|
+
component.supportsInterface(type(IRiskModule).interfaceId)),
|
|
311
|
+
"PolicyPool: Not the right kind"
|
|
157
312
|
);
|
|
158
313
|
|
|
159
|
-
|
|
160
|
-
|
|
314
|
+
comp.status = ComponentStatus.active;
|
|
315
|
+
comp.kind = kind;
|
|
316
|
+
if (kind == ComponentKind.premiumsAccount) {
|
|
317
|
+
IPremiumsAccount pa = IPremiumsAccount(address(component));
|
|
318
|
+
IEToken etk = pa.juniorEtk();
|
|
319
|
+
if (address(etk) != address(0)) {
|
|
320
|
+
etk.addBorrower(address(pa));
|
|
321
|
+
}
|
|
322
|
+
etk = pa.seniorEtk();
|
|
323
|
+
if (address(etk) != address(0)) {
|
|
324
|
+
etk.addBorrower(address(pa));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
emit ComponentStatusChanged(component, kind, ComponentStatus.active);
|
|
161
328
|
}
|
|
162
329
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
330
|
+
/**
|
|
331
|
+
* @dev Removes a component from the protocol. The component needs to be in `deprecated` status before doing this
|
|
332
|
+
* operation.
|
|
333
|
+
*
|
|
334
|
+
* Requirements:
|
|
335
|
+
* - Must be called by a user with the {LEVEL1_ROLE}
|
|
336
|
+
* - The component status is `deprecated`.
|
|
337
|
+
*
|
|
338
|
+
* Events:
|
|
339
|
+
* - Emits {ComponentStatusChanged} with status inactive.
|
|
340
|
+
*
|
|
341
|
+
* @param component The address of component contract. Must be a component added before.
|
|
342
|
+
*/
|
|
343
|
+
function removeComponent(IPolicyPoolComponent component) external onlyRole(LEVEL1_ROLE) {
|
|
344
|
+
Component storage comp = _components[component];
|
|
345
|
+
require(comp.status == ComponentStatus.deprecated, "Component not deprecated");
|
|
346
|
+
if (comp.kind == ComponentKind.eToken) {
|
|
347
|
+
require(
|
|
348
|
+
IEToken(address(component)).totalSupply() == 0,
|
|
349
|
+
"EToken has liquidity, can't be removed"
|
|
350
|
+
);
|
|
351
|
+
} else if (comp.kind == ComponentKind.riskModule) {
|
|
352
|
+
require(
|
|
353
|
+
IRiskModule(address(component)).activeExposure() == 0,
|
|
354
|
+
"Can't remove a module with active policies"
|
|
355
|
+
);
|
|
356
|
+
} else if (comp.kind == ComponentKind.premiumsAccount) {
|
|
357
|
+
IPremiumsAccount pa = IPremiumsAccount(address(component));
|
|
358
|
+
require(pa.purePremiums() == 0, "Can't remove a PremiumsAccount with premiums");
|
|
359
|
+
IEToken etk = pa.juniorEtk();
|
|
360
|
+
if (address(etk) != address(0)) {
|
|
361
|
+
etk.removeBorrower(address(pa));
|
|
362
|
+
}
|
|
363
|
+
etk = pa.seniorEtk();
|
|
364
|
+
if (address(etk) != address(0)) {
|
|
365
|
+
etk.removeBorrower(address(pa));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
emit ComponentStatusChanged(component, comp.kind, ComponentStatus.inactive);
|
|
369
|
+
delete _components[component];
|
|
169
370
|
}
|
|
170
371
|
|
|
171
|
-
|
|
372
|
+
/**
|
|
373
|
+
* @dev Changes the status of a component.
|
|
374
|
+
*
|
|
375
|
+
* Requirements:
|
|
376
|
+
* - Must be called by a user with the {LEVEL1_ROLE} if the new status is `active` or `deprecated`.
|
|
377
|
+
* - Must be called by a user with the {GUARDIAN_ROLE} if the new status is `suspended`.
|
|
378
|
+
*
|
|
379
|
+
* Events:
|
|
380
|
+
* - Emits {ComponentStatusChanged} with the new status.
|
|
381
|
+
*
|
|
382
|
+
* @param component The address of component contract. Must be a component added before.
|
|
383
|
+
* @param newStatus The new status, must be either `active`, `deprecated` or `suspended`.
|
|
384
|
+
*/
|
|
385
|
+
function changeComponentStatus(IPolicyPoolComponent component, ComponentStatus newStatus)
|
|
172
386
|
external
|
|
173
387
|
onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
|
|
174
388
|
{
|
|
175
|
-
|
|
176
|
-
require(status !=
|
|
389
|
+
Component storage comp = _components[component];
|
|
390
|
+
require(comp.status != ComponentStatus.inactive, "Component not found");
|
|
177
391
|
require(
|
|
178
|
-
newStatus
|
|
179
|
-
|
|
392
|
+
(newStatus == ComponentStatus.active && _access.hasRole(LEVEL1_ROLE, _msgSender())) ||
|
|
393
|
+
(newStatus == ComponentStatus.suspended && _access.hasRole(GUARDIAN_ROLE, _msgSender())) ||
|
|
394
|
+
(newStatus == ComponentStatus.deprecated && _access.hasRole(LEVEL1_ROLE, _msgSender())),
|
|
395
|
+
"Only GUARDIAN can suspend / Only LEVEL1 can activate/deprecate"
|
|
180
396
|
);
|
|
181
|
-
|
|
182
|
-
emit
|
|
397
|
+
comp.status = newStatus;
|
|
398
|
+
emit ComponentStatusChanged(component, comp.kind, newStatus);
|
|
183
399
|
}
|
|
184
400
|
|
|
185
|
-
|
|
186
|
-
|
|
401
|
+
/**
|
|
402
|
+
* @dev Returns the status of a component.
|
|
403
|
+
*
|
|
404
|
+
* @param component The address of the component
|
|
405
|
+
* @return The status of the component. See {ComponentStatus}
|
|
406
|
+
*/
|
|
407
|
+
function getComponentStatus(IPolicyPoolComponent component)
|
|
408
|
+
external
|
|
409
|
+
view
|
|
410
|
+
returns (ComponentStatus)
|
|
411
|
+
{
|
|
412
|
+
return _components[component].status;
|
|
187
413
|
}
|
|
188
414
|
|
|
189
|
-
function
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
emit PremiumsAccountStatusChanged(pa, ETokenStatus.active);
|
|
206
|
-
// TODO: functions for deactivating premiumsAccount (and remove them as borrower)
|
|
415
|
+
function _etkStatus(IEToken eToken) internal view returns (ComponentStatus) {
|
|
416
|
+
Component storage comp = _components[IPolicyPoolComponent(address(eToken))];
|
|
417
|
+
require(comp.kind == ComponentKind.eToken, "Component is not an eToken");
|
|
418
|
+
return comp.status;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function _rmStatus(IRiskModule riskModule) internal view returns (ComponentStatus) {
|
|
422
|
+
Component storage comp = _components[IPolicyPoolComponent(address(riskModule))];
|
|
423
|
+
require(comp.kind == ComponentKind.riskModule, "Component is not a RiskModule");
|
|
424
|
+
return comp.status;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function _paStatus(IPremiumsAccount premiumsAccount) internal view returns (ComponentStatus) {
|
|
428
|
+
Component storage comp = _components[IPolicyPoolComponent(address(premiumsAccount))];
|
|
429
|
+
require(comp.kind == ComponentKind.premiumsAccount, "Component is not a PremiumsAccount");
|
|
430
|
+
return comp.status;
|
|
207
431
|
}
|
|
208
432
|
|
|
209
433
|
function deposit(IEToken eToken, uint256 amount) external override whenNotPaused {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
_currency.safeTransferFrom(
|
|
213
|
-
eToken.deposit(
|
|
434
|
+
require(_etkStatus(eToken) == ComponentStatus.active, "eToken is not active");
|
|
435
|
+
uint256 balanceBefore = _currency.balanceOf(address(eToken));
|
|
436
|
+
_currency.safeTransferFrom(_msgSender(), address(eToken), amount);
|
|
437
|
+
eToken.deposit(_msgSender(), _currency.balanceOf(address(eToken)) - balanceBefore);
|
|
214
438
|
}
|
|
215
439
|
|
|
216
440
|
function withdraw(IEToken eToken, uint256 amount)
|
|
@@ -219,48 +443,54 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
|
219
443
|
whenNotPaused
|
|
220
444
|
returns (uint256)
|
|
221
445
|
{
|
|
222
|
-
|
|
446
|
+
ComponentStatus etkStatus = _etkStatus(eToken);
|
|
223
447
|
require(
|
|
224
|
-
etkStatus ==
|
|
448
|
+
etkStatus == ComponentStatus.active || etkStatus == ComponentStatus.deprecated,
|
|
225
449
|
"eToken not found or withdraws not allowed"
|
|
226
450
|
);
|
|
227
|
-
address provider =
|
|
451
|
+
address provider = _msgSender();
|
|
228
452
|
return eToken.withdraw(provider, amount);
|
|
229
453
|
}
|
|
230
454
|
|
|
231
455
|
function newPolicy(
|
|
232
456
|
Policy.PolicyData memory policy,
|
|
233
|
-
address
|
|
457
|
+
address payer,
|
|
458
|
+
address policyHolder,
|
|
234
459
|
uint96 internalId
|
|
235
460
|
) external override whenNotPaused returns (uint256) {
|
|
461
|
+
// Checks
|
|
236
462
|
IRiskModule rm = policy.riskModule;
|
|
237
|
-
require(address(rm) ==
|
|
238
|
-
|
|
463
|
+
require(address(rm) == _msgSender(), "Only the RM can create new policies");
|
|
464
|
+
require(_rmStatus(rm) == ComponentStatus.active, "RM module not found or not active");
|
|
465
|
+
IPremiumsAccount pa = rm.premiumsAccount();
|
|
466
|
+
require(_paStatus(pa) == ComponentStatus.active, "PremiumsAccount not found or not active");
|
|
467
|
+
|
|
468
|
+
// Effects
|
|
239
469
|
policy.id = (uint256(uint160(address(rm))) << 96) + internalId;
|
|
470
|
+
require(_policies[policy.id] == bytes32(0), "Policy already exists");
|
|
240
471
|
_policies[policy.id] = policy.hash();
|
|
241
|
-
|
|
472
|
+
|
|
473
|
+
// Interactions
|
|
242
474
|
pa.policyCreated(policy);
|
|
243
|
-
_policyNFT.safeMint(customer, policy.id);
|
|
244
475
|
|
|
245
476
|
// Distribute the premium
|
|
246
|
-
_currency.safeTransferFrom(
|
|
247
|
-
if (policy.srCoc > 0)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
477
|
+
_currency.safeTransferFrom(payer, address(pa), policy.purePremium);
|
|
478
|
+
if (policy.srCoc > 0) _currency.safeTransferFrom(payer, address(pa.seniorEtk()), policy.srCoc);
|
|
479
|
+
if (policy.jrCoc > 0) _currency.safeTransferFrom(payer, address(pa.juniorEtk()), policy.jrCoc);
|
|
480
|
+
_currency.safeTransferFrom(payer, _treasury, policy.ensuroCommission);
|
|
481
|
+
if (policy.partnerCommission > 0 && payer != rm.wallet())
|
|
482
|
+
_currency.safeTransferFrom(payer, rm.wallet(), policy.partnerCommission);
|
|
483
|
+
/**
|
|
484
|
+
* This code does up to 5 ERC20 transfers. This can be avoided to reduce the gas cost, by implementing delayed
|
|
485
|
+
* transfers. This might be considered in the future, but to avoid increasing the complexity and since so far we
|
|
486
|
+
* operate on low gas-cost blockchains, we keep it as it is.
|
|
487
|
+
*/
|
|
488
|
+
_safeMint(policyHolder, policy.id, "");
|
|
255
489
|
|
|
256
490
|
emit NewPolicy(rm, policy);
|
|
257
491
|
return policy.id;
|
|
258
492
|
}
|
|
259
493
|
|
|
260
|
-
function _balance() internal view returns (uint256) {
|
|
261
|
-
return _currency.balanceOf(address(this));
|
|
262
|
-
}
|
|
263
|
-
|
|
264
494
|
function _validatePolicy(Policy.PolicyData memory policy) internal view {
|
|
265
495
|
require(policy.id != 0 && policy.hash() == _policies[policy.id], "Policy not found");
|
|
266
496
|
}
|
|
@@ -286,10 +516,17 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
|
286
516
|
return _resolvePolicy(policy, customerWon ? policy.payout : 0, false);
|
|
287
517
|
}
|
|
288
518
|
|
|
519
|
+
function isActive(uint256 policyId) external view override returns (bool) {
|
|
520
|
+
return _policies[policyId] != bytes32(0);
|
|
521
|
+
}
|
|
522
|
+
|
|
289
523
|
/**
|
|
290
524
|
* @dev Internal function that handles the different alternative resolutions for a policy, with or without payout and
|
|
291
525
|
* expiration.
|
|
292
526
|
*
|
|
527
|
+
* Events:
|
|
528
|
+
* - Emits {PolicyResolved} with the payout
|
|
529
|
+
*
|
|
293
530
|
* @param policy A policy created with {Policy-initialize}
|
|
294
531
|
* @param payout The amount to paid to the policyholder
|
|
295
532
|
* @param expired True for expiration resolution (`payout` must be 0)
|
|
@@ -299,26 +536,39 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
|
299
536
|
uint256 payout,
|
|
300
537
|
bool expired
|
|
301
538
|
) internal {
|
|
539
|
+
// Checks
|
|
302
540
|
_validatePolicy(policy);
|
|
303
541
|
IRiskModule rm = policy.riskModule;
|
|
304
|
-
require(expired || address(rm) ==
|
|
542
|
+
require(expired || address(rm) == _msgSender(), "Only the RM can resolve policies");
|
|
305
543
|
require(payout == 0 || policy.expiration > block.timestamp, "Can't pay expired policy");
|
|
306
|
-
|
|
544
|
+
ComponentStatus compStatus = _rmStatus(rm);
|
|
545
|
+
require(
|
|
546
|
+
compStatus == ComponentStatus.active || compStatus == ComponentStatus.deprecated,
|
|
547
|
+
"Module must be active or deprecated to process resolutions"
|
|
548
|
+
);
|
|
307
549
|
require(payout <= policy.payout, "payout > policy.payout");
|
|
308
550
|
|
|
309
551
|
bool customerWon = payout > 0;
|
|
310
552
|
|
|
553
|
+
IPremiumsAccount pa = rm.premiumsAccount();
|
|
554
|
+
compStatus = _paStatus(pa);
|
|
555
|
+
require(
|
|
556
|
+
compStatus == ComponentStatus.active || compStatus == ComponentStatus.deprecated,
|
|
557
|
+
"PremiumsAccount must be active or deprecated to process resolutions"
|
|
558
|
+
);
|
|
559
|
+
// Effects
|
|
560
|
+
delete _policies[policy.id];
|
|
561
|
+
// Interactions
|
|
311
562
|
if (customerWon) {
|
|
312
|
-
address policyOwner =
|
|
313
|
-
|
|
563
|
+
address policyOwner = ownerOf(policy.id);
|
|
564
|
+
pa.policyResolvedWithPayout(policyOwner, policy, payout);
|
|
314
565
|
} else {
|
|
315
|
-
|
|
566
|
+
pa.policyExpired(policy);
|
|
316
567
|
}
|
|
317
568
|
|
|
318
569
|
rm.releaseExposure(policy.payout);
|
|
319
570
|
|
|
320
571
|
emit PolicyResolved(policy.riskModule, policy.id, payout);
|
|
321
|
-
delete _policies[policy.id];
|
|
322
572
|
if (payout > 0) {
|
|
323
573
|
_notifyPayout(policy.id, payout);
|
|
324
574
|
} else {
|
|
@@ -331,7 +581,7 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
|
331
581
|
* contract explicitly reverts. Doesn't reverts is the callback is not implemented.
|
|
332
582
|
*/
|
|
333
583
|
function _notifyPayout(uint256 policyId, uint256 payout) internal {
|
|
334
|
-
address customer =
|
|
584
|
+
address customer = ownerOf(policyId);
|
|
335
585
|
if (!AddressUpgradeable.isContract(customer)) return;
|
|
336
586
|
try
|
|
337
587
|
IPolicyHolder(customer).onPayoutReceived(_msgSender(), address(this), policyId, payout)
|
|
@@ -356,7 +606,7 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
|
356
606
|
* @dev Notifies the expiration with a callback if the policyholder is a contract. Never reverts.
|
|
357
607
|
*/
|
|
358
608
|
function _notifyExpiration(uint256 policyId) internal {
|
|
359
|
-
address customer =
|
|
609
|
+
address customer = ownerOf(policyId);
|
|
360
610
|
if (!AddressUpgradeable.isContract(customer)) return;
|
|
361
611
|
try IPolicyHolder(customer).onPolicyExpired(_msgSender(), address(this), policyId) returns (
|
|
362
612
|
bytes4
|
|
@@ -366,4 +616,19 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
|
|
|
366
616
|
return;
|
|
367
617
|
}
|
|
368
618
|
}
|
|
619
|
+
|
|
620
|
+
function _beforeTokenTransfer(
|
|
621
|
+
address from,
|
|
622
|
+
address to,
|
|
623
|
+
uint256 tokenId
|
|
624
|
+
) internal override whenNotPaused {
|
|
625
|
+
super._beforeTokenTransfer(from, to, tokenId);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
630
|
+
* variables without shifting down storage in the inheritance chain.
|
|
631
|
+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
632
|
+
*/
|
|
633
|
+
uint256[47] private __gap;
|
|
369
634
|
}
|