@ensuro/core 2.0.0-beta6 → 2.0.0-beta8
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 +74 -2
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +213 -0
- package/build/contracts/EToken.sol/EToken.json +114 -45
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
- package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +197 -0
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +324 -100
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +122 -2
- package/build/contracts/Reserve.sol/Reserve.json +71 -0
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +3 -3
- package/build/contracts/interfaces/IAccessManager.sol/IAccessManager.json +96 -0
- package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +103 -0
- package/build/contracts/interfaces/IEToken.sol/IEToken.json +13 -0
- package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +6 -14
- package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
- package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +7 -15
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +674 -0
- package/contracts/AccessManager.sol +39 -8
- package/contracts/ERC4626AssetManager.sol +49 -0
- package/contracts/EToken.sol +72 -52
- package/contracts/LiquidityThresholdAssetManager.sol +159 -0
- package/contracts/PolicyPool.sol +155 -135
- package/contracts/PolicyPoolComponent.sol +29 -19
- package/contracts/PremiumsAccount.sol +125 -62
- package/contracts/Reserve.sol +86 -2
- package/contracts/RiskModule.sol +17 -6
- package/contracts/TrustfulRiskModule.sol +17 -2
- package/contracts/interfaces/IAccessManager.sol +23 -5
- package/contracts/interfaces/IAssetManager.sol +22 -0
- package/contracts/interfaces/IEToken.sol +21 -1
- package/contracts/interfaces/IPolicyPool.sol +6 -11
- package/contracts/interfaces/IPremiumsAccount.sol +5 -0
- package/contracts/mocks/FixedRateVault.sol +85 -0
- package/contracts/mocks/PolicyPoolMock.sol +2 -5
- package/contracts/mocks/RiskModuleMock.sol +69 -0
- package/js/test-utils.js +35 -26
- package/package.json +1 -1
- package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
- package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
- package/contracts/PolicyNFT.sol +0 -96
- package/contracts/interfaces/IPolicyNFT.sol +0 -15
|
@@ -38,24 +38,47 @@ contract AccessManager is Initializable, AccessControlUpgradeable, UUPSUpgradeab
|
|
|
38
38
|
// solhint-disable-next-line no-empty-blocks
|
|
39
39
|
function _authorizeUpgrade(address) internal override onlyRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {}
|
|
40
40
|
|
|
41
|
-
function getComponentRole(address component, bytes32 role)
|
|
41
|
+
function getComponentRole(address component, bytes32 role)
|
|
42
|
+
public
|
|
43
|
+
pure
|
|
44
|
+
override
|
|
45
|
+
returns (bytes32)
|
|
46
|
+
{
|
|
42
47
|
return bytes32(bytes20(component)) ^ role;
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
function hasComponentRole(
|
|
51
|
+
address component,
|
|
52
|
+
bytes32 role,
|
|
53
|
+
address account,
|
|
54
|
+
bool alsoGlobal
|
|
55
|
+
) public view override returns (bool) {
|
|
56
|
+
return
|
|
57
|
+
(alsoGlobal && hasRole(role, account)) || hasRole(getComponentRole(component, role), account);
|
|
58
|
+
}
|
|
59
|
+
|
|
45
60
|
function checkComponentRole(
|
|
46
61
|
address component,
|
|
47
62
|
bytes32 role,
|
|
48
|
-
address account
|
|
63
|
+
address account,
|
|
64
|
+
bool alsoGlobal
|
|
49
65
|
) external view override {
|
|
50
|
-
|
|
66
|
+
if (!alsoGlobal || !hasRole(role, account)) {
|
|
67
|
+
_checkRole(getComponentRole(component, role), account);
|
|
68
|
+
}
|
|
51
69
|
}
|
|
52
70
|
|
|
53
|
-
function
|
|
71
|
+
function checkComponentRole2(
|
|
54
72
|
address component,
|
|
55
|
-
bytes32
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
73
|
+
bytes32 role1,
|
|
74
|
+
bytes32 role2,
|
|
75
|
+
address account,
|
|
76
|
+
bool alsoGlobal
|
|
77
|
+
) external view override {
|
|
78
|
+
if (alsoGlobal && hasRole(role1, account)) return;
|
|
79
|
+
if (hasRole(getComponentRole(component, role1), account)) return;
|
|
80
|
+
if (alsoGlobal && hasRole(role2, account)) return;
|
|
81
|
+
_checkRole(getComponentRole(component, role2), account);
|
|
59
82
|
}
|
|
60
83
|
|
|
61
84
|
function checkRole(bytes32 role, address account) external view override {
|
|
@@ -69,4 +92,12 @@ contract AccessManager is Initializable, AccessControlUpgradeable, UUPSUpgradeab
|
|
|
69
92
|
) external view override {
|
|
70
93
|
if (!hasRole(role1, account)) _checkRole(role2, account);
|
|
71
94
|
}
|
|
95
|
+
|
|
96
|
+
function grantComponentRole(
|
|
97
|
+
address component,
|
|
98
|
+
bytes32 role,
|
|
99
|
+
address account
|
|
100
|
+
) external onlyRole(getRoleAdmin(getComponentRole(component, role))) {
|
|
101
|
+
_grantRole(getComponentRole(component, role), account);
|
|
102
|
+
}
|
|
72
103
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {LiquidityThresholdAssetManager} from "./LiquidityThresholdAssetManager.sol";
|
|
5
|
+
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
6
|
+
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title Asset Manager that deploys the funds into an ERC4626 vault
|
|
10
|
+
* @dev Using liquidity thresholds defined in {LiquidityThresholdAssetManager}, deploys the funds into an ERC4626 vault.
|
|
11
|
+
* @custom:security-contact security@ensuro.co
|
|
12
|
+
* @author Ensuro
|
|
13
|
+
*/
|
|
14
|
+
contract ERC4626AssetManager is LiquidityThresholdAssetManager {
|
|
15
|
+
IERC4626 internal immutable _vault;
|
|
16
|
+
|
|
17
|
+
constructor(IERC20Metadata asset_, IERC4626 vault_) LiquidityThresholdAssetManager(asset_) {
|
|
18
|
+
_vault = vault_;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function connect() public override {
|
|
22
|
+
super.connect();
|
|
23
|
+
_asset.approve(address(_vault), type(uint256).max); // infinite approval to the vault
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function _invest(uint256 amount) internal override {
|
|
27
|
+
super._invest(amount);
|
|
28
|
+
_vault.deposit(amount, address(this));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function _deinvest(uint256 amount) internal override {
|
|
32
|
+
super._deinvest(amount);
|
|
33
|
+
_vault.withdraw(amount, address(this), address(this));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function deinvestAll() external override returns (int256 earnings) {
|
|
37
|
+
DiamondStorage storage ds = diamondStorage();
|
|
38
|
+
uint256 assets = _vault.redeem(_vault.balanceOf(address(this)), address(this), address(this));
|
|
39
|
+
earnings = int256(assets) - int256(uint256(ds.lastInvestmentValue));
|
|
40
|
+
ds.lastInvestmentValue = uint128(assets);
|
|
41
|
+
emit MoneyDeinvested(assets);
|
|
42
|
+
emit EarningsRecorded(earnings);
|
|
43
|
+
return earnings;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getInvestmentValue() public view override returns (uint256) {
|
|
47
|
+
return _vault.convertToAssets(_vault.balanceOf(address(this)));
|
|
48
|
+
}
|
|
49
|
+
}
|
package/contracts/EToken.sol
CHANGED
|
@@ -9,6 +9,7 @@ import {IEToken} from "./interfaces/IEToken.sol";
|
|
|
9
9
|
import {IAccessManager} from "./interfaces/IAccessManager.sol";
|
|
10
10
|
import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
|
|
11
11
|
import {ILPWhitelist} from "./interfaces/ILPWhitelist.sol";
|
|
12
|
+
import {IAssetManager} from "./interfaces/IAssetManager.sol";
|
|
12
13
|
import {WadRayMath} from "./WadRayMath.sol";
|
|
13
14
|
import {TimeScaled} from "./TimeScaled.sol";
|
|
14
15
|
|
|
@@ -59,9 +60,12 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
59
60
|
|
|
60
61
|
PackedParams internal _params;
|
|
61
62
|
|
|
63
|
+
IAssetManager internal _assetManager;
|
|
64
|
+
|
|
62
65
|
event InternalLoan(address indexed borrower, uint256 value, uint256 amountAsked);
|
|
63
66
|
event InternalLoanRepaid(address indexed borrower, uint256 value);
|
|
64
|
-
event
|
|
67
|
+
event InternalBorrowerAdded(address indexed borrower);
|
|
68
|
+
event InternalBorrowerRemoved(address indexed borrower, uint256 defaultedDebt);
|
|
65
69
|
|
|
66
70
|
modifier onlyBorrower() {
|
|
67
71
|
require(_loans[_msgSender()].scale != 0, "The caller must be a borrower");
|
|
@@ -407,7 +411,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
407
411
|
address from,
|
|
408
412
|
address to,
|
|
409
413
|
uint256 amount
|
|
410
|
-
) internal virtual {
|
|
414
|
+
) internal virtual whenNotPaused {
|
|
411
415
|
require(
|
|
412
416
|
from == address(0) ||
|
|
413
417
|
to == address(0) ||
|
|
@@ -447,6 +451,14 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
447
451
|
else return 0;
|
|
448
452
|
}
|
|
449
453
|
|
|
454
|
+
function assetManager() public view override returns (IAssetManager) {
|
|
455
|
+
return _assetManager;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function _setAssetManager(IAssetManager newAM) internal override {
|
|
459
|
+
_assetManager = newAM;
|
|
460
|
+
}
|
|
461
|
+
|
|
450
462
|
function scr() public view virtual override returns (uint256) {
|
|
451
463
|
return uint256(_scr.scr);
|
|
452
464
|
}
|
|
@@ -475,7 +487,12 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
475
487
|
return uint256(_scr.scr).wadDiv(this.totalSupply());
|
|
476
488
|
}
|
|
477
489
|
|
|
478
|
-
function lockScr(uint256 scrAmount, uint256 policyInterestRate)
|
|
490
|
+
function lockScr(uint256 scrAmount, uint256 policyInterestRate)
|
|
491
|
+
external
|
|
492
|
+
override
|
|
493
|
+
onlyBorrower
|
|
494
|
+
whenNotPaused
|
|
495
|
+
{
|
|
479
496
|
require(
|
|
480
497
|
scrAmount <= this.fundsAvailableToLock(),
|
|
481
498
|
"Not enought funds available to cover the SCR"
|
|
@@ -523,6 +540,10 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
523
540
|
_discreteChange(adjustment);
|
|
524
541
|
}
|
|
525
542
|
|
|
543
|
+
function _assetEarnings(int256 earnings) internal override {
|
|
544
|
+
_discreteChange(earnings);
|
|
545
|
+
}
|
|
546
|
+
|
|
526
547
|
function _discreteChange(int256 amount) internal {
|
|
527
548
|
_tsScaled.discreteChange(amount, tokenInterestRate());
|
|
528
549
|
_updateTokenInterestRate();
|
|
@@ -532,7 +553,6 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
532
553
|
external
|
|
533
554
|
override
|
|
534
555
|
onlyPolicyPool
|
|
535
|
-
whenNotPaused
|
|
536
556
|
returns (uint256)
|
|
537
557
|
{
|
|
538
558
|
require(
|
|
@@ -557,7 +577,6 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
557
577
|
external
|
|
558
578
|
override
|
|
559
579
|
onlyPolicyPool
|
|
560
|
-
whenNotPaused
|
|
561
580
|
returns (uint256)
|
|
562
581
|
{
|
|
563
582
|
uint256 balance = balanceOf(provider);
|
|
@@ -576,10 +595,16 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
576
595
|
TimeScaled.ScaledAmount storage loan = _loans[borrower];
|
|
577
596
|
if (loan.scale == 0) {
|
|
578
597
|
loan.init();
|
|
579
|
-
emit
|
|
598
|
+
emit InternalBorrowerAdded(borrower);
|
|
580
599
|
}
|
|
581
600
|
}
|
|
582
601
|
|
|
602
|
+
function removeBorrower(address borrower) external override onlyPolicyPool {
|
|
603
|
+
uint256 defaultedDebt = getLoan(borrower);
|
|
604
|
+
delete _loans[borrower];
|
|
605
|
+
emit InternalBorrowerRemoved(borrower, defaultedDebt);
|
|
606
|
+
}
|
|
607
|
+
|
|
583
608
|
function internalLoan(
|
|
584
609
|
uint256 amount,
|
|
585
610
|
address receiver,
|
|
@@ -620,59 +645,54 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
620
645
|
return uint256(_params.internalLoanInterestRate) * 1e14; // to wad 4 -> 18 digits
|
|
621
646
|
}
|
|
622
647
|
|
|
623
|
-
function
|
|
624
|
-
external
|
|
625
|
-
onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
|
|
626
|
-
{
|
|
627
|
-
bool tweak = !hasPoolRole(LEVEL2_ROLE);
|
|
628
|
-
require(
|
|
629
|
-
!tweak || _isTweakWad(internalLoanInterestRate(), newRate, 3e17),
|
|
630
|
-
"Tweak exceeded: internalLoanInterestRate tweaks only up to 30%"
|
|
631
|
-
);
|
|
632
|
-
// This call changes the interest rate without updating the current loans up to this point
|
|
633
|
-
// So, if interest rate goes from 5% to 6%, this change will be retroactive to the lastUpdate of each
|
|
634
|
-
// loan. Since it's a permissioned call, I'm ok with this. If a caller wants to reduce the impact, it can
|
|
635
|
-
// issue 1 wei repayLoan to each active loan, forcing the update of the scales
|
|
636
|
-
_params.internalLoanInterestRate = uint16(newRate / 1e14);
|
|
637
|
-
_parameterChanged(IAccessManager.GovernanceActions.setInternalLoanInterestRate, newRate, tweak);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function setLiquidityRequirement(uint256 newRate)
|
|
648
|
+
function setParam(Parameter param, uint256 newValue)
|
|
641
649
|
external
|
|
642
|
-
|
|
650
|
+
onlyGlobalOrComponentRole2(LEVEL2_ROLE, LEVEL3_ROLE)
|
|
643
651
|
{
|
|
644
652
|
bool tweak = !hasPoolRole(LEVEL2_ROLE);
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
653
|
+
if (param == Parameter.liquidityRequirement) {
|
|
654
|
+
require(
|
|
655
|
+
!tweak || _isTweakWad(liquidityRequirement(), newValue, 1e17),
|
|
656
|
+
"Tweak exceeded: liquidityRequirement tweaks only up to 10%"
|
|
657
|
+
);
|
|
658
|
+
_params.liquidityRequirement = uint16(newValue / 1e14);
|
|
659
|
+
} else if (param == Parameter.minUtilizationRate) {
|
|
660
|
+
require(
|
|
661
|
+
!tweak || _isTweakWad(minUtilizationRate(), newValue, 3e17),
|
|
662
|
+
"Tweak exceeded: minUtilizationRate tweaks only up to 30%"
|
|
663
|
+
);
|
|
664
|
+
_params.minUtilizationRate = uint16(newValue / 1e14);
|
|
665
|
+
} else if (param == Parameter.maxUtilizationRate) {
|
|
666
|
+
require(
|
|
667
|
+
!tweak || _isTweakWad(maxUtilizationRate(), newValue, 3e17),
|
|
668
|
+
"Tweak exceeded: maxUtilizationRate tweaks only up to 30%"
|
|
669
|
+
);
|
|
670
|
+
_params.maxUtilizationRate = uint16(newValue / 1e14);
|
|
671
|
+
} else if (param == Parameter.internalLoanInterestRate) {
|
|
672
|
+
require(
|
|
673
|
+
!tweak || _isTweakWad(internalLoanInterestRate(), newValue, 3e17),
|
|
674
|
+
"Tweak exceeded: internalLoanInterestRate tweaks only up to 30%"
|
|
675
|
+
);
|
|
676
|
+
// This call changes the interest rate without updating the current loans up to this point
|
|
677
|
+
// So, if interest rate goes from 5% to 6%, this change will be retroactive to the lastUpdate of each
|
|
678
|
+
// loan. Since it's a permissioned call, I'm ok with this. If a caller wants to reduce the impact, it can
|
|
679
|
+
// issue 1 wei repayLoan to each active loan, forcing the update of the scales
|
|
680
|
+
_params.internalLoanInterestRate = uint16(newValue / 1e14);
|
|
681
|
+
} else {
|
|
682
|
+
revert("Invalid param!");
|
|
683
|
+
}
|
|
684
|
+
_parameterChanged(
|
|
685
|
+
IAccessManager.GovernanceActions(
|
|
686
|
+
uint256(IAccessManager.GovernanceActions.setLiquidityRequirement) + uint256(param)
|
|
687
|
+
),
|
|
688
|
+
newValue,
|
|
689
|
+
tweak
|
|
668
690
|
);
|
|
669
|
-
_params.minUtilizationRate = uint16(newRate / 1e14);
|
|
670
|
-
_parameterChanged(IAccessManager.GovernanceActions.setMinUtilizationRate, newRate, tweak);
|
|
671
691
|
}
|
|
672
692
|
|
|
673
693
|
function setWhitelist(ILPWhitelist lpWhitelist_)
|
|
674
694
|
external
|
|
675
|
-
|
|
695
|
+
onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
|
|
676
696
|
{
|
|
677
697
|
require(
|
|
678
698
|
address(lpWhitelist_) == address(0) ||
|
|
@@ -680,7 +700,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
680
700
|
"Component not linked to this PolicyPool"
|
|
681
701
|
);
|
|
682
702
|
_params.whitelist = lpWhitelist_;
|
|
683
|
-
|
|
703
|
+
_componentChanged(IAccessManager.GovernanceActions.setLPWhitelist, address(lpWhitelist_));
|
|
684
704
|
}
|
|
685
705
|
|
|
686
706
|
function whitelist() external view returns (ILPWhitelist) {
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {WadRayMath} from "./WadRayMath.sol";
|
|
5
|
+
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
6
|
+
import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
|
|
7
|
+
import {IAssetManager} from "./interfaces/IAssetManager.sol";
|
|
8
|
+
import {IAccessManager} from "./interfaces/IAccessManager.sol";
|
|
9
|
+
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @title Base class for asset management strategies that use thresholds for cash and investment balance
|
|
13
|
+
* @dev Base class for asset management strategies that use thresholds for cash and investment balance.
|
|
14
|
+
* The specific asset management strategy needs to be implemented by child contracts.
|
|
15
|
+
* Settings liquidityMin, liquidityMiddle, liquidityMax are the thresholds used to define how much liquidity
|
|
16
|
+
* to keep in the PolicyPool and when to invest/deinvest. Every invest/deinvest operation tries to leave the
|
|
17
|
+
* cash at liquidityMiddle.
|
|
18
|
+
* @custom:security-contact security@ensuro.co
|
|
19
|
+
* @author Ensuro
|
|
20
|
+
*/
|
|
21
|
+
abstract contract LiquidityThresholdAssetManager is IAssetManager {
|
|
22
|
+
using WadRayMath for uint256;
|
|
23
|
+
|
|
24
|
+
IERC20Metadata internal immutable _asset;
|
|
25
|
+
event GovernanceAction(IAccessManager.GovernanceActions indexed action, uint256 value);
|
|
26
|
+
|
|
27
|
+
struct DiamondStorage {
|
|
28
|
+
uint32 liquidityMin; // stored with 0 decimals
|
|
29
|
+
uint32 liquidityMiddle; // stored with 0 decimals
|
|
30
|
+
uint32 liquidityMax; // stored with 0 decimals
|
|
31
|
+
uint128 lastInvestmentValue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
modifier validateParamsAfterChange() {
|
|
35
|
+
_;
|
|
36
|
+
_validateParameters();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
constructor(IERC20Metadata asset_) {
|
|
40
|
+
_asset = asset_;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function diamondStorage() internal pure returns (DiamondStorage storage ds) {
|
|
44
|
+
// Inspired from https://eips.ethereum.org/EIPS/eip-2535#facets-state-variables-and-diamond-storage
|
|
45
|
+
// Set the position of our struct in contract storage
|
|
46
|
+
bytes32 storagePosition = keccak256("co.ensuro.LiquidityThresholdAssetManager");
|
|
47
|
+
// solhint-disable-next-line no-inline-assembly
|
|
48
|
+
assembly {
|
|
49
|
+
ds.slot := storagePosition
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function _validateParameters() internal view {
|
|
54
|
+
DiamondStorage storage ds = diamondStorage();
|
|
55
|
+
|
|
56
|
+
require(
|
|
57
|
+
ds.liquidityMin <= ds.liquidityMiddle && ds.liquidityMiddle <= ds.liquidityMax,
|
|
58
|
+
"Validation: Liquidity limits are invalid"
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function connect() public virtual override {
|
|
63
|
+
require(
|
|
64
|
+
IPolicyPoolComponent(address(this)).policyPool().currency() == _asset,
|
|
65
|
+
"Asset mismatch"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function recordEarnings() external virtual override returns (int256) {
|
|
70
|
+
uint256 investmentValue = getInvestmentValue();
|
|
71
|
+
DiamondStorage storage ds = diamondStorage();
|
|
72
|
+
int256 earnings = int256(investmentValue) - int256(uint256(ds.lastInvestmentValue));
|
|
73
|
+
ds.lastInvestmentValue = uint128(investmentValue);
|
|
74
|
+
emit EarningsRecorded(earnings);
|
|
75
|
+
return earnings;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @dev Returns the current value of the investment portfolio
|
|
80
|
+
*/
|
|
81
|
+
function getInvestmentValue() public view virtual returns (uint256);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @dev Rebalances cash between PolicyPool wallet and
|
|
85
|
+
*/
|
|
86
|
+
function rebalance() external virtual override {
|
|
87
|
+
uint256 cash = _asset.balanceOf(address(this));
|
|
88
|
+
if (cash > liquidityMax()) {
|
|
89
|
+
_invest(cash - liquidityMiddle());
|
|
90
|
+
} else if (cash < liquidityMin()) {
|
|
91
|
+
uint256 deinvestAmount = Math.min(getInvestmentValue(), liquidityMiddle() - cash);
|
|
92
|
+
if (deinvestAmount > 0) {
|
|
93
|
+
_deinvest(deinvestAmount);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @dev This is called from PolicyPool when doesn't have enought money for payment.
|
|
100
|
+
* After the call, there should be enought money in PolicyPool.currency().balanceOf(this) to
|
|
101
|
+
* do the payment
|
|
102
|
+
* @param paymentAmount The amount of the payment
|
|
103
|
+
*/
|
|
104
|
+
function refillWallet(uint256 paymentAmount) external override {
|
|
105
|
+
uint256 cash = _asset.balanceOf(address(this));
|
|
106
|
+
require(cash < paymentAmount, "No need to refill the wallet for this payment");
|
|
107
|
+
uint256 investmentValue = getInvestmentValue();
|
|
108
|
+
// try to leave the pool balance at liquidity_middle after the payment
|
|
109
|
+
uint256 deinvest = paymentAmount + liquidityMiddle() - cash;
|
|
110
|
+
if (deinvest > investmentValue) deinvest = investmentValue;
|
|
111
|
+
_deinvest(deinvest);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function _invest(uint256 amount) internal virtual {
|
|
115
|
+
DiamondStorage storage ds = diamondStorage();
|
|
116
|
+
ds.lastInvestmentValue += uint128(amount);
|
|
117
|
+
emit MoneyInvested(amount);
|
|
118
|
+
// must be reimplemented do the actual cash movement
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function _deinvest(uint256 amount) internal virtual {
|
|
122
|
+
DiamondStorage storage ds = diamondStorage();
|
|
123
|
+
ds.lastInvestmentValue -= uint128(Math.min(uint256(ds.lastInvestmentValue), amount));
|
|
124
|
+
emit MoneyDeinvested(amount);
|
|
125
|
+
// must be reimplemented do the actual cash movement
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function liquidityMin() public view returns (uint256) {
|
|
129
|
+
return diamondStorage().liquidityMin * 10**_asset.decimals();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function liquidityMiddle() public view returns (uint256) {
|
|
133
|
+
return diamondStorage().liquidityMiddle * 10**_asset.decimals();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function liquidityMax() public view returns (uint256) {
|
|
137
|
+
return diamondStorage().liquidityMax * 10**_asset.decimals();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function setLiquidityThresholds(
|
|
141
|
+
uint256 min,
|
|
142
|
+
uint256 middle,
|
|
143
|
+
uint256 max
|
|
144
|
+
) external validateParamsAfterChange {
|
|
145
|
+
DiamondStorage storage ds = diamondStorage();
|
|
146
|
+
if (min != type(uint256).max) {
|
|
147
|
+
ds.liquidityMin = uint32(min / 10**_asset.decimals());
|
|
148
|
+
emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMin, min);
|
|
149
|
+
}
|
|
150
|
+
if (middle != type(uint256).max) {
|
|
151
|
+
ds.liquidityMiddle = uint32(middle / 10**_asset.decimals());
|
|
152
|
+
emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMiddle, middle);
|
|
153
|
+
}
|
|
154
|
+
if (max != type(uint256).max) {
|
|
155
|
+
ds.liquidityMax = uint32(max / 10**_asset.decimals());
|
|
156
|
+
emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMax, max);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|