@ensuro/core 1.2.0

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 (55) hide show
  1. package/README.md +93 -0
  2. package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +772 -0
  3. package/build/contracts/BaseAssetManager.sol/BaseAssetManager.json +494 -0
  4. package/build/contracts/DataTypes.sol/DataTypes.json +10 -0
  5. package/build/contracts/EToken.sol/EToken.json +1148 -0
  6. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +1093 -0
  7. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +412 -0
  8. package/build/contracts/Policy.sol/Policy.json +10 -0
  9. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +570 -0
  10. package/build/contracts/PolicyPool.sol/PolicyPool.json +1257 -0
  11. package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +281 -0
  12. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +660 -0
  13. package/build/contracts/RiskModule.sol/RiskModule.json +748 -0
  14. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +1032 -0
  15. package/build/contracts/WadRayMath.sol/WadRayMath.json +10 -0
  16. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +50 -0
  17. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +322 -0
  18. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +571 -0
  19. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +602 -0
  20. package/build/interfaces/IAssetManager.sol/IAssetManager.json +31 -0
  21. package/build/interfaces/IEToken.sol/IEToken.json +491 -0
  22. package/build/interfaces/IInsolvencyHook.sol/IInsolvencyHook.json +42 -0
  23. package/build/interfaces/ILPWhitelist.sol/ILPWhitelist.json +74 -0
  24. package/build/interfaces/IPolicyNFT.sol/IPolicyNFT.json +321 -0
  25. package/build/interfaces/IPolicyPool.sol/IPolicyPool.json +445 -0
  26. package/build/interfaces/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +24 -0
  27. package/build/interfaces/IPolicyPoolConfig.sol/IPolicyPoolConfig.json +328 -0
  28. package/build/interfaces/IRiskModule.sol/IRiskModule.json +141 -0
  29. package/contracts/AaveAssetManager.sol +326 -0
  30. package/contracts/BaseAssetManager.sol +283 -0
  31. package/contracts/DataTypes.sol +287 -0
  32. package/contracts/EToken.sol +773 -0
  33. package/contracts/FlightDelayRiskModule.sol +208 -0
  34. package/contracts/LPManualWhitelist.sol +57 -0
  35. package/contracts/Policy.sol +104 -0
  36. package/contracts/PolicyNFT.sol +96 -0
  37. package/contracts/PolicyPool.sol +716 -0
  38. package/contracts/PolicyPoolComponent.sol +164 -0
  39. package/contracts/PolicyPoolConfig.sol +231 -0
  40. package/contracts/RiskModule.sol +323 -0
  41. package/contracts/TrustfulRiskModule.sol +81 -0
  42. package/contracts/WadRayMath.sol +135 -0
  43. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +371 -0
  44. package/contracts/upgraded/IPolicyNFTv1.sol +15 -0
  45. package/contracts/upgraded/PolicyNFTv1.sol +126 -0
  46. package/interfaces/IAssetManager.sol +23 -0
  47. package/interfaces/IEToken.sol +44 -0
  48. package/interfaces/IInsolvencyHook.sol +27 -0
  49. package/interfaces/ILPWhitelist.sol +23 -0
  50. package/interfaces/IPolicyNFT.sol +15 -0
  51. package/interfaces/IPolicyPool.sol +44 -0
  52. package/interfaces/IPolicyPoolComponent.sol +13 -0
  53. package/interfaces/IPolicyPoolConfig.sol +81 -0
  54. package/interfaces/IRiskModule.sol +29 -0
  55. package/package.json +30 -0
@@ -0,0 +1,773 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
5
+ import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
6
+ import {PolicyPoolComponent} from "./PolicyPoolComponent.sol";
7
+ import {IEToken} from "../interfaces/IEToken.sol";
8
+ import {IPolicyPoolConfig} from "../interfaces/IPolicyPoolConfig.sol";
9
+ import {IInsolvencyHook} from "../interfaces/IInsolvencyHook.sol";
10
+ import {WadRayMath} from "./WadRayMath.sol";
11
+
12
+ /**
13
+ * @title Ensuro ERC20 EToken - interest-bearing token
14
+ * @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.
19
+ * @custom:security-contact security@ensuro.co
20
+ * @author Ensuro
21
+ */
22
+ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
23
+ uint256 public constant MIN_SCALE = 1e17; // 0.0000000001 == 1e-10 in ray
24
+
25
+ using WadRayMath for uint256;
26
+
27
+ uint256 internal constant SECONDS_PER_YEAR = 365 days;
28
+
29
+ // Attributes taken from ERC20
30
+ mapping(address => uint256) private _balances;
31
+ mapping(address => mapping(address => uint256)) private _allowances;
32
+ uint256 private _totalSupply; // non-scaled totalSupply
33
+
34
+ string private _name;
35
+ string private _symbol;
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;
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
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;
51
+
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;
55
+
56
+ event PoolLoan(uint256 value);
57
+ event PoolLoanRepaid(uint256 value);
58
+
59
+ modifier onlyAssetManager() {
60
+ require(
61
+ _msgSender() == address(_policyPool.config().assetManager()),
62
+ "The caller must be the PolicyPool's AssetManager"
63
+ );
64
+ _;
65
+ }
66
+
67
+ modifier validateParamsAfterChange() {
68
+ _;
69
+ _validateParameters();
70
+ }
71
+
72
+ /// @custom:oz-upgrades-unsafe-allow constructor
73
+ // solhint-disable-next-line no-empty-blocks
74
+ constructor(IPolicyPool policyPool_) PolicyPoolComponent(policyPool_) {}
75
+
76
+ /**
77
+ * @dev Initializes the eToken
78
+ * @param expirationPeriod_ Maximum expirationPeriod (from block.timestamp) of policies to be accepted
79
+ * @param liquidityRequirement_ Liquidity requirement to allow withdrawal (in Ray - default=1 Ray)
80
+ * @param maxUtilizationRate_ Max utilization rate (scr/totalSupply) (in Ray - default=1 Ray)
81
+ * @param poolLoanInterestRate_ Rate of loans givencrto the policy pool (in Ray)
82
+ * @param name_ Name of the eToken
83
+ * @param symbol_ Symbol of the eToken
84
+ */
85
+ function initialize(
86
+ string memory name_,
87
+ string memory symbol_,
88
+ uint40 expirationPeriod_,
89
+ uint256 liquidityRequirement_,
90
+ uint256 maxUtilizationRate_,
91
+ uint256 poolLoanInterestRate_
92
+ ) public initializer {
93
+ __PolicyPoolComponent_init();
94
+ __EToken_init_unchained(
95
+ name_,
96
+ symbol_,
97
+ expirationPeriod_,
98
+ liquidityRequirement_,
99
+ maxUtilizationRate_,
100
+ poolLoanInterestRate_
101
+ );
102
+ }
103
+
104
+ // solhint-disable-next-line func-name-mixedcase
105
+ function __EToken_init_unchained(
106
+ string memory name_,
107
+ string memory symbol_,
108
+ uint40 expirationPeriod_,
109
+ uint256 liquidityRequirement_,
110
+ uint256 maxUtilizationRate_,
111
+ uint256 poolLoanInterestRate_
112
+ ) internal initializer {
113
+ _name = name_;
114
+ _symbol = symbol_;
115
+ _expirationPeriod = expirationPeriod_;
116
+ _scaleFactor = WadRayMath.ray();
117
+ _lastScaleUpdate = uint40(block.timestamp);
118
+ _scr = 0;
119
+ _scrInterestRate = 0;
120
+ _tokenInterestRate = 0;
121
+ _liquidityRequirement = liquidityRequirement_;
122
+ _maxUtilizationRate = maxUtilizationRate_;
123
+ _acceptAllRMs = true;
124
+
125
+ _poolLoan = 0;
126
+ _poolLoanInterestRate = poolLoanInterestRate_;
127
+ _poolLoanScale = WadRayMath.ray();
128
+ _poolLoanLastUpdate = uint40(block.timestamp);
129
+ _validateParameters();
130
+ }
131
+
132
+ // runs validation on EToken parameters
133
+ function _validateParameters() internal view {
134
+ require(
135
+ _liquidityRequirement >= 8e26 && _liquidityRequirement <= 13e26,
136
+ "Validation: liquidityRequirement must be [0.8, 1.3]"
137
+ );
138
+ require(
139
+ _maxUtilizationRate >= 5e26 && _maxUtilizationRate <= WadRayMath.RAY,
140
+ "Validation: maxUtilizationRate must be [0.5, 1]"
141
+ );
142
+ require(_poolLoanInterestRate <= 5e26, "Validation: poolLoanInterestRate must be <= 50%");
143
+ }
144
+
145
+ /*** BEGIN ERC20 methods - mainly copied from OpenZeppelin but changes in events and scaledAmount */
146
+
147
+ /**
148
+ * @dev Returns the name of the token.
149
+ */
150
+ function name() public view virtual override returns (string memory) {
151
+ return _name;
152
+ }
153
+
154
+ /**
155
+ * @dev Returns the symbol of the token, usually a shorter version of the
156
+ * name.
157
+ */
158
+ function symbol() public view virtual override returns (string memory) {
159
+ return _symbol;
160
+ }
161
+
162
+ /**
163
+ * @dev Returns the number of decimals used to get its user representation.
164
+ * For example, if `decimals` equals `2`, a balance of `505` tokens should
165
+ * be displayed to a user as `5,05` (`505 / 10 ** 2`).
166
+ *
167
+ * Tokens usually opt for a value of 18, imitating the relationship between
168
+ * Ether and Wei. This is the value {ERC20} uses, unless this function is
169
+ * overloaded;
170
+ *
171
+ * NOTE: This information is only used for _display_ purposes: it in
172
+ * no way affects any of the arithmetic of the contract, including
173
+ * {IERC20-balanceOf} and {IERC20-transfer}.
174
+ */
175
+ function decimals() public view virtual override returns (uint8) {
176
+ return _policyPool.currency().decimals();
177
+ }
178
+
179
+ /**
180
+ * @dev See {IERC20-totalSupply}.
181
+ */
182
+ function totalSupply() public view virtual override returns (uint256) {
183
+ return _totalSupply.wadToRay().rayMul(_calculateCurrentScale()).rayToWad();
184
+ }
185
+
186
+ /**
187
+ * @dev See {IERC20-balanceOf}.
188
+ */
189
+ function balanceOf(address account) public view virtual override returns (uint256) {
190
+ uint256 principalBalance = _balances[account];
191
+ if (principalBalance == 0) return 0;
192
+ return principalBalance.wadToRay().rayMul(_calculateCurrentScale()).rayToWad();
193
+ }
194
+
195
+ /**
196
+ * @dev See {IERC20-transfer}.
197
+ *
198
+ * Requirements:
199
+ *
200
+ * - `recipient` cannot be the zero address.
201
+ * - the caller must have a balance of at least `amount`.
202
+ */
203
+ function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
204
+ _transfer(_msgSender(), recipient, amount);
205
+ return true;
206
+ }
207
+
208
+ /**
209
+ * @dev See {IERC20-allowance}.
210
+ */
211
+ function allowance(address owner, address spender)
212
+ public
213
+ view
214
+ virtual
215
+ override
216
+ returns (uint256)
217
+ {
218
+ return _allowances[owner][spender];
219
+ }
220
+
221
+ /**
222
+ * @dev See {IERC20-approve}.
223
+ *
224
+ * Requirements:
225
+ *
226
+ * - `spender` cannot be the zero address.
227
+ */
228
+ function approve(address spender, uint256 amount) public virtual override returns (bool) {
229
+ _approve(_msgSender(), spender, amount);
230
+ return true;
231
+ }
232
+
233
+ /**
234
+ * @dev See {IERC20-transferFrom}.
235
+ *
236
+ * Emits an {Approval} event indicating the updated allowance. This is not
237
+ * required by the EIP. See the note at the beginning of {ERC20}.
238
+ *
239
+ * Requirements:
240
+ *
241
+ * - `sender` and `recipient` cannot be the zero address.
242
+ * - `sender` must have a balance of at least `amount`.
243
+ * - the caller must have allowance for ``sender``'s tokens of at least
244
+ * `amount`.
245
+ */
246
+ function transferFrom(
247
+ address sender,
248
+ address recipient,
249
+ uint256 amount
250
+ ) public virtual override returns (bool) {
251
+ _transfer(sender, recipient, amount);
252
+
253
+ uint256 currentAllowance = _allowances[sender][_msgSender()];
254
+ require(currentAllowance >= amount, "EToken: transfer amount exceeds allowance");
255
+ _approve(sender, _msgSender(), currentAllowance - amount);
256
+
257
+ return true;
258
+ }
259
+
260
+ /**
261
+ * @dev Atomically increases the allowance granted to `spender` by the caller.
262
+ *
263
+ * This is an alternative to {approve} that can be used as a mitigation for
264
+ * problems described in {IERC20-approve}.
265
+ *
266
+ * Emits an {Approval} event indicating the updated allowance.
267
+ *
268
+ * Requirements:
269
+ *
270
+ * - `spender` cannot be the zero address.
271
+ */
272
+ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
273
+ _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
274
+ return true;
275
+ }
276
+
277
+ /**
278
+ * @dev Atomically decreases the allowance granted to `spender` by the caller.
279
+ *
280
+ * This is an alternative to {approve} that can be used as a mitigation for
281
+ * problems described in {IERC20-approve}.
282
+ *
283
+ * Emits an {Approval} event indicating the updated allowance.
284
+ *
285
+ * Requirements:
286
+ *
287
+ * - `spender` cannot be the zero address.
288
+ * - `spender` must have allowance for the caller of at least
289
+ * `subtractedValue`.
290
+ */
291
+ function decreaseAllowance(address spender, uint256 subtractedValue)
292
+ public
293
+ virtual
294
+ returns (bool)
295
+ {
296
+ uint256 currentAllowance = _allowances[_msgSender()][spender];
297
+ require(currentAllowance >= subtractedValue, "EToken: decreased allowance below zero");
298
+ _approve(_msgSender(), spender, currentAllowance - subtractedValue);
299
+
300
+ return true;
301
+ }
302
+
303
+ function _scaleAmount(uint256 amount) internal view returns (uint256) {
304
+ return amount.wadToRay().rayDiv(_calculateCurrentScale()).rayToWad();
305
+ }
306
+
307
+ /**
308
+ * @dev Moves tokens `amount` from `sender` to `recipient`.
309
+ *
310
+ * This is internal function is equivalent to {transfer}, and can be used to
311
+ * e.g. implement automatic token fees, slashing mechanisms, etc.
312
+ *
313
+ * Emits a {Transfer} event.
314
+ *
315
+ * Requirements:
316
+ *
317
+ * - `sender` cannot be the zero address.
318
+ * - `recipient` cannot be the zero address.
319
+ * - `sender` must have a balance of at least `amount`.
320
+ */
321
+ function _transfer(
322
+ address sender,
323
+ address recipient,
324
+ uint256 amount
325
+ ) internal virtual {
326
+ require(sender != address(0), "EToken: transfer from the zero address");
327
+ require(recipient != address(0), "EToken: transfer to the zero address");
328
+
329
+ _beforeTokenTransfer(sender, recipient, amount);
330
+ uint256 scaledAmount = _scaleAmount(amount);
331
+
332
+ uint256 senderBalance = _balances[sender];
333
+ require(senderBalance >= scaledAmount, "EToken: transfer amount exceeds balance");
334
+ _balances[sender] = senderBalance - scaledAmount;
335
+ _balances[recipient] += scaledAmount;
336
+
337
+ emit Transfer(sender, recipient, amount);
338
+ }
339
+
340
+ /** @dev Creates `amount` tokens and assigns them to `account`, increasing
341
+ * the total supply.
342
+ *
343
+ * Emits a {Transfer} event with `from` set to the zero address.
344
+ *
345
+ * Requirements:
346
+ *
347
+ * - `to` cannot be the zero address.
348
+ */
349
+ function _mint(address account, uint256 amount) internal virtual {
350
+ require(account != address(0), "EToken: mint to the zero address");
351
+
352
+ _beforeTokenTransfer(address(0), account, amount);
353
+ uint256 scaledAmount = _scaleAmount(amount);
354
+
355
+ _totalSupply += scaledAmount;
356
+ _balances[account] += scaledAmount;
357
+ emit Transfer(address(0), account, amount);
358
+ }
359
+
360
+ /**
361
+ * @dev Destroys `amount` tokens from `account`, reducing the
362
+ * total supply.
363
+ *
364
+ * Emits a {Transfer} event with `to` set to the zero address.
365
+ *
366
+ * Requirements:
367
+ *
368
+ * - `account` cannot be the zero address.
369
+ * - `account` must have at least `amount` tokens.
370
+ */
371
+ function _burn(address account, uint256 amount) internal virtual {
372
+ require(account != address(0), "EToken: burn from the zero address");
373
+ _beforeTokenTransfer(account, address(0), amount);
374
+
375
+ uint256 scaledAmount = _scaleAmount(amount);
376
+ uint256 accountBalance = _balances[account];
377
+ require(accountBalance >= scaledAmount, "EToken: burn amount exceeds balance");
378
+ _balances[account] = accountBalance - scaledAmount;
379
+ _totalSupply -= scaledAmount;
380
+
381
+ emit Transfer(account, address(0), amount);
382
+ }
383
+
384
+ /**
385
+ * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
386
+ *
387
+ * This internal function is equivalent to `approve`, and can be used to
388
+ * e.g. set automatic allowances for certain subsystems, etc.
389
+ *
390
+ * Emits an {Approval} event.
391
+ *
392
+ * Requirements:
393
+ *
394
+ * - `owner` cannot be the zero address.
395
+ * - `spender` cannot be the zero address.
396
+ */
397
+ function _approve(
398
+ address owner,
399
+ address spender,
400
+ uint256 amount
401
+ ) internal virtual {
402
+ require(owner != address(0), "EToken: approve from the zero address");
403
+ require(spender != address(0), "EToken: approve to the zero address");
404
+
405
+ _allowances[owner][spender] = amount;
406
+ emit Approval(owner, spender, amount);
407
+ }
408
+
409
+ /**
410
+ * @dev Hook that is called before any transfer of tokens. This includes
411
+ * minting and burning.
412
+ *
413
+ * Calling conditions:
414
+ *
415
+ * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
416
+ * will be to transferred to `to`.
417
+ * - when `from` is zero, `amount` tokens will be minted for `to`.
418
+ * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
419
+ * - `from` and `to` are never both zero.
420
+ *
421
+ * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
422
+ */
423
+ function _beforeTokenTransfer(
424
+ address from,
425
+ address to,
426
+ uint256 amount
427
+ ) internal virtual {
428
+ require(
429
+ from == address(0) ||
430
+ to == address(0) ||
431
+ address(policyPool().config().lpWhitelist()) == address(0) ||
432
+ policyPool().config().lpWhitelist().acceptsTransfer(this, from, to, amount),
433
+ "Transfer not allowed - Liquidity Provider not whitelisted"
434
+ );
435
+ }
436
+
437
+ /*** END ERC20 methods - mainly copied from OpenZeppelin but changes in events and scaledAmount */
438
+
439
+ function _updateCurrentScale() internal {
440
+ if (uint40(block.timestamp) == _lastScaleUpdate) return;
441
+ _scaleFactor = _calculateCurrentScale();
442
+ require(_scaleFactor >= MIN_SCALE, "Scale too small, can lead to rounding errors");
443
+ _lastScaleUpdate = uint40(block.timestamp);
444
+ }
445
+
446
+ function _updateTokenInterestRate() internal {
447
+ uint256 totalSupply_ = this.totalSupply().wadToRay();
448
+ if (totalSupply_ == 0) _tokenInterestRate = 0;
449
+ else _tokenInterestRate = _scrInterestRate.rayMul(_scr.wadToRay()).rayDiv(totalSupply_);
450
+ }
451
+
452
+ function _calculateCurrentScale() internal view returns (uint256) {
453
+ if (uint40(block.timestamp) <= _lastScaleUpdate) return _scaleFactor;
454
+ uint256 timeDifference = block.timestamp - _lastScaleUpdate;
455
+ return
456
+ _scaleFactor.rayMul(
457
+ ((_tokenInterestRate * timeDifference) / SECONDS_PER_YEAR) + WadRayMath.ray()
458
+ );
459
+ }
460
+
461
+ function getCurrentScale(bool updated) public view returns (uint256) {
462
+ if (updated) return _calculateCurrentScale();
463
+ else return _scaleFactor;
464
+ }
465
+
466
+ function ocean() public view virtual override returns (uint256) {
467
+ uint256 totalSupply_ = this.totalSupply();
468
+ if (totalSupply_ > _scr) return totalSupply_ - _scr;
469
+ else return 0;
470
+ }
471
+
472
+ function oceanForNewScr() public view virtual override returns (uint256) {
473
+ uint256 totalSupply_ = this.totalSupply();
474
+ if (totalSupply_ > _scr) return (totalSupply_ - _scr).wadMul(_maxUtilizationRate.rayToWad());
475
+ else return 0;
476
+ }
477
+
478
+ function scr() public view virtual override returns (uint256) {
479
+ return _scr;
480
+ }
481
+
482
+ function scrInterestRate() public view returns (uint256) {
483
+ return _scrInterestRate;
484
+ }
485
+
486
+ function tokenInterestRate() public view returns (uint256) {
487
+ return _tokenInterestRate;
488
+ }
489
+
490
+ function liquidityRequirement() public view returns (uint256) {
491
+ return _liquidityRequirement;
492
+ }
493
+
494
+ function maxUtilizationRate() public view returns (uint256) {
495
+ return _maxUtilizationRate;
496
+ }
497
+
498
+ function expirationPeriod() public view returns (uint40) {
499
+ return _expirationPeriod;
500
+ }
501
+
502
+ function utilizationRate() public view returns (uint256) {
503
+ return _scr.wadDiv(this.totalSupply()).wadToRay();
504
+ }
505
+
506
+ function lockScr(uint256 policyInterestRate, uint256 scrAmount) external override onlyPolicyPool {
507
+ require(scrAmount <= this.ocean(), "Not enought OCEAN to cover the SCR");
508
+ _updateCurrentScale();
509
+ if (_scr == 0) {
510
+ _scr = scrAmount;
511
+ _scrInterestRate = policyInterestRate;
512
+ } else {
513
+ uint256 origScr = _scr.wadToRay();
514
+ _scr += scrAmount;
515
+ _scrInterestRate = (_scrInterestRate.rayMul(origScr) +
516
+ policyInterestRate.rayMul(scrAmount.wadToRay())).rayDiv(_scr.wadToRay());
517
+ }
518
+ emit SCRLocked(policyInterestRate, scrAmount);
519
+ _updateTokenInterestRate();
520
+ }
521
+
522
+ function unlockScr(uint256 policyInterestRate, uint256 scrAmount)
523
+ external
524
+ override
525
+ onlyPolicyPool
526
+ {
527
+ require(scrAmount <= _scr, "Current SCR less than the amount you want to unlock");
528
+ _updateCurrentScale();
529
+
530
+ if (_scr == scrAmount) {
531
+ _scr = 0;
532
+ _scrInterestRate = 0;
533
+ } else {
534
+ uint256 origScr = _scr.wadToRay();
535
+ _scr -= scrAmount;
536
+ _scrInterestRate = (_scrInterestRate.rayMul(origScr) -
537
+ policyInterestRate.rayMul(scrAmount.wadToRay())).rayDiv(_scr.wadToRay());
538
+ }
539
+ emit SCRUnlocked(policyInterestRate, scrAmount);
540
+ _updateTokenInterestRate();
541
+ }
542
+
543
+ function _discreteChange(uint256 amount, bool positive) internal {
544
+ uint256 newTotalSupply = positive ? (totalSupply() + amount) : (totalSupply() - amount);
545
+ _scaleFactor = newTotalSupply.wadToRay().rayDiv(_totalSupply.wadToRay());
546
+ require(_scaleFactor >= MIN_SCALE, "Scale too small, can lead to rounding errors");
547
+ _updateTokenInterestRate();
548
+ }
549
+
550
+ function discreteEarning(uint256 amount, bool positive) external override onlyPolicyPool {
551
+ _updateCurrentScale();
552
+ _discreteChange(amount, positive);
553
+ }
554
+
555
+ function assetEarnings(uint256 amount, bool positive) external override onlyAssetManager {
556
+ _updateCurrentScale();
557
+ _discreteChange(amount, positive);
558
+ }
559
+
560
+ function deposit(address provider, uint256 amount)
561
+ external
562
+ override
563
+ onlyPolicyPool
564
+ whenNotPaused
565
+ returns (uint256)
566
+ {
567
+ require(
568
+ address(policyPool().config().lpWhitelist()) == address(0) ||
569
+ policyPool().config().lpWhitelist().acceptsDeposit(this, provider, amount),
570
+ "Liquidity Provider not whitelisted"
571
+ );
572
+ _updateCurrentScale();
573
+ _mint(provider, amount);
574
+ _updateTokenInterestRate();
575
+ return balanceOf(provider);
576
+ }
577
+
578
+ function totalWithdrawable() public view virtual override returns (uint256) {
579
+ uint256 locked = _scr
580
+ .wadToRay()
581
+ .rayMul(WadRayMath.ray() + _scrInterestRate)
582
+ .rayMul(_liquidityRequirement)
583
+ .rayToWad();
584
+ uint256 totalSupply_ = totalSupply();
585
+ if (totalSupply_ >= locked) return totalSupply_ - locked;
586
+ else return 0;
587
+ }
588
+
589
+ function withdraw(address provider, uint256 amount)
590
+ external
591
+ override
592
+ onlyPolicyPool
593
+ whenNotPaused
594
+ returns (uint256)
595
+ {
596
+ _updateCurrentScale();
597
+ uint256 balance = balanceOf(provider);
598
+ if (balance == 0) return 0;
599
+ if (amount > balance) amount = balance;
600
+ uint256 withdrawable = totalWithdrawable();
601
+ if (amount > withdrawable) amount = withdrawable;
602
+ if (amount == 0) return 0;
603
+ _burn(provider, amount);
604
+ _updateTokenInterestRate();
605
+ return amount;
606
+ }
607
+
608
+ function accepts(address riskModule, uint40 policyExpiration)
609
+ public
610
+ view
611
+ virtual
612
+ override
613
+ returns (bool)
614
+ {
615
+ if (paused()) return false;
616
+ if (_acceptAllRMs == _acceptExceptions[riskModule]) {
617
+ // all accepted except this one or all rejected and this one is not an exception
618
+ return false;
619
+ }
620
+ return policyExpiration < (uint40(block.timestamp) + _expirationPeriod);
621
+ }
622
+
623
+ function _updatePoolLoanScale() internal {
624
+ if (uint40(block.timestamp) == _poolLoanLastUpdate) return;
625
+ _poolLoanScale = _getPoolLoanScale();
626
+ _poolLoanLastUpdate = uint40(block.timestamp);
627
+ }
628
+
629
+ function _maxNegativeAdjustment() internal view returns (uint256) {
630
+ uint256 ts = totalSupply();
631
+ uint256 minTs = _totalSupply.wadToRay().rayMul(MIN_SCALE * 10).rayToWad();
632
+ if (ts > minTs) return ts - minTs;
633
+ else return 0;
634
+ }
635
+
636
+ function lendToPool(uint256 amount, bool fromOcean)
637
+ external
638
+ override
639
+ onlyPolicyPool
640
+ returns (uint256)
641
+ {
642
+ if (fromOcean && amount > ocean()) amount = ocean();
643
+ if (!fromOcean && amount > totalSupply()) amount = totalSupply();
644
+ if (amount > _maxNegativeAdjustment()) {
645
+ amount = _maxNegativeAdjustment();
646
+ if (amount == 0) return amount;
647
+ }
648
+ if (_poolLoan == 0) {
649
+ _poolLoan = amount;
650
+ _poolLoanScale = WadRayMath.ray();
651
+ _poolLoanLastUpdate = uint40(block.timestamp);
652
+ } else {
653
+ _updatePoolLoanScale();
654
+ _poolLoan += amount.wadToRay().rayDiv(_poolLoanScale).rayToWad();
655
+ }
656
+ _updateCurrentScale(); // shouldn't do anything because lendToPool is after unlock_scr but doing anyway
657
+ _discreteChange(amount, false);
658
+ emit PoolLoan(amount);
659
+ if (!fromOcean && _scr > totalSupply()) {
660
+ // Notify insolvency_hook - Insuficient solvency
661
+ IInsolvencyHook hook = _policyPool.config().insolvencyHook();
662
+ if (address(hook) != address(0)) {
663
+ hook.insolventEToken(this, _scr - totalSupply());
664
+ }
665
+ }
666
+ return amount;
667
+ }
668
+
669
+ function repayPoolLoan(uint256 amount) external override onlyPolicyPool {
670
+ _updatePoolLoanScale();
671
+ _poolLoan = (getPoolLoan() - amount).wadToRay().rayDiv(_poolLoanScale).rayToWad();
672
+ _updateCurrentScale(); // shouldn't do anything because lendToPool is after unlock_scr but doing anyway
673
+ _discreteChange(amount, true);
674
+ emit PoolLoanRepaid(amount);
675
+ }
676
+
677
+ function _getPoolLoanScale() internal view returns (uint256) {
678
+ if (uint40(block.timestamp) <= _poolLoanLastUpdate) return _poolLoanScale;
679
+ uint256 timeDifference = block.timestamp - _poolLoanLastUpdate;
680
+ return
681
+ _poolLoanScale.rayMul(
682
+ ((_poolLoanInterestRate * timeDifference) / SECONDS_PER_YEAR) + WadRayMath.ray()
683
+ );
684
+ }
685
+
686
+ function getPoolLoan() public view virtual override returns (uint256) {
687
+ if (_poolLoan == 0) return 0;
688
+ return _poolLoan.wadToRay().rayMul(_getPoolLoanScale()).rayToWad();
689
+ }
690
+
691
+ function poolLoanInterestRate() public view returns (uint256) {
692
+ return _poolLoanInterestRate;
693
+ }
694
+
695
+ function acceptAllRMs() public view returns (bool) {
696
+ return _acceptAllRMs;
697
+ }
698
+
699
+ function isAcceptException(address riskModule) public view returns (bool) {
700
+ return _acceptExceptions[riskModule];
701
+ }
702
+
703
+ function setPoolLoanInterestRate(uint256 newRate)
704
+ external
705
+ onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
706
+ validateParamsAfterChange
707
+ {
708
+ bool tweak = !hasPoolRole(LEVEL2_ROLE);
709
+ require(
710
+ !tweak || _isTweakRay(_poolLoanInterestRate, newRate, 3e26),
711
+ "Tweak exceeded: poolLoanInterestRate tweaks only up to 30%"
712
+ );
713
+ _updatePoolLoanScale();
714
+ _poolLoanInterestRate = newRate;
715
+ _parameterChanged(IPolicyPoolConfig.GovernanceActions.setPoolLoanInterestRate, newRate, tweak);
716
+ }
717
+
718
+ function setLiquidityRequirement(uint256 newRate)
719
+ external
720
+ onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
721
+ validateParamsAfterChange
722
+ {
723
+ bool tweak = !hasPoolRole(LEVEL2_ROLE);
724
+ require(
725
+ !tweak || _isTweakRay(_liquidityRequirement, newRate, 1e26),
726
+ "Tweak exceeded: liquidityRequirement tweaks only up to 10%"
727
+ );
728
+ _liquidityRequirement = newRate;
729
+ _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityRequirement, newRate, tweak);
730
+ }
731
+
732
+ function setMaxUtilizationRate(uint256 newRate)
733
+ external
734
+ onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
735
+ validateParamsAfterChange
736
+ {
737
+ bool tweak = !hasPoolRole(LEVEL2_ROLE);
738
+ require(
739
+ !tweak || _isTweakRay(_maxUtilizationRate, newRate, 3e26),
740
+ "Tweak exceeded: maxUtilizationRate tweaks only up to 30%"
741
+ );
742
+ _maxUtilizationRate = newRate;
743
+ _parameterChanged(IPolicyPoolConfig.GovernanceActions.setMaxUtilizationRate, newRate, tweak);
744
+ }
745
+
746
+ function setAcceptAllRMs(bool acceptAllRMs_)
747
+ external
748
+ onlyPoolRole(LEVEL2_ROLE)
749
+ validateParamsAfterChange
750
+ {
751
+ _acceptAllRMs = acceptAllRMs_;
752
+ _parameterChanged(
753
+ IPolicyPoolConfig.GovernanceActions.setAcceptAllRMs,
754
+ acceptAllRMs_ ? 1 : 0,
755
+ false
756
+ );
757
+ }
758
+
759
+ function setAcceptException(address riskModule, bool isException)
760
+ external
761
+ onlyPoolRole(LEVEL2_ROLE)
762
+ validateParamsAfterChange
763
+ {
764
+ _acceptExceptions[riskModule] = isException;
765
+ uint256 value = uint160(riskModule);
766
+ if (!isException) value |= (1 << 255); // if changed to NOT exception activate first bit
767
+ _parameterChanged(IPolicyPoolConfig.GovernanceActions.setAcceptException, value, false);
768
+ }
769
+
770
+ function getInvestable() public view virtual override returns (uint256) {
771
+ return _scr + ocean() + getPoolLoan();
772
+ }
773
+ }