@ensuro/core 2.2.1 → 2.4.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/build/contracts/AccessManager.sol/AccessManager.json +2 -2
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +2 -2
- package/build/contracts/EToken.sol/EToken.json +2 -2
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +2 -2
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +41 -2
- package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +2 -2
- package/build/contracts/TieredSignedQuoteRiskModule.sol/TieredSignedQuoteRiskModule.json +1316 -0
- package/build/contracts/TimeScaled.sol/TimeScaled.json +2 -2
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +2 -2
- package/build/contracts/migration/LPManualWhitelistV20Upgraded.sol/LPManualWhitelistV20Upgraded.json +2 -2
- package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +6 -6
- package/build/contracts/mocks/InterfaceIdCalculator.sol/InterfaceIdCalculator.json +2 -2
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +2 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +2 -2
- package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
- package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
- package/contracts/PolicyPool.sol +3 -2
- package/contracts/PremiumsAccount.sol +61 -56
- package/contracts/RiskModule.sol +5 -38
- package/contracts/SignedQuoteRiskModule.sol +3 -3
- package/contracts/TieredSignedQuoteRiskModule.sol +165 -0
- package/contracts/TrustfulRiskModule.sol +7 -2
- package/js/deploy.js +31 -7
- package/js/test-utils.js +41 -29
- package/package.json +1 -1
|
@@ -35,6 +35,7 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
35
35
|
using SafeCast for uint256;
|
|
36
36
|
|
|
37
37
|
bytes32 public constant WITHDRAW_WON_PREMIUMS_ROLE = keccak256("WITHDRAW_WON_PREMIUMS_ROLE");
|
|
38
|
+
bytes32 public constant REPAY_LOANS_ROLE = keccak256("REPAY_LOANS_ROLE");
|
|
38
39
|
uint256 internal constant FOUR_DECIMAL_TO_WAD = 1e14;
|
|
39
40
|
uint16 internal constant HUNDRED_PERCENT = 1e4;
|
|
40
41
|
|
|
@@ -92,6 +93,19 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
92
93
|
*/
|
|
93
94
|
event WonPremiumsInOut(bool moneyIn, uint256 value);
|
|
94
95
|
|
|
96
|
+
/**
|
|
97
|
+
* @dev Modifier to make a function callable only by a certain global or component role.
|
|
98
|
+
* In addition to checking the sender's role, `address(0)` 's role is also
|
|
99
|
+
* considered. Granting a role to `address(0)` (at global or component level) is equivalent
|
|
100
|
+
* to enabling this role for everyone.
|
|
101
|
+
*/
|
|
102
|
+
modifier onlyGlobalOrComponentOrOpenRole(bytes32 role) {
|
|
103
|
+
if (!_policyPool.access().hasComponentRole(address(this), role, address(0), true)) {
|
|
104
|
+
_policyPool.access().checkComponentRole(address(this), role, _msgSender(), true);
|
|
105
|
+
}
|
|
106
|
+
_;
|
|
107
|
+
}
|
|
108
|
+
|
|
95
109
|
/**
|
|
96
110
|
* @dev Constructor of the contract, sets the immutable fields.
|
|
97
111
|
*
|
|
@@ -176,13 +190,10 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
176
190
|
* loans to cover asset losses.
|
|
177
191
|
*/
|
|
178
192
|
function _assetEarnings(int256 earningsOrLosses) internal override {
|
|
179
|
-
if (earningsOrLosses
|
|
180
|
-
uint256
|
|
181
|
-
if (address(_seniorEtk) != address(0)) earnings = _repayLoan(earnings, _seniorEtk);
|
|
182
|
-
if (address(_juniorEtk) != address(0)) earnings = _repayLoan(earnings, _juniorEtk);
|
|
183
|
-
_storePurePremiumWon(earnings);
|
|
193
|
+
if (earningsOrLosses >= 0) {
|
|
194
|
+
_storePurePremiumWon(uint256(earningsOrLosses));
|
|
184
195
|
} else {
|
|
185
|
-
require(_payFromPremiums(
|
|
196
|
+
require(_payFromPremiums(-earningsOrLosses) == 0, "Losses can't exceed maxDeficit");
|
|
186
197
|
}
|
|
187
198
|
}
|
|
188
199
|
|
|
@@ -230,6 +241,14 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
230
241
|
return _surplus;
|
|
231
242
|
}
|
|
232
243
|
|
|
244
|
+
/**
|
|
245
|
+
* @dev Returns the amount of funds available to cover losses or repay eToken loans.
|
|
246
|
+
*/
|
|
247
|
+
function fundsAvailable() public view returns (uint256) {
|
|
248
|
+
// This is guaranteed to be positive because _maxDeficit is negative and always gte _surplus in absolute value
|
|
249
|
+
return uint256(_surplus - _maxDeficit(deficitRatio()));
|
|
250
|
+
}
|
|
251
|
+
|
|
233
252
|
function seniorEtk() external view override returns (IEToken) {
|
|
234
253
|
return _seniorEtk;
|
|
235
254
|
}
|
|
@@ -301,8 +320,6 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
301
320
|
external
|
|
302
321
|
onlyGlobalOrComponentRole(LEVEL2_ROLE)
|
|
303
322
|
{
|
|
304
|
-
require(newRatio <= 1e18, "Validation: deficitRatio must be <= 1");
|
|
305
|
-
|
|
306
323
|
uint16 truncatedRatio = (newRatio / FOUR_DECIMAL_TO_WAD).toUint16();
|
|
307
324
|
require(
|
|
308
325
|
uint256(truncatedRatio) * FOUR_DECIMAL_TO_WAD == newRatio,
|
|
@@ -311,6 +328,8 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
311
328
|
|
|
312
329
|
int256 maxDeficit = _maxDeficit(newRatio);
|
|
313
330
|
require(adjustment || _surplus >= maxDeficit, "Validation: surplus must be >= maxDeficit");
|
|
331
|
+
_params.deficitRatio = truncatedRatio;
|
|
332
|
+
_validateParameters();
|
|
314
333
|
|
|
315
334
|
IAccessManager.GovernanceActions action = IAccessManager.GovernanceActions.setDeficitRatio;
|
|
316
335
|
if (_surplus < maxDeficit) {
|
|
@@ -320,7 +339,6 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
320
339
|
_borrowFromEtk(borrow, address(this), address(_juniorEtk) != address(0));
|
|
321
340
|
action = IAccessManager.GovernanceActions.setDeficitRatioWithAdjustment;
|
|
322
341
|
}
|
|
323
|
-
_params.deficitRatio = truncatedRatio;
|
|
324
342
|
_parameterChanged(action, newRatio, false);
|
|
325
343
|
}
|
|
326
344
|
|
|
@@ -368,7 +386,6 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
368
386
|
address receiver,
|
|
369
387
|
bool jrEtk
|
|
370
388
|
) internal {
|
|
371
|
-
require(receiver != address(0), "PremiumsAccount: receiver cannot be the zero address");
|
|
372
389
|
uint256 left = borrow;
|
|
373
390
|
if (jrEtk) {
|
|
374
391
|
if (_juniorEtk.getLoan(address(this)) + borrow <= jrLoanLimit()) {
|
|
@@ -395,8 +412,8 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
395
412
|
* @param toPay The amount to pay.
|
|
396
413
|
* @return The amount that couldn't be paid from the premiums account.
|
|
397
414
|
*/
|
|
398
|
-
function _payFromPremiums(
|
|
399
|
-
int256 newSurplus = _surplus -
|
|
415
|
+
function _payFromPremiums(int256 toPay) internal returns (uint256) {
|
|
416
|
+
int256 newSurplus = _surplus - toPay;
|
|
400
417
|
int256 maxDeficit = _maxDeficit(deficitRatio());
|
|
401
418
|
if (newSurplus >= maxDeficit) {
|
|
402
419
|
_surplus = newSurplus;
|
|
@@ -413,7 +430,6 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
413
430
|
* @param purePremiumWon The amount earned
|
|
414
431
|
*/
|
|
415
432
|
function _storePurePremiumWon(uint256 purePremiumWon) internal {
|
|
416
|
-
if (purePremiumWon == 0) return;
|
|
417
433
|
_surplus += int256(purePremiumWon);
|
|
418
434
|
}
|
|
419
435
|
|
|
@@ -432,15 +448,8 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
432
448
|
* @param amount The amount to be transferred.
|
|
433
449
|
*/
|
|
434
450
|
function receiveGrant(uint256 amount) external {
|
|
435
|
-
|
|
436
|
-
// the money to pay the eTokens
|
|
451
|
+
_storePurePremiumWon(amount);
|
|
437
452
|
currency().safeTransferFrom(_msgSender(), address(this), amount);
|
|
438
|
-
uint256 purePremiumWon = amount;
|
|
439
|
-
if (address(_seniorEtk) != address(0)) purePremiumWon = _repayLoan(purePremiumWon, _seniorEtk);
|
|
440
|
-
if (address(_juniorEtk) != address(0)) purePremiumWon = _repayLoan(purePremiumWon, _juniorEtk);
|
|
441
|
-
|
|
442
|
-
// Finally store purePremiumWon
|
|
443
|
-
_storePurePremiumWon(purePremiumWon);
|
|
444
453
|
emit WonPremiumsInOut(true, amount);
|
|
445
454
|
}
|
|
446
455
|
|
|
@@ -497,23 +506,14 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
497
506
|
uint256 payout
|
|
498
507
|
) external override onlyPolicyPool whenNotPaused {
|
|
499
508
|
_activePurePremiums -= policy.purePremium;
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
if (address(_seniorEtk) != address(0))
|
|
503
|
-
purePremiumWon = _repayLoan(purePremiumWon, _seniorEtk);
|
|
504
|
-
if (address(_juniorEtk) != address(0))
|
|
505
|
-
purePremiumWon = _repayLoan(purePremiumWon, _juniorEtk);
|
|
506
|
-
_storePurePremiumWon(purePremiumWon);
|
|
509
|
+
uint256 borrowFromScr = _payFromPremiums(int256(payout) - int256(policy.purePremium));
|
|
510
|
+
if (borrowFromScr != 0) {
|
|
507
511
|
_unlockScr(policy);
|
|
508
|
-
|
|
512
|
+
_borrowFromEtk(borrowFromScr, policyHolder, policy.jrScr > 0);
|
|
509
513
|
} else {
|
|
510
|
-
uint256 borrowFromScr = _payFromPremiums(payout - policy.purePremium);
|
|
511
514
|
_unlockScr(policy);
|
|
512
|
-
if (borrowFromScr > 0) {
|
|
513
|
-
_borrowFromEtk(borrowFromScr, policyHolder, policy.jrScr > 0);
|
|
514
|
-
}
|
|
515
|
-
_transferTo(policyHolder, payout - borrowFromScr);
|
|
516
515
|
}
|
|
516
|
+
_transferTo(policyHolder, payout - borrowFromScr);
|
|
517
517
|
}
|
|
518
518
|
|
|
519
519
|
/**
|
|
@@ -538,18 +538,37 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
538
538
|
}
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
+
/**
|
|
542
|
+
* @dev Function that repays the loan(s) if fundsAvailable
|
|
543
|
+
*
|
|
544
|
+
* @return available The funds still available after repayment
|
|
545
|
+
*/
|
|
546
|
+
function repayLoans()
|
|
547
|
+
external
|
|
548
|
+
onlyGlobalOrComponentOrOpenRole(REPAY_LOANS_ROLE)
|
|
549
|
+
whenNotPaused
|
|
550
|
+
returns (uint256 available)
|
|
551
|
+
{
|
|
552
|
+
available = fundsAvailable();
|
|
553
|
+
if (available != 0 && address(_seniorEtk) != address(0))
|
|
554
|
+
available = _repayLoan(available, _seniorEtk);
|
|
555
|
+
if (available != 0 && address(_juniorEtk) != address(0))
|
|
556
|
+
available = _repayLoan(available, _juniorEtk);
|
|
557
|
+
return available;
|
|
558
|
+
}
|
|
559
|
+
|
|
541
560
|
/**
|
|
542
561
|
* @dev Internal function that repays a loan taken (if any outstanding) from the an eToken
|
|
543
562
|
*
|
|
544
|
-
* @param
|
|
563
|
+
* @param fundsAvailable_ The amount of funds available for the repayment
|
|
545
564
|
* @param etk The eToken with the potential debt
|
|
546
565
|
* @return The excess amount of the purePremiumWon that wasn't used for the loan repayment.
|
|
547
566
|
*/
|
|
548
|
-
function _repayLoan(uint256
|
|
549
|
-
if (purePremiumWon < NEGLIGIBLE_AMOUNT) return purePremiumWon;
|
|
567
|
+
function _repayLoan(uint256 fundsAvailable_, IEToken etk) internal returns (uint256) {
|
|
550
568
|
uint256 borrowedFromEtk = etk.getLoan(address(this));
|
|
551
|
-
if (borrowedFromEtk == 0) return
|
|
552
|
-
uint256 repayAmount = Math.min(
|
|
569
|
+
if (borrowedFromEtk == 0) return fundsAvailable_;
|
|
570
|
+
uint256 repayAmount = Math.min(fundsAvailable_, borrowedFromEtk);
|
|
571
|
+
_surplus -= int256(repayAmount);
|
|
553
572
|
|
|
554
573
|
// If not enough liquidity, it deinvests from the asset manager
|
|
555
574
|
if (currency().balanceOf(address(this)) < repayAmount) {
|
|
@@ -567,7 +586,7 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
567
586
|
currency().approve(address(etk), borrowedFromEtk);
|
|
568
587
|
}
|
|
569
588
|
etk.repayLoan(repayAmount, address(this));
|
|
570
|
-
return
|
|
589
|
+
return fundsAvailable_ - repayAmount;
|
|
571
590
|
}
|
|
572
591
|
|
|
573
592
|
function policyExpired(Policy.PolicyData memory policy)
|
|
@@ -576,22 +595,8 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
576
595
|
onlyPolicyPool
|
|
577
596
|
whenNotPaused
|
|
578
597
|
{
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
// If negative _activePurePremiums, repay this first (shouldn't happen)
|
|
583
|
-
int256 maxDeficit = _maxDeficit(deficitRatio());
|
|
584
|
-
if (_surplus < maxDeficit) {
|
|
585
|
-
// Covers the excess of deficit first
|
|
586
|
-
purePremiumWon -= uint256(-_surplus + maxDeficit);
|
|
587
|
-
_surplus = maxDeficit;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (address(_seniorEtk) != address(0)) purePremiumWon = _repayLoan(purePremiumWon, _seniorEtk);
|
|
591
|
-
if (address(_juniorEtk) != address(0)) purePremiumWon = _repayLoan(purePremiumWon, _juniorEtk);
|
|
592
|
-
|
|
593
|
-
// Finally store purePremiumWon
|
|
594
|
-
_storePurePremiumWon(purePremiumWon);
|
|
598
|
+
_activePurePremiums -= policy.purePremium;
|
|
599
|
+
_storePurePremiumWon(policy.purePremium);
|
|
595
600
|
_unlockScr(policy);
|
|
596
601
|
}
|
|
597
602
|
|
package/contracts/RiskModule.sol
CHANGED
|
@@ -26,8 +26,8 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
26
26
|
|
|
27
27
|
uint256 internal constant FOUR_DECIMAL_TO_WAD = 1e14;
|
|
28
28
|
uint16 internal constant HUNDRED_PERCENT = 1e4;
|
|
29
|
-
uint16 internal constant MIN_MOC = 5e3;
|
|
30
|
-
uint16 internal constant MAX_MOC = 4e4;
|
|
29
|
+
uint16 internal constant MIN_MOC = 5e3; // 50%
|
|
30
|
+
uint16 internal constant MAX_MOC = 4e4; // 400%
|
|
31
31
|
|
|
32
32
|
// For parameters that can be changed by the risk module provider
|
|
33
33
|
bytes32 public constant RM_PROVIDER_ROLE = keccak256("RM_PROVIDER_ROLE");
|
|
@@ -339,7 +339,8 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
/**
|
|
342
|
-
* @dev Called from child contracts to create policies (after they validated the pricing)
|
|
342
|
+
* @dev Called from child contracts to create policies (after they validated the pricing).
|
|
343
|
+
* whenNotPaused validation must be done in the external method.
|
|
343
344
|
*
|
|
344
345
|
* @param payout The exposure (maximum payout) of the policy
|
|
345
346
|
* @param premium The premium that will be paid by the policyHolder
|
|
@@ -357,7 +358,7 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
357
358
|
address payer,
|
|
358
359
|
address onBehalfOf,
|
|
359
360
|
uint96 internalId
|
|
360
|
-
) internal
|
|
361
|
+
) internal virtual returns (Policy.PolicyData memory) {
|
|
361
362
|
return
|
|
362
363
|
_newPolicyWithParams(
|
|
363
364
|
payout,
|
|
@@ -371,40 +372,6 @@ abstract contract RiskModule is IRiskModule, PolicyPoolComponent {
|
|
|
371
372
|
);
|
|
372
373
|
}
|
|
373
374
|
|
|
374
|
-
/**
|
|
375
|
-
* @dev Called from child contracts to create policies (after they validated the pricing), receives custom params
|
|
376
|
-
*
|
|
377
|
-
* @param payout The exposure (maximum payout) of the policy
|
|
378
|
-
* @param premium The premium that will be paid by the policyHolder
|
|
379
|
-
* @param lossProb The probability of having to pay the maximum payout (wad)
|
|
380
|
-
* @param payer The account that pays for the premium
|
|
381
|
-
* @param expiration The expiration of the policy (timestamp)
|
|
382
|
-
* @param onBehalfOf The policy holder
|
|
383
|
-
* @param internalId An id that's unique within this module and it will be used to identify the policy
|
|
384
|
-
*/
|
|
385
|
-
function _newPolicyCustomParams(
|
|
386
|
-
uint256 payout,
|
|
387
|
-
uint256 premium,
|
|
388
|
-
uint256 lossProb,
|
|
389
|
-
uint40 expiration,
|
|
390
|
-
address payer,
|
|
391
|
-
address onBehalfOf,
|
|
392
|
-
uint96 internalId,
|
|
393
|
-
Params memory params_
|
|
394
|
-
) internal whenNotPaused returns (Policy.PolicyData memory) {
|
|
395
|
-
return
|
|
396
|
-
_newPolicyWithParams(
|
|
397
|
-
payout,
|
|
398
|
-
premium,
|
|
399
|
-
lossProb,
|
|
400
|
-
expiration,
|
|
401
|
-
payer,
|
|
402
|
-
onBehalfOf,
|
|
403
|
-
internalId,
|
|
404
|
-
params_
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
375
|
/**
|
|
409
376
|
* @dev Internal method without whenNotPaused, MUST be called from other function that has this modifier
|
|
410
377
|
*/
|
|
@@ -159,7 +159,7 @@ contract SignedQuoteRiskModule is RiskModule {
|
|
|
159
159
|
bytes32 quoteSignatureR,
|
|
160
160
|
bytes32 quoteSignatureVS,
|
|
161
161
|
uint40 quoteValidUntil
|
|
162
|
-
) external returns (Policy.PolicyData memory createdPolicy) {
|
|
162
|
+
) external whenNotPaused returns (Policy.PolicyData memory createdPolicy) {
|
|
163
163
|
return
|
|
164
164
|
_newPolicySigned(
|
|
165
165
|
payout,
|
|
@@ -207,7 +207,7 @@ contract SignedQuoteRiskModule is RiskModule {
|
|
|
207
207
|
bytes32 quoteSignatureR,
|
|
208
208
|
bytes32 quoteSignatureVS,
|
|
209
209
|
uint40 quoteValidUntil
|
|
210
|
-
) external returns (uint256) {
|
|
210
|
+
) external whenNotPaused returns (uint256) {
|
|
211
211
|
return
|
|
212
212
|
_newPolicySigned(
|
|
213
213
|
payout,
|
|
@@ -255,7 +255,7 @@ contract SignedQuoteRiskModule is RiskModule {
|
|
|
255
255
|
bytes32 quoteSignatureR,
|
|
256
256
|
bytes32 quoteSignatureVS,
|
|
257
257
|
uint40 quoteValidUntil
|
|
258
|
-
) external returns (uint256) {
|
|
258
|
+
) external whenNotPaused returns (uint256) {
|
|
259
259
|
require(
|
|
260
260
|
onBehalfOf == _msgSender() || currency().allowance(onBehalfOf, _msgSender()) > 0,
|
|
261
261
|
"Sender is not authorized to create policies onBehalfOf"
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
5
|
+
import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
|
|
6
|
+
import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
|
|
7
|
+
import {SignedQuoteRiskModule} from "./SignedQuoteRiskModule.sol";
|
|
8
|
+
import {Policy} from "./Policy.sol";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @title Tiered SignedQuote Risk Module
|
|
12
|
+
* @dev Risk Module that for policy creation verifies the different components of the price have been signed by a
|
|
13
|
+
* trusted account (PRICER_ROLE). For the resolution (resolvePolicy), it has to be called by an authorized user.
|
|
14
|
+
*
|
|
15
|
+
* It allows different collaterallization levels for different policy types, by defining tiers or buckets of loss
|
|
16
|
+
* probability.
|
|
17
|
+
*
|
|
18
|
+
* Each bucket's loss probability represents the upper bound of the bucket (inclusive). The global parameters of
|
|
19
|
+
* the contract represent the 100% bucket.
|
|
20
|
+
*
|
|
21
|
+
* @custom:security-contact security@ensuro.co
|
|
22
|
+
* @author Ensuro
|
|
23
|
+
*/
|
|
24
|
+
contract TieredSignedQuoteRiskModule is SignedQuoteRiskModule {
|
|
25
|
+
using SafeCast for uint256;
|
|
26
|
+
|
|
27
|
+
uint8 public constant MAX_BUCKETS = 4;
|
|
28
|
+
|
|
29
|
+
struct PackedBuckets {
|
|
30
|
+
uint64[MAX_BUCKETS] lossProbs;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
PackedBuckets internal _buckets;
|
|
34
|
+
PackedParams[MAX_BUCKETS] internal _bucketParams;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @dev Emitted when a new risk bucket is created.
|
|
38
|
+
* @param lossProb The loss probability of the new bucket.
|
|
39
|
+
* @param params The packed parameters of the new bucket.
|
|
40
|
+
*/
|
|
41
|
+
event NewBucket(uint256 lossProb, Params params);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @dev Emitted when the risks buckets are reset and only the default one remains
|
|
45
|
+
*/
|
|
46
|
+
event BucketsReset();
|
|
47
|
+
|
|
48
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
49
|
+
constructor(
|
|
50
|
+
IPolicyPool policyPool_,
|
|
51
|
+
IPremiumsAccount premiumsAccount_,
|
|
52
|
+
bool creationIsOpen_
|
|
53
|
+
) SignedQuoteRiskModule(policyPool_, premiumsAccount_, creationIsOpen_) {} // solhint-disable-line no-empty-blocks
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @dev Adds a new risk bucket with the given loss probability and parameters after the last one.
|
|
57
|
+
*
|
|
58
|
+
* Requirements:
|
|
59
|
+
*
|
|
60
|
+
* - The caller must have the LEVEL1_ROLE or LEVEL2_ROLE
|
|
61
|
+
*
|
|
62
|
+
* @param lossProb The loss probability of the new bucket.
|
|
63
|
+
* @param params_ The parameters of the new bucket.
|
|
64
|
+
*/
|
|
65
|
+
function pushBucket(uint256 lossProb, Params calldata params_)
|
|
66
|
+
external
|
|
67
|
+
onlyGlobalOrComponentRole2(LEVEL1_ROLE, LEVEL2_ROLE)
|
|
68
|
+
{
|
|
69
|
+
uint256 newBucket = 0;
|
|
70
|
+
if (_buckets.lossProbs[0] != 0) {
|
|
71
|
+
for (
|
|
72
|
+
newBucket = 1;
|
|
73
|
+
newBucket < MAX_BUCKETS && _buckets.lossProbs[newBucket] != 0;
|
|
74
|
+
newBucket++
|
|
75
|
+
) {} // solhint-disable-line no-empty-blocks
|
|
76
|
+
require(newBucket < MAX_BUCKETS, "No more than 4 buckets accepted");
|
|
77
|
+
require(
|
|
78
|
+
lossProb > uint256(_buckets.lossProbs[newBucket - 1]),
|
|
79
|
+
"lossProb <= last lossProb - reset instead"
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
_buckets.lossProbs[newBucket] = lossProb.toUint64();
|
|
83
|
+
_bucketParams[newBucket] = PackedParams({
|
|
84
|
+
moc: _wadTo4(params_.moc),
|
|
85
|
+
jrCollRatio: _wadTo4(params_.jrCollRatio),
|
|
86
|
+
collRatio: _wadTo4(params_.collRatio),
|
|
87
|
+
ensuroPpFee: _wadTo4(params_.ensuroPpFee),
|
|
88
|
+
ensuroCocFee: _wadTo4(params_.ensuroCocFee),
|
|
89
|
+
jrRoc: _wadTo4(params_.jrRoc),
|
|
90
|
+
srRoc: _wadTo4(params_.srRoc),
|
|
91
|
+
maxPayoutPerPolicy: type(uint32).max, // unused, but needs to be > 0
|
|
92
|
+
exposureLimit: type(uint32).max, //unused, but needs to be > 0
|
|
93
|
+
maxDuration: type(uint16).max //unused
|
|
94
|
+
});
|
|
95
|
+
if (newBucket < MAX_BUCKETS - 1) {
|
|
96
|
+
_buckets.lossProbs[newBucket + 1] = 0;
|
|
97
|
+
}
|
|
98
|
+
_validatePackedParams(_bucketParams[newBucket]);
|
|
99
|
+
emit NewBucket(lossProb, params_);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function resetBuckets() external onlyGlobalOrComponentRole2(LEVEL1_ROLE, LEVEL2_ROLE) {
|
|
103
|
+
_buckets.lossProbs[0] = 0;
|
|
104
|
+
emit BucketsReset();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @dev returns the risk bucket parameters for the given loss probability
|
|
109
|
+
*/
|
|
110
|
+
function bucketParams(uint256 lossProb) public view returns (Params memory) {
|
|
111
|
+
for (uint256 i = 0; i < MAX_BUCKETS && _buckets.lossProbs[i] != 0; i++) {
|
|
112
|
+
if (lossProb <= _buckets.lossProbs[i]) {
|
|
113
|
+
return _unpackParams(_bucketParams[i]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return this.params();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function _newPolicy(
|
|
120
|
+
uint256 payout,
|
|
121
|
+
uint256 premium,
|
|
122
|
+
uint256 lossProb,
|
|
123
|
+
uint40 expiration,
|
|
124
|
+
address payer,
|
|
125
|
+
address onBehalfOf,
|
|
126
|
+
uint96 internalId
|
|
127
|
+
) internal override whenNotPaused returns (Policy.PolicyData memory) {
|
|
128
|
+
return
|
|
129
|
+
_newPolicyWithParams(
|
|
130
|
+
payout,
|
|
131
|
+
premium,
|
|
132
|
+
lossProb,
|
|
133
|
+
expiration,
|
|
134
|
+
payer,
|
|
135
|
+
onBehalfOf,
|
|
136
|
+
internalId,
|
|
137
|
+
bucketParams(lossProb)
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function getMinimumPremium(
|
|
142
|
+
uint256 payout,
|
|
143
|
+
uint256 lossProb,
|
|
144
|
+
uint40 expiration
|
|
145
|
+
) public view virtual override returns (uint256) {
|
|
146
|
+
return _getMinimumPremium(payout, lossProb, expiration, bucketParams(lossProb));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @dev returns the current risk bucket limits
|
|
151
|
+
*/
|
|
152
|
+
function buckets() external view returns (uint256[MAX_BUCKETS] memory result) {
|
|
153
|
+
for (uint256 i = 0; i < MAX_BUCKETS && _buckets.lossProbs[i] > 0; i++) {
|
|
154
|
+
result[i] = _buckets.lossProbs[i];
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
161
|
+
* variables without shifting down storage in the inheritance chain.
|
|
162
|
+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
163
|
+
*/
|
|
164
|
+
uint256[45] private __gap;
|
|
165
|
+
}
|
|
@@ -78,7 +78,7 @@ contract TrustfulRiskModule is RiskModule {
|
|
|
78
78
|
uint40 expiration,
|
|
79
79
|
address onBehalfOf,
|
|
80
80
|
uint96 internalId
|
|
81
|
-
) external onlyComponentRole(PRICER_ROLE) returns (uint256) {
|
|
81
|
+
) external whenNotPaused onlyComponentRole(PRICER_ROLE) returns (uint256) {
|
|
82
82
|
return
|
|
83
83
|
_newPolicy(
|
|
84
84
|
payout,
|
|
@@ -115,7 +115,12 @@ contract TrustfulRiskModule is RiskModule {
|
|
|
115
115
|
uint40 expiration,
|
|
116
116
|
address onBehalfOf,
|
|
117
117
|
uint96 internalId
|
|
118
|
-
)
|
|
118
|
+
)
|
|
119
|
+
external
|
|
120
|
+
whenNotPaused
|
|
121
|
+
onlyComponentRole(PRICER_ROLE)
|
|
122
|
+
returns (Policy.PolicyData memory createdPolicy)
|
|
123
|
+
{
|
|
119
124
|
return
|
|
120
125
|
_newPolicy(
|
|
121
126
|
payout,
|
package/js/deploy.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const upgrades_core = require("@openzeppelin/upgrades-core");
|
|
2
2
|
const helpers = require("@nomicfoundation/hardhat-network-helpers");
|
|
3
3
|
const fs = require("fs");
|
|
4
|
-
|
|
4
|
+
const { request } = require("undici");
|
|
5
5
|
const ethers = require("ethers");
|
|
6
6
|
|
|
7
7
|
const _BN = ethers.BigNumber.from;
|
|
@@ -14,6 +14,10 @@ const WhitelistStatus = {
|
|
|
14
14
|
blacklisted: 2,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
const reservesOpts = {
|
|
18
|
+
unsafeAllow: ["delegatecall"],
|
|
19
|
+
};
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* Creates a fixed-point conversion function for the desired number of decimals
|
|
19
23
|
* @param decimals The number of decimals. Must be >= 6.
|
|
@@ -115,10 +119,28 @@ async function verifyContract(hre, contract, isProxy, constructorArguments) {
|
|
|
115
119
|
constructorArguments: constructorArguments,
|
|
116
120
|
});
|
|
117
121
|
if (isProxy) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
+
// the following should work but it fails with the current @openzeppelin/hardhat-upgrades version (1.20.0)
|
|
123
|
+
// await hre.run("verify:verify", {
|
|
124
|
+
// address: contract.address,
|
|
125
|
+
// });
|
|
126
|
+
|
|
127
|
+
// so we use the following workaround
|
|
128
|
+
const endpoints = await hre.run("verify:get-etherscan-endpoint");
|
|
129
|
+
const params = new URLSearchParams({
|
|
130
|
+
module: "contract",
|
|
131
|
+
action: "verifyproxycontract",
|
|
132
|
+
apiKey: hre.config.etherscan.apiKey,
|
|
133
|
+
address: contract.address,
|
|
134
|
+
});
|
|
135
|
+
const response = await request(endpoints.urls.apiURL, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
138
|
+
body: params.toString(),
|
|
139
|
+
});
|
|
140
|
+
if (response.statusCode != 200)
|
|
141
|
+
throw new Error(`Etherscan replied with ${response.statusCode}: ${await response.body.text()}`);
|
|
142
|
+
const body = await response.body.json();
|
|
143
|
+
if (body.status != 1) throw new Error(`Etherscan replied with ${body}`);
|
|
122
144
|
}
|
|
123
145
|
} catch (error) {
|
|
124
146
|
console.log("Error verifying contract", error);
|
|
@@ -141,7 +163,7 @@ async function deployContract({ saveAddr, verify, contractClass, constructorArgs
|
|
|
141
163
|
}
|
|
142
164
|
|
|
143
165
|
async function deployProxyContract(
|
|
144
|
-
{ saveAddr, verify, contractClass, constructorArgs, initializeArgs, initializer },
|
|
166
|
+
{ saveAddr, verify, contractClass, constructorArgs, initializeArgs, initializer, deployProxyArgs },
|
|
145
167
|
hre
|
|
146
168
|
) {
|
|
147
169
|
const ContractFactory = await hre.ethers.getContractFactory(contractClass);
|
|
@@ -149,7 +171,7 @@ async function deployProxyContract(
|
|
|
149
171
|
constructorArgs: constructorArgs,
|
|
150
172
|
kind: "uups",
|
|
151
173
|
initializer: initializer,
|
|
152
|
-
|
|
174
|
+
...deployProxyArgs,
|
|
153
175
|
});
|
|
154
176
|
if (verify) {
|
|
155
177
|
// From https://ethereum.stackexchange.com/a/119622/79726
|
|
@@ -266,6 +288,7 @@ async function deployEToken(
|
|
|
266
288
|
contractClass: "EToken",
|
|
267
289
|
constructorArgs: [poolAddress],
|
|
268
290
|
initializeArgs: [etkName, etkSymbol, _W(maxUtilizationRate), _W(poolLoanInterestRate)],
|
|
291
|
+
deployProxyArgs: reservesOpts,
|
|
269
292
|
...opts,
|
|
270
293
|
},
|
|
271
294
|
hre
|
|
@@ -284,6 +307,7 @@ async function deployPremiumsAccount({ poolAddress, juniorEtk, seniorEtk, runAs,
|
|
|
284
307
|
contractClass: "PremiumsAccount",
|
|
285
308
|
constructorArgs: [poolAddress, juniorEtk, seniorEtk],
|
|
286
309
|
initializeArgs: [],
|
|
310
|
+
deployProxyArgs: reservesOpts,
|
|
287
311
|
...opts,
|
|
288
312
|
},
|
|
289
313
|
hre
|