@ensuro/core 1.2.0 → 1.2.1

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 (29) hide show
  1. package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +2 -51
  2. package/build/contracts/EToken.sol/EToken.json +2 -2
  3. package/build/contracts/Exchange.sol/Exchange.json +483 -0
  4. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +2 -2
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
  6. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +2 -2
  7. package/build/contracts/PolicyPool.sol/PolicyPool.json +2 -2
  8. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +28 -2
  9. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +2 -2
  10. package/build/contracts/extras/IPriceRiskModule.sol/IPriceRiskModule.json +253 -0
  11. package/build/contracts/extras/PriceRiskModule.sol/PriceRiskModule.json +1036 -0
  12. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +2 -2
  13. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +2 -2
  14. package/build/interfaces/IExchange.sol/IExchange.json +153 -0
  15. package/build/interfaces/IExchange.sol/IPriceOracle.json +30 -0
  16. package/build/interfaces/IPolicyPoolConfig.sol/IPolicyPoolConfig.json +13 -0
  17. package/contracts/AaveAssetManager.sol +35 -63
  18. package/contracts/BaseAssetManager.sol +55 -37
  19. package/contracts/EToken.sol +3 -19
  20. package/contracts/Exchange.sol +142 -0
  21. package/contracts/PolicyPool.sol +13 -0
  22. package/contracts/PolicyPoolComponent.sol +4 -0
  23. package/contracts/PolicyPoolConfig.sol +16 -0
  24. package/contracts/RiskModule.sol +5 -26
  25. package/contracts/extras/IPriceRiskModule.sol +41 -0
  26. package/contracts/extras/PriceRiskModule.sol +243 -0
  27. package/interfaces/IExchange.sol +47 -0
  28. package/interfaces/IPolicyPoolConfig.sol +4 -0
  29. package/package.json +1 -1
@@ -37,11 +37,6 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
37
37
 
38
38
  address internal _wallet; // Address of the RiskModule provider
39
39
 
40
- modifier validateParamsAfterChange() {
41
- _;
42
- _validateParameters();
43
- }
44
-
45
40
  /// @custom:oz-upgrades-unsafe-allow constructor
46
41
  // solhint-disable-next-line no-empty-blocks
47
42
  constructor(IPolicyPool policyPool_) PolicyPoolComponent(policyPool_) {}
@@ -104,7 +99,7 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
104
99
  }
105
100
 
106
101
  // runs validation on RiskModule parameters
107
- function _validateParameters() internal view {
102
+ function _validateParameters() internal view override {
108
103
  require(
109
104
  _scrPercentage <= WadRayMath.RAY && _scrPercentage > 0,
110
105
  "Validation: scrPercentage must be <=1"
@@ -159,7 +154,6 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
159
154
  function setScrPercentage(uint256 newScrPercentage)
160
155
  external
161
156
  onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
162
- validateParamsAfterChange
163
157
  {
164
158
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
165
159
  require(
@@ -174,11 +168,7 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
174
168
  );
175
169
  }
176
170
 
177
- function setMoc(uint256 newMoc)
178
- external
179
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
180
- validateParamsAfterChange
181
- {
171
+ function setMoc(uint256 newMoc) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
182
172
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
183
173
  require(!tweak || _isTweakRay(_moc, newMoc, 1e26), "Tweak exceeded: moc tweaks only up to 10%");
184
174
  _moc = newMoc;
@@ -188,7 +178,6 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
188
178
  function setScrInterestRate(uint256 newScrInterestRate)
189
179
  external
190
180
  onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
191
- validateParamsAfterChange
192
181
  {
193
182
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
194
183
  require(
@@ -203,11 +192,7 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
203
192
  );
204
193
  }
205
194
 
206
- function setEnsuroFee(uint256 newEnsuroFee)
207
- external
208
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
209
- validateParamsAfterChange
210
- {
195
+ function setEnsuroFee(uint256 newEnsuroFee) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
211
196
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
212
197
  require(
213
198
  !tweak || _isTweakRay(_ensuroFee, newEnsuroFee, 3e26),
@@ -220,7 +205,6 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
220
205
  function setMaxScrPerPolicy(uint256 newMaxScrPerPolicy)
221
206
  external
222
207
  onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
223
- validateParamsAfterChange
224
208
  {
225
209
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
226
210
  require(
@@ -238,7 +222,6 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
238
222
  function setScrLimit(uint256 newScrLimit)
239
223
  external
240
224
  onlyPoolRole3(LEVEL1_ROLE, LEVEL2_ROLE, LEVEL3_ROLE)
241
- validateParamsAfterChange
242
225
  {
243
226
  bool tweak = !hasPoolRole(LEVEL2_ROLE) && !hasPoolRole(LEVEL1_ROLE);
244
227
  require(
@@ -256,11 +239,7 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
256
239
  _parameterChanged(IPolicyPoolConfig.GovernanceActions.setScrLimit, newScrLimit, tweak);
257
240
  }
258
241
 
259
- function setWallet(address wallet_)
260
- external
261
- onlyRole(RM_PROVIDER_ROLE)
262
- validateParamsAfterChange
263
- {
242
+ function setWallet(address wallet_) external onlyRole(RM_PROVIDER_ROLE) {
264
243
  _wallet = wallet_;
265
244
  _parameterChanged(
266
245
  IPolicyPoolConfig.GovernanceActions.setWallet,
@@ -273,7 +252,7 @@ abstract contract RiskModule is IRiskModule, AccessControlUpgradeable, PolicyPoo
273
252
  uint256 payout,
274
253
  uint256 lossProb,
275
254
  uint40 expiration
276
- ) external view returns (uint256) {
255
+ ) public view returns (uint256) {
277
256
  uint256 purePremium = payout.wadToRay().rayMul(lossProb.rayMul(moc())).rayToWad();
278
257
  uint256 scr = payout.wadMul(scrPercentage().rayToWad()) - purePremium;
279
258
  uint256 interestRate = (
@@ -0,0 +1,41 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {IRiskModule} from "../../interfaces/IRiskModule.sol";
5
+ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
6
+
7
+ /**
8
+ * @title IPriceRiskModule interface
9
+ * @dev Interface for price risk module
10
+ * @author Ensuro
11
+ */
12
+ interface IPriceRiskModule is IRiskModule {
13
+ /**
14
+ * @dev Returns the premium and lossProb of the policy
15
+ * @param triggerPrice Price of the asset_ that will trigger the policy (expressed in _currency)
16
+ * @param lower If true -> triggers if the price is lower, If false -> triggers if the price is higher
17
+ * @param payout Expressed in policyPool.currency()
18
+ * @param expiration Expiration of the policy
19
+ * @return premium Premium that needs to be paid
20
+ * @return lossProb Probability of paying the maximum payout
21
+ */
22
+ function pricePolicy(
23
+ uint256 triggerPrice,
24
+ bool lower,
25
+ uint256 payout,
26
+ uint40 expiration
27
+ ) external view returns (uint256 premium, uint256 lossProb);
28
+
29
+ function newPolicy(
30
+ uint256 triggerPrice,
31
+ bool lower,
32
+ uint256 payout,
33
+ uint40 expiration
34
+ ) external returns (uint256);
35
+
36
+ function triggerPolicy(uint256 policyId) external;
37
+
38
+ function referenceCurrency() external view returns (IERC20Metadata);
39
+
40
+ function asset() external view returns (IERC20Metadata);
41
+ }
@@ -0,0 +1,243 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
5
+ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
6
+ import {IPolicyPool} from "../../interfaces/IPolicyPool.sol";
7
+ import {IPolicyPoolConfig} from "../../interfaces/IPolicyPoolConfig.sol";
8
+ import {RiskModule} from "../RiskModule.sol";
9
+ import {Policy} from "../Policy.sol";
10
+ import {WadRayMath} from "../WadRayMath.sol";
11
+ import {IPriceRiskModule} from "./IPriceRiskModule.sol";
12
+
13
+ /**
14
+ * @title PriceRiskModule
15
+ * @dev Risk Module that triggers the payout if the price of an asset is lower or higher than trigger price
16
+ * @custom:security-contact security@ensuro.co
17
+ * @author Ensuro
18
+ */
19
+ contract PriceRiskModule is RiskModule, IPriceRiskModule {
20
+ using SafeERC20 for IERC20Metadata;
21
+ using WadRayMath for uint256;
22
+
23
+ bytes32 public constant CUSTOMER_ROLE = keccak256("CUSTOMER_ROLE");
24
+ bytes32 public constant PRICER_ROLE = keccak256("PRICER_ROLE");
25
+
26
+ uint8 public constant PRICE_SLOTS = 30;
27
+
28
+ /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
29
+ IERC20Metadata internal immutable _asset;
30
+ /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
31
+ IERC20Metadata internal immutable _referenceCurrency;
32
+ /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
33
+ uint256 internal immutable _slotSize;
34
+
35
+ struct PolicyData {
36
+ Policy.PolicyData ensuroPolicy;
37
+ uint256 triggerPrice;
38
+ bool lower; // If true -> triggers if the price is lower, If false -> triggers if the price is higher
39
+ }
40
+
41
+ mapping(uint256 => PolicyData) internal _policies;
42
+
43
+ // Duration (in hours) of the protection * (1 if lower else -1) => cummulative density function
44
+ // [0] = prob of ([0, infinite%)
45
+ // [1] = prob of ([1, infinite%)
46
+ // ...
47
+ // [PRICE_SLOTS - 1] = prob of ([PRICE_SLOTS - 1, -infinite%)
48
+ mapping(int40 => uint256[PRICE_SLOTS]) internal _cdf;
49
+
50
+ uint96 internal _internalId;
51
+
52
+ event NewPricePolicy(
53
+ address indexed customer,
54
+ uint256 policyId,
55
+ uint256 triggerPrice,
56
+ bool lower
57
+ );
58
+
59
+ /**
60
+ * @dev Constructs the LiquidationProtectionRiskModule
61
+ * @param policyPool_ The policyPool
62
+ * @param asset_ Address of the asset which price want to protect
63
+ * @param referenceCurrency_ Address of the comparison asset (price will be price(asset)/price(currency))
64
+ * @param slotSize_ Size of each percentage slot in the pdf function (in wad)
65
+ */
66
+ /// @custom:oz-upgrades-unsafe-allow constructor
67
+ constructor(
68
+ IPolicyPool policyPool_,
69
+ IERC20Metadata asset_,
70
+ IERC20Metadata referenceCurrency_,
71
+ uint256 slotSize_
72
+ ) RiskModule(policyPool_) {
73
+ _asset = asset_;
74
+ _referenceCurrency = referenceCurrency_;
75
+ _slotSize = slotSize_;
76
+ }
77
+
78
+ /**
79
+ * @dev Initializes the RiskModule
80
+ * @param name_ Name of the Risk Module
81
+ * @param scrPercentage_ Solvency Capital Requirement percentage, to calculate
82
+ * capital requirement as % of (payout - premium) (in ray)
83
+ * @param ensuroFee_ % of premium that will go for Ensuro treasury (in ray)
84
+ * @param scrInterestRate_ cost of capital (in ray)
85
+ * @param maxScrPerPolicy_ Max SCR to be allocated to this module (in wad)
86
+ * @param scrLimit_ Max SCR to be allocated to this module (in wad)
87
+ * @param wallet_ Address of the RiskModule provider
88
+ */
89
+ function initialize(
90
+ string memory name_,
91
+ uint256 scrPercentage_,
92
+ uint256 ensuroFee_,
93
+ uint256 scrInterestRate_,
94
+ uint256 maxScrPerPolicy_,
95
+ uint256 scrLimit_,
96
+ address wallet_
97
+ ) public initializer {
98
+ __RiskModule_init(
99
+ name_,
100
+ scrPercentage_,
101
+ ensuroFee_,
102
+ scrInterestRate_,
103
+ maxScrPerPolicy_,
104
+ scrLimit_,
105
+ wallet_
106
+ );
107
+ _internalId = 1;
108
+ }
109
+
110
+ function _getCurrentPrice() internal view returns (uint256) {
111
+ uint256 ret = policyPool().config().exchange().convert(
112
+ address(_asset),
113
+ address(_referenceCurrency),
114
+ 10**_asset.decimals()
115
+ );
116
+ require(ret != 0, "Price not available");
117
+ return ret;
118
+ }
119
+
120
+ /**
121
+ * @dev Returns the premium and lossProb of the policy
122
+ * @param triggerPrice Price of the asset_ that will trigger the policy (expressed in _referenceCurrency)
123
+ * @param lower If true -> triggers if the price is lower, If false -> triggers if the price is higher
124
+ * @param payout Expressed in policyPool.currency()
125
+ * @param expiration Expiration of the policy
126
+ * @return premium Premium that needs to be paid
127
+ * @return lossProb Probability of paying the maximum payout
128
+ */
129
+ function pricePolicy(
130
+ uint256 triggerPrice,
131
+ bool lower,
132
+ uint256 payout,
133
+ uint40 expiration
134
+ ) public view override returns (uint256 premium, uint256 lossProb) {
135
+ uint256 currentPrice = _getCurrentPrice();
136
+ require(
137
+ (lower && currentPrice > triggerPrice) || (!lower && currentPrice < triggerPrice),
138
+ "Price already at trigger value"
139
+ );
140
+ lossProb = _computeLossProb(currentPrice, triggerPrice, expiration - uint40(block.timestamp));
141
+ if (lossProb == 0) return (0, 0);
142
+ premium = getMinimumPremium(payout, lossProb, expiration); // TODO: extra fee for RiskModule?
143
+ return (premium, lossProb);
144
+ }
145
+
146
+ function _computeLossProb(
147
+ uint256 currentPrice,
148
+ uint256 triggerPrice,
149
+ uint40 duration
150
+ ) internal view returns (uint256) {
151
+ uint256[PRICE_SLOTS] storage pdf = _cdf[
152
+ int40((duration + 1800) / 3600) * (currentPrice > triggerPrice ? int40(1) : int40(-1))
153
+ ];
154
+ uint256 priceJump;
155
+ uint256 decimalConv = 10**(18 - _referenceCurrency.decimals());
156
+ // Calculate the jump percentage as integer with simetric rounding
157
+ if (currentPrice > triggerPrice) {
158
+ priceJump =
159
+ WadRayMath.wad() -
160
+ (triggerPrice * decimalConv).wadDiv(currentPrice * decimalConv);
161
+ } else {
162
+ priceJump =
163
+ (triggerPrice * decimalConv).wadDiv(currentPrice * decimalConv) -
164
+ WadRayMath.wad();
165
+ }
166
+
167
+ uint8 downPerc = uint8((priceJump + _slotSize / 2) / _slotSize);
168
+
169
+ if (downPerc >= PRICE_SLOTS) {
170
+ return pdf[PRICE_SLOTS - 1];
171
+ } else {
172
+ return pdf[downPerc];
173
+ }
174
+ }
175
+
176
+ function newPolicy(
177
+ uint256 triggerPrice,
178
+ bool lower,
179
+ uint256 payout,
180
+ uint40 expiration
181
+ ) external override returns (uint256) {
182
+ (uint256 premium, uint256 lossProb) = pricePolicy(triggerPrice, lower, payout, expiration);
183
+ require(premium > 0, "Either duration or percentage jump not supported");
184
+
185
+ uint256 policyId = (uint256(uint160(address(this))) << 96) + _internalId;
186
+ PolicyData storage priceRiskPolicy = _policies[policyId];
187
+ address customer = _msgSender();
188
+ priceRiskPolicy.ensuroPolicy = _newPolicy(
189
+ payout,
190
+ premium,
191
+ lossProb,
192
+ expiration,
193
+ customer,
194
+ _internalId
195
+ );
196
+ _internalId += 1;
197
+ priceRiskPolicy.triggerPrice = triggerPrice;
198
+ priceRiskPolicy.lower = lower;
199
+ emit NewPricePolicy(customer, policyId, triggerPrice, lower);
200
+ return policyId;
201
+ }
202
+
203
+ function triggerPolicy(uint256 policyId) external override whenNotPaused {
204
+ PolicyData storage policy = _policies[policyId];
205
+ uint256 currentPrice = _getCurrentPrice();
206
+ require(
207
+ !policy.lower || currentPrice <= policy.triggerPrice,
208
+ "Condition not met CurrentPrice > triggerPrice"
209
+ );
210
+ require(
211
+ policy.lower || currentPrice >= policy.triggerPrice,
212
+ "Condition not met CurrentPrice < triggerPrice"
213
+ );
214
+
215
+ _policyPool.resolvePolicy(policy.ensuroPolicy, policy.ensuroPolicy.payout);
216
+ }
217
+
218
+ /**
219
+ * @dev Sets the probability distribution for a given duration
220
+ * @param duration Duration of the policy in hours (simetric rounding) positive if probability of lower price
221
+ * negative if probability of higher price
222
+ * @param cdf Array where cdf[i] = prob of price lower/higher than i% of current price
223
+ */
224
+ function setCDF(int40 duration, uint256[PRICE_SLOTS] calldata cdf)
225
+ external
226
+ onlyRole(PRICER_ROLE)
227
+ whenNotPaused
228
+ {
229
+ _cdf[duration] = cdf;
230
+ }
231
+
232
+ function getCDF(int40 duration) external view returns (uint256[PRICE_SLOTS] memory) {
233
+ return _cdf[duration];
234
+ }
235
+
236
+ function referenceCurrency() external view override returns (IERC20Metadata) {
237
+ return _referenceCurrency;
238
+ }
239
+
240
+ function asset() external view override returns (IERC20Metadata) {
241
+ return _asset;
242
+ }
243
+ }
@@ -0,0 +1,47 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ /**
5
+ * @title IPriceOracle - Interface for external price oracle to get assets' prices
6
+ * @author Ensuro
7
+ */
8
+ interface IPriceOracle {
9
+ /**
10
+ * @dev Returns the price of the asset in ETH
11
+ * @param asset Address of a ERC20 asset
12
+ * @return Price of the asset in ETH (Wad)
13
+ */
14
+ function getAssetPrice(address asset) external view returns (uint256);
15
+ }
16
+
17
+ /**
18
+ * @title IExchange - Interface that handles exchange operations between tokens
19
+ * @author Ensuro
20
+ */
21
+ interface IExchange {
22
+ function convert(
23
+ address assetFrom,
24
+ address assetTo,
25
+ uint256 amount
26
+ ) external view returns (uint256);
27
+
28
+ function getAmountIn(
29
+ address assetIn,
30
+ address assetOut,
31
+ uint256 amountOut
32
+ ) external view returns (uint256);
33
+
34
+ function getSwapRouter() external view returns (address);
35
+
36
+ function getPriceOracle() external view returns (IPriceOracle);
37
+
38
+ function sell(
39
+ address assetIn,
40
+ address assetOut,
41
+ uint256 amountInExact,
42
+ address outAddr,
43
+ uint256 deadline
44
+ ) external view returns (bytes memory);
45
+
46
+ function decodeSwapOut(bytes memory responseData) external view returns (uint256);
47
+ }
@@ -6,6 +6,7 @@ import {IAssetManager} from "./IAssetManager.sol";
6
6
  import {IInsolvencyHook} from "./IInsolvencyHook.sol";
7
7
  import {ILPWhitelist} from "./ILPWhitelist.sol";
8
8
  import {IRiskModule} from "./IRiskModule.sol";
9
+ import {IExchange} from "./IExchange.sol";
9
10
 
10
11
  /**
11
12
  * @title IPolicyPoolAccess - Interface for the contract that handles roles for the PolicyPool and components
@@ -45,6 +46,7 @@ interface IPolicyPoolConfig is IAccessControlUpgradeable {
45
46
  setMaxSlippage,
46
47
  setAcceptAllRMs, // EToken Governance action - value == boolean
47
48
  setAcceptException, // EToken Governance action - value == (!isException << 255)|address
49
+ setExchange, // Changes exchange helper contract
48
50
  last
49
51
  }
50
52
 
@@ -73,6 +75,8 @@ interface IPolicyPoolConfig is IAccessControlUpgradeable {
73
75
 
74
76
  function lpWhitelist() external view returns (ILPWhitelist);
75
77
 
78
+ function exchange() external view returns (IExchange);
79
+
76
80
  function treasury() external view returns (address);
77
81
 
78
82
  function checkAcceptsNewPolicy(IRiskModule riskModule) external view;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ensuro/core",
3
3
  "description": "Ensuro - Decentralized insurance protocol",
4
- "version": "1.2.0",
4
+ "version": "1.2.1",
5
5
  "files": [
6
6
  "**/*.sol",
7
7
  "/build/contracts/*.json",