@ensuro/core 1.2.7 → 2.0.0-beta3
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.
- package/README.md +50 -17
- package/build/contracts/EToken.sol/EToken.json +144 -173
- package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +101 -323
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +15 -2
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyNFT.sol/PolicyNFT.json +16 -3
- package/build/contracts/PolicyPool.sol/PolicyPool.json +105 -351
- package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +13 -0
- package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +77 -98
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +761 -0
- package/build/contracts/{BaseAssetManager.sol/BaseAssetManager.json → Reserve.sol/Reserve.json} +15 -202
- package/build/contracts/RiskModule.sol/RiskModule.json +83 -310
- package/build/contracts/TimeScaled.sol/TimeScaled.json +24 -0
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +131 -333
- package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +15 -2
- package/build/contracts/dependencies/ERC1967Proxy.sol/ERC1967Proxy.json +64 -0
- package/build/{interfaces/IExchange.sol/IExchange.json → contracts/dependencies/IERC20Metadata.sol/IERC20Metadata.json} +83 -79
- package/build/{interfaces → contracts/interfaces}/IEToken.sol/IEToken.json +32 -97
- package/build/{interfaces → contracts/interfaces}/ILPWhitelist.sol/ILPWhitelist.json +1 -1
- package/build/{interfaces → contracts/interfaces}/IPolicyHolder.sol/IPolicyHolder.json +1 -1
- package/build/{interfaces → contracts/interfaces}/IPolicyNFT.sol/IPolicyNFT.json +1 -1
- package/build/{interfaces → contracts/interfaces}/IPolicyPool.sol/IPolicyPool.json +121 -108
- package/build/{interfaces → contracts/interfaces}/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +1 -1
- package/build/{interfaces → contracts/interfaces}/IPolicyPoolConfig.sol/IPolicyPoolConfig.json +20 -49
- package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +302 -0
- package/build/{interfaces → contracts/interfaces}/IRiskModule.sol/IRiskModule.json +58 -34
- package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +43 -0
- package/build/{interfaces/IInsolvencyHook.sol/IInsolvencyHook.json → contracts/mocks/IMintableERC20.sol/IMintableERC20.json} +13 -8
- package/build/contracts/mocks/LinkTokenMock.sol/LinkTokenMock.json +370 -0
- package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +202 -0
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +35 -0
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +603 -0
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +92 -0
- package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +343 -0
- package/build/contracts/mocks/TestNFT.sol/TestNFT.json +388 -0
- package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +16 -3
- package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +16 -3
- package/contracts/EToken.sol +210 -267
- package/contracts/FlightDelayRiskModule.sol +30 -21
- package/contracts/LPManualWhitelist.sol +4 -4
- package/contracts/Policy.sol +48 -44
- package/contracts/PolicyNFT.sol +2 -2
- package/contracts/PolicyPool.sol +114 -467
- package/contracts/PolicyPoolComponent.sol +8 -3
- package/contracts/PolicyPoolConfig.sol +29 -91
- package/contracts/PremiumsAccount.sol +274 -0
- package/contracts/Reserve.sol +34 -0
- package/contracts/RiskModule.sol +216 -164
- package/contracts/TimeScaled.sol +134 -0
- package/contracts/TrustfulRiskModule.sol +23 -21
- package/contracts/dependencies/ERC1967Proxy.sol +19 -0
- package/contracts/dependencies/IERC20Metadata.sol +19 -0
- package/contracts/interfaces/IEToken.sol +172 -0
- package/{interfaces → contracts/interfaces}/ILPWhitelist.sol +0 -0
- package/{interfaces → contracts/interfaces}/IPolicyHolder.sol +0 -0
- package/{interfaces → contracts/interfaces}/IPolicyNFT.sol +0 -0
- package/contracts/interfaces/IPolicyPool.sol +138 -0
- package/{interfaces → contracts/interfaces}/IPolicyPoolComponent.sol +0 -0
- package/{interfaces → contracts/interfaces}/IPolicyPoolConfig.sol +29 -22
- package/contracts/interfaces/IPremiumsAccount.sol +71 -0
- package/contracts/interfaces/IRiskModule.sol +60 -0
- package/contracts/mocks/ForwardProxy.sol +66 -0
- package/contracts/mocks/IMintableERC20.sol +13 -0
- package/contracts/mocks/LinkTokenMock.sol +30 -0
- package/contracts/mocks/PolicyHolderMock.sol +68 -0
- package/contracts/mocks/PolicyPoolComponentMock.sol +17 -0
- package/contracts/mocks/PolicyPoolMock.sol +122 -0
- package/contracts/mocks/TestCurrency.sol +35 -0
- package/contracts/mocks/TestNFT.sol +23 -0
- package/contracts/upgraded/PolicyNFTv1.sol +1 -1
- package/js/test-utils.js +278 -0
- package/package.json +2 -2
- package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +0 -742
- package/build/contracts/DataTypes.sol/DataTypes.json +0 -10
- package/build/contracts/Exchange.sol/Exchange.json +0 -591
- package/build/contracts/extras/IPriceRiskModule.sol/IPriceRiskModule.json +0 -253
- package/build/contracts/extras/PriceRiskModule.sol/PriceRiskModule.json +0 -1055
- package/build/interfaces/IAssetManager.sol/IAssetManager.json +0 -31
- package/build/interfaces/IExchange.sol/IPriceOracle.json +0 -30
- package/contracts/AaveAssetManager.sol +0 -298
- package/contracts/BaseAssetManager.sol +0 -301
- package/contracts/DataTypes.sol +0 -287
- package/contracts/Exchange.sol +0 -189
- package/contracts/extras/IPriceRiskModule.sol +0 -41
- package/contracts/extras/PriceRiskModule.sol +0 -243
- package/interfaces/IAssetManager.sol +0 -23
- package/interfaces/IEToken.sol +0 -48
- package/interfaces/IExchange.sol +0 -59
- package/interfaces/IInsolvencyHook.sol +0 -27
- package/interfaces/IPolicyPool.sol +0 -44
- package/interfaces/IRiskModule.sol +0 -29
package/contracts/EToken.sol
CHANGED
|
@@ -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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Every operation that changes the utilization rate (_scr/totalSupply) or the
|
|
18
|
-
* first the
|
|
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
|
|
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
|
-
|
|
38
|
-
uint256 internal _scaleFactor; // in Ray
|
|
39
|
-
uint40 internal _lastScaleUpdate;
|
|
39
|
+
TimeScaled.ScaledAmount internal _tsScaled; // Total Supply scaled
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
uint256 internal _poolLoanInterestRate; // in Ray
|
|
49
|
-
uint256 internal _poolLoanScale; // in Ray
|
|
50
|
-
uint40 internal _poolLoanLastUpdate;
|
|
47
|
+
Scr internal _scr;
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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_)
|
|
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
|
|
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
|
|
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
|
|
97
|
+
uint256 internalLoanInterestRate_
|
|
107
98
|
) internal initializer {
|
|
108
99
|
_name = name_;
|
|
109
100
|
_symbol = symbol_;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
121
|
+
_params.liquidityRequirement >= 8e3 && _params.liquidityRequirement <= 13e3,
|
|
131
122
|
"Validation: liquidityRequirement must be [0.8, 1.3]"
|
|
132
123
|
);
|
|
133
124
|
require(
|
|
134
|
-
|
|
125
|
+
_params.maxUtilizationRate >= 5e3 && _params.maxUtilizationRate <= 1e4,
|
|
135
126
|
"Validation: maxUtilizationRate must be [0.5, 1]"
|
|
136
127
|
);
|
|
137
|
-
require(
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
427
|
-
|
|
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()
|
|
443
|
-
if (totalSupply_ == 0)
|
|
444
|
-
else
|
|
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
|
|
458
|
-
else return
|
|
433
|
+
if (updated) return _tsScaled.getScale(tokenInterestRate());
|
|
434
|
+
else return uint256(_tsScaled.scale);
|
|
459
435
|
}
|
|
460
436
|
|
|
461
|
-
function
|
|
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
|
|
443
|
+
function fundsAvailableToLock() public view returns (uint256) {
|
|
468
444
|
uint256 totalSupply_ = this.totalSupply();
|
|
469
|
-
if (totalSupply_ >
|
|
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
|
|
455
|
+
return uint256(_scr.interestRate);
|
|
479
456
|
}
|
|
480
457
|
|
|
481
458
|
function tokenInterestRate() public view override returns (uint256) {
|
|
482
|
-
return
|
|
459
|
+
return uint256(_scr.tokenInterestRate);
|
|
483
460
|
}
|
|
484
461
|
|
|
485
462
|
function liquidityRequirement() public view returns (uint256) {
|
|
486
|
-
return
|
|
463
|
+
return uint256(_params.liquidityRequirement) * 1e14; // 4 -> 18 decimals
|
|
487
464
|
}
|
|
488
465
|
|
|
489
466
|
function maxUtilizationRate() public view returns (uint256) {
|
|
490
|
-
return
|
|
467
|
+
return uint256(_params.maxUtilizationRate) * 1e14; // 4 -> 18 decimals
|
|
491
468
|
}
|
|
492
469
|
|
|
493
|
-
function
|
|
494
|
-
return
|
|
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())
|
|
475
|
+
return uint256(_scr.scr).wadDiv(this.totalSupply());
|
|
499
476
|
}
|
|
500
477
|
|
|
501
|
-
function lockScr(uint256
|
|
502
|
-
require(
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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(
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
{
|
|
522
|
-
require(scrAmount <= _scr, "Current SCR less than the amount you want to unlock");
|
|
523
|
-
|
|
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
|
-
|
|
509
|
+
if (uint256(_scr.scr) == scrAmount) {
|
|
510
|
+
_scr.scr = 0;
|
|
511
|
+
_scr.interestRate = 0;
|
|
528
512
|
} else {
|
|
529
|
-
uint256 origScr = _scr.
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
|
|
523
|
+
_discreteChange(adjustment);
|
|
536
524
|
}
|
|
537
525
|
|
|
538
|
-
function _discreteChange(
|
|
539
|
-
|
|
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(
|
|
564
|
-
|
|
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.
|
|
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
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
|
683
|
-
|
|
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
|
|
687
|
-
|
|
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
|
|
691
|
-
return
|
|
619
|
+
function internalLoanInterestRate() public view returns (uint256) {
|
|
620
|
+
return uint256(_params.internalLoanInterestRate) * 1e14; // to wad 4 -> 18 digits
|
|
692
621
|
}
|
|
693
622
|
|
|
694
|
-
function
|
|
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 ||
|
|
701
|
-
"Tweak exceeded:
|
|
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 ||
|
|
650
|
+
!tweak || _isTweakWad(liquidityRequirement(), newRate, 1e17),
|
|
715
651
|
"Tweak exceeded: liquidityRequirement tweaks only up to 10%"
|
|
716
652
|
);
|
|
717
|
-
|
|
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 ||
|
|
660
|
+
!tweak || _isTweakWad(maxUtilizationRate(), newRate, 3e17),
|
|
725
661
|
"Tweak exceeded: maxUtilizationRate tweaks only up to 30%"
|
|
726
662
|
);
|
|
727
|
-
|
|
663
|
+
_params.maxUtilizationRate = uint16(newRate / 1e14);
|
|
728
664
|
_parameterChanged(IPolicyPoolConfig.GovernanceActions.setMaxUtilizationRate, newRate, tweak);
|
|
729
665
|
}
|
|
730
666
|
|
|
731
|
-
function
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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
|
|
677
|
+
function setWhitelist(ILPWhitelist lpWhitelist_)
|
|
741
678
|
external
|
|
742
|
-
|
|
679
|
+
onlyPoolRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
|
|
743
680
|
{
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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
|
|
751
|
-
return
|
|
693
|
+
function whitelist() external view returns (ILPWhitelist) {
|
|
694
|
+
return _params.whitelist;
|
|
752
695
|
}
|
|
753
696
|
}
|