@ensuro/core 2.0.0-beta1 → 2.0.0-beta11

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 (84) hide show
  1. package/README.md +36 -14
  2. package/build/contracts/AccessManager.sol/AccessManager.json +564 -0
  3. package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +238 -0
  4. package/build/contracts/EToken.sol/EToken.json +147 -46
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +23 -4
  6. package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +222 -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 +21 -2
  10. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +142 -16
  11. package/build/contracts/Reserve.sol/Reserve.json +85 -8
  12. package/build/contracts/RiskModule.sol/RiskModule.json +21 -2
  13. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +979 -0
  14. package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
  15. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +24 -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 +128 -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/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +19 -0
  22. package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
  23. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
  24. package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +2 -2
  25. package/build/contracts/mocks/InterfaceIdCalculator.sol/InterfaceIdCalculator.json +180 -0
  26. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +28 -2
  27. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +21 -2
  28. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +118 -19
  29. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +6 -6
  30. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +783 -0
  31. package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
  32. package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
  33. package/contracts/AccessManager.sol +132 -0
  34. package/contracts/ERC4626AssetManager.sol +55 -0
  35. package/contracts/EToken.sol +208 -102
  36. package/contracts/LPManualWhitelist.sol +19 -3
  37. package/contracts/LiquidityThresholdAssetManager.sol +175 -0
  38. package/contracts/Policy.sol +5 -3
  39. package/contracts/PolicyPool.sol +381 -116
  40. package/contracts/PolicyPoolComponent.sol +61 -44
  41. package/contracts/PremiumsAccount.sol +324 -86
  42. package/contracts/Reserve.sol +199 -4
  43. package/contracts/RiskModule.sol +62 -31
  44. package/contracts/SignedQuoteRiskModule.sol +248 -0
  45. package/contracts/TimeScaled.sol +19 -15
  46. package/contracts/TrustfulRiskModule.sol +69 -2
  47. package/contracts/dependencies/WadRayMath.sol +126 -0
  48. package/contracts/interfaces/IAccessManager.sol +148 -0
  49. package/contracts/interfaces/IAssetManager.sol +89 -0
  50. package/contracts/interfaces/IEToken.sol +25 -2
  51. package/contracts/interfaces/ILPWhitelist.sol +17 -0
  52. package/contracts/interfaces/IPolicyPool.sol +21 -14
  53. package/contracts/interfaces/IPolicyPoolComponent.sol +5 -1
  54. package/contracts/interfaces/IPremiumsAccount.sol +5 -0
  55. package/contracts/interfaces/IRiskModule.sol +67 -4
  56. package/contracts/mocks/FixedRateVault.sol +85 -0
  57. package/contracts/mocks/InterfaceIdCalculator.sol +32 -0
  58. package/contracts/mocks/PolicyHolderMock.sol +21 -0
  59. package/contracts/mocks/PolicyPoolComponentMock.sol +10 -0
  60. package/contracts/mocks/PolicyPoolMock.sol +41 -15
  61. package/contracts/mocks/RiskModuleMock.sol +75 -0
  62. package/js/deploy.js +704 -0
  63. package/js/test-utils.js +339 -159
  64. package/package.json +1 -1
  65. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -890
  66. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
  67. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -691
  68. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -63
  69. package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
  70. package/build/contracts/mocks/LinkTokenMock.sol/LinkTokenMock.json +0 -370
  71. package/build/contracts/mocks/TestCurrencyUUPS.sol/TestCurrencyUUPS.json +0 -353
  72. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
  73. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -584
  74. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -615
  75. package/contracts/FlightDelayRiskModule.sol +0 -217
  76. package/contracts/PolicyNFT.sol +0 -96
  77. package/contracts/PolicyPoolConfig.sol +0 -204
  78. package/contracts/WadRayMath.sol +0 -135
  79. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
  80. package/contracts/interfaces/IPolicyNFT.sol +0 -15
  81. package/contracts/interfaces/IPolicyPoolConfig.sol +0 -96
  82. package/contracts/mocks/LinkTokenMock.sol +0 -30
  83. package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
  84. package/contracts/upgraded/PolicyNFTv1.sol +0 -126
@@ -1,13 +1,19 @@
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";
7
+ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
8
+ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
6
9
  import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
7
10
  import {Reserve} from "./Reserve.sol";
8
11
  import {IEToken} from "./interfaces/IEToken.sol";
9
- import {IPolicyPoolConfig} from "./interfaces/IPolicyPoolConfig.sol";
10
- import {WadRayMath} from "./WadRayMath.sol";
12
+ import {IAccessManager} from "./interfaces/IAccessManager.sol";
13
+ import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
14
+ import {ILPWhitelist} from "./interfaces/ILPWhitelist.sol";
15
+ import {IAssetManager} from "./interfaces/IAssetManager.sol";
16
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
11
17
  import {TimeScaled} from "./TimeScaled.sol";
12
18
 
13
19
  /**
@@ -24,8 +30,14 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
24
30
  using WadRayMath for uint256;
25
31
  using TimeScaled for TimeScaled.ScaledAmount;
26
32
  using SafeERC20 for IERC20Metadata;
33
+ using SafeCast for uint256;
27
34
 
28
- uint256 internal constant SECONDS_PER_YEAR = 365 days;
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
29
41
 
30
42
  // Attributes taken from ERC20
31
43
  mapping(address => uint256) private _balances;
@@ -52,13 +64,17 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
52
64
  uint16 minUtilizationRate; // Min utilization rate, to reject deposits that leave UR under this value - 4 decimals
53
65
  uint16 maxUtilizationRate; // Max utilization rate, to reject lockScr that leave UR above this value - 4 decimals
54
66
  uint16 internalLoanInterestRate; // Annualized interest rate charged to internal borrowers (premiums accounts) - 4dec
67
+ ILPWhitelist whitelist; // Whitelist for deposits and transfers
55
68
  }
56
69
 
57
70
  PackedParams internal _params;
58
71
 
72
+ IAssetManager internal _assetManager;
73
+
59
74
  event InternalLoan(address indexed borrower, uint256 value, uint256 amountAsked);
60
75
  event InternalLoanRepaid(address indexed borrower, uint256 value);
61
- event PoolBorrowerAdded(address borrower);
76
+ event InternalBorrowerAdded(address indexed borrower);
77
+ event InternalBorrowerRemoved(address indexed borrower, uint256 defaultedDebt);
62
78
 
63
79
  modifier onlyBorrower() {
64
80
  require(_loans[_msgSender()].scale != 0, "The caller must be a borrower");
@@ -82,7 +98,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
82
98
  uint256 maxUtilizationRate_,
83
99
  uint256 internalLoanInterestRate_
84
100
  ) public initializer {
85
- __PolicyPoolComponent_init();
101
+ __Reserve_init();
86
102
  __EToken_init_unchained(name_, symbol_, maxUtilizationRate_, internalLoanInterestRate_);
87
103
  }
88
104
 
@@ -92,7 +108,9 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
92
108
  string memory symbol_,
93
109
  uint256 maxUtilizationRate_,
94
110
  uint256 internalLoanInterestRate_
95
- ) internal initializer {
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");
96
114
  _name = name_;
97
115
  _symbol = symbol_;
98
116
  _tsScaled.init();
@@ -102,28 +120,52 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
102
120
  tokenInterestRate: 0
103
121
  }); */
104
122
  _params = PackedParams({
105
- maxUtilizationRate: uint16(maxUtilizationRate_ / 1e14),
106
- liquidityRequirement: 1e4,
123
+ maxUtilizationRate: _wadTo4(maxUtilizationRate_),
124
+ liquidityRequirement: HUNDRED_PERCENT,
107
125
  minUtilizationRate: 0,
108
- internalLoanInterestRate: uint16(internalLoanInterestRate_ / 1e14)
126
+ internalLoanInterestRate: _wadTo4(internalLoanInterestRate_),
127
+ whitelist: ILPWhitelist(address(0))
109
128
  });
110
129
 
111
130
  _validateParameters();
112
131
  }
113
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
+
114
144
  // runs validation on EToken parameters
115
145
  function _validateParameters() internal view override {
116
146
  require(
117
- _params.liquidityRequirement >= 8e3 && _params.liquidityRequirement <= 13e3,
147
+ _params.liquidityRequirement >= LIQ_REQ_MIN && _params.liquidityRequirement <= LIQ_REQ_MAX,
118
148
  "Validation: liquidityRequirement must be [0.8, 1.3]"
119
149
  );
120
150
  require(
121
- _params.maxUtilizationRate >= 5e3 && _params.maxUtilizationRate <= 1e4,
151
+ _params.maxUtilizationRate >= MAX_UR_MIN && _params.maxUtilizationRate <= HUNDRED_PERCENT,
122
152
  "Validation: maxUtilizationRate must be [0.5, 1]"
123
153
  );
124
- require(_params.minUtilizationRate <= 1e4, "Validation: minUtilizationRate must be [0, 1]");
125
154
  require(
126
- _params.internalLoanInterestRate <= 5e3,
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,
127
169
  "Validation: internalLoanInterestRate must be <= 50%"
128
170
  );
129
171
  }
@@ -234,15 +276,34 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
234
276
  address recipient,
235
277
  uint256 amount
236
278
  ) public virtual override returns (bool) {
279
+ address spender = _msgSender();
280
+ _spendAllowance(sender, spender, amount);
237
281
  _transfer(sender, recipient, amount);
238
-
239
- uint256 currentAllowance = _allowances[sender][_msgSender()];
240
- require(currentAllowance >= amount, "EToken: transfer amount exceeds allowance");
241
- _approve(sender, _msgSender(), currentAllowance - amount);
242
-
243
282
  return true;
244
283
  }
245
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
+
246
307
  /**
247
308
  * @dev Atomically increases the allowance granted to `spender` by the caller.
248
309
  *
@@ -256,7 +317,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
256
317
  * - `spender` cannot be the zero address.
257
318
  */
258
319
  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
259
- _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
320
+ _approve(_msgSender(), spender, allowance(_msgSender(), spender) + addedValue);
260
321
  return true;
261
322
  }
262
323
 
@@ -330,6 +391,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
330
391
  */
331
392
  function _mint(address account, uint256 amount) internal virtual {
332
393
  require(account != address(0), "EToken: mint to the zero address");
394
+ require(amount > 0, "EToken: amount to mint should be greater than zero");
333
395
 
334
396
  _beforeTokenTransfer(address(0), account, amount);
335
397
  uint256 scaledAmount = _tsScaled.add(amount, tokenInterestRate());
@@ -403,12 +465,12 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
403
465
  address from,
404
466
  address to,
405
467
  uint256 amount
406
- ) internal virtual {
468
+ ) internal virtual whenNotPaused {
407
469
  require(
408
470
  from == address(0) ||
409
471
  to == address(0) ||
410
- address(policyPool().config().lpWhitelist()) == address(0) ||
411
- policyPool().config().lpWhitelist().acceptsTransfer(this, from, to, amount),
472
+ address(_params.whitelist) == address(0) ||
473
+ _params.whitelist.acceptsTransfer(this, from, to, amount),
412
474
  "Transfer not allowed - Liquidity Provider not whitelisted"
413
475
  );
414
476
  }
@@ -419,9 +481,10 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
419
481
  uint256 totalSupply_ = this.totalSupply();
420
482
  if (totalSupply_ == 0) _scr.tokenInterestRate = 0;
421
483
  else {
422
- _scr.tokenInterestRate = uint64(
423
- uint256(_scr.interestRate).wadMul(uint256(_scr.scr)).wadDiv(totalSupply_)
424
- );
484
+ _scr.tokenInterestRate = uint256(_scr.interestRate)
485
+ .wadMul(uint256(_scr.scr))
486
+ .wadDiv(totalSupply_)
487
+ .toUint64();
425
488
  }
426
489
  }
427
490
 
@@ -443,6 +506,25 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
443
506
  else return 0;
444
507
  }
445
508
 
509
+ function assetManager() public view override returns (IAssetManager) {
510
+ return _assetManager;
511
+ }
512
+
513
+ function _setAssetManager(IAssetManager newAM) internal override {
514
+ _assetManager = newAM;
515
+ }
516
+
517
+ // solhint-disable-next-line func-name-mixedcase
518
+ function _4toWad(uint16 value) internal pure returns (uint256) {
519
+ // 4 decimals to Wad (18 decimals)
520
+ return uint256(value) * FOUR_DECIMAL_TO_WAD;
521
+ }
522
+
523
+ function _wadTo4(uint256 value) internal pure returns (uint16) {
524
+ // Wad to 4 decimals
525
+ return (value / FOUR_DECIMAL_TO_WAD).toUint16();
526
+ }
527
+
446
528
  function scr() public view virtual override returns (uint256) {
447
529
  return uint256(_scr.scr);
448
530
  }
@@ -456,39 +538,41 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
456
538
  }
457
539
 
458
540
  function liquidityRequirement() public view returns (uint256) {
459
- return uint256(_params.liquidityRequirement) * 1e14; // 4 -> 18 decimals
541
+ return _4toWad(_params.liquidityRequirement);
460
542
  }
461
543
 
462
544
  function maxUtilizationRate() public view returns (uint256) {
463
- return uint256(_params.maxUtilizationRate) * 1e14; // 4 -> 18 decimals
545
+ return _4toWad(_params.maxUtilizationRate);
464
546
  }
465
547
 
466
548
  function minUtilizationRate() public view returns (uint256) {
467
- return uint256(_params.minUtilizationRate) * 1e14; // 4 -> 18 decimals
549
+ return _4toWad(_params.minUtilizationRate);
468
550
  }
469
551
 
470
552
  function utilizationRate() public view returns (uint256) {
471
553
  return uint256(_scr.scr).wadDiv(this.totalSupply());
472
554
  }
473
555
 
474
- function lockScr(uint256 scrAmount, uint256 policyInterestRate) external override onlyBorrower {
556
+ function lockScr(uint256 scrAmount, uint256 policyInterestRate)
557
+ external
558
+ override
559
+ onlyBorrower
560
+ whenNotPaused
561
+ {
475
562
  require(
476
563
  scrAmount <= this.fundsAvailableToLock(),
477
- "Not enought funds available to cover the SCR"
564
+ "Not enough funds available to cover the SCR"
478
565
  );
479
566
  _tsScaled.updateScale(tokenInterestRate());
480
567
  if (_scr.scr == 0) {
481
- _scr.scr = uint128(scrAmount);
482
- _scr.interestRate = uint64(policyInterestRate);
568
+ _scr.scr = scrAmount.toUint128();
569
+ _scr.interestRate = policyInterestRate.toUint64();
483
570
  } else {
484
571
  uint256 origScr = uint256(_scr.scr);
485
572
  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);
573
+ _scr.interestRate = (uint256(_scr.interestRate).wadMul(origScr) +
574
+ policyInterestRate.wadMul(scrAmount)).wadDiv(newScr).toUint64();
575
+ _scr.scr = newScr.toUint128();
492
576
  }
493
577
  emit SCRLocked(policyInterestRate, scrAmount);
494
578
  _updateTokenInterestRate();
@@ -498,7 +582,7 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
498
582
  uint256 scrAmount,
499
583
  uint256 policyInterestRate,
500
584
  int256 adjustment
501
- ) external override onlyBorrower {
585
+ ) external override onlyBorrower whenNotPaused {
502
586
  require(scrAmount <= uint256(_scr.scr), "Current SCR less than the amount you want to unlock");
503
587
  _tsScaled.updateScale(tokenInterestRate());
504
588
 
@@ -508,17 +592,18 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
508
592
  } else {
509
593
  uint256 origScr = uint256(_scr.scr);
510
594
  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);
595
+ _scr.interestRate = (uint256(_scr.interestRate).wadMul(origScr) -
596
+ policyInterestRate.wadMul(scrAmount)).wadDiv(newScr).toUint64();
597
+ _scr.scr = newScr.toUint128();
517
598
  }
518
599
  emit SCRUnlocked(policyInterestRate, scrAmount);
519
600
  _discreteChange(adjustment);
520
601
  }
521
602
 
603
+ function _assetEarnings(int256 earnings) internal override {
604
+ _discreteChange(earnings);
605
+ }
606
+
522
607
  function _discreteChange(int256 amount) internal {
523
608
  _tsScaled.discreteChange(amount, tokenInterestRate());
524
609
  _updateTokenInterestRate();
@@ -528,12 +613,11 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
528
613
  external
529
614
  override
530
615
  onlyPolicyPool
531
- whenNotPaused
532
616
  returns (uint256)
533
617
  {
534
618
  require(
535
- address(policyPool().config().lpWhitelist()) == address(0) ||
536
- policyPool().config().lpWhitelist().acceptsDeposit(this, provider, amount),
619
+ address(_params.whitelist) == address(0) ||
620
+ _params.whitelist.acceptsDeposit(this, provider, amount),
537
621
  "Liquidity Provider not whitelisted"
538
622
  );
539
623
  _mint(provider, amount);
@@ -553,14 +637,15 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
553
637
  external
554
638
  override
555
639
  onlyPolicyPool
556
- whenNotPaused
557
640
  returns (uint256)
558
641
  {
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;
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
+ */
648
+ amount = Math.min(amount, Math.min(balanceOf(provider), totalWithdrawable()));
564
649
  if (amount == 0) return 0;
565
650
  _burn(provider, amount);
566
651
  _updateTokenInterestRate();
@@ -569,25 +654,32 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
569
654
  }
570
655
 
571
656
  function addBorrower(address borrower) external override onlyPolicyPool {
657
+ require(borrower != address(0), "EToken: Borrower cannot be the zero address");
572
658
  TimeScaled.ScaledAmount storage loan = _loans[borrower];
573
659
  if (loan.scale == 0) {
574
660
  loan.init();
575
- emit PoolBorrowerAdded(borrower);
661
+ emit InternalBorrowerAdded(borrower);
576
662
  }
577
663
  }
578
664
 
665
+ function removeBorrower(address borrower) external override onlyPolicyPool {
666
+ require(borrower != address(0), "EToken: Borrower cannot be the zero address");
667
+ uint256 defaultedDebt = getLoan(borrower);
668
+ delete _loans[borrower];
669
+ emit InternalBorrowerRemoved(borrower, defaultedDebt);
670
+ }
671
+
579
672
  function internalLoan(
580
673
  uint256 amount,
581
674
  address receiver,
582
675
  bool fromAvailable
583
676
  ) external override onlyBorrower whenNotPaused returns (uint256) {
584
677
  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
- }
678
+ amount = Math.min(
679
+ Math.min(amount, fromAvailable ? fundsAvailable() : totalSupply()),
680
+ _tsScaled.maxNegativeAdjustment(tokenInterestRate())
681
+ );
682
+ if (amount == 0) return amountAsked;
591
683
  TimeScaled.ScaledAmount storage loan = _loans[_msgSender()];
592
684
  loan.add(amount, internalLoanInterestRate());
593
685
  _discreteChange(-int256(amount));
@@ -596,77 +688,91 @@ contract EToken is Reserve, IERC20Metadata, IEToken {
596
688
  return amountAsked - amount;
597
689
  }
598
690
 
599
- 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.");
600
693
  // Anyone can call this method, since it has to pay
601
- currency().safeTransferFrom(_msgSender(), address(this), amount);
602
694
  TimeScaled.ScaledAmount storage loan = _loans[onBehalfOf];
603
695
  require(loan.scale != 0, "Not a registered borrower");
604
696
  loan.sub(amount, internalLoanInterestRate());
605
697
  _discreteChange(int256(amount));
606
698
  emit InternalLoanRepaid(onBehalfOf, amount);
699
+ // Interaction at the end for security reasons
700
+ currency().safeTransferFrom(_msgSender(), address(this), amount);
607
701
  }
608
702
 
609
703
  function getLoan(address borrower) public view virtual override returns (uint256) {
610
704
  TimeScaled.ScaledAmount storage loan = _loans[borrower];
611
- if (loan.scale == 0) return 0;
612
705
  return loan.getScaledAmount(internalLoanInterestRate());
613
706
  }
614
707
 
615
708
  function internalLoanInterestRate() public view returns (uint256) {
616
- return uint256(_params.internalLoanInterestRate) * 1e14; // to wad 4 -> 18 digits
709
+ return _4toWad(_params.internalLoanInterestRate);
617
710
  }
618
711
 
619
- function setInternalLoanInterestRate(uint256 newRate)
712
+ function setParam(Parameter param, uint256 newValue)
620
713
  external
621
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
714
+ onlyGlobalOrComponentRole2(LEVEL2_ROLE, LEVEL3_ROLE)
622
715
  {
623
716
  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);
717
+ if (param == Parameter.liquidityRequirement) {
718
+ require(
719
+ !tweak || _isTweakWad(liquidityRequirement(), newValue, 1e17),
720
+ "Tweak exceeded: liquidityRequirement tweaks only up to 10%"
721
+ );
722
+ _params.liquidityRequirement = _wadTo4(newValue);
723
+ } else if (param == Parameter.minUtilizationRate) {
724
+ require(
725
+ !tweak || _isTweakWad(minUtilizationRate(), newValue, 3e17),
726
+ "Tweak exceeded: minUtilizationRate tweaks only up to 30%"
727
+ );
728
+ _params.minUtilizationRate = _wadTo4(newValue);
729
+ } else if (param == Parameter.maxUtilizationRate) {
730
+ require(
731
+ !tweak || _isTweakWad(maxUtilizationRate(), newValue, 3e17),
732
+ "Tweak exceeded: maxUtilizationRate tweaks only up to 30%"
733
+ );
734
+ _params.maxUtilizationRate = _wadTo4(newValue);
735
+ } else if (param == Parameter.internalLoanInterestRate) {
736
+ require(
737
+ !tweak || _isTweakWad(internalLoanInterestRate(), newValue, 3e17),
738
+ "Tweak exceeded: internalLoanInterestRate tweaks only up to 30%"
739
+ );
740
+ // This call changes the interest rate without updating the current loans up to this point
741
+ // So, if interest rate goes from 5% to 6%, this change will be retroactive to the lastUpdate of each
742
+ // loan. Since it's a permissioned call, I'm ok with this. If a caller wants to reduce the impact, it can
743
+ // issue 1 wei repayLoan to each active loan, forcing the update of the scales
744
+ _params.internalLoanInterestRate = _wadTo4(newValue);
745
+ }
633
746
  _parameterChanged(
634
- IPolicyPoolConfig.GovernanceActions.setInternalLoanInterestRate,
635
- newRate,
747
+ IAccessManager.GovernanceActions(
748
+ uint256(IAccessManager.GovernanceActions.setLiquidityRequirement) + uint256(param)
749
+ ),
750
+ newValue,
636
751
  tweak
637
752
  );
638
753
  }
639
754
 
640
- function setLiquidityRequirement(uint256 newRate)
755
+ function setWhitelist(ILPWhitelist lpWhitelist_)
641
756
  external
642
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
757
+ onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
643
758
  {
644
- bool tweak = !hasPoolRole(LEVEL2_ROLE);
645
759
  require(
646
- !tweak || _isTweakWad(liquidityRequirement(), newRate, 1e17),
647
- "Tweak exceeded: liquidityRequirement tweaks only up to 10%"
760
+ address(lpWhitelist_) == address(0) ||
761
+ IPolicyPoolComponent(address(lpWhitelist_)).policyPool() == _policyPool,
762
+ "Component not linked to this PolicyPool"
648
763
  );
649
- _params.liquidityRequirement = uint16(newRate / 1e14);
650
- _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityRequirement, newRate, tweak);
764
+ _params.whitelist = lpWhitelist_;
765
+ _componentChanged(IAccessManager.GovernanceActions.setLPWhitelist, address(lpWhitelist_));
651
766
  }
652
767
 
653
- function setMaxUtilizationRate(uint256 newRate) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
654
- bool tweak = !hasPoolRole(LEVEL2_ROLE);
655
- require(
656
- !tweak || _isTweakWad(maxUtilizationRate(), newRate, 3e17),
657
- "Tweak exceeded: maxUtilizationRate tweaks only up to 30%"
658
- );
659
- _params.maxUtilizationRate = uint16(newRate / 1e14);
660
- _parameterChanged(IPolicyPoolConfig.GovernanceActions.setMaxUtilizationRate, newRate, tweak);
768
+ function whitelist() external view returns (ILPWhitelist) {
769
+ return _params.whitelist;
661
770
  }
662
771
 
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);
671
- }
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;
672
778
  }
@@ -32,10 +32,19 @@ 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
- _whitelisted[provider] = whitelisted;
38
- emit LPWhitelisted(provider, whitelisted);
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
  }