@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.
- package/README.md +31 -14
- package/build/contracts/AccessManager.sol/AccessManager.json +7 -2
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +28 -3
- package/build/contracts/EToken.sol/EToken.json +21 -15
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +21 -2
- package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +26 -1
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +40 -2
- package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +19 -0
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +21 -15
- package/build/contracts/Reserve.sol/Reserve.json +19 -13
- package/build/contracts/RiskModule.sol/RiskModule.json +19 -0
- package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +1110 -0
- package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +137 -2
- package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
- package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +26 -1
- package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +38 -0
- package/build/contracts/interfaces/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +19 -0
- package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +2 -2
- 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 +115 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +111 -2
- package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
- package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
- package/contracts/AccessManager.sol +31 -2
- package/contracts/ERC4626AssetManager.sol +8 -2
- package/contracts/EToken.sol +129 -60
- package/contracts/LPManualWhitelist.sol +18 -2
- package/contracts/LiquidityThresholdAssetManager.sol +28 -12
- package/contracts/Policy.sol +5 -3
- package/contracts/PolicyPool.sol +221 -29
- package/contracts/PolicyPoolComponent.sol +36 -25
- package/contracts/PremiumsAccount.sol +220 -32
- package/contracts/Reserve.sol +130 -19
- package/contracts/RiskModule.sol +53 -20
- package/contracts/SignedQuoteRiskModule.sol +310 -0
- package/contracts/TimeScaled.sol +19 -15
- package/contracts/TrustfulRiskModule.sol +107 -6
- package/contracts/dependencies/WadRayMath.sol +126 -0
- package/contracts/interfaces/IAccessManager.sol +68 -13
- package/contracts/interfaces/IAssetManager.sol +69 -2
- package/contracts/interfaces/IEToken.sol +4 -1
- package/contracts/interfaces/ILPWhitelist.sol +17 -0
- package/contracts/interfaces/IPolicyPool.sol +19 -1
- package/contracts/interfaces/IPolicyPoolComponent.sol +5 -1
- package/contracts/interfaces/IRiskModule.sol +67 -4
- 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 +31 -0
- package/contracts/mocks/RiskModuleMock.sol +7 -1
- package/js/deploy.js +734 -0
- package/js/test-utils.js +184 -69
- package/package.json +4 -2
- package/scripts/change-pragma.sh +19 -0
- package/scripts/deploySmokeTest-fork.sh +53 -0
- package/scripts/deploySmokeTest.sh +51 -0
- package/scripts/storageLayout.js +33 -0
- package/scripts/utils.sh +65 -0
- 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 {
|
|
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
|
|
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 =
|
|
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
|
|
100
|
-
* After the call, there should be
|
|
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
|
-
|
|
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 +=
|
|
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 -=
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
}
|
package/contracts/Policy.sol
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/contracts/PolicyPool.sol
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
|
159
|
-
|
|
220
|
+
function __PolicyPool_init_unchained(address treasury_) internal onlyInitializing {
|
|
221
|
+
_setTreasury(treasury_);
|
|
160
222
|
}
|
|
161
223
|
|
|
162
|
-
|
|
163
|
-
|
|
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
|
|
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,
|
|
251
|
-
(newStatus == ComponentStatus.suspended && _access.hasRole(GUARDIAN_ROLE,
|
|
252
|
-
(newStatus == ComponentStatus.deprecated && _access.hasRole(LEVEL1_ROLE,
|
|
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.
|
|
288
|
-
|
|
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 =
|
|
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) ==
|
|
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
|
-
|
|
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) ==
|
|
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
|
}
|