@ensuro/core 1.2.7 → 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 (109) hide show
  1. package/README.md +63 -27
  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 +217 -190
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +17 -4
  6. package/build/contracts/{extras/IPriceRiskModule.sol/IPriceRiskModule.json → LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json} +81 -131
  7. package/build/contracts/Policy.sol/Policy.json +2 -2
  8. package/build/contracts/PolicyPool.sol/PolicyPool.json +425 -295
  9. package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +15 -2
  10. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +868 -0
  11. package/build/contracts/{BaseAssetManager.sol/BaseAssetManager.json → Reserve.sol/Reserve.json} +37 -166
  12. package/build/contracts/RiskModule.sol/RiskModule.json +85 -312
  13. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +960 -0
  14. package/build/contracts/{DataTypes.sol/DataTypes.json → TimeScaled.sol/TimeScaled.json} +4 -4
  15. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +134 -336
  16. package/build/contracts/dependencies/ERC1967Proxy.sol/ERC1967Proxy.json +64 -0
  17. package/build/{interfaces/IExchange.sol/IExchange.json → contracts/dependencies/IERC20Metadata.sol/IERC20Metadata.json} +83 -79
  18. package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
  19. package/build/{interfaces/IPolicyPoolConfig.sol/IPolicyPoolConfig.json → contracts/interfaces/IAccessManager.sol/IAccessManager.json} +79 -77
  20. package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +109 -0
  21. package/build/{interfaces → contracts/interfaces}/IEToken.sol/IEToken.json +39 -91
  22. package/build/{interfaces → contracts/interfaces}/ILPWhitelist.sol/ILPWhitelist.json +1 -1
  23. package/build/{interfaces → contracts/interfaces}/IPolicyHolder.sol/IPolicyHolder.json +1 -1
  24. package/build/{interfaces → contracts/interfaces}/IPolicyPool.sol/IPolicyPool.json +137 -100
  25. package/build/{interfaces → contracts/interfaces}/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +1 -1
  26. package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +315 -0
  27. package/build/{interfaces → contracts/interfaces}/IRiskModule.sol/IRiskModule.json +58 -34
  28. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
  29. package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +43 -0
  30. package/build/{interfaces/IInsolvencyHook.sol/IInsolvencyHook.json → contracts/mocks/IMintableERC20.sol/IMintableERC20.json} +13 -8
  31. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +228 -0
  32. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +35 -0
  33. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +702 -0
  34. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +92 -0
  35. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +764 -0
  36. package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +343 -0
  37. package/build/contracts/mocks/TestNFT.sol/TestNFT.json +388 -0
  38. package/contracts/AccessManager.sol +103 -0
  39. package/contracts/ERC4626AssetManager.sol +52 -0
  40. package/contracts/EToken.sol +254 -299
  41. package/contracts/LPManualWhitelist.sol +4 -4
  42. package/contracts/LiquidityThresholdAssetManager.sol +160 -0
  43. package/contracts/Policy.sol +49 -45
  44. package/contracts/PolicyPool.sol +379 -514
  45. package/contracts/PolicyPoolComponent.sol +41 -41
  46. package/contracts/PremiumsAccount.sol +480 -0
  47. package/contracts/Reserve.sol +213 -0
  48. package/contracts/RiskModule.sol +233 -174
  49. package/contracts/SignedQuoteRiskModule.sol +241 -0
  50. package/contracts/TimeScaled.sol +138 -0
  51. package/contracts/TrustfulRiskModule.sol +85 -23
  52. package/contracts/dependencies/ERC1967Proxy.sol +19 -0
  53. package/contracts/dependencies/IERC20Metadata.sol +19 -0
  54. package/contracts/dependencies/WadRayMath.sol +126 -0
  55. package/contracts/interfaces/IAccessManager.sol +148 -0
  56. package/contracts/interfaces/IAssetManager.sol +84 -0
  57. package/contracts/interfaces/IEToken.sol +195 -0
  58. package/contracts/interfaces/ILPWhitelist.sol +40 -0
  59. package/{interfaces → contracts/interfaces}/IPolicyHolder.sol +0 -0
  60. package/contracts/interfaces/IPolicyPool.sol +145 -0
  61. package/{interfaces → contracts/interfaces}/IPolicyPoolComponent.sol +3 -0
  62. package/contracts/interfaces/IPremiumsAccount.sol +76 -0
  63. package/contracts/interfaces/IRiskModule.sol +123 -0
  64. package/contracts/mocks/FixedRateVault.sol +85 -0
  65. package/contracts/mocks/ForwardProxy.sol +66 -0
  66. package/contracts/mocks/IMintableERC20.sol +13 -0
  67. package/contracts/mocks/PolicyHolderMock.sol +89 -0
  68. package/contracts/mocks/PolicyPoolComponentMock.sol +17 -0
  69. package/contracts/mocks/PolicyPoolMock.sol +148 -0
  70. package/contracts/mocks/RiskModuleMock.sol +75 -0
  71. package/contracts/mocks/TestCurrency.sol +35 -0
  72. package/contracts/mocks/TestNFT.sol +23 -0
  73. package/js/test-utils.js +356 -0
  74. package/package.json +2 -2
  75. package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +0 -742
  76. package/build/contracts/Exchange.sol/Exchange.json +0 -591
  77. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -1112
  78. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -570
  79. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -686
  80. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -50
  81. package/build/contracts/extras/PriceRiskModule.sol/PriceRiskModule.json +0 -1055
  82. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
  83. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -571
  84. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -602
  85. package/build/interfaces/IAssetManager.sol/IAssetManager.json +0 -31
  86. package/build/interfaces/IExchange.sol/IPriceOracle.json +0 -30
  87. package/build/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
  88. package/contracts/AaveAssetManager.sol +0 -298
  89. package/contracts/BaseAssetManager.sol +0 -301
  90. package/contracts/DataTypes.sol +0 -287
  91. package/contracts/Exchange.sol +0 -189
  92. package/contracts/FlightDelayRiskModule.sol +0 -208
  93. package/contracts/PolicyNFT.sol +0 -96
  94. package/contracts/PolicyPoolConfig.sol +0 -247
  95. package/contracts/WadRayMath.sol +0 -135
  96. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
  97. package/contracts/extras/IPriceRiskModule.sol +0 -41
  98. package/contracts/extras/PriceRiskModule.sol +0 -243
  99. package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
  100. package/contracts/upgraded/PolicyNFTv1.sol +0 -126
  101. package/interfaces/IAssetManager.sol +0 -23
  102. package/interfaces/IEToken.sol +0 -48
  103. package/interfaces/IExchange.sol +0 -59
  104. package/interfaces/IInsolvencyHook.sol +0 -27
  105. package/interfaces/ILPWhitelist.sol +0 -23
  106. package/interfaces/IPolicyNFT.sol +0 -15
  107. package/interfaces/IPolicyPool.sol +0 -44
  108. package/interfaces/IPolicyPoolConfig.sol +0 -87
  109. package/interfaces/IRiskModule.sol +0 -29
@@ -2,139 +2,139 @@
2
2
  pragma solidity ^0.8.0;
3
3
 
4
4
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
5
- import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
6
- import {PolicyPoolComponent} from "./PolicyPoolComponent.sol";
7
- import {IEToken} from "../interfaces/IEToken.sol";
8
- import {IPolicyPoolConfig} from "../interfaces/IPolicyPoolConfig.sol";
9
- import {IInsolvencyHook} from "../interfaces/IInsolvencyHook.sol";
10
- import {WadRayMath} from "./WadRayMath.sol";
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";
8
+ import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
9
+ import {Reserve} from "./Reserve.sol";
10
+ import {IEToken} from "./interfaces/IEToken.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";
16
+ import {TimeScaled} from "./TimeScaled.sol";
11
17
 
12
18
  /**
13
19
  * @title Ensuro ERC20 EToken - interest-bearing token
14
20
  * @dev Implementation of the interest/earnings bearing token for the Ensuro protocol.
15
- * The _scaleFactor scales the balances stored in _balances. _scaleFactor grows continuoulsly at
16
- * _tokenInterestRate.
17
- * Every operation that changes the utilization rate (_scr/totalSupply) or the _scrInterestRate, updates
18
- * first the _scaleFactor accumulating the interest accrued since _lastScaleUpdate.
21
+ * `_tsScaled.scale` scales the balances stored in _balances. _tsScaled (totalSupply scaled) grows
22
+ * continuoulsly at tokenInterestRate().
23
+ * Every operation that changes the utilization rate (_scr.scr/totalSupply) or the _scr.interestRate, updates
24
+ * first the _tsScaled.scale accumulating the interest accrued since _tsScaled.lastUpdate.
19
25
  * @custom:security-contact security@ensuro.co
20
26
  * @author Ensuro
21
27
  */
22
- contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
23
- uint256 public constant MIN_SCALE = 1e17; // 0.0000000001 == 1e-10 in ray
24
-
28
+ contract EToken is Reserve, IERC20Metadata, IEToken {
25
29
  using WadRayMath for uint256;
26
-
27
- uint256 internal constant SECONDS_PER_YEAR = 365 days;
30
+ using TimeScaled for TimeScaled.ScaledAmount;
31
+ using SafeERC20 for IERC20Metadata;
32
+ using SafeCast for uint256;
28
33
 
29
34
  // Attributes taken from ERC20
30
35
  mapping(address => uint256) private _balances;
31
36
  mapping(address => mapping(address => uint256)) private _allowances;
32
- uint256 private _totalSupply; // non-scaled totalSupply
33
37
 
34
38
  string private _name;
35
39
  string private _symbol;
36
40
 
37
- uint40 internal _expirationPeriod; // in seconds, the maximum duration of policies this eToken can back up
38
- uint256 internal _scaleFactor; // in Ray
39
- uint40 internal _lastScaleUpdate;
41
+ TimeScaled.ScaledAmount internal _tsScaled; // Total Supply scaled
40
42
 
41
- uint256 internal _scr; // in Wad - Capital locked as Solvency Capital Requirement of backed up policies
42
- uint256 internal _scrInterestRate; // in Ray - Interest rate received in exchange of solvency capital
43
- uint256 internal _tokenInterestRate; // in Ray - Overall interest rate of the token
44
- uint256 internal _liquidityRequirement; // in Ray - Liquidity requirement to lock more than SCR
45
- uint256 internal _maxUtilizationRate; // in Ray - Maximum SCR/totalSupply rate for backup up new policies
43
+ struct Scr {
44
+ uint128 scr; // in Wad - Capital locked as Solvency Capital Requirement of backed up policies
45
+ uint64 interestRate; // in Wad - Interest rate received in exchange of solvency capital
46
+ uint64 tokenInterestRate; // in Wad - Overall interest rate of the token
47
+ }
46
48
 
47
- uint256 internal _poolLoan; // in Wad - Capital that was used to pay defaults of the premium pool. Took as a loan.
48
- uint256 internal _poolLoanInterestRate; // in Ray
49
- uint256 internal _poolLoanScale; // in Ray
50
- uint40 internal _poolLoanLastUpdate;
49
+ Scr internal _scr;
51
50
 
52
- bool internal _acceptAllRMs; // By defaults, accepts (backs up) any RiskModule;
53
- // Exceptions to the accept all or accept none policy defined before
54
- mapping(address => bool) internal _acceptExceptions;
51
+ // Mapping that keeps track of allowed borrowers (PremiumsAccount) and their current debt
52
+ mapping(address => TimeScaled.ScaledAmount) internal _loans;
55
53
 
56
- event PoolLoan(uint256 value);
57
- event PoolLoanRepaid(uint256 value);
54
+ struct PackedParams {
55
+ uint16 liquidityRequirement; // Liquidity requirement to lock more/less than SCR - 4 decimals
56
+ uint16 minUtilizationRate; // Min utilization rate, to reject deposits that leave UR under this value - 4 decimals
57
+ uint16 maxUtilizationRate; // Max utilization rate, to reject lockScr that leave UR above this value - 4 decimals
58
+ uint16 internalLoanInterestRate; // Annualized interest rate charged to internal borrowers (premiums accounts) - 4dec
59
+ ILPWhitelist whitelist; // Whitelist for deposits and transfers
60
+ }
58
61
 
59
- modifier onlyAssetManager() {
60
- require(
61
- _msgSender() == address(_policyPool.config().assetManager()),
62
- "The caller must be the PolicyPool's AssetManager"
63
- );
62
+ PackedParams internal _params;
63
+
64
+ IAssetManager internal _assetManager;
65
+
66
+ event InternalLoan(address indexed borrower, uint256 value, uint256 amountAsked);
67
+ event InternalLoanRepaid(address indexed borrower, uint256 value);
68
+ event InternalBorrowerAdded(address indexed borrower);
69
+ event InternalBorrowerRemoved(address indexed borrower, uint256 defaultedDebt);
70
+
71
+ modifier onlyBorrower() {
72
+ require(_loans[_msgSender()].scale != 0, "The caller must be a borrower");
64
73
  _;
65
74
  }
66
75
 
67
76
  /// @custom:oz-upgrades-unsafe-allow constructor
68
77
  // solhint-disable-next-line no-empty-blocks
69
- constructor(IPolicyPool policyPool_) PolicyPoolComponent(policyPool_) {}
78
+ constructor(IPolicyPool policyPool_) Reserve(policyPool_) {}
70
79
 
71
80
  /**
72
81
  * @dev Initializes the eToken
73
- * @param expirationPeriod_ Maximum expirationPeriod (from block.timestamp) of policies to be accepted
74
- * @param liquidityRequirement_ Liquidity requirement to allow withdrawal (in Ray - default=1 Ray)
75
82
  * @param maxUtilizationRate_ Max utilization rate (scr/totalSupply) (in Ray - default=1 Ray)
76
- * @param poolLoanInterestRate_ Rate of loans givencrto the policy pool (in Ray)
83
+ * @param internalLoanInterestRate_ Rate of loans givencrto the policy pool (in Ray)
77
84
  * @param name_ Name of the eToken
78
85
  * @param symbol_ Symbol of the eToken
79
86
  */
80
87
  function initialize(
81
88
  string memory name_,
82
89
  string memory symbol_,
83
- uint40 expirationPeriod_,
84
- uint256 liquidityRequirement_,
85
90
  uint256 maxUtilizationRate_,
86
- uint256 poolLoanInterestRate_
91
+ uint256 internalLoanInterestRate_
87
92
  ) public initializer {
88
93
  __PolicyPoolComponent_init();
89
- __EToken_init_unchained(
90
- name_,
91
- symbol_,
92
- expirationPeriod_,
93
- liquidityRequirement_,
94
- maxUtilizationRate_,
95
- poolLoanInterestRate_
96
- );
94
+ __EToken_init_unchained(name_, symbol_, maxUtilizationRate_, internalLoanInterestRate_);
97
95
  }
98
96
 
99
97
  // solhint-disable-next-line func-name-mixedcase
100
98
  function __EToken_init_unchained(
101
99
  string memory name_,
102
100
  string memory symbol_,
103
- uint40 expirationPeriod_,
104
- uint256 liquidityRequirement_,
105
101
  uint256 maxUtilizationRate_,
106
- uint256 poolLoanInterestRate_
102
+ uint256 internalLoanInterestRate_
107
103
  ) internal initializer {
108
104
  _name = name_;
109
105
  _symbol = symbol_;
110
- _expirationPeriod = expirationPeriod_;
111
- _scaleFactor = WadRayMath.ray();
112
- _lastScaleUpdate = uint40(block.timestamp);
113
- _scr = 0;
114
- _scrInterestRate = 0;
115
- _tokenInterestRate = 0;
116
- _liquidityRequirement = liquidityRequirement_;
117
- _maxUtilizationRate = maxUtilizationRate_;
118
- _acceptAllRMs = true;
119
-
120
- _poolLoan = 0;
121
- _poolLoanInterestRate = poolLoanInterestRate_;
122
- _poolLoanScale = WadRayMath.ray();
123
- _poolLoanLastUpdate = uint40(block.timestamp);
106
+ _tsScaled.init();
107
+ /* _scr = Scr({
108
+ scr: 0,
109
+ interestRate: 0,
110
+ tokenInterestRate: 0
111
+ }); */
112
+ _params = PackedParams({
113
+ maxUtilizationRate: _wadTo4(maxUtilizationRate_),
114
+ liquidityRequirement: 1e4,
115
+ minUtilizationRate: 0,
116
+ internalLoanInterestRate: _wadTo4(internalLoanInterestRate_),
117
+ whitelist: ILPWhitelist(address(0))
118
+ });
119
+
124
120
  _validateParameters();
125
121
  }
126
122
 
127
123
  // runs validation on EToken parameters
128
124
  function _validateParameters() internal view override {
129
125
  require(
130
- _liquidityRequirement >= 8e26 && _liquidityRequirement <= 13e26,
126
+ _params.liquidityRequirement >= 8e3 && _params.liquidityRequirement <= 13e3,
131
127
  "Validation: liquidityRequirement must be [0.8, 1.3]"
132
128
  );
133
129
  require(
134
- _maxUtilizationRate >= 5e26 && _maxUtilizationRate <= WadRayMath.RAY,
130
+ _params.maxUtilizationRate >= 5e3 && _params.maxUtilizationRate <= 1e4,
135
131
  "Validation: maxUtilizationRate must be [0.5, 1]"
136
132
  );
137
- require(_poolLoanInterestRate <= 5e26, "Validation: poolLoanInterestRate must be <= 50%");
133
+ require(_params.minUtilizationRate <= 1e4, "Validation: minUtilizationRate must be [0, 1]");
134
+ require(
135
+ _params.internalLoanInterestRate <= 5e3,
136
+ "Validation: internalLoanInterestRate must be <= 50%"
137
+ );
138
138
  }
139
139
 
140
140
  /*** BEGIN ERC20 methods - mainly copied from OpenZeppelin but changes in events and scaledAmount */
@@ -175,7 +175,7 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
175
175
  * @dev See {IERC20-totalSupply}.
176
176
  */
177
177
  function totalSupply() public view virtual override returns (uint256) {
178
- return _totalSupply.wadToRay().rayMul(_calculateCurrentScale()).rayToWad();
178
+ return _tsScaled.getScaledAmount(tokenInterestRate());
179
179
  }
180
180
 
181
181
  /**
@@ -184,7 +184,7 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
184
184
  function balanceOf(address account) public view virtual override returns (uint256) {
185
185
  uint256 principalBalance = _balances[account];
186
186
  if (principalBalance == 0) return 0;
187
- return principalBalance.wadToRay().rayMul(_calculateCurrentScale()).rayToWad();
187
+ return _tsScaled.getScale(tokenInterestRate()).rayMul(principalBalance.wadToRay()).rayToWad();
188
188
  }
189
189
 
190
190
  /**
@@ -295,10 +295,6 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
295
295
  return true;
296
296
  }
297
297
 
298
- function _scaleAmount(uint256 amount) internal view returns (uint256) {
299
- return amount.wadToRay().rayDiv(_calculateCurrentScale()).rayToWad();
300
- }
301
-
302
298
  /**
303
299
  * @dev Moves tokens `amount` from `sender` to `recipient`.
304
300
  *
@@ -322,7 +318,7 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
322
318
  require(recipient != address(0), "EToken: transfer to the zero address");
323
319
 
324
320
  _beforeTokenTransfer(sender, recipient, amount);
325
- uint256 scaledAmount = _scaleAmount(amount);
321
+ uint256 scaledAmount = _tsScaled.scaleAmountNow(tokenInterestRate(), amount);
326
322
 
327
323
  uint256 senderBalance = _balances[sender];
328
324
  require(senderBalance >= scaledAmount, "EToken: transfer amount exceeds balance");
@@ -345,9 +341,7 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
345
341
  require(account != address(0), "EToken: mint to the zero address");
346
342
 
347
343
  _beforeTokenTransfer(address(0), account, amount);
348
- uint256 scaledAmount = _scaleAmount(amount);
349
-
350
- _totalSupply += scaledAmount;
344
+ uint256 scaledAmount = _tsScaled.add(amount, tokenInterestRate());
351
345
  _balances[account] += scaledAmount;
352
346
  emit Transfer(address(0), account, amount);
353
347
  }
@@ -367,11 +361,10 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
367
361
  require(account != address(0), "EToken: burn from the zero address");
368
362
  _beforeTokenTransfer(account, address(0), amount);
369
363
 
370
- uint256 scaledAmount = _scaleAmount(amount);
364
+ uint256 scaledAmount = _tsScaled.sub(amount, tokenInterestRate());
371
365
  uint256 accountBalance = _balances[account];
372
366
  require(accountBalance >= scaledAmount, "EToken: burn amount exceeds balance");
373
367
  _balances[account] = accountBalance - scaledAmount;
374
- _totalSupply -= scaledAmount;
375
368
 
376
369
  emit Transfer(account, address(0), amount);
377
370
  }
@@ -419,159 +412,169 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
419
412
  address from,
420
413
  address to,
421
414
  uint256 amount
422
- ) internal virtual {
415
+ ) internal virtual whenNotPaused {
423
416
  require(
424
417
  from == address(0) ||
425
418
  to == address(0) ||
426
- address(policyPool().config().lpWhitelist()) == address(0) ||
427
- policyPool().config().lpWhitelist().acceptsTransfer(this, from, to, amount),
419
+ address(_params.whitelist) == address(0) ||
420
+ _params.whitelist.acceptsTransfer(this, from, to, amount),
428
421
  "Transfer not allowed - Liquidity Provider not whitelisted"
429
422
  );
430
423
  }
431
424
 
432
425
  /*** END ERC20 methods - mainly copied from OpenZeppelin but changes in events and scaledAmount */
433
426
 
434
- function _updateCurrentScale() internal {
435
- if (uint40(block.timestamp) == _lastScaleUpdate) return;
436
- _scaleFactor = _calculateCurrentScale();
437
- require(_scaleFactor >= MIN_SCALE, "Scale too small, can lead to rounding errors");
438
- _lastScaleUpdate = uint40(block.timestamp);
439
- }
440
-
441
427
  function _updateTokenInterestRate() internal {
442
- uint256 totalSupply_ = this.totalSupply().wadToRay();
443
- if (totalSupply_ == 0) _tokenInterestRate = 0;
444
- else _tokenInterestRate = _scrInterestRate.rayMul(_scr.wadToRay()).rayDiv(totalSupply_);
445
- }
446
-
447
- function _calculateCurrentScale() internal view returns (uint256) {
448
- if (uint40(block.timestamp) <= _lastScaleUpdate) return _scaleFactor;
449
- uint256 timeDifference = block.timestamp - _lastScaleUpdate;
450
- return
451
- _scaleFactor.rayMul(
452
- ((_tokenInterestRate * timeDifference) / SECONDS_PER_YEAR) + WadRayMath.ray()
453
- );
428
+ uint256 totalSupply_ = this.totalSupply();
429
+ if (totalSupply_ == 0) _scr.tokenInterestRate = 0;
430
+ else {
431
+ _scr.tokenInterestRate = uint256(_scr.interestRate)
432
+ .wadMul(uint256(_scr.scr))
433
+ .wadDiv(totalSupply_)
434
+ .toUint64();
435
+ }
454
436
  }
455
437
 
456
438
  function getCurrentScale(bool updated) public view returns (uint256) {
457
- if (updated) return _calculateCurrentScale();
458
- else return _scaleFactor;
439
+ if (updated) return _tsScaled.getScale(tokenInterestRate());
440
+ else return uint256(_tsScaled.scale);
459
441
  }
460
442
 
461
- function ocean() public view virtual override returns (uint256) {
443
+ function fundsAvailable() public view returns (uint256) {
462
444
  uint256 totalSupply_ = this.totalSupply();
463
- if (totalSupply_ > _scr) return totalSupply_ - _scr;
445
+ if (totalSupply_ > uint256(_scr.scr)) return totalSupply_ - uint256(_scr.scr);
464
446
  else return 0;
465
447
  }
466
448
 
467
- function oceanForNewScr() public view virtual override returns (uint256) {
449
+ function fundsAvailableToLock() public view returns (uint256) {
468
450
  uint256 totalSupply_ = this.totalSupply();
469
- if (totalSupply_ > _scr) return (totalSupply_ - _scr).wadMul(_maxUtilizationRate.rayToWad());
451
+ if (totalSupply_ > uint256(_scr.scr))
452
+ return (totalSupply_ - uint256(_scr.scr)).wadMul(maxUtilizationRate());
470
453
  else return 0;
471
454
  }
472
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
+
473
475
  function scr() public view virtual override returns (uint256) {
474
- return _scr;
476
+ return uint256(_scr.scr);
475
477
  }
476
478
 
477
479
  function scrInterestRate() public view override returns (uint256) {
478
- return _scrInterestRate;
480
+ return uint256(_scr.interestRate);
479
481
  }
480
482
 
481
483
  function tokenInterestRate() public view override returns (uint256) {
482
- return _tokenInterestRate;
484
+ return uint256(_scr.tokenInterestRate);
483
485
  }
484
486
 
485
487
  function liquidityRequirement() public view returns (uint256) {
486
- return _liquidityRequirement;
488
+ return _4toWad(_params.liquidityRequirement);
487
489
  }
488
490
 
489
491
  function maxUtilizationRate() public view returns (uint256) {
490
- return _maxUtilizationRate;
492
+ return _4toWad(_params.maxUtilizationRate);
491
493
  }
492
494
 
493
- function expirationPeriod() public view returns (uint40) {
494
- return _expirationPeriod;
495
+ function minUtilizationRate() public view returns (uint256) {
496
+ return _4toWad(_params.minUtilizationRate);
495
497
  }
496
498
 
497
499
  function utilizationRate() public view returns (uint256) {
498
- return _scr.wadDiv(this.totalSupply()).wadToRay();
500
+ return uint256(_scr.scr).wadDiv(this.totalSupply());
499
501
  }
500
502
 
501
- function lockScr(uint256 policyInterestRate, uint256 scrAmount) external override onlyPolicyPool {
502
- require(scrAmount <= this.ocean(), "Not enought OCEAN to cover the SCR");
503
- _updateCurrentScale();
504
- if (_scr == 0) {
505
- _scr = scrAmount;
506
- _scrInterestRate = policyInterestRate;
503
+ function lockScr(uint256 scrAmount, uint256 policyInterestRate)
504
+ external
505
+ override
506
+ onlyBorrower
507
+ whenNotPaused
508
+ {
509
+ require(
510
+ scrAmount <= this.fundsAvailableToLock(),
511
+ "Not enought funds available to cover the SCR"
512
+ );
513
+ _tsScaled.updateScale(tokenInterestRate());
514
+ if (_scr.scr == 0) {
515
+ _scr.scr = scrAmount.toUint128();
516
+ _scr.interestRate = policyInterestRate.toUint64();
507
517
  } else {
508
- uint256 origScr = _scr.wadToRay();
509
- _scr += scrAmount;
510
- _scrInterestRate = (_scrInterestRate.rayMul(origScr) +
511
- policyInterestRate.rayMul(scrAmount.wadToRay())).rayDiv(_scr.wadToRay());
518
+ uint256 origScr = uint256(_scr.scr);
519
+ uint256 newScr = origScr + scrAmount;
520
+ _scr.interestRate = (uint256(_scr.interestRate).wadMul(origScr) +
521
+ policyInterestRate.wadMul(scrAmount)).wadDiv(newScr).toUint64();
522
+ _scr.scr = newScr.toUint128();
512
523
  }
513
524
  emit SCRLocked(policyInterestRate, scrAmount);
514
525
  _updateTokenInterestRate();
515
526
  }
516
527
 
517
- function unlockScr(uint256 policyInterestRate, uint256 scrAmount)
518
- external
519
- override
520
- onlyPolicyPool
521
- {
522
- require(scrAmount <= _scr, "Current SCR less than the amount you want to unlock");
523
- _updateCurrentScale();
528
+ function unlockScr(
529
+ uint256 scrAmount,
530
+ uint256 policyInterestRate,
531
+ int256 adjustment
532
+ ) external override onlyBorrower {
533
+ require(scrAmount <= uint256(_scr.scr), "Current SCR less than the amount you want to unlock");
534
+ _tsScaled.updateScale(tokenInterestRate());
524
535
 
525
- if (_scr == scrAmount) {
526
- _scr = 0;
527
- _scrInterestRate = 0;
536
+ if (uint256(_scr.scr) == scrAmount) {
537
+ _scr.scr = 0;
538
+ _scr.interestRate = 0;
528
539
  } else {
529
- uint256 origScr = _scr.wadToRay();
530
- _scr -= scrAmount;
531
- _scrInterestRate = (_scrInterestRate.rayMul(origScr) -
532
- policyInterestRate.rayMul(scrAmount.wadToRay())).rayDiv(_scr.wadToRay());
540
+ uint256 origScr = uint256(_scr.scr);
541
+ uint256 newScr = origScr - scrAmount;
542
+ _scr.interestRate = (uint256(_scr.interestRate).wadMul(origScr) -
543
+ policyInterestRate.wadMul(scrAmount)).wadDiv(newScr).toUint64();
544
+ _scr.scr = newScr.toUint128();
533
545
  }
534
546
  emit SCRUnlocked(policyInterestRate, scrAmount);
535
- _updateTokenInterestRate();
536
- }
537
-
538
- function _discreteChange(uint256 amount, bool positive) internal {
539
- uint256 newTotalSupply = positive ? (totalSupply() + amount) : (totalSupply() - amount);
540
- _scaleFactor = newTotalSupply.wadToRay().rayDiv(_totalSupply.wadToRay());
541
- require(_scaleFactor >= MIN_SCALE, "Scale too small, can lead to rounding errors");
542
- _updateTokenInterestRate();
547
+ _discreteChange(adjustment);
543
548
  }
544
549
 
545
- function discreteEarning(uint256 amount, bool positive) external override onlyPolicyPool {
546
- _updateCurrentScale();
547
- _discreteChange(amount, positive);
550
+ function _assetEarnings(int256 earnings) internal override {
551
+ _discreteChange(earnings);
548
552
  }
549
553
 
550
- function assetEarnings(uint256 amount, bool positive) external override onlyAssetManager {
551
- _updateCurrentScale();
552
- _discreteChange(amount, positive);
554
+ function _discreteChange(int256 amount) internal {
555
+ _tsScaled.discreteChange(amount, tokenInterestRate());
556
+ _updateTokenInterestRate();
553
557
  }
554
558
 
555
559
  function deposit(address provider, uint256 amount)
556
560
  external
557
561
  override
558
562
  onlyPolicyPool
559
- whenNotPaused
560
563
  returns (uint256)
561
564
  {
562
565
  require(
563
- address(policyPool().config().lpWhitelist()) == address(0) ||
564
- policyPool().config().lpWhitelist().acceptsDeposit(this, provider, amount),
566
+ address(_params.whitelist) == address(0) ||
567
+ _params.whitelist.acceptsDeposit(this, provider, amount),
565
568
  "Liquidity Provider not whitelisted"
566
569
  );
567
- _updateCurrentScale();
568
570
  _mint(provider, amount);
569
571
  _updateTokenInterestRate();
572
+ require(utilizationRate() >= minUtilizationRate(), "Deposit rejected - Utilization Rate < min");
570
573
  return balanceOf(provider);
571
574
  }
572
575
 
573
576
  function totalWithdrawable() public view virtual override returns (uint256) {
574
- uint256 locked = _scr.wadToRay().rayMul(_liquidityRequirement).rayToWad();
577
+ uint256 locked = uint256(_scr.scr).wadMul(liquidityRequirement());
575
578
  uint256 totalSupply_ = totalSupply();
576
579
  if (totalSupply_ >= locked) return totalSupply_ - locked;
577
580
  else return 0;
@@ -581,173 +584,125 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
581
584
  external
582
585
  override
583
586
  onlyPolicyPool
584
- whenNotPaused
585
587
  returns (uint256)
586
588
  {
587
- _updateCurrentScale();
588
- uint256 balance = balanceOf(provider);
589
- if (balance == 0) return 0;
590
- if (amount > balance) amount = balance;
591
- uint256 withdrawable = totalWithdrawable();
592
- if (amount > withdrawable) amount = withdrawable;
589
+ amount = Math.min(amount, Math.min(balanceOf(provider), totalWithdrawable()));
593
590
  if (amount == 0) return 0;
594
591
  _burn(provider, amount);
595
592
  _updateTokenInterestRate();
593
+ _transferTo(provider, amount);
596
594
  return amount;
597
595
  }
598
596
 
599
- function accepts(address riskModule, uint40 policyExpiration)
600
- public
601
- view
602
- virtual
603
- override
604
- returns (bool)
605
- {
606
- if (paused()) return false;
607
- if (_acceptAllRMs == _acceptExceptions[riskModule]) {
608
- // all accepted except this one or all rejected and this one is not an exception
609
- return false;
597
+ function addBorrower(address borrower) external override onlyPolicyPool {
598
+ TimeScaled.ScaledAmount storage loan = _loans[borrower];
599
+ if (loan.scale == 0) {
600
+ loan.init();
601
+ emit InternalBorrowerAdded(borrower);
610
602
  }
611
- return policyExpiration < (uint40(block.timestamp) + _expirationPeriod);
612
603
  }
613
604
 
614
- function _updatePoolLoanScale() internal {
615
- if (uint40(block.timestamp) == _poolLoanLastUpdate) return;
616
- _poolLoanScale = _getPoolLoanScale();
617
- _poolLoanLastUpdate = uint40(block.timestamp);
605
+ function removeBorrower(address borrower) external override onlyPolicyPool {
606
+ uint256 defaultedDebt = getLoan(borrower);
607
+ delete _loans[borrower];
608
+ emit InternalBorrowerRemoved(borrower, defaultedDebt);
618
609
  }
619
610
 
620
- function _maxNegativeAdjustment() internal view returns (uint256) {
621
- uint256 ts = totalSupply();
622
- uint256 minTs = _totalSupply.wadToRay().rayMul(MIN_SCALE * 10).rayToWad();
623
- if (ts > minTs) return ts - minTs;
624
- else return 0;
625
- }
626
-
627
- function lendToPool(uint256 amount, bool fromOcean)
628
- external
629
- override
630
- onlyPolicyPool
631
- returns (uint256)
632
- {
633
- if (fromOcean && amount > ocean()) amount = ocean();
634
- if (!fromOcean && amount > totalSupply()) amount = totalSupply();
635
- if (amount > _maxNegativeAdjustment()) {
636
- amount = _maxNegativeAdjustment();
637
- if (amount == 0) return amount;
638
- }
639
- if (_poolLoan == 0) {
640
- _poolLoan = amount;
641
- _poolLoanScale = WadRayMath.ray();
642
- _poolLoanLastUpdate = uint40(block.timestamp);
643
- } else {
644
- _updatePoolLoanScale();
645
- _poolLoan += amount.wadToRay().rayDiv(_poolLoanScale).rayToWad();
646
- }
647
- _updateCurrentScale(); // shouldn't do anything because lendToPool is after unlock_scr but doing anyway
648
- _discreteChange(amount, false);
649
- emit PoolLoan(amount);
650
- if (!fromOcean && _scr > totalSupply()) {
651
- // Notify insolvency_hook - Insuficient solvency
652
- IInsolvencyHook hook = _policyPool.config().insolvencyHook();
653
- if (address(hook) != address(0)) {
654
- hook.insolventEToken(this, _scr - totalSupply());
655
- }
656
- }
657
- return amount;
658
- }
659
-
660
- function repayPoolLoan(uint256 amount) external override onlyPolicyPool {
661
- _updatePoolLoanScale();
662
- _poolLoan = (getPoolLoan() - amount).wadToRay().rayDiv(_poolLoanScale).rayToWad();
663
- _updateCurrentScale(); // shouldn't do anything because lendToPool is after unlock_scr but doing anyway
664
- _discreteChange(amount, true);
665
- emit PoolLoanRepaid(amount);
666
- }
667
-
668
- function _getPoolLoanScale() internal view returns (uint256) {
669
- if (uint40(block.timestamp) <= _poolLoanLastUpdate) return _poolLoanScale;
670
- uint256 timeDifference = block.timestamp - _poolLoanLastUpdate;
671
- return
672
- _poolLoanScale.rayMul(
673
- ((_poolLoanInterestRate * timeDifference) / SECONDS_PER_YEAR) + WadRayMath.ray()
674
- );
675
- }
676
-
677
- function getPoolLoan() public view virtual override returns (uint256) {
678
- if (_poolLoan == 0) return 0;
679
- return _poolLoan.wadToRay().rayMul(_getPoolLoanScale()).rayToWad();
611
+ function internalLoan(
612
+ uint256 amount,
613
+ address receiver,
614
+ bool fromAvailable
615
+ ) external override onlyBorrower whenNotPaused returns (uint256) {
616
+ uint256 amountAsked = amount;
617
+ amount = Math.min(
618
+ Math.min(amount, fromAvailable ? fundsAvailable() : totalSupply()),
619
+ _tsScaled.maxNegativeAdjustment(tokenInterestRate())
620
+ );
621
+ if (amount == 0) return amountAsked;
622
+ TimeScaled.ScaledAmount storage loan = _loans[_msgSender()];
623
+ loan.add(amount, internalLoanInterestRate());
624
+ _discreteChange(-int256(amount));
625
+ _transferTo(receiver, amount);
626
+ emit InternalLoan(_msgSender(), amount, amountAsked);
627
+ return amountAsked - amount;
680
628
  }
681
629
 
682
- function poolLoanInterestRate() public view returns (uint256) {
683
- return _poolLoanInterestRate;
630
+ function repayLoan(uint256 amount, address onBehalfOf) external override {
631
+ // Anyone can call this method, since it has to pay
632
+ currency().safeTransferFrom(_msgSender(), address(this), amount);
633
+ TimeScaled.ScaledAmount storage loan = _loans[onBehalfOf];
634
+ require(loan.scale != 0, "Not a registered borrower");
635
+ loan.sub(amount, internalLoanInterestRate());
636
+ _discreteChange(int256(amount));
637
+ emit InternalLoanRepaid(onBehalfOf, amount);
684
638
  }
685
639
 
686
- function acceptAllRMs() public view returns (bool) {
687
- return _acceptAllRMs;
640
+ function getLoan(address borrower) public view virtual override returns (uint256) {
641
+ TimeScaled.ScaledAmount storage loan = _loans[borrower];
642
+ return loan.getScaledAmount(internalLoanInterestRate());
688
643
  }
689
644
 
690
- function isAcceptException(address riskModule) public view returns (bool) {
691
- return _acceptExceptions[riskModule];
645
+ function internalLoanInterestRate() public view returns (uint256) {
646
+ return _4toWad(_params.internalLoanInterestRate);
692
647
  }
693
648
 
694
- function setPoolLoanInterestRate(uint256 newRate)
649
+ function setParam(Parameter param, uint256 newValue)
695
650
  external
696
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
651
+ onlyGlobalOrComponentRole2(LEVEL2_ROLE, LEVEL3_ROLE)
697
652
  {
698
653
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
699
- require(
700
- !tweak || _isTweakRay(_poolLoanInterestRate, newRate, 3e26),
701
- "Tweak exceeded: poolLoanInterestRate tweaks only up to 30%"
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
+ }
683
+ _parameterChanged(
684
+ IAccessManager.GovernanceActions(
685
+ uint256(IAccessManager.GovernanceActions.setLiquidityRequirement) + uint256(param)
686
+ ),
687
+ newValue,
688
+ tweak
702
689
  );
703
- _updatePoolLoanScale();
704
- _poolLoanInterestRate = newRate;
705
- _parameterChanged(IPolicyPoolConfig.GovernanceActions.setPoolLoanInterestRate, newRate, tweak);
706
690
  }
707
691
 
708
- function setLiquidityRequirement(uint256 newRate)
692
+ function setWhitelist(ILPWhitelist lpWhitelist_)
709
693
  external
710
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
694
+ onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
711
695
  {
712
- bool tweak = !hasPoolRole(LEVEL2_ROLE);
713
- require(
714
- !tweak || _isTweakRay(_liquidityRequirement, newRate, 1e26),
715
- "Tweak exceeded: liquidityRequirement tweaks only up to 10%"
716
- );
717
- _liquidityRequirement = newRate;
718
- _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityRequirement, newRate, tweak);
719
- }
720
-
721
- function setMaxUtilizationRate(uint256 newRate) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
722
- bool tweak = !hasPoolRole(LEVEL2_ROLE);
723
696
  require(
724
- !tweak || _isTweakRay(_maxUtilizationRate, newRate, 3e26),
725
- "Tweak exceeded: maxUtilizationRate tweaks only up to 30%"
726
- );
727
- _maxUtilizationRate = newRate;
728
- _parameterChanged(IPolicyPoolConfig.GovernanceActions.setMaxUtilizationRate, newRate, tweak);
729
- }
730
-
731
- function setAcceptAllRMs(bool acceptAllRMs_) external onlyPoolRole(LEVEL2_ROLE) {
732
- _acceptAllRMs = acceptAllRMs_;
733
- _parameterChanged(
734
- IPolicyPoolConfig.GovernanceActions.setAcceptAllRMs,
735
- acceptAllRMs_ ? 1 : 0,
736
- false
697
+ address(lpWhitelist_) == address(0) ||
698
+ IPolicyPoolComponent(address(lpWhitelist_)).policyPool() == _policyPool,
699
+ "Component not linked to this PolicyPool"
737
700
  );
701
+ _params.whitelist = lpWhitelist_;
702
+ _componentChanged(IAccessManager.GovernanceActions.setLPWhitelist, address(lpWhitelist_));
738
703
  }
739
704
 
740
- function setAcceptException(address riskModule, bool isException)
741
- external
742
- onlyPoolRole(LEVEL2_ROLE)
743
- {
744
- _acceptExceptions[riskModule] = isException;
745
- uint256 value = uint160(riskModule);
746
- if (!isException) value |= (1 << 255); // if changed to NOT exception activate first bit
747
- _parameterChanged(IPolicyPoolConfig.GovernanceActions.setAcceptException, value, false);
748
- }
749
-
750
- function getInvestable() public view virtual override returns (uint256) {
751
- return _scr + ocean() + getPoolLoan();
705
+ function whitelist() external view returns (ILPWhitelist) {
706
+ return _params.whitelist;
752
707
  }
753
708
  }