@ensuro/core 2.0.0-beta1 → 2.0.0-beta10
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 +23 -14
- package/build/contracts/AccessManager.sol/AccessManager.json +559 -0
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +219 -0
- package/build/contracts/EToken.sol/EToken.json +134 -52
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +4 -4
- package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +203 -0
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +437 -61
- package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +2 -2
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +123 -16
- package/build/contracts/Reserve.sol/Reserve.json +66 -8
- package/build/contracts/RiskModule.sol/RiskModule.json +2 -2
- package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +960 -0
- package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +5 -5
- package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
- package/build/contracts/interfaces/{IPolicyPoolConfig.sol/IPolicyPoolConfig.json → IAccessManager.sol/IAccessManager.json} +77 -59
- package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +109 -0
- package/build/contracts/interfaces/IEToken.sol/IEToken.json +13 -0
- package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +40 -16
- package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
- package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
- package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +2 -2
- package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +28 -2
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +118 -19
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +6 -6
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +764 -0
- package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
- package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
- package/contracts/AccessManager.sol +103 -0
- package/contracts/ERC4626AssetManager.sol +52 -0
- package/contracts/EToken.sol +123 -87
- package/contracts/LPManualWhitelist.sol +1 -1
- package/contracts/LiquidityThresholdAssetManager.sol +160 -0
- package/contracts/Policy.sol +1 -1
- package/contracts/PolicyPool.sol +333 -115
- package/contracts/PolicyPoolComponent.sol +36 -41
- package/contracts/PremiumsAccount.sol +285 -79
- package/contracts/Reserve.sol +183 -4
- package/contracts/RiskModule.sol +26 -19
- package/contracts/SignedQuoteRiskModule.sol +241 -0
- package/contracts/TimeScaled.sol +19 -15
- package/contracts/TrustfulRiskModule.sol +62 -2
- package/contracts/dependencies/WadRayMath.sol +126 -0
- package/contracts/interfaces/IAccessManager.sol +148 -0
- package/contracts/interfaces/IAssetManager.sol +84 -0
- package/contracts/interfaces/IEToken.sol +25 -2
- package/contracts/interfaces/ILPWhitelist.sol +17 -0
- package/contracts/interfaces/IPolicyPool.sol +20 -13
- package/contracts/interfaces/IPolicyPoolComponent.sol +3 -0
- package/contracts/interfaces/IPremiumsAccount.sol +5 -0
- package/contracts/interfaces/IRiskModule.sol +67 -4
- package/contracts/mocks/FixedRateVault.sol +85 -0
- package/contracts/mocks/PolicyHolderMock.sol +21 -0
- package/contracts/mocks/PolicyPoolMock.sol +41 -15
- package/contracts/mocks/RiskModuleMock.sol +75 -0
- package/js/test-utils.js +227 -149
- package/package.json +1 -1
- package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -890
- package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
- package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -691
- package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -63
- package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
- package/build/contracts/mocks/LinkTokenMock.sol/LinkTokenMock.json +0 -370
- package/build/contracts/mocks/TestCurrencyUUPS.sol/TestCurrencyUUPS.json +0 -353
- package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
- package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -584
- package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -615
- package/contracts/FlightDelayRiskModule.sol +0 -217
- package/contracts/PolicyNFT.sol +0 -96
- package/contracts/PolicyPoolConfig.sol +0 -204
- package/contracts/WadRayMath.sol +0 -135
- package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
- package/contracts/interfaces/IPolicyNFT.sol +0 -15
- package/contracts/interfaces/IPolicyPoolConfig.sol +0 -96
- package/contracts/mocks/LinkTokenMock.sol +0 -30
- package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
- package/contracts/upgraded/PolicyNFTv1.sol +0 -126
package/contracts/EToken.sol
CHANGED
|
@@ -3,11 +3,16 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
4
|
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
5
5
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
|
+
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
7
|
+
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
|
6
8
|
import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
|
|
7
9
|
import {Reserve} from "./Reserve.sol";
|
|
8
10
|
import {IEToken} from "./interfaces/IEToken.sol";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
+
import {IAccessManager} from "./interfaces/IAccessManager.sol";
|
|
12
|
+
import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
|
|
13
|
+
import {ILPWhitelist} from "./interfaces/ILPWhitelist.sol";
|
|
14
|
+
import {IAssetManager} from "./interfaces/IAssetManager.sol";
|
|
15
|
+
import {WadRayMath} from "./dependencies/WadRayMath.sol";
|
|
11
16
|
import {TimeScaled} from "./TimeScaled.sol";
|
|
12
17
|
|
|
13
18
|
/**
|
|
@@ -24,8 +29,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
24
29
|
using WadRayMath for uint256;
|
|
25
30
|
using TimeScaled for TimeScaled.ScaledAmount;
|
|
26
31
|
using SafeERC20 for IERC20Metadata;
|
|
27
|
-
|
|
28
|
-
uint256 internal constant SECONDS_PER_YEAR = 365 days;
|
|
32
|
+
using SafeCast for uint256;
|
|
29
33
|
|
|
30
34
|
// Attributes taken from ERC20
|
|
31
35
|
mapping(address => uint256) private _balances;
|
|
@@ -52,13 +56,17 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
52
56
|
uint16 minUtilizationRate; // Min utilization rate, to reject deposits that leave UR under this value - 4 decimals
|
|
53
57
|
uint16 maxUtilizationRate; // Max utilization rate, to reject lockScr that leave UR above this value - 4 decimals
|
|
54
58
|
uint16 internalLoanInterestRate; // Annualized interest rate charged to internal borrowers (premiums accounts) - 4dec
|
|
59
|
+
ILPWhitelist whitelist; // Whitelist for deposits and transfers
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
PackedParams internal _params;
|
|
58
63
|
|
|
64
|
+
IAssetManager internal _assetManager;
|
|
65
|
+
|
|
59
66
|
event InternalLoan(address indexed borrower, uint256 value, uint256 amountAsked);
|
|
60
67
|
event InternalLoanRepaid(address indexed borrower, uint256 value);
|
|
61
|
-
event
|
|
68
|
+
event InternalBorrowerAdded(address indexed borrower);
|
|
69
|
+
event InternalBorrowerRemoved(address indexed borrower, uint256 defaultedDebt);
|
|
62
70
|
|
|
63
71
|
modifier onlyBorrower() {
|
|
64
72
|
require(_loans[_msgSender()].scale != 0, "The caller must be a borrower");
|
|
@@ -102,10 +110,11 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
102
110
|
tokenInterestRate: 0
|
|
103
111
|
}); */
|
|
104
112
|
_params = PackedParams({
|
|
105
|
-
maxUtilizationRate:
|
|
113
|
+
maxUtilizationRate: _wadTo4(maxUtilizationRate_),
|
|
106
114
|
liquidityRequirement: 1e4,
|
|
107
115
|
minUtilizationRate: 0,
|
|
108
|
-
internalLoanInterestRate:
|
|
116
|
+
internalLoanInterestRate: _wadTo4(internalLoanInterestRate_),
|
|
117
|
+
whitelist: ILPWhitelist(address(0))
|
|
109
118
|
});
|
|
110
119
|
|
|
111
120
|
_validateParameters();
|
|
@@ -403,12 +412,12 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
403
412
|
address from,
|
|
404
413
|
address to,
|
|
405
414
|
uint256 amount
|
|
406
|
-
) internal virtual {
|
|
415
|
+
) internal virtual whenNotPaused {
|
|
407
416
|
require(
|
|
408
417
|
from == address(0) ||
|
|
409
418
|
to == address(0) ||
|
|
410
|
-
address(
|
|
411
|
-
|
|
419
|
+
address(_params.whitelist) == address(0) ||
|
|
420
|
+
_params.whitelist.acceptsTransfer(this, from, to, amount),
|
|
412
421
|
"Transfer not allowed - Liquidity Provider not whitelisted"
|
|
413
422
|
);
|
|
414
423
|
}
|
|
@@ -419,9 +428,10 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
419
428
|
uint256 totalSupply_ = this.totalSupply();
|
|
420
429
|
if (totalSupply_ == 0) _scr.tokenInterestRate = 0;
|
|
421
430
|
else {
|
|
422
|
-
_scr.tokenInterestRate =
|
|
423
|
-
|
|
424
|
-
|
|
431
|
+
_scr.tokenInterestRate = uint256(_scr.interestRate)
|
|
432
|
+
.wadMul(uint256(_scr.scr))
|
|
433
|
+
.wadDiv(totalSupply_)
|
|
434
|
+
.toUint64();
|
|
425
435
|
}
|
|
426
436
|
}
|
|
427
437
|
|
|
@@ -443,6 +453,25 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
443
453
|
else return 0;
|
|
444
454
|
}
|
|
445
455
|
|
|
456
|
+
function assetManager() public view override returns (IAssetManager) {
|
|
457
|
+
return _assetManager;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function _setAssetManager(IAssetManager newAM) internal override {
|
|
461
|
+
_assetManager = newAM;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// solhint-disable-next-line func-name-mixedcase
|
|
465
|
+
function _4toWad(uint16 value) internal pure returns (uint256) {
|
|
466
|
+
// 4 decimals to Wad (18 decimals)
|
|
467
|
+
return uint256(value) * 1e14;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function _wadTo4(uint256 value) internal pure returns (uint16) {
|
|
471
|
+
// Wad to 4 decimals
|
|
472
|
+
return (value / 1e14).toUint16();
|
|
473
|
+
}
|
|
474
|
+
|
|
446
475
|
function scr() public view virtual override returns (uint256) {
|
|
447
476
|
return uint256(_scr.scr);
|
|
448
477
|
}
|
|
@@ -456,39 +485,41 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
456
485
|
}
|
|
457
486
|
|
|
458
487
|
function liquidityRequirement() public view returns (uint256) {
|
|
459
|
-
return
|
|
488
|
+
return _4toWad(_params.liquidityRequirement);
|
|
460
489
|
}
|
|
461
490
|
|
|
462
491
|
function maxUtilizationRate() public view returns (uint256) {
|
|
463
|
-
return
|
|
492
|
+
return _4toWad(_params.maxUtilizationRate);
|
|
464
493
|
}
|
|
465
494
|
|
|
466
495
|
function minUtilizationRate() public view returns (uint256) {
|
|
467
|
-
return
|
|
496
|
+
return _4toWad(_params.minUtilizationRate);
|
|
468
497
|
}
|
|
469
498
|
|
|
470
499
|
function utilizationRate() public view returns (uint256) {
|
|
471
500
|
return uint256(_scr.scr).wadDiv(this.totalSupply());
|
|
472
501
|
}
|
|
473
502
|
|
|
474
|
-
function lockScr(uint256 scrAmount, uint256 policyInterestRate)
|
|
503
|
+
function lockScr(uint256 scrAmount, uint256 policyInterestRate)
|
|
504
|
+
external
|
|
505
|
+
override
|
|
506
|
+
onlyBorrower
|
|
507
|
+
whenNotPaused
|
|
508
|
+
{
|
|
475
509
|
require(
|
|
476
510
|
scrAmount <= this.fundsAvailableToLock(),
|
|
477
511
|
"Not enought funds available to cover the SCR"
|
|
478
512
|
);
|
|
479
513
|
_tsScaled.updateScale(tokenInterestRate());
|
|
480
514
|
if (_scr.scr == 0) {
|
|
481
|
-
_scr.scr =
|
|
482
|
-
_scr.interestRate =
|
|
515
|
+
_scr.scr = scrAmount.toUint128();
|
|
516
|
+
_scr.interestRate = policyInterestRate.toUint64();
|
|
483
517
|
} else {
|
|
484
518
|
uint256 origScr = uint256(_scr.scr);
|
|
485
519
|
uint256 newScr = origScr + scrAmount;
|
|
486
|
-
_scr.interestRate =
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
)
|
|
490
|
-
);
|
|
491
|
-
_scr.scr = uint128(newScr);
|
|
520
|
+
_scr.interestRate = (uint256(_scr.interestRate).wadMul(origScr) +
|
|
521
|
+
policyInterestRate.wadMul(scrAmount)).wadDiv(newScr).toUint64();
|
|
522
|
+
_scr.scr = newScr.toUint128();
|
|
492
523
|
}
|
|
493
524
|
emit SCRLocked(policyInterestRate, scrAmount);
|
|
494
525
|
_updateTokenInterestRate();
|
|
@@ -508,17 +539,18 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
508
539
|
} else {
|
|
509
540
|
uint256 origScr = uint256(_scr.scr);
|
|
510
541
|
uint256 newScr = origScr - scrAmount;
|
|
511
|
-
_scr.interestRate =
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
)
|
|
515
|
-
);
|
|
516
|
-
_scr.scr = uint128(newScr);
|
|
542
|
+
_scr.interestRate = (uint256(_scr.interestRate).wadMul(origScr) -
|
|
543
|
+
policyInterestRate.wadMul(scrAmount)).wadDiv(newScr).toUint64();
|
|
544
|
+
_scr.scr = newScr.toUint128();
|
|
517
545
|
}
|
|
518
546
|
emit SCRUnlocked(policyInterestRate, scrAmount);
|
|
519
547
|
_discreteChange(adjustment);
|
|
520
548
|
}
|
|
521
549
|
|
|
550
|
+
function _assetEarnings(int256 earnings) internal override {
|
|
551
|
+
_discreteChange(earnings);
|
|
552
|
+
}
|
|
553
|
+
|
|
522
554
|
function _discreteChange(int256 amount) internal {
|
|
523
555
|
_tsScaled.discreteChange(amount, tokenInterestRate());
|
|
524
556
|
_updateTokenInterestRate();
|
|
@@ -528,12 +560,11 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
528
560
|
external
|
|
529
561
|
override
|
|
530
562
|
onlyPolicyPool
|
|
531
|
-
whenNotPaused
|
|
532
563
|
returns (uint256)
|
|
533
564
|
{
|
|
534
565
|
require(
|
|
535
|
-
address(
|
|
536
|
-
|
|
566
|
+
address(_params.whitelist) == address(0) ||
|
|
567
|
+
_params.whitelist.acceptsDeposit(this, provider, amount),
|
|
537
568
|
"Liquidity Provider not whitelisted"
|
|
538
569
|
);
|
|
539
570
|
_mint(provider, amount);
|
|
@@ -553,14 +584,9 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
553
584
|
external
|
|
554
585
|
override
|
|
555
586
|
onlyPolicyPool
|
|
556
|
-
whenNotPaused
|
|
557
587
|
returns (uint256)
|
|
558
588
|
{
|
|
559
|
-
|
|
560
|
-
if (balance == 0) return 0;
|
|
561
|
-
if (amount > balance) amount = balance;
|
|
562
|
-
uint256 withdrawable = totalWithdrawable();
|
|
563
|
-
if (amount > withdrawable) amount = withdrawable;
|
|
589
|
+
amount = Math.min(amount, Math.min(balanceOf(provider), totalWithdrawable()));
|
|
564
590
|
if (amount == 0) return 0;
|
|
565
591
|
_burn(provider, amount);
|
|
566
592
|
_updateTokenInterestRate();
|
|
@@ -572,22 +598,27 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
572
598
|
TimeScaled.ScaledAmount storage loan = _loans[borrower];
|
|
573
599
|
if (loan.scale == 0) {
|
|
574
600
|
loan.init();
|
|
575
|
-
emit
|
|
601
|
+
emit InternalBorrowerAdded(borrower);
|
|
576
602
|
}
|
|
577
603
|
}
|
|
578
604
|
|
|
605
|
+
function removeBorrower(address borrower) external override onlyPolicyPool {
|
|
606
|
+
uint256 defaultedDebt = getLoan(borrower);
|
|
607
|
+
delete _loans[borrower];
|
|
608
|
+
emit InternalBorrowerRemoved(borrower, defaultedDebt);
|
|
609
|
+
}
|
|
610
|
+
|
|
579
611
|
function internalLoan(
|
|
580
612
|
uint256 amount,
|
|
581
613
|
address receiver,
|
|
582
614
|
bool fromAvailable
|
|
583
615
|
) external override onlyBorrower whenNotPaused returns (uint256) {
|
|
584
616
|
uint256 amountAsked = amount;
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
617
|
+
amount = Math.min(
|
|
618
|
+
Math.min(amount, fromAvailable ? fundsAvailable() : totalSupply()),
|
|
619
|
+
_tsScaled.maxNegativeAdjustment(tokenInterestRate())
|
|
620
|
+
);
|
|
621
|
+
if (amount == 0) return amountAsked;
|
|
591
622
|
TimeScaled.ScaledAmount storage loan = _loans[_msgSender()];
|
|
592
623
|
loan.add(amount, internalLoanInterestRate());
|
|
593
624
|
_discreteChange(-int256(amount));
|
|
@@ -608,65 +639,70 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
|
|
|
608
639
|
|
|
609
640
|
function getLoan(address borrower) public view virtual override returns (uint256) {
|
|
610
641
|
TimeScaled.ScaledAmount storage loan = _loans[borrower];
|
|
611
|
-
if (loan.scale == 0) return 0;
|
|
612
642
|
return loan.getScaledAmount(internalLoanInterestRate());
|
|
613
643
|
}
|
|
614
644
|
|
|
615
645
|
function internalLoanInterestRate() public view returns (uint256) {
|
|
616
|
-
return
|
|
646
|
+
return _4toWad(_params.internalLoanInterestRate);
|
|
617
647
|
}
|
|
618
648
|
|
|
619
|
-
function
|
|
649
|
+
function setParam(Parameter param, uint256 newValue)
|
|
620
650
|
external
|
|
621
|
-
|
|
651
|
+
onlyGlobalOrComponentRole2(LEVEL2_ROLE, LEVEL3_ROLE)
|
|
622
652
|
{
|
|
623
653
|
bool tweak = !hasPoolRole(LEVEL2_ROLE);
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
654
|
+
if (param == Parameter.liquidityRequirement) {
|
|
655
|
+
require(
|
|
656
|
+
!tweak || _isTweakWad(liquidityRequirement(), newValue, 1e17),
|
|
657
|
+
"Tweak exceeded: liquidityRequirement tweaks only up to 10%"
|
|
658
|
+
);
|
|
659
|
+
_params.liquidityRequirement = _wadTo4(newValue);
|
|
660
|
+
} else if (param == Parameter.minUtilizationRate) {
|
|
661
|
+
require(
|
|
662
|
+
!tweak || _isTweakWad(minUtilizationRate(), newValue, 3e17),
|
|
663
|
+
"Tweak exceeded: minUtilizationRate tweaks only up to 30%"
|
|
664
|
+
);
|
|
665
|
+
_params.minUtilizationRate = _wadTo4(newValue);
|
|
666
|
+
} else if (param == Parameter.maxUtilizationRate) {
|
|
667
|
+
require(
|
|
668
|
+
!tweak || _isTweakWad(maxUtilizationRate(), newValue, 3e17),
|
|
669
|
+
"Tweak exceeded: maxUtilizationRate tweaks only up to 30%"
|
|
670
|
+
);
|
|
671
|
+
_params.maxUtilizationRate = _wadTo4(newValue);
|
|
672
|
+
} else if (param == Parameter.internalLoanInterestRate) {
|
|
673
|
+
require(
|
|
674
|
+
!tweak || _isTweakWad(internalLoanInterestRate(), newValue, 3e17),
|
|
675
|
+
"Tweak exceeded: internalLoanInterestRate tweaks only up to 30%"
|
|
676
|
+
);
|
|
677
|
+
// This call changes the interest rate without updating the current loans up to this point
|
|
678
|
+
// So, if interest rate goes from 5% to 6%, this change will be retroactive to the lastUpdate of each
|
|
679
|
+
// loan. Since it's a permissioned call, I'm ok with this. If a caller wants to reduce the impact, it can
|
|
680
|
+
// issue 1 wei repayLoan to each active loan, forcing the update of the scales
|
|
681
|
+
_params.internalLoanInterestRate = _wadTo4(newValue);
|
|
682
|
+
}
|
|
633
683
|
_parameterChanged(
|
|
634
|
-
|
|
635
|
-
|
|
684
|
+
IAccessManager.GovernanceActions(
|
|
685
|
+
uint256(IAccessManager.GovernanceActions.setLiquidityRequirement) + uint256(param)
|
|
686
|
+
),
|
|
687
|
+
newValue,
|
|
636
688
|
tweak
|
|
637
689
|
);
|
|
638
690
|
}
|
|
639
691
|
|
|
640
|
-
function
|
|
692
|
+
function setWhitelist(ILPWhitelist lpWhitelist_)
|
|
641
693
|
external
|
|
642
|
-
|
|
694
|
+
onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
|
|
643
695
|
{
|
|
644
|
-
bool tweak = !hasPoolRole(LEVEL2_ROLE);
|
|
645
|
-
require(
|
|
646
|
-
!tweak || _isTweakWad(liquidityRequirement(), newRate, 1e17),
|
|
647
|
-
"Tweak exceeded: liquidityRequirement tweaks only up to 10%"
|
|
648
|
-
);
|
|
649
|
-
_params.liquidityRequirement = uint16(newRate / 1e14);
|
|
650
|
-
_parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityRequirement, newRate, tweak);
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
function setMaxUtilizationRate(uint256 newRate) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
|
|
654
|
-
bool tweak = !hasPoolRole(LEVEL2_ROLE);
|
|
655
696
|
require(
|
|
656
|
-
|
|
657
|
-
|
|
697
|
+
address(lpWhitelist_) == address(0) ||
|
|
698
|
+
IPolicyPoolComponent(address(lpWhitelist_)).policyPool() == _policyPool,
|
|
699
|
+
"Component not linked to this PolicyPool"
|
|
658
700
|
);
|
|
659
|
-
_params.
|
|
660
|
-
|
|
701
|
+
_params.whitelist = lpWhitelist_;
|
|
702
|
+
_componentChanged(IAccessManager.GovernanceActions.setLPWhitelist, address(lpWhitelist_));
|
|
661
703
|
}
|
|
662
704
|
|
|
663
|
-
function
|
|
664
|
-
|
|
665
|
-
require(
|
|
666
|
-
!tweak || _isTweakWad(minUtilizationRate(), newRate, 3e17),
|
|
667
|
-
"Tweak exceeded: minUtilizationRate tweaks only up to 30%"
|
|
668
|
-
);
|
|
669
|
-
_params.minUtilizationRate = uint16(newRate / 1e14);
|
|
670
|
-
_parameterChanged(IPolicyPoolConfig.GovernanceActions.setMinUtilizationRate, newRate, tweak);
|
|
705
|
+
function whitelist() external view returns (ILPWhitelist) {
|
|
706
|
+
return _params.whitelist;
|
|
671
707
|
}
|
|
672
708
|
}
|
|
@@ -32,7 +32,7 @@ contract LPManualWhitelist is ILPWhitelist, PolicyPoolComponent {
|
|
|
32
32
|
|
|
33
33
|
function whitelistAddress(address provider, bool whitelisted)
|
|
34
34
|
external
|
|
35
|
-
|
|
35
|
+
onlyComponentRole(LP_WHITELIST_ROLE)
|
|
36
36
|
{
|
|
37
37
|
_whitelisted[provider] = whitelisted;
|
|
38
38
|
emit LPWhitelisted(provider, whitelisted);
|
|
@@ -0,0 +1,160 @@
|
|
|
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 {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 SafeCast 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 = investmentValue.toUint128();
|
|
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 returns (uint256 deinvest) {
|
|
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
|
+
deinvest = paymentAmount + liquidityMiddle() - cash;
|
|
110
|
+
if (deinvest > investmentValue) deinvest = investmentValue;
|
|
111
|
+
_deinvest(deinvest);
|
|
112
|
+
return deinvest;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function _invest(uint256 amount) internal virtual {
|
|
116
|
+
DiamondStorage storage ds = diamondStorage();
|
|
117
|
+
ds.lastInvestmentValue += amount.toUint128();
|
|
118
|
+
emit MoneyInvested(amount);
|
|
119
|
+
// must be reimplemented do the actual cash movement
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function _deinvest(uint256 amount) internal virtual {
|
|
123
|
+
DiamondStorage storage ds = diamondStorage();
|
|
124
|
+
ds.lastInvestmentValue -= Math.min(uint256(ds.lastInvestmentValue), amount).toUint128();
|
|
125
|
+
emit MoneyDeinvested(amount);
|
|
126
|
+
// must be reimplemented do the actual cash movement
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function liquidityMin() public view returns (uint256) {
|
|
130
|
+
return diamondStorage().liquidityMin * 10**_asset.decimals();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function liquidityMiddle() public view returns (uint256) {
|
|
134
|
+
return diamondStorage().liquidityMiddle * 10**_asset.decimals();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function liquidityMax() public view returns (uint256) {
|
|
138
|
+
return diamondStorage().liquidityMax * 10**_asset.decimals();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function setLiquidityThresholds(
|
|
142
|
+
uint256 min,
|
|
143
|
+
uint256 middle,
|
|
144
|
+
uint256 max
|
|
145
|
+
) external validateParamsAfterChange {
|
|
146
|
+
DiamondStorage storage ds = diamondStorage();
|
|
147
|
+
if (min != type(uint256).max) {
|
|
148
|
+
ds.liquidityMin = (min / 10**_asset.decimals()).toUint32();
|
|
149
|
+
emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMin, min);
|
|
150
|
+
}
|
|
151
|
+
if (middle != type(uint256).max) {
|
|
152
|
+
ds.liquidityMiddle = (middle / 10**_asset.decimals()).toUint32();
|
|
153
|
+
emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMiddle, middle);
|
|
154
|
+
}
|
|
155
|
+
if (max != type(uint256).max) {
|
|
156
|
+
ds.liquidityMax = (max / 10**_asset.decimals()).toUint32();
|
|
157
|
+
emit GovernanceAction(IAccessManager.GovernanceActions.setLiquidityMax, max);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
package/contracts/Policy.sol
CHANGED