@ensuro/core 1.2.0 → 1.2.3

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 (33) hide show
  1. package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +2 -51
  2. package/build/contracts/DataTypes.sol/DataTypes.json +2 -2
  3. package/build/contracts/EToken.sol/EToken.json +2 -2
  4. package/build/contracts/Exchange.sol/Exchange.json +483 -0
  5. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +2 -2
  6. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
  7. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +2 -2
  8. package/build/contracts/PolicyPool.sol/PolicyPool.json +2 -90
  9. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +28 -2
  10. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +2 -2
  11. package/build/contracts/extras/IPriceRiskModule.sol/IPriceRiskModule.json +253 -0
  12. package/build/contracts/extras/PriceRiskModule.sol/PriceRiskModule.json +1036 -0
  13. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +2 -2
  14. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +2 -2
  15. package/build/interfaces/IEToken.sol/IEToken.json +26 -0
  16. package/build/interfaces/IExchange.sol/IExchange.json +153 -0
  17. package/build/interfaces/IExchange.sol/IPriceOracle.json +30 -0
  18. package/build/interfaces/IPolicyHolder.sol/IPolicyHolder.json +108 -0
  19. package/build/interfaces/IPolicyPoolConfig.sol/IPolicyPoolConfig.json +13 -0
  20. package/contracts/AaveAssetManager.sol +35 -63
  21. package/contracts/BaseAssetManager.sol +55 -37
  22. package/contracts/EToken.sol +3 -19
  23. package/contracts/Exchange.sol +142 -0
  24. package/contracts/PolicyPool.sol +41 -35
  25. package/contracts/PolicyPoolComponent.sol +4 -0
  26. package/contracts/PolicyPoolConfig.sol +16 -0
  27. package/contracts/RiskModule.sol +5 -26
  28. package/contracts/extras/IPriceRiskModule.sol +41 -0
  29. package/contracts/extras/PriceRiskModule.sol +243 -0
  30. package/interfaces/IExchange.sol +47 -0
  31. package/interfaces/IPolicyHolder.sol +42 -0
  32. package/interfaces/IPolicyPoolConfig.sol +4 -0
  33. package/package.json +1 -1
@@ -2,6 +2,7 @@
2
2
  pragma solidity ^0.8.0;
3
3
 
4
4
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
5
+ import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
5
6
  import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
6
7
  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7
8
  import {IPolicyPoolConfig} from "../interfaces/IPolicyPoolConfig.sol";
@@ -13,6 +14,7 @@ import {IInsolvencyHook} from "../interfaces/IInsolvencyHook.sol";
13
14
  import {IPolicyPoolComponent} from "../interfaces/IPolicyPoolComponent.sol";
14
15
  import {IEToken} from "../interfaces/IEToken.sol";
15
16
  import {IPolicyNFT} from "../interfaces/IPolicyNFT.sol";
17
+ import {IPolicyHolder} from "../interfaces/IPolicyHolder.sol";
16
18
  import {IAssetManager} from "../interfaces/IAssetManager.sol";
17
19
  import {Policy} from "./Policy.sol";
18
20
  import {WadRayMath} from "./WadRayMath.sol";
@@ -44,7 +46,6 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
44
46
  // solhint-disable-next-line var-name-mixedcase
45
47
  uint256 public immutable NEGLIGIBLE_AMOUNT; // init as 10**(decimals-3) == 0.001 USD
46
48
 
47
- bytes32 public constant REBALANCE_ROLE = keccak256("REBALANCE_ROLE");
48
49
  bytes32 public constant WITHDRAW_WON_PREMIUMS_ROLE = keccak256("WITHDRAW_WON_PREMIUMS_ROLE");
49
50
 
50
51
  bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
@@ -464,6 +465,45 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
464
465
  emit PolicyResolved(policy.riskModule, policy.id, payout);
465
466
  delete _policies[policy.id];
466
467
  delete _policiesFunds[policy.id];
468
+ if (payout > 0) {
469
+ _notifyPayout(policy.id, payout);
470
+ } else {
471
+ _notifyExpiration(policy.id);
472
+ }
473
+ }
474
+
475
+ function _notifyPayout(uint256 policyId, uint256 payout) internal {
476
+ address customer = _policyNFT.ownerOf(policyId);
477
+ if (!AddressUpgradeable.isContract(customer)) return;
478
+ try
479
+ IPolicyHolder(customer).onPayoutReceived(_msgSender(), address(this), policyId, payout)
480
+ returns (bytes4 retval) {
481
+ require(
482
+ retval == IPolicyHolder.onPayoutReceived.selector,
483
+ "Invalid return value from Policy Holder"
484
+ );
485
+ } catch (bytes memory reason) {
486
+ if (reason.length == 0) {
487
+ return; // Not implemented, it's fine
488
+ } else {
489
+ // solhint-disable-next-line no-inline-assembly
490
+ assembly {
491
+ revert(add(32, reason), mload(reason))
492
+ }
493
+ }
494
+ }
495
+ }
496
+
497
+ function _notifyExpiration(uint256 policyId) internal {
498
+ address customer = _policyNFT.ownerOf(policyId);
499
+ if (!AddressUpgradeable.isContract(customer)) return;
500
+ try IPolicyHolder(customer).onPolicyExpired(_msgSender(), address(this), policyId) returns (
501
+ bytes4
502
+ ) {
503
+ return;
504
+ } catch {
505
+ return;
506
+ }
467
507
  }
468
508
 
469
509
  function _interestAdjustment(Policy.PolicyData memory policy)
@@ -609,40 +649,6 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable {
609
649
  return amount;
610
650
  }
611
651
 
612
- function rebalancePolicy(Policy.PolicyData calldata policy)
613
- external
614
- onlyRole(REBALANCE_ROLE)
615
- whenNotPaused
616
- {
617
- _validatePolicy(policy);
618
- DataTypes.ETokenToWadMap storage policyFunds = _policiesFunds[policy.id];
619
- uint256 ocean = 0;
620
-
621
- // Iterates all the tokens
622
- // If locked - unlocks - finally stores the available ocean in policyFunds
623
- for (uint256 i = 0; i < _eTokens.length(); i++) {
624
- (IEToken etk, DataTypes.ETokenStatus etkStatus) = _eTokens.at(i);
625
- uint256 etkOcean = 0;
626
- (bool locked, uint256 etkScr) = policyFunds.tryGet(etk);
627
- if (locked) {
628
- etk.unlockScr(policy.interestRate(), etkScr);
629
- }
630
- if (
631
- etkStatus == DataTypes.ETokenStatus.active &&
632
- etk.accepts(address(policy.riskModule), policy.expiration)
633
- ) etkOcean = etk.oceanForNewScr();
634
- if (etkOcean == 0) {
635
- if (locked) policyFunds.remove(etk);
636
- } else {
637
- policyFunds.set(etk, etkOcean);
638
- ocean += etkOcean;
639
- }
640
- }
641
-
642
- _distributeScr(policy.scr, policy.interestRate(), ocean, policyFunds);
643
- emit PolicyRebalanced(policy.riskModule, policy.id);
644
- }
645
-
646
652
  function getInvestable() external view override returns (uint256) {
647
653
  uint256 borrowedFromEtk = 0;
648
654
  for (uint256 i = 0; i < _eTokens.length(); i++) {
@@ -134,11 +134,15 @@ abstract contract PolicyPoolComponent is
134
134
  }
135
135
  }
136
136
 
137
+ // solhint-disable-next-line no-empty-blocks
138
+ function _validateParameters() internal view virtual {} // Must be reimplemented with specific validations
139
+
137
140
  function _parameterChanged(
138
141
  IPolicyPoolConfig.GovernanceActions action,
139
142
  uint256 value,
140
143
  bool tweak
141
144
  ) internal {
145
+ _validateParameters();
142
146
  if (tweak) _registerTweak(action);
143
147
  emit GovernanceAction(action, value);
144
148
  }
@@ -10,6 +10,7 @@ import {IPolicyPoolConfig} from "../interfaces/IPolicyPoolConfig.sol";
10
10
  import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
11
11
  import {IRiskModule} from "../interfaces/IRiskModule.sol";
12
12
  import {ILPWhitelist} from "../interfaces/ILPWhitelist.sol";
13
+ import {IExchange} from "../interfaces/IExchange.sol";
13
14
  import {IPolicyPoolComponent} from "../interfaces/IPolicyPoolComponent.sol";
14
15
  import {WadRayMath} from "./WadRayMath.sol";
15
16
 
@@ -40,6 +41,7 @@ contract PolicyPoolConfig is
40
41
  IInsolvencyHook internal _insolvencyHook; // Contract that handles insolvency situations
41
42
  IPolicyPool internal _policyPool;
42
43
  ILPWhitelist internal _lpWhitelist; // Contract that handles whitelisting of Liquidity Providers
44
+ IExchange internal _exchange; // Contract that handles exchange operations between assets
43
45
 
44
46
  mapping(IRiskModule => RiskModuleStatus) private _riskModules;
45
47
 
@@ -168,6 +170,20 @@ contract PolicyPoolConfig is
168
170
  return _lpWhitelist;
169
171
  }
170
172
 
173
+ function setExchange(IExchange exchange_) external onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {
174
+ require(
175
+ address(exchange_) == address(0) ||
176
+ IPolicyPoolComponent(address(exchange_)).policyPool() == _policyPool,
177
+ "Component not linked to this PolicyPool"
178
+ );
179
+ _exchange = exchange_;
180
+ emit ComponentChanged(GovernanceActions.setExchange, address(_exchange));
181
+ }
182
+
183
+ function exchange() external view override returns (IExchange) {
184
+ return _exchange;
185
+ }
186
+
171
187
  function addRiskModule(IRiskModule riskModule) external onlyRole2(LEVEL1_ROLE, LEVEL2_ROLE) {
172
188
  require(
173
189
  _riskModules[riskModule] == RiskModuleStatus.inactive,
@@ -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
+ }
@@ -0,0 +1,42 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
5
+
6
+ /**
7
+ * @title Policy holder interface
8
+ * @dev Interface for any contract that wants to be a holder of Ensuro Policies and receive the payouts
9
+ */
10
+ interface IPolicyHolder is IERC721Receiver {
11
+ /**
12
+ * @dev Whenever an Policy is expired or resolved with payout = 0, this function is called
13
+ *
14
+ * It should return its Solidity selector to confirm the payout.
15
+ * If interface is not implemented by the recipient, it will be ignored and the payout will be successful.
16
+ * No mather what's the return value or even if this function reverts, this function will not revert the policy
17
+ * expiration.
18
+ *
19
+ * The selector can be obtained in Solidity with `IPolicyPool.onPolicyExpired.selector`.
20
+ */
21
+ function onPolicyExpired(
22
+ address operator,
23
+ address from,
24
+ uint256 policyId
25
+ ) external returns (bytes4);
26
+
27
+ /**
28
+ * @dev Whenever an Policy is resolved with payout > 0, this function is called
29
+ *
30
+ * It must return its Solidity selector to confirm the payout.
31
+ * If interface is not implemented by the recipient, it will be ignored and the payout will be successful.
32
+ * If any other value is returned or it reverts, the policy resolution / payout will be reverted.
33
+ *
34
+ * The selector can be obtained in Solidity with `IPolicyPool.onPayoutReceived.selector`.
35
+ */
36
+ function onPayoutReceived(
37
+ address operator,
38
+ address from,
39
+ uint256 policyId,
40
+ uint256 amount
41
+ ) external returns (bytes4);
42
+ }
@@ -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.3",
5
5
  "files": [
6
6
  "**/*.sol",
7
7
  "/build/contracts/*.json",