@ensuro/core 1.2.7 → 2.0.0-beta1

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