@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.
Files changed (79) hide show
  1. package/README.md +23 -14
  2. package/build/contracts/AccessManager.sol/AccessManager.json +559 -0
  3. package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +219 -0
  4. package/build/contracts/EToken.sol/EToken.json +134 -52
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +4 -4
  6. package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +203 -0
  7. package/build/contracts/Policy.sol/Policy.json +2 -2
  8. package/build/contracts/PolicyPool.sol/PolicyPool.json +437 -61
  9. package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +2 -2
  10. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +123 -16
  11. package/build/contracts/Reserve.sol/Reserve.json +66 -8
  12. package/build/contracts/RiskModule.sol/RiskModule.json +2 -2
  13. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +960 -0
  14. package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
  15. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +5 -5
  16. package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
  17. package/build/contracts/interfaces/{IPolicyPoolConfig.sol/IPolicyPoolConfig.json → IAccessManager.sol/IAccessManager.json} +77 -59
  18. package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +109 -0
  19. package/build/contracts/interfaces/IEToken.sol/IEToken.json +13 -0
  20. package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +40 -16
  21. package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
  22. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
  23. package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +2 -2
  24. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +28 -2
  25. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
  26. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +118 -19
  27. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +6 -6
  28. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +764 -0
  29. package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
  30. package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
  31. package/contracts/AccessManager.sol +103 -0
  32. package/contracts/ERC4626AssetManager.sol +52 -0
  33. package/contracts/EToken.sol +123 -87
  34. package/contracts/LPManualWhitelist.sol +1 -1
  35. package/contracts/LiquidityThresholdAssetManager.sol +160 -0
  36. package/contracts/Policy.sol +1 -1
  37. package/contracts/PolicyPool.sol +333 -115
  38. package/contracts/PolicyPoolComponent.sol +36 -41
  39. package/contracts/PremiumsAccount.sol +285 -79
  40. package/contracts/Reserve.sol +183 -4
  41. package/contracts/RiskModule.sol +26 -19
  42. package/contracts/SignedQuoteRiskModule.sol +241 -0
  43. package/contracts/TimeScaled.sol +19 -15
  44. package/contracts/TrustfulRiskModule.sol +62 -2
  45. package/contracts/dependencies/WadRayMath.sol +126 -0
  46. package/contracts/interfaces/IAccessManager.sol +148 -0
  47. package/contracts/interfaces/IAssetManager.sol +84 -0
  48. package/contracts/interfaces/IEToken.sol +25 -2
  49. package/contracts/interfaces/ILPWhitelist.sol +17 -0
  50. package/contracts/interfaces/IPolicyPool.sol +20 -13
  51. package/contracts/interfaces/IPolicyPoolComponent.sol +3 -0
  52. package/contracts/interfaces/IPremiumsAccount.sol +5 -0
  53. package/contracts/interfaces/IRiskModule.sol +67 -4
  54. package/contracts/mocks/FixedRateVault.sol +85 -0
  55. package/contracts/mocks/PolicyHolderMock.sol +21 -0
  56. package/contracts/mocks/PolicyPoolMock.sol +41 -15
  57. package/contracts/mocks/RiskModuleMock.sol +75 -0
  58. package/js/test-utils.js +227 -149
  59. package/package.json +1 -1
  60. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -890
  61. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
  62. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -691
  63. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -63
  64. package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
  65. package/build/contracts/mocks/LinkTokenMock.sol/LinkTokenMock.json +0 -370
  66. package/build/contracts/mocks/TestCurrencyUUPS.sol/TestCurrencyUUPS.json +0 -353
  67. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
  68. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -584
  69. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -615
  70. package/contracts/FlightDelayRiskModule.sol +0 -217
  71. package/contracts/PolicyNFT.sol +0 -96
  72. package/contracts/PolicyPoolConfig.sol +0 -204
  73. package/contracts/WadRayMath.sol +0 -135
  74. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
  75. package/contracts/interfaces/IPolicyNFT.sol +0 -15
  76. package/contracts/interfaces/IPolicyPoolConfig.sol +0 -96
  77. package/contracts/mocks/LinkTokenMock.sol +0 -30
  78. package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
  79. package/contracts/upgraded/PolicyNFTv1.sol +0 -126
@@ -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 {IPolicyPoolConfig} from "./interfaces/IPolicyPoolConfig.sol";
10
- import {WadRayMath} from "./WadRayMath.sol";
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 PoolBorrowerAdded(address borrower);
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: uint16(maxUtilizationRate_ / 1e14),
113
+ maxUtilizationRate: _wadTo4(maxUtilizationRate_),
106
114
  liquidityRequirement: 1e4,
107
115
  minUtilizationRate: 0,
108
- internalLoanInterestRate: uint16(internalLoanInterestRate_ / 1e14)
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(policyPool().config().lpWhitelist()) == address(0) ||
411
- policyPool().config().lpWhitelist().acceptsTransfer(this, from, to, amount),
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 = uint64(
423
- uint256(_scr.interestRate).wadMul(uint256(_scr.scr)).wadDiv(totalSupply_)
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 uint256(_params.liquidityRequirement) * 1e14; // 4 -> 18 decimals
488
+ return _4toWad(_params.liquidityRequirement);
460
489
  }
461
490
 
462
491
  function maxUtilizationRate() public view returns (uint256) {
463
- return uint256(_params.maxUtilizationRate) * 1e14; // 4 -> 18 decimals
492
+ return _4toWad(_params.maxUtilizationRate);
464
493
  }
465
494
 
466
495
  function minUtilizationRate() public view returns (uint256) {
467
- return uint256(_params.minUtilizationRate) * 1e14; // 4 -> 18 decimals
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) external override onlyBorrower {
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 = uint128(scrAmount);
482
- _scr.interestRate = uint64(policyInterestRate);
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 = uint64(
487
- (uint256(_scr.interestRate).wadMul(origScr) + policyInterestRate.wadMul(scrAmount)).wadDiv(
488
- newScr
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 = uint64(
512
- (uint256(_scr.interestRate).wadMul(origScr) - policyInterestRate.wadMul(scrAmount)).wadDiv(
513
- newScr
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(policyPool().config().lpWhitelist()) == address(0) ||
536
- policyPool().config().lpWhitelist().acceptsDeposit(this, provider, amount),
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
- uint256 balance = balanceOf(provider);
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 PoolBorrowerAdded(borrower);
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
- if (fromAvailable && amount > fundsAvailable()) amount = fundsAvailable();
586
- if (!fromAvailable && amount > totalSupply()) amount = totalSupply();
587
- if (amount > _tsScaled.maxNegativeAdjustment(tokenInterestRate())) {
588
- amount = _tsScaled.maxNegativeAdjustment(tokenInterestRate());
589
- if (amount == 0) return amountAsked;
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 uint256(_params.internalLoanInterestRate) * 1e14; // to wad 4 -> 18 digits
646
+ return _4toWad(_params.internalLoanInterestRate);
617
647
  }
618
648
 
619
- function setInternalLoanInterestRate(uint256 newRate)
649
+ function setParam(Parameter param, uint256 newValue)
620
650
  external
621
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
651
+ onlyGlobalOrComponentRole2(LEVEL2_ROLE, LEVEL3_ROLE)
622
652
  {
623
653
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
624
- require(
625
- !tweak || _isTweakWad(internalLoanInterestRate(), newRate, 3e17),
626
- "Tweak exceeded: internalLoanInterestRate tweaks only up to 30%"
627
- );
628
- // This call changes the interest rate without updating the current loans up to this point
629
- // So, if interest rate goes from 5% to 6%, this change will be retroactive to the lastUpdate of each
630
- // loan. Since it's a permissioned call, I'm ok with this. If a caller wants to reduce the impact, it can
631
- // issue 1 wei repayLoan to each active loan, forcing the update of the scales
632
- _params.internalLoanInterestRate = uint16(newRate / 1e14);
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
- IPolicyPoolConfig.GovernanceActions.setInternalLoanInterestRate,
635
- newRate,
684
+ IAccessManager.GovernanceActions(
685
+ uint256(IAccessManager.GovernanceActions.setLiquidityRequirement) + uint256(param)
686
+ ),
687
+ newValue,
636
688
  tweak
637
689
  );
638
690
  }
639
691
 
640
- function setLiquidityRequirement(uint256 newRate)
692
+ function setWhitelist(ILPWhitelist lpWhitelist_)
641
693
  external
642
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
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
- !tweak || _isTweakWad(maxUtilizationRate(), newRate, 3e17),
657
- "Tweak exceeded: maxUtilizationRate tweaks only up to 30%"
697
+ address(lpWhitelist_) == address(0) ||
698
+ IPolicyPoolComponent(address(lpWhitelist_)).policyPool() == _policyPool,
699
+ "Component not linked to this PolicyPool"
658
700
  );
659
- _params.maxUtilizationRate = uint16(newRate / 1e14);
660
- _parameterChanged(IPolicyPoolConfig.GovernanceActions.setMaxUtilizationRate, newRate, tweak);
701
+ _params.whitelist = lpWhitelist_;
702
+ _componentChanged(IAccessManager.GovernanceActions.setLPWhitelist, address(lpWhitelist_));
661
703
  }
662
704
 
663
- function setMinUtilizationRate(uint256 newRate) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
664
- bool tweak = !hasPoolRole(LEVEL2_ROLE);
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
- onlyPoolRole(LP_WHITELIST_ROLE)
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
+ }
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  pragma solidity ^0.8.0;
3
- import {WadRayMath} from "./WadRayMath.sol";
3
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
4
4
  import {IRiskModule} from "./interfaces/IRiskModule.sol";
5
5
 
6
6
  /**