@ensuro/core 1.2.6 → 2.0.0-beta2

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