@ensuro/core 2.0.0-beta9 → 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 +14 -0
- package/build/contracts/AccessManager.sol/AccessManager.json +7 -2
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +21 -2
- package/build/contracts/EToken.sol/EToken.json +21 -2
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +21 -2
- package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +19 -0
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +21 -2
- package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +19 -0
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +21 -2
- package/build/contracts/Reserve.sol/Reserve.json +19 -0
- 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 +2 -2
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +137 -2
- package/build/contracts/dependencies/WadRayMath.sol/WadRayMath.json +2 -2
- package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +19 -0
- package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +19 -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 +2 -2
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +21 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +21 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +21 -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 +30 -1
- package/contracts/ERC4626AssetManager.sol +3 -0
- package/contracts/EToken.sol +89 -19
- package/contracts/LPManualWhitelist.sol +18 -2
- package/contracts/LiquidityThresholdAssetManager.sol +17 -2
- package/contracts/Policy.sol +4 -2
- package/contracts/PolicyPool.sol +72 -13
- package/contracts/PolicyPoolComponent.sol +29 -3
- package/contracts/PremiumsAccount.sol +72 -24
- package/contracts/Reserve.sol +19 -3
- package/contracts/RiskModule.sol +46 -13
- package/contracts/SignedQuoteRiskModule.sol +310 -0
- package/contracts/TrustfulRiskModule.sol +104 -3
- package/contracts/interfaces/IAssetManager.sol +8 -3
- package/contracts/interfaces/IPolicyPool.sol +15 -4
- package/contracts/interfaces/IPolicyPoolComponent.sol +2 -1
- package/contracts/mocks/InterfaceIdCalculator.sol +32 -0
- package/contracts/mocks/PolicyPoolComponentMock.sol +10 -0
- package/contracts/mocks/PolicyPoolMock.sol +4 -0
- package/js/deploy.js +734 -0
- package/js/test-utils.js +139 -37
- 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
|
@@ -9,6 +9,16 @@ import {IAccessManager} from "./interfaces/IAccessManager.sol";
|
|
|
9
9
|
/**
|
|
10
10
|
* @title AccessManager - Protocol access roles
|
|
11
11
|
* @dev Contract that holds the access roles for PolicyPool and other components of the protocol.
|
|
12
|
+
*
|
|
13
|
+
* [CAUTION]
|
|
14
|
+
* ====
|
|
15
|
+
* Avoid leaving a this contract without DEFAULT_ADMIN_ROLE.
|
|
16
|
+
*
|
|
17
|
+
* This contract includes the methods `revokeRole()` and `renounceRole()` that allow to revoke or renounce to
|
|
18
|
+
* specific roles. Even when there are valid reasons to leave these methods (for example revoking the initial
|
|
19
|
+
* DEFAULT_ADMIN_ROLE of the deployer account to leave just the governance account), it's good to mention these
|
|
20
|
+
* methods have to be used with care, avoiding leaving the contract without any default admin.
|
|
21
|
+
*
|
|
12
22
|
* @custom:security-contact security@ensuro.co
|
|
13
23
|
* @author Ensuro
|
|
14
24
|
*/
|
|
@@ -24,6 +34,11 @@ contract AccessManager is Initializable, AccessControlUpgradeable, UUPSUpgradeab
|
|
|
24
34
|
_;
|
|
25
35
|
}
|
|
26
36
|
|
|
37
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
38
|
+
constructor() {
|
|
39
|
+
_disableInitializers();
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
function initialize() public initializer {
|
|
28
43
|
__AccessControl_init();
|
|
29
44
|
__UUPSUpgradeable_init();
|
|
@@ -31,13 +46,20 @@ contract AccessManager is Initializable, AccessControlUpgradeable, UUPSUpgradeab
|
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
// solhint-disable-next-line func-name-mixedcase
|
|
34
|
-
function __AccessManager_init_unchained() internal
|
|
49
|
+
function __AccessManager_init_unchained() internal onlyInitializing {
|
|
35
50
|
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
|
|
36
51
|
}
|
|
37
52
|
|
|
38
53
|
// solhint-disable-next-line no-empty-blocks
|
|
39
54
|
function _authorizeUpgrade(address) internal override onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {}
|
|
40
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @dev See {IERC165-supportsInterface}.
|
|
58
|
+
*/
|
|
59
|
+
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
|
60
|
+
return super.supportsInterface(interfaceId) || interfaceId == type(IAccessManager).interfaceId;
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
function getComponentRole(address component, bytes32 role)
|
|
42
64
|
public
|
|
43
65
|
pure
|
|
@@ -100,4 +122,11 @@ contract AccessManager is Initializable, AccessControlUpgradeable, UUPSUpgradeab
|
|
|
100
122
|
) external onlyRole(getRoleAdmin(getComponentRole(component, role))) {
|
|
101
123
|
_grantRole(getComponentRole(component, role), account);
|
|
102
124
|
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
128
|
+
* variables without shifting down storage in the inheritance chain.
|
|
129
|
+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
130
|
+
*/
|
|
131
|
+
uint256[50] private __gap;
|
|
103
132
|
}
|
|
@@ -11,6 +11,8 @@ import {LiquidityThresholdAssetManager} from "./LiquidityThresholdAssetManager.s
|
|
|
11
11
|
* @dev Using liquidity thresholds defined in {LiquidityThresholdAssetManager}, deploys the funds into an ERC4626 vault.
|
|
12
12
|
* @custom:security-contact security@ensuro.co
|
|
13
13
|
* @author Ensuro
|
|
14
|
+
*
|
|
15
|
+
* @notice This contracts uses Diamond Storage and should not define state variables outside of that. See the diamondStorage method for more details.
|
|
14
16
|
*/
|
|
15
17
|
contract ERC4626AssetManager is LiquidityThresholdAssetManager {
|
|
16
18
|
using SafeCast for uint256;
|
|
@@ -18,6 +20,7 @@ contract ERC4626AssetManager is LiquidityThresholdAssetManager {
|
|
|
18
20
|
IERC4626 internal immutable _vault;
|
|
19
21
|
|
|
20
22
|
constructor(IERC20Metadata asset_, IERC4626 vault_) LiquidityThresholdAssetManager(asset_) {
|
|
23
|
+
require(address(vault_) != address(0), "ERC4626AssetManager: vault cannot be zero address");
|
|
21
24
|
_vault = vault_;
|
|
22
25
|
}
|
|
23
26
|
|
package/contracts/EToken.sol
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
4
5
|
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
5
6
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
7
|
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
@@ -31,6 +32,13 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
31
32
|
using SafeERC20 for IERC20Metadata;
|
|
32
33
|
using SafeCast for uint256;
|
|
33
34
|
|
|
35
|
+
uint256 internal constant FOUR_DECIMAL_TO_WAD = 1e14;
|
|
36
|
+
uint16 internal constant HUNDRED_PERCENT = 1e4;
|
|
37
|
+
uint16 internal constant MAX_UR_MIN = 5e3; // 50% - Minimum value for Max Utilization Rate
|
|
38
|
+
uint16 internal constant LIQ_REQ_MIN = 8e3; // 80%
|
|
39
|
+
uint16 internal constant LIQ_REQ_MAX = 13e3; // 130%
|
|
40
|
+
uint16 internal constant INT_LOAN_IR_MAX = 5e3; // 50% - Maximum value for InternalLoan interest rate
|
|
41
|
+
|
|
34
42
|
// Attributes taken from ERC20
|
|
35
43
|
mapping(address => uint256) private _balances;
|
|
36
44
|
mapping(address => mapping(address => uint256)) private _allowances;
|
|
@@ -90,7 +98,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
90
98
|
uint256 maxUtilizationRate_,
|
|
91
99
|
uint256 internalLoanInterestRate_
|
|
92
100
|
) public initializer {
|
|
93
|
-
|
|
101
|
+
__Reserve_init();
|
|
94
102
|
__EToken_init_unchained(name_, symbol_, maxUtilizationRate_, internalLoanInterestRate_);
|
|
95
103
|
}
|
|
96
104
|
|
|
@@ -100,7 +108,9 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
100
108
|
string memory symbol_,
|
|
101
109
|
uint256 maxUtilizationRate_,
|
|
102
110
|
uint256 internalLoanInterestRate_
|
|
103
|
-
) internal
|
|
111
|
+
) internal onlyInitializing {
|
|
112
|
+
require(bytes(name_).length > 0, "EToken: name cannot be empty");
|
|
113
|
+
require(bytes(symbol_).length > 0, "EToken: symbol cannot be empty");
|
|
104
114
|
_name = name_;
|
|
105
115
|
_symbol = symbol_;
|
|
106
116
|
_tsScaled.init();
|
|
@@ -111,7 +121,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
111
121
|
}); */
|
|
112
122
|
_params = PackedParams({
|
|
113
123
|
maxUtilizationRate: _wadTo4(maxUtilizationRate_),
|
|
114
|
-
liquidityRequirement:
|
|
124
|
+
liquidityRequirement: HUNDRED_PERCENT,
|
|
115
125
|
minUtilizationRate: 0,
|
|
116
126
|
internalLoanInterestRate: _wadTo4(internalLoanInterestRate_),
|
|
117
127
|
whitelist: ILPWhitelist(address(0))
|
|
@@ -120,19 +130,42 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
120
130
|
_validateParameters();
|
|
121
131
|
}
|
|
122
132
|
|
|
133
|
+
/**
|
|
134
|
+
* @dev See {IERC165-supportsInterface}.
|
|
135
|
+
*/
|
|
136
|
+
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
|
137
|
+
return
|
|
138
|
+
super.supportsInterface(interfaceId) ||
|
|
139
|
+
interfaceId == type(IERC20).interfaceId ||
|
|
140
|
+
interfaceId == type(IERC20Metadata).interfaceId ||
|
|
141
|
+
interfaceId == type(IEToken).interfaceId;
|
|
142
|
+
}
|
|
143
|
+
|
|
123
144
|
// runs validation on EToken parameters
|
|
124
145
|
function _validateParameters() internal view override {
|
|
125
146
|
require(
|
|
126
|
-
_params.liquidityRequirement >=
|
|
147
|
+
_params.liquidityRequirement >= LIQ_REQ_MIN && _params.liquidityRequirement <= LIQ_REQ_MAX,
|
|
127
148
|
"Validation: liquidityRequirement must be [0.8, 1.3]"
|
|
128
149
|
);
|
|
129
150
|
require(
|
|
130
|
-
_params.maxUtilizationRate >=
|
|
151
|
+
_params.maxUtilizationRate >= MAX_UR_MIN && _params.maxUtilizationRate <= HUNDRED_PERCENT,
|
|
131
152
|
"Validation: maxUtilizationRate must be [0.5, 1]"
|
|
132
153
|
);
|
|
133
|
-
require(_params.minUtilizationRate <= 1e4, "Validation: minUtilizationRate must be [0, 1]");
|
|
134
154
|
require(
|
|
135
|
-
_params.
|
|
155
|
+
_params.minUtilizationRate <= HUNDRED_PERCENT,
|
|
156
|
+
"Validation: minUtilizationRate must be [0, 1]"
|
|
157
|
+
);
|
|
158
|
+
/*
|
|
159
|
+
* We don't validate minUtilizationRate < maxUtilizationRate because the opposite is valid too.
|
|
160
|
+
* These limits aren't strong limits on the values the utilization rate can take, but instead they are
|
|
161
|
+
* limits on specific operations.
|
|
162
|
+
* `minUtilizationRate` is used to avoid new deposits to dilute the yields of existing LPs, but it doesn't
|
|
163
|
+
* prevent the UR from going down in other operations (`unlockScr` for example).
|
|
164
|
+
* `maxUtilizationRate` is used to prevent selling more coverage when UR is too high, only checked on `lockScr`
|
|
165
|
+
* operations, but not in withdrawals or other operations.
|
|
166
|
+
*/
|
|
167
|
+
require(
|
|
168
|
+
_params.internalLoanInterestRate <= INT_LOAN_IR_MAX,
|
|
136
169
|
"Validation: internalLoanInterestRate must be <= 50%"
|
|
137
170
|
);
|
|
138
171
|
}
|
|
@@ -243,15 +276,34 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
243
276
|
address recipient,
|
|
244
277
|
uint256 amount
|
|
245
278
|
) public virtual override returns (bool) {
|
|
279
|
+
address spender = _msgSender();
|
|
280
|
+
_spendAllowance(sender, spender, amount);
|
|
246
281
|
_transfer(sender, recipient, amount);
|
|
247
|
-
|
|
248
|
-
uint256 currentAllowance = _allowances[sender][_msgSender()];
|
|
249
|
-
require(currentAllowance >= amount, "EToken: transfer amount exceeds allowance");
|
|
250
|
-
_approve(sender, _msgSender(), currentAllowance - amount);
|
|
251
|
-
|
|
252
282
|
return true;
|
|
253
283
|
}
|
|
254
284
|
|
|
285
|
+
/**
|
|
286
|
+
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
|
|
287
|
+
*
|
|
288
|
+
* Does not update the allowance amount in case of infinite allowance.
|
|
289
|
+
* Revert if not enough allowance is available.
|
|
290
|
+
*
|
|
291
|
+
* Might emit an {Approval} event.
|
|
292
|
+
*/
|
|
293
|
+
function _spendAllowance(
|
|
294
|
+
address owner,
|
|
295
|
+
address spender,
|
|
296
|
+
uint256 amount
|
|
297
|
+
) internal virtual {
|
|
298
|
+
uint256 currentAllowance = allowance(owner, spender);
|
|
299
|
+
if (currentAllowance != type(uint256).max) {
|
|
300
|
+
require(currentAllowance >= amount, "EToken: insufficient allowance");
|
|
301
|
+
unchecked {
|
|
302
|
+
_approve(owner, spender, currentAllowance - amount);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
255
307
|
/**
|
|
256
308
|
* @dev Atomically increases the allowance granted to `spender` by the caller.
|
|
257
309
|
*
|
|
@@ -265,7 +317,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
265
317
|
* - `spender` cannot be the zero address.
|
|
266
318
|
*/
|
|
267
319
|
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
|
|
268
|
-
_approve(_msgSender(), spender,
|
|
320
|
+
_approve(_msgSender(), spender, allowance(_msgSender(), spender) + addedValue);
|
|
269
321
|
return true;
|
|
270
322
|
}
|
|
271
323
|
|
|
@@ -339,6 +391,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
339
391
|
*/
|
|
340
392
|
function _mint(address account, uint256 amount) internal virtual {
|
|
341
393
|
require(account != address(0), "EToken: mint to the zero address");
|
|
394
|
+
require(amount > 0, "EToken: amount to mint should be greater than zero");
|
|
342
395
|
|
|
343
396
|
_beforeTokenTransfer(address(0), account, amount);
|
|
344
397
|
uint256 scaledAmount = _tsScaled.add(amount, tokenInterestRate());
|
|
@@ -464,12 +517,12 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
464
517
|
// solhint-disable-next-line func-name-mixedcase
|
|
465
518
|
function _4toWad(uint16 value) internal pure returns (uint256) {
|
|
466
519
|
// 4 decimals to Wad (18 decimals)
|
|
467
|
-
return uint256(value) *
|
|
520
|
+
return uint256(value) * FOUR_DECIMAL_TO_WAD;
|
|
468
521
|
}
|
|
469
522
|
|
|
470
523
|
function _wadTo4(uint256 value) internal pure returns (uint16) {
|
|
471
524
|
// Wad to 4 decimals
|
|
472
|
-
return (value /
|
|
525
|
+
return (value / FOUR_DECIMAL_TO_WAD).toUint16();
|
|
473
526
|
}
|
|
474
527
|
|
|
475
528
|
function scr() public view virtual override returns (uint256) {
|
|
@@ -508,7 +561,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
508
561
|
{
|
|
509
562
|
require(
|
|
510
563
|
scrAmount <= this.fundsAvailableToLock(),
|
|
511
|
-
"Not
|
|
564
|
+
"Not enough funds available to cover the SCR"
|
|
512
565
|
);
|
|
513
566
|
_tsScaled.updateScale(tokenInterestRate());
|
|
514
567
|
if (_scr.scr == 0) {
|
|
@@ -529,7 +582,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
529
582
|
uint256 scrAmount,
|
|
530
583
|
uint256 policyInterestRate,
|
|
531
584
|
int256 adjustment
|
|
532
|
-
) external override onlyBorrower {
|
|
585
|
+
) external override onlyBorrower whenNotPaused {
|
|
533
586
|
require(scrAmount <= uint256(_scr.scr), "Current SCR less than the amount you want to unlock");
|
|
534
587
|
_tsScaled.updateScale(tokenInterestRate());
|
|
535
588
|
|
|
@@ -586,6 +639,12 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
586
639
|
onlyPolicyPool
|
|
587
640
|
returns (uint256)
|
|
588
641
|
{
|
|
642
|
+
/**
|
|
643
|
+
* Here we don't check for maxUtilizationRate because that limit only affects locking more capital (`lockScr`), but
|
|
644
|
+
* doesn't affects the right of liquidity providers to withdraw their funds.
|
|
645
|
+
* The only limit for withdraws is the `totalWithdrawable()` function, that's affected by the relation between the
|
|
646
|
+
* scr and the totalSupply.
|
|
647
|
+
*/
|
|
589
648
|
amount = Math.min(amount, Math.min(balanceOf(provider), totalWithdrawable()));
|
|
590
649
|
if (amount == 0) return 0;
|
|
591
650
|
_burn(provider, amount);
|
|
@@ -595,6 +654,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
595
654
|
}
|
|
596
655
|
|
|
597
656
|
function addBorrower(address borrower) external override onlyPolicyPool {
|
|
657
|
+
require(borrower != address(0), "EToken: Borrower cannot be the zero address");
|
|
598
658
|
TimeScaled.ScaledAmount storage loan = _loans[borrower];
|
|
599
659
|
if (loan.scale == 0) {
|
|
600
660
|
loan.init();
|
|
@@ -603,6 +663,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
603
663
|
}
|
|
604
664
|
|
|
605
665
|
function removeBorrower(address borrower) external override onlyPolicyPool {
|
|
666
|
+
require(borrower != address(0), "EToken: Borrower cannot be the zero address");
|
|
606
667
|
uint256 defaultedDebt = getLoan(borrower);
|
|
607
668
|
delete _loans[borrower];
|
|
608
669
|
emit InternalBorrowerRemoved(borrower, defaultedDebt);
|
|
@@ -627,14 +688,16 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
627
688
|
return amountAsked - amount;
|
|
628
689
|
}
|
|
629
690
|
|
|
630
|
-
function repayLoan(uint256 amount, address onBehalfOf) external override {
|
|
691
|
+
function repayLoan(uint256 amount, address onBehalfOf) external override whenNotPaused {
|
|
692
|
+
require(amount > 0, "EToken: amount should be greater than zero.");
|
|
631
693
|
// Anyone can call this method, since it has to pay
|
|
632
|
-
currency().safeTransferFrom(_msgSender(), address(this), amount);
|
|
633
694
|
TimeScaled.ScaledAmount storage loan = _loans[onBehalfOf];
|
|
634
695
|
require(loan.scale != 0, "Not a registered borrower");
|
|
635
696
|
loan.sub(amount, internalLoanInterestRate());
|
|
636
697
|
_discreteChange(int256(amount));
|
|
637
698
|
emit InternalLoanRepaid(onBehalfOf, amount);
|
|
699
|
+
// Interaction at the end for security reasons
|
|
700
|
+
currency().safeTransferFrom(_msgSender(), address(this), amount);
|
|
638
701
|
}
|
|
639
702
|
|
|
640
703
|
function getLoan(address borrower) public view virtual override returns (uint256) {
|
|
@@ -705,4 +768,11 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
705
768
|
function whitelist() external view returns (ILPWhitelist) {
|
|
706
769
|
return _params.whitelist;
|
|
707
770
|
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
774
|
+
* variables without shifting down storage in the inheritance chain.
|
|
775
|
+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
776
|
+
*/
|
|
777
|
+
uint256[41] private __gap;
|
|
708
778
|
}
|
|
@@ -34,8 +34,17 @@ contract LPManualWhitelist is ILPWhitelist, PolicyPoolComponent {
|
|
|
34
34
|
external
|
|
35
35
|
onlyComponentRole(LP_WHITELIST_ROLE)
|
|
36
36
|
{
|
|
37
|
-
_whitelisted[provider]
|
|
38
|
-
|
|
37
|
+
if (_whitelisted[provider] != whitelisted) {
|
|
38
|
+
_whitelisted[provider] = whitelisted;
|
|
39
|
+
emit LPWhitelisted(provider, whitelisted);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @dev See {IERC165-supportsInterface}.
|
|
45
|
+
*/
|
|
46
|
+
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
|
47
|
+
return super.supportsInterface(interfaceId) || interfaceId == type(ILPWhitelist).interfaceId;
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
function acceptsDeposit(
|
|
@@ -54,4 +63,11 @@ contract LPManualWhitelist is ILPWhitelist, PolicyPoolComponent {
|
|
|
54
63
|
) external view override returns (bool) {
|
|
55
64
|
return _whitelisted[providerTo];
|
|
56
65
|
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
69
|
+
* variables without shifting down storage in the inheritance chain.
|
|
70
|
+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
71
|
+
*/
|
|
72
|
+
uint256[49] private __gap;
|
|
57
73
|
}
|
|
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
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,6 +18,8 @@ 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
25
|
using SafeCast for uint256;
|
|
@@ -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
|
|
|
@@ -96,8 +103,8 @@ 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
|
*/
|
|
@@ -157,4 +164,12 @@ abstract contract LiquidityThresholdAssetManager is IAssetManager {
|
|
|
157
164
|
emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMax, max);
|
|
158
165
|
}
|
|
159
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
|
+
}
|
|
160
175
|
}
|
package/contracts/Policy.sol
CHANGED
|
@@ -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
|
@@ -166,7 +166,7 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
* @dev Modifier that checks the caller has any of the given
|
|
169
|
+
* @dev Modifier that checks the caller has any of the given roles
|
|
170
170
|
*/
|
|
171
171
|
modifier onlyRole2(bytes32 role1, bytes32 role2) {
|
|
172
172
|
_access.checkRole2(role1, role2, _msgSender());
|
|
@@ -182,6 +182,9 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
182
182
|
*/
|
|
183
183
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
184
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();
|
|
185
188
|
_access = access_;
|
|
186
189
|
_currency = currency_;
|
|
187
190
|
}
|
|
@@ -198,19 +201,36 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
198
201
|
string memory symbol_,
|
|
199
202
|
address treasury_
|
|
200
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");
|
|
201
206
|
__UUPSUpgradeable_init();
|
|
202
207
|
__ERC721_init(name_, symbol_);
|
|
203
208
|
__Pausable_init();
|
|
204
209
|
__PolicyPool_init_unchained(treasury_);
|
|
205
210
|
}
|
|
206
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
|
+
|
|
207
219
|
// solhint-disable-next-line func-name-mixedcase
|
|
208
|
-
function __PolicyPool_init_unchained(address treasury_) internal
|
|
209
|
-
|
|
220
|
+
function __PolicyPool_init_unchained(address treasury_) internal onlyInitializing {
|
|
221
|
+
_setTreasury(treasury_);
|
|
210
222
|
}
|
|
211
223
|
|
|
212
|
-
|
|
213
|
-
|
|
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
|
+
}
|
|
214
234
|
|
|
215
235
|
/**
|
|
216
236
|
* @dev Pauses the contract. When the contract is paused, several operations are rejected: deposits, withdrawals, new
|
|
@@ -241,6 +261,12 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
241
261
|
return _currency;
|
|
242
262
|
}
|
|
243
263
|
|
|
264
|
+
function _setTreasury(address treasury_) internal {
|
|
265
|
+
require(treasury_ != address(0), "PolicyPool: treasury cannot be the zero address");
|
|
266
|
+
_treasury = treasury_;
|
|
267
|
+
emit ComponentChanged(IAccessManager.GovernanceActions.setTreasury, _treasury);
|
|
268
|
+
}
|
|
269
|
+
|
|
244
270
|
/**
|
|
245
271
|
* @dev Changes the address of the treasury, the one that receives the protocol fees.
|
|
246
272
|
*
|
|
@@ -251,8 +277,7 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
251
277
|
* - Emits {ComponentChanged} with action = setTreasury and the address of the new treasury.
|
|
252
278
|
*/
|
|
253
279
|
function setTreasury(address treasury_) external onlyRole(LEVEL1_ROLE) {
|
|
254
|
-
|
|
255
|
-
emit ComponentChanged(IAccessManager.GovernanceActions.setTreasury, _treasury);
|
|
280
|
+
_setTreasury(treasury_);
|
|
256
281
|
}
|
|
257
282
|
|
|
258
283
|
/**
|
|
@@ -285,6 +310,15 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
285
310
|
require(comp.status == ComponentStatus.inactive, "Component already in the pool");
|
|
286
311
|
require(component.policyPool() == this, "Component not linked to this pool");
|
|
287
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
|
+
|
|
288
322
|
comp.status = ComponentStatus.active;
|
|
289
323
|
comp.kind = kind;
|
|
290
324
|
if (kind == ComponentKind.premiumsAccount) {
|
|
@@ -406,8 +440,9 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
406
440
|
|
|
407
441
|
function deposit(IEToken eToken, uint256 amount) external override whenNotPaused {
|
|
408
442
|
require(_etkStatus(eToken) == ComponentStatus.active, "eToken is not active");
|
|
443
|
+
uint256 balanceBefore = _currency.balanceOf(address(eToken));
|
|
409
444
|
_currency.safeTransferFrom(_msgSender(), address(eToken), amount);
|
|
410
|
-
eToken.deposit(_msgSender(),
|
|
445
|
+
eToken.deposit(_msgSender(), _currency.balanceOf(address(eToken)) - balanceBefore);
|
|
411
446
|
}
|
|
412
447
|
|
|
413
448
|
function withdraw(IEToken eToken, uint256 amount)
|
|
@@ -431,15 +466,20 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
431
466
|
address policyHolder,
|
|
432
467
|
uint96 internalId
|
|
433
468
|
) external override whenNotPaused returns (uint256) {
|
|
469
|
+
// Checks
|
|
434
470
|
IRiskModule rm = policy.riskModule;
|
|
435
471
|
require(address(rm) == _msgSender(), "Only the RM can create new policies");
|
|
436
472
|
require(_rmStatus(rm) == ComponentStatus.active, "RM module not found or not active");
|
|
437
|
-
policy.id = (uint256(uint160(address(rm))) << 96) + internalId;
|
|
438
|
-
_policies[policy.id] = policy.hash();
|
|
439
473
|
IPremiumsAccount pa = rm.premiumsAccount();
|
|
440
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
|
|
441
482
|
pa.policyCreated(policy);
|
|
442
|
-
_safeMint(policyHolder, policy.id, "");
|
|
443
483
|
|
|
444
484
|
// Distribute the premium
|
|
445
485
|
_currency.safeTransferFrom(payer, address(pa), policy.purePremium);
|
|
@@ -448,7 +488,12 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
448
488
|
_currency.safeTransferFrom(payer, _treasury, policy.ensuroCommission);
|
|
449
489
|
if (policy.partnerCommission > 0 && payer != rm.wallet())
|
|
450
490
|
_currency.safeTransferFrom(payer, rm.wallet(), policy.partnerCommission);
|
|
451
|
-
|
|
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, "");
|
|
452
497
|
|
|
453
498
|
emit NewPolicy(rm, policy);
|
|
454
499
|
return policy.id;
|
|
@@ -483,6 +528,10 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
483
528
|
return _policies[policyId] != bytes32(0);
|
|
484
529
|
}
|
|
485
530
|
|
|
531
|
+
function getPolicyHash(uint256 policyId) external view override returns (bytes32) {
|
|
532
|
+
return _policies[policyId];
|
|
533
|
+
}
|
|
534
|
+
|
|
486
535
|
/**
|
|
487
536
|
* @dev Internal function that handles the different alternative resolutions for a policy, with or without payout and
|
|
488
537
|
* expiration.
|
|
@@ -499,6 +548,7 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
499
548
|
uint256 payout,
|
|
500
549
|
bool expired
|
|
501
550
|
) internal {
|
|
551
|
+
// Checks
|
|
502
552
|
_validatePolicy(policy);
|
|
503
553
|
IRiskModule rm = policy.riskModule;
|
|
504
554
|
require(expired || address(rm) == _msgSender(), "Only the RM can resolve policies");
|
|
@@ -518,6 +568,9 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
518
568
|
compStatus == ComponentStatus.active || compStatus == ComponentStatus.deprecated,
|
|
519
569
|
"PremiumsAccount must be active or deprecated to process resolutions"
|
|
520
570
|
);
|
|
571
|
+
// Effects
|
|
572
|
+
delete _policies[policy.id];
|
|
573
|
+
// Interactions
|
|
521
574
|
if (customerWon) {
|
|
522
575
|
address policyOwner = ownerOf(policy.id);
|
|
523
576
|
pa.policyResolvedWithPayout(policyOwner, policy, payout);
|
|
@@ -528,7 +581,6 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
528
581
|
rm.releaseExposure(policy.payout);
|
|
529
582
|
|
|
530
583
|
emit PolicyResolved(policy.riskModule, policy.id, payout);
|
|
531
|
-
delete _policies[policy.id];
|
|
532
584
|
if (payout > 0) {
|
|
533
585
|
_notifyPayout(policy.id, payout);
|
|
534
586
|
} else {
|
|
@@ -584,4 +636,11 @@ contract PolicyPool is IPolicyPool, PausableUpgradeable, UUPSUpgradeable, ERC721
|
|
|
584
636
|
) internal override whenNotPaused {
|
|
585
637
|
super._beforeTokenTransfer(from, to, tokenId);
|
|
586
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;
|
|
587
646
|
}
|