@ensuro/core 2.0.0-beta8 → 2.0.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.
- package/README.md +31 -14
- package/build/contracts/AccessManager.sol/AccessManager.json +7 -2
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +28 -3
- package/build/contracts/EToken.sol/EToken.json +21 -15
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +21 -2
- package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +26 -1
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +40 -2
- package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +19 -0
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +21 -15
- package/build/contracts/Reserve.sol/Reserve.json +19 -13
- package/build/contracts/RiskModule.sol/RiskModule.json +19 -0
- package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +1110 -0
- package/build/contracts/TimeScaled.sol/TimeScaled.json +3 -17
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +137 -2
- package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
- package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +26 -1
- package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +38 -0
- package/build/contracts/interfaces/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +19 -0
- package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +2 -2
- package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +2 -2
- package/build/contracts/mocks/InterfaceIdCalculator.sol/InterfaceIdCalculator.json +180 -0
- package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +28 -2
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +21 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +115 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +111 -2
- package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +2 -2
- package/build/contracts/mocks/TestNFT.sol/TestNFT.json +2 -2
- package/contracts/AccessManager.sol +31 -2
- package/contracts/ERC4626AssetManager.sol +8 -2
- package/contracts/EToken.sol +129 -60
- package/contracts/LPManualWhitelist.sol +18 -2
- package/contracts/LiquidityThresholdAssetManager.sol +28 -12
- package/contracts/Policy.sol +5 -3
- package/contracts/PolicyPool.sol +221 -29
- package/contracts/PolicyPoolComponent.sol +36 -25
- package/contracts/PremiumsAccount.sol +220 -32
- package/contracts/Reserve.sol +130 -19
- package/contracts/RiskModule.sol +53 -20
- package/contracts/SignedQuoteRiskModule.sol +310 -0
- package/contracts/TimeScaled.sol +19 -15
- package/contracts/TrustfulRiskModule.sol +107 -6
- package/contracts/dependencies/WadRayMath.sol +126 -0
- package/contracts/interfaces/IAccessManager.sol +68 -13
- package/contracts/interfaces/IAssetManager.sol +69 -2
- package/contracts/interfaces/IEToken.sol +4 -1
- package/contracts/interfaces/ILPWhitelist.sol +17 -0
- package/contracts/interfaces/IPolicyPool.sol +19 -1
- package/contracts/interfaces/IPolicyPoolComponent.sol +5 -1
- package/contracts/interfaces/IRiskModule.sol +67 -4
- package/contracts/mocks/InterfaceIdCalculator.sol +32 -0
- package/contracts/mocks/PolicyHolderMock.sol +21 -0
- package/contracts/mocks/PolicyPoolComponentMock.sol +10 -0
- package/contracts/mocks/PolicyPoolMock.sol +31 -0
- package/contracts/mocks/RiskModuleMock.sol +7 -1
- package/js/deploy.js +734 -0
- package/js/test-utils.js +184 -69
- package/package.json +4 -2
- package/scripts/change-pragma.sh +19 -0
- package/scripts/deploySmokeTest-fork.sh +53 -0
- package/scripts/deploySmokeTest.sh +51 -0
- package/scripts/storageLayout.js +33 -0
- package/scripts/utils.sh +65 -0
- package/contracts/WadRayMath.sol +0 -135
|
@@ -3,19 +3,20 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
4
|
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
5
5
|
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
6
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
6
7
|
import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
|
|
7
8
|
import {IPolicyPoolComponent} from "./interfaces/IPolicyPoolComponent.sol";
|
|
8
9
|
import {IAccessManager} from "./interfaces/IAccessManager.sol";
|
|
9
10
|
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
|
|
10
|
-
import {WadRayMath} from "./WadRayMath.sol";
|
|
11
|
+
import {WadRayMath} from "./dependencies/WadRayMath.sol";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @title Base class for PolicyPool components
|
|
14
15
|
* @dev This is the base class of all the components of the protocol that are linked to the PolicyPool and created
|
|
15
16
|
* after it.
|
|
16
17
|
* Holds the reference to _policyPool as immutable, also provides access to common admin roles:
|
|
17
|
-
* - LEVEL1_ROLE: High impact changes like upgrades or other critical operations
|
|
18
|
-
* - LEVEL2_ROLE: Mid-impact changes like
|
|
18
|
+
* - LEVEL1_ROLE: High impact changes like upgrades, adding or removing components or other critical operations
|
|
19
|
+
* - LEVEL2_ROLE: Mid-impact changes like changing some parameters
|
|
19
20
|
* - LEVEL3_ROLE: Low-impact changes like changing some parameters up to given percentage (tweaks)
|
|
20
21
|
* - GUARDIAN_ROLE: For emergency operations oriented to protect the protocol in case of attacks or hacking.
|
|
21
22
|
*
|
|
@@ -51,17 +52,17 @@ abstract contract PolicyPoolComponent is
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
modifier onlyComponentRole(bytes32 role) {
|
|
54
|
-
_policyPool.access().checkComponentRole(address(this), role,
|
|
55
|
+
_policyPool.access().checkComponentRole(address(this), role, _msgSender(), false);
|
|
55
56
|
_;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
modifier onlyGlobalOrComponentRole(bytes32 role) {
|
|
59
|
-
_policyPool.access().checkComponentRole(address(this), role,
|
|
60
|
+
_policyPool.access().checkComponentRole(address(this), role, _msgSender(), true);
|
|
60
61
|
_;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
modifier onlyGlobalOrComponentRole2(bytes32 role1, bytes32 role2) {
|
|
64
|
-
_policyPool.access().checkComponentRole2(address(this), role1, role2,
|
|
65
|
+
_policyPool.access().checkComponentRole2(address(this), role1, role2, _msgSender(), true);
|
|
65
66
|
_;
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -71,19 +72,24 @@ abstract contract PolicyPoolComponent is
|
|
|
71
72
|
bytes32 role3
|
|
72
73
|
) {
|
|
73
74
|
IAccessManager access = _policyPool.access();
|
|
74
|
-
if (!access.hasComponentRole(address(this), role1,
|
|
75
|
-
_policyPool.access().checkComponentRole2(address(this), role2, role3,
|
|
75
|
+
if (!access.hasComponentRole(address(this), role1, _msgSender(), true)) {
|
|
76
|
+
_policyPool.access().checkComponentRole2(address(this), role2, role3, _msgSender(), true);
|
|
76
77
|
}
|
|
77
78
|
_;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
81
82
|
constructor(IPolicyPool policyPool_) {
|
|
83
|
+
require(
|
|
84
|
+
address(policyPool_) != address(0),
|
|
85
|
+
"PolicyPoolComponent: policyPool cannot be zero address"
|
|
86
|
+
);
|
|
87
|
+
_disableInitializers();
|
|
82
88
|
_policyPool = policyPool_;
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
// solhint-disable-next-line func-name-mixedcase
|
|
86
|
-
function __PolicyPoolComponent_init() internal
|
|
92
|
+
function __PolicyPoolComponent_init() internal onlyInitializing {
|
|
87
93
|
__UUPSUpgradeable_init();
|
|
88
94
|
__Pausable_init();
|
|
89
95
|
}
|
|
@@ -94,12 +100,25 @@ abstract contract PolicyPoolComponent is
|
|
|
94
100
|
override
|
|
95
101
|
onlyGlobalOrComponentRole2(GUARDIAN_ROLE, LEVEL1_ROLE)
|
|
96
102
|
{
|
|
103
|
+
_upgradeValidations(newImpl);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function _upgradeValidations(address newImpl) internal view virtual {
|
|
97
107
|
require(
|
|
98
108
|
IPolicyPoolComponent(newImpl).policyPool() == _policyPool,
|
|
99
109
|
"Can't upgrade changing the PolicyPool!"
|
|
100
110
|
);
|
|
101
111
|
}
|
|
102
112
|
|
|
113
|
+
/**
|
|
114
|
+
* @dev See {IERC165-supportsInterface}.
|
|
115
|
+
*/
|
|
116
|
+
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
|
117
|
+
return
|
|
118
|
+
interfaceId == type(IERC165).interfaceId ||
|
|
119
|
+
interfaceId == type(IPolicyPoolComponent).interfaceId;
|
|
120
|
+
}
|
|
121
|
+
|
|
103
122
|
function pause() public onlyGlobalOrComponentRole(GUARDIAN_ROLE) {
|
|
104
123
|
_pause();
|
|
105
124
|
}
|
|
@@ -117,22 +136,7 @@ abstract contract PolicyPoolComponent is
|
|
|
117
136
|
}
|
|
118
137
|
|
|
119
138
|
function hasPoolRole(bytes32 role) internal view returns (bool) {
|
|
120
|
-
return _policyPool.access().hasComponentRole(address(this), role,
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function _isTweakRay(
|
|
124
|
-
uint256 oldValue,
|
|
125
|
-
uint256 newValue,
|
|
126
|
-
uint256 maxTweak
|
|
127
|
-
) internal pure returns (bool) {
|
|
128
|
-
if (oldValue == newValue) return true;
|
|
129
|
-
if (oldValue == 0) return maxTweak >= WadRayMath.RAY;
|
|
130
|
-
if (newValue == 0) return false;
|
|
131
|
-
if (oldValue < newValue) {
|
|
132
|
-
return (newValue.rayDiv(oldValue) - WadRayMath.RAY) <= maxTweak;
|
|
133
|
-
} else {
|
|
134
|
-
return (WadRayMath.RAY - newValue.rayDiv(oldValue)) <= maxTweak;
|
|
135
|
-
}
|
|
139
|
+
return _policyPool.access().hasComponentRole(address(this), role, _msgSender(), true);
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
function _isTweakWad(
|
|
@@ -186,4 +190,11 @@ abstract contract PolicyPoolComponent is
|
|
|
186
190
|
}
|
|
187
191
|
}
|
|
188
192
|
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
196
|
+
* variables without shifting down storage in the inheritance chain.
|
|
197
|
+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
198
|
+
*/
|
|
199
|
+
uint256[49] private __gap;
|
|
189
200
|
}
|
|
@@ -4,10 +4,12 @@ pragma solidity ^0.8.0;
|
|
|
4
4
|
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
|
5
5
|
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
6
6
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
7
|
-
import {
|
|
7
|
+
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
8
|
+
import {WadRayMath} from "./dependencies/WadRayMath.sol";
|
|
8
9
|
import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
|
|
9
10
|
import {IEToken} from "./interfaces/IEToken.sol";
|
|
10
11
|
import {Reserve} from "./Reserve.sol";
|
|
12
|
+
import {IAccessManager} from "./interfaces/IAccessManager.sol";
|
|
11
13
|
import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
|
|
12
14
|
import {Policy} from "./Policy.sol";
|
|
13
15
|
import {IEToken} from "./interfaces/IEToken.sol";
|
|
@@ -15,7 +17,14 @@ import {IAssetManager} from "./interfaces/IAssetManager.sol";
|
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* @title Ensuro Premiums Account
|
|
18
|
-
* @dev This contract holds the premiums of a set of risk modules
|
|
20
|
+
* @dev This contract holds the pure premiums of a set of risk modules. The pure premiums is the part of the premium
|
|
21
|
+
* that is expected to cover the losses. The contract keeps track of the pure premiums of the active policies
|
|
22
|
+
* (_activePurePremiums) and the surplus or deficit generated by the finalized policies (pure premiums collected -
|
|
23
|
+
* losses).
|
|
24
|
+
*
|
|
25
|
+
* Collaborates with a junior {EToken} and a senior {EToken} that act as lenders when the premiums aren't enough to
|
|
26
|
+
* cover the losses.
|
|
27
|
+
*
|
|
19
28
|
* @custom:security-contact security@ensuro.co
|
|
20
29
|
* @author Ensuro
|
|
21
30
|
*/
|
|
@@ -23,15 +32,37 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
23
32
|
using Policy for Policy.PolicyData;
|
|
24
33
|
using WadRayMath for uint256;
|
|
25
34
|
using SafeERC20 for IERC20Metadata;
|
|
35
|
+
using SafeCast for uint256;
|
|
26
36
|
|
|
27
37
|
bytes32 public constant WITHDRAW_WON_PREMIUMS_ROLE = keccak256("WITHDRAW_WON_PREMIUMS_ROLE");
|
|
38
|
+
uint256 internal constant FOUR_DECIMAL_TO_WAD = 1e14;
|
|
39
|
+
uint16 internal constant HUNDRED_PERCENT = 1e4;
|
|
28
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @dev The Junior eToken is the first {EToken} to which the PremiumsAccount will go for credit when it runs out of
|
|
43
|
+
* money. Optional (address(0)).
|
|
44
|
+
*/
|
|
29
45
|
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
|
30
46
|
IEToken internal immutable _juniorEtk;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @dev The Senior eToken is the second {EToken} to which the PremiumsAccount will go for credit, after trying before
|
|
50
|
+
* with the junior eToken, when it runs out of money. Optional (address(0)).
|
|
51
|
+
*/
|
|
31
52
|
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
|
32
53
|
IEToken internal immutable _seniorEtk;
|
|
33
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @dev The active pure premiums field keeps track of the pure premiums collected by the active policies of risk
|
|
57
|
+
* modules linked with this PremiumsAccount.
|
|
58
|
+
*/
|
|
34
59
|
uint256 internal _activePurePremiums; // sum of pure-premiums of active policies - In Wad
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @dev The surplus field keeps track of the surplus or deficit (when negative) of the actual payouts made by the
|
|
63
|
+
* PremiumsAccount versus the collected pure premiums. On the negative side, it has a limit defined by `_maxDeficit()`,
|
|
64
|
+
* after that limit, internal loans are taken from the eTokens.
|
|
65
|
+
*/
|
|
35
66
|
int256 internal _surplus;
|
|
36
67
|
|
|
37
68
|
struct PackedParams {
|
|
@@ -41,14 +72,22 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
41
72
|
|
|
42
73
|
PackedParams internal _params;
|
|
43
74
|
|
|
44
|
-
|
|
45
|
-
* Premiums can come in (for free, without liability) with receiveGrant.
|
|
75
|
+
/**
|
|
76
|
+
* Premiums can come in (for "free", without liability) with receiveGrant.
|
|
46
77
|
* And can come out (withdrawed to treasury) with withdrawWonPremiums
|
|
78
|
+
*
|
|
79
|
+
* @param moneyIn Indicates if money came in or out (false).
|
|
80
|
+
* @param value The amount of money received or given
|
|
47
81
|
*/
|
|
48
82
|
event WonPremiumsInOut(bool moneyIn, uint256 value);
|
|
49
83
|
|
|
84
|
+
/**
|
|
85
|
+
* @dev Constructor of the contract, sets the immutable fields.
|
|
86
|
+
*
|
|
87
|
+
* @param juniorEtk_ Address of the Junior EToken (first loss lender). `address(0)` if not present.
|
|
88
|
+
* @param seniorEtk_ Address of the Senior EToken (2nd loss lender). `address(0)` if not present.
|
|
89
|
+
*/
|
|
50
90
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
51
|
-
// solhint-disable-next-line no-empty-blocks
|
|
52
91
|
constructor(
|
|
53
92
|
IPolicyPool policyPool_,
|
|
54
93
|
IEToken juniorEtk_,
|
|
@@ -59,35 +98,54 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
59
98
|
}
|
|
60
99
|
|
|
61
100
|
/**
|
|
62
|
-
* @dev
|
|
101
|
+
* @dev Initializes the PremiumsAccount
|
|
63
102
|
*/
|
|
64
103
|
function initialize() public initializer {
|
|
65
104
|
__PremiumsAccount_init();
|
|
66
105
|
}
|
|
67
106
|
|
|
68
107
|
/**
|
|
69
|
-
* @dev Initializes the PremiumsAccount
|
|
108
|
+
* @dev Initializes the PremiumsAccount (to be called by subclasses)
|
|
70
109
|
*/
|
|
71
110
|
// solhint-disable-next-line func-name-mixedcase
|
|
72
|
-
function __PremiumsAccount_init() internal
|
|
73
|
-
|
|
111
|
+
function __PremiumsAccount_init() internal onlyInitializing {
|
|
112
|
+
__Reserve_init();
|
|
74
113
|
__PremiumsAccount_init_unchained();
|
|
75
114
|
}
|
|
76
115
|
|
|
77
116
|
// solhint-disable-next-line func-name-mixedcase
|
|
78
|
-
function __PremiumsAccount_init_unchained() internal
|
|
117
|
+
function __PremiumsAccount_init_unchained() internal onlyInitializing {
|
|
79
118
|
/*
|
|
80
119
|
_activePurePremiums = 0;
|
|
81
120
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
_params = PackedParams({deficitRatio: 1e4, assetManager: IAssetManager(address(0))});
|
|
121
|
+
_params = PackedParams({
|
|
122
|
+
deficitRatio: HUNDRED_PERCENT,
|
|
123
|
+
assetManager: IAssetManager(address(0))
|
|
124
|
+
});
|
|
88
125
|
_validateParameters();
|
|
89
126
|
}
|
|
90
127
|
|
|
128
|
+
function _upgradeValidations(address newImpl) internal view virtual override {
|
|
129
|
+
super._upgradeValidations(newImpl);
|
|
130
|
+
IPremiumsAccount newPA = IPremiumsAccount(newImpl);
|
|
131
|
+
require(
|
|
132
|
+
newPA.juniorEtk() == _juniorEtk || address(_juniorEtk) == address(0),
|
|
133
|
+
"Can't upgrade changing the Junior ETK unless to non-zero"
|
|
134
|
+
);
|
|
135
|
+
require(
|
|
136
|
+
newPA.seniorEtk() == _seniorEtk || address(_seniorEtk) == address(0),
|
|
137
|
+
"Can't upgrade changing the Senior ETK unless to non-zero"
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @dev See {IERC165-supportsInterface}.
|
|
143
|
+
*/
|
|
144
|
+
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
|
145
|
+
return
|
|
146
|
+
super.supportsInterface(interfaceId) || interfaceId == type(IPremiumsAccount).interfaceId;
|
|
147
|
+
}
|
|
148
|
+
|
|
91
149
|
function assetManager() public view override returns (IAssetManager) {
|
|
92
150
|
return _params.assetManager;
|
|
93
151
|
}
|
|
@@ -96,6 +154,14 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
96
154
|
_params.assetManager = newAM;
|
|
97
155
|
}
|
|
98
156
|
|
|
157
|
+
/**
|
|
158
|
+
* @dev This is called by the {Reserve} base class to record the earnings generated by the asset management.
|
|
159
|
+
*
|
|
160
|
+
* @param earningsOrLosses Indicates the amount earned since last time earnings where recorded.
|
|
161
|
+
* - If positive, repays the loans and accumulates the rest in the surplus.
|
|
162
|
+
* - If negative (losses) substracts it from surplus. It never can exceed _maxDeficit and doesn't takes
|
|
163
|
+
* loans to cover asset losses.
|
|
164
|
+
*/
|
|
99
165
|
function _assetEarnings(int256 earningsOrLosses) internal override {
|
|
100
166
|
if (earningsOrLosses > 0) {
|
|
101
167
|
uint256 earnings = uint256(earningsOrLosses);
|
|
@@ -103,14 +169,13 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
103
169
|
if (address(_juniorEtk) != address(0)) earnings = _repayLoan(earnings, _juniorEtk);
|
|
104
170
|
_storePurePremiumWon(earnings);
|
|
105
171
|
} else {
|
|
106
|
-
_payFromPremiums(uint256(-earningsOrLosses));
|
|
172
|
+
require(_payFromPremiums(uint256(-earningsOrLosses)) == 0, "Losses can't exceed maxDeficit");
|
|
107
173
|
}
|
|
108
174
|
}
|
|
109
175
|
|
|
110
|
-
// solhint-disable-next-line no-empty-blocks
|
|
111
176
|
function _validateParameters() internal view override {
|
|
112
177
|
require(
|
|
113
|
-
_params.deficitRatio <=
|
|
178
|
+
_params.deficitRatio <= HUNDRED_PERCENT && _params.deficitRatio >= 0,
|
|
114
179
|
"Validation: deficitRatio must be <= 1"
|
|
115
180
|
);
|
|
116
181
|
}
|
|
@@ -119,18 +184,35 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
119
184
|
return uint256(int256(_activePurePremiums) + _surplus);
|
|
120
185
|
}
|
|
121
186
|
|
|
187
|
+
/**
|
|
188
|
+
* @dev Returns the total amount of pure premiums that were collected by the active policies of the risk modules
|
|
189
|
+
* linked to this PremiumsAccount.
|
|
190
|
+
*/
|
|
122
191
|
function activePurePremiums() external view returns (uint256) {
|
|
123
192
|
return _activePurePremiums;
|
|
124
193
|
}
|
|
125
194
|
|
|
195
|
+
/**
|
|
196
|
+
* @dev Returns the surplus between pure premiums collected and payouts of finalized policies. Returns 0 if no surplus
|
|
197
|
+
* or deficit.
|
|
198
|
+
*/
|
|
126
199
|
function wonPurePremiums() external view returns (uint256) {
|
|
127
200
|
return _surplus >= 0 ? uint256(_surplus) : 0;
|
|
128
201
|
}
|
|
129
202
|
|
|
203
|
+
/**
|
|
204
|
+
* @dev Returns the amount of active pure premiums that was used to cover payouts of finalized policies (in excess of
|
|
205
|
+
* collected pure premiums). This is limited by `_maxDeficit()`
|
|
206
|
+
*/
|
|
130
207
|
function borrowedActivePP() external view returns (uint256) {
|
|
131
208
|
return _surplus >= 0 ? 0 : uint256(-_surplus);
|
|
132
209
|
}
|
|
133
210
|
|
|
211
|
+
/**
|
|
212
|
+
* @dev Returns the surplus between pure premiums collected and payouts of finalized policies. Losses where more than
|
|
213
|
+
* premiums collected, returns a negative number that indicates the amount of the active pure premiums that was used
|
|
214
|
+
* to cover finalized premiums.
|
|
215
|
+
*/
|
|
134
216
|
function surplus() external view returns (int256) {
|
|
135
217
|
return _surplus;
|
|
136
218
|
}
|
|
@@ -143,35 +225,83 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
143
225
|
return _juniorEtk;
|
|
144
226
|
}
|
|
145
227
|
|
|
228
|
+
/**
|
|
229
|
+
* @dev Returns the maximum deficit that's supported by the PremiumsAccount. If more money is needed, it must take
|
|
230
|
+
* loans from the eTokens. The value is calculated as a fraction of the active pure premiums. The fraction is
|
|
231
|
+
* regulated by the `deficitRatio` parameter that indicates the percentage of the active pure premiums that can be
|
|
232
|
+
* used to cover payouts of finalized policies. In many cases is fine to use the active pure premiums to cover the
|
|
233
|
+
* losses because in most cases the policies with payout are triggered long time before the policies without payout.
|
|
234
|
+
* But this also can be dangerous because it can be postponing the losses that should impact on liquidity providers.
|
|
235
|
+
*
|
|
236
|
+
* @param ratio The ratio used in the calculation of the deficit. It's the deficitRatio parameter (whether the current
|
|
237
|
+
* one or the new one when it's being modified).
|
|
238
|
+
*/
|
|
146
239
|
function _maxDeficit(uint256 ratio) internal view returns (int256) {
|
|
147
240
|
return -int256(_activePurePremiums.wadMul(ratio));
|
|
148
241
|
}
|
|
149
242
|
|
|
243
|
+
/**
|
|
244
|
+
* @dev Returns the percentage of the active pure premiums that can be used to cover losses of finalized policies.
|
|
245
|
+
*/
|
|
150
246
|
function deficitRatio() public view returns (uint256) {
|
|
151
|
-
return uint256(_params.deficitRatio) *
|
|
247
|
+
return uint256(_params.deficitRatio) * FOUR_DECIMAL_TO_WAD; // 4 -> 18 decimals
|
|
152
248
|
}
|
|
153
249
|
|
|
250
|
+
/**
|
|
251
|
+
* @dev Changes the `deficitRatio` parameter.
|
|
252
|
+
*
|
|
253
|
+
* Requirements:
|
|
254
|
+
* - onlyGlobalOrComponentRole(LEVEL2_ROLE)
|
|
255
|
+
*
|
|
256
|
+
* Events:
|
|
257
|
+
* - Emits GovernanceAction with action = setDeficitRatio or setDeficitRatioWithAdjustment if an adjustment was made.
|
|
258
|
+
*
|
|
259
|
+
* @param adjustment If true and the new ratio leaves `_surplus < -_maxDeficit()`, it adjusts the _surplus to the new
|
|
260
|
+
* `_maxDeficit()` and borrows the difference from the eTokens.
|
|
261
|
+
* If false and the new ratio leaves `_surplus < -_maxDeficit()`, the operation is reverted.
|
|
262
|
+
*/
|
|
154
263
|
function setDeficitRatio(uint256 newRatio, bool adjustment)
|
|
155
264
|
external
|
|
156
265
|
onlyComponentRole(LEVEL2_ROLE)
|
|
157
266
|
{
|
|
158
|
-
require(newRatio <= 1e18
|
|
267
|
+
require(newRatio <= 1e18, "Validation: deficitRatio must be <= 1");
|
|
268
|
+
|
|
269
|
+
uint16 truncatedRatio = (newRatio / FOUR_DECIMAL_TO_WAD).toUint16();
|
|
270
|
+
require(
|
|
271
|
+
uint256(truncatedRatio) * FOUR_DECIMAL_TO_WAD == newRatio,
|
|
272
|
+
"Validation: only up to 4 decimals allowed"
|
|
273
|
+
);
|
|
274
|
+
|
|
159
275
|
int256 maxDeficit = _maxDeficit(newRatio);
|
|
160
276
|
require(adjustment || _surplus >= maxDeficit, "Validation: surplus must be >= maxDeficit");
|
|
277
|
+
|
|
278
|
+
IAccessManager.GovernanceActions action = IAccessManager.GovernanceActions.setDeficitRatio;
|
|
161
279
|
if (_surplus < maxDeficit) {
|
|
162
280
|
// Do the adjustment
|
|
163
281
|
uint256 borrow = uint256(-_surplus + maxDeficit);
|
|
164
282
|
_surplus = maxDeficit;
|
|
165
283
|
_borrowFromEtk(borrow, address(this), address(_juniorEtk) != address(0));
|
|
284
|
+
action = IAccessManager.GovernanceActions.setDeficitRatioWithAdjustment;
|
|
166
285
|
}
|
|
167
|
-
_params.deficitRatio =
|
|
286
|
+
_params.deficitRatio = truncatedRatio;
|
|
287
|
+
_parameterChanged(action, newRatio, false);
|
|
168
288
|
}
|
|
169
289
|
|
|
290
|
+
/**
|
|
291
|
+
* @dev Internal function called when money in the PremiumsAccount is not enough and we need to borrow from the
|
|
292
|
+
* eTokens.
|
|
293
|
+
*
|
|
294
|
+
* @param borrow The amount to borrow.
|
|
295
|
+
* @param receiver The address that will receive the money of the loan. Usually is the policy holder if this is called
|
|
296
|
+
* in the context of a policy payout.
|
|
297
|
+
* @param jrEtk If true it indicates that the loan is asked first from the junior eToken.
|
|
298
|
+
*/
|
|
170
299
|
function _borrowFromEtk(
|
|
171
300
|
uint256 borrow,
|
|
172
301
|
address receiver,
|
|
173
302
|
bool jrEtk
|
|
174
303
|
) internal {
|
|
304
|
+
require(receiver != address(0), "PremiumsAccount: receiver cannot be the zero address");
|
|
175
305
|
uint256 left;
|
|
176
306
|
if (jrEtk) {
|
|
177
307
|
// Consume Junior Pool until exhausted
|
|
@@ -186,6 +316,13 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
186
316
|
}
|
|
187
317
|
}
|
|
188
318
|
|
|
319
|
+
/**
|
|
320
|
+
* @dev Updates the `_surplus` field with the payment made. Since the _surplus can never exceed `_maxDeficit()`,
|
|
321
|
+
* returns the remaining amount in case something can't be paid from the PremiumsAccount.
|
|
322
|
+
*
|
|
323
|
+
* @param toPay The amount to pay.
|
|
324
|
+
* @return The amount that couldn't be paid from the premiums account.
|
|
325
|
+
*/
|
|
189
326
|
function _payFromPremiums(uint256 toPay) internal returns (uint256) {
|
|
190
327
|
int256 newSurplus = _surplus - int256(toPay);
|
|
191
328
|
int256 maxDeficit = _maxDeficit(deficitRatio());
|
|
@@ -197,44 +334,61 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
197
334
|
return uint256(-newSurplus + maxDeficit);
|
|
198
335
|
}
|
|
199
336
|
|
|
337
|
+
/**
|
|
338
|
+
* @dev Stores an earned pure premium. Adds to the surplus, increasing the surplus if it was positive or reducing the
|
|
339
|
+
* deficit if it was negative.
|
|
340
|
+
*
|
|
341
|
+
* @param purePremiumWon The amount earned
|
|
342
|
+
*/
|
|
200
343
|
function _storePurePremiumWon(uint256 purePremiumWon) internal {
|
|
201
344
|
if (purePremiumWon == 0) return;
|
|
202
345
|
_surplus += int256(purePremiumWon);
|
|
203
346
|
}
|
|
204
347
|
|
|
205
|
-
// TODO: restore repayETokenLoan?
|
|
206
|
-
|
|
207
348
|
/**
|
|
208
349
|
*
|
|
209
350
|
* Endpoint to receive "free money" and inject that money into the premium pool.
|
|
210
351
|
*
|
|
211
352
|
* Can be used for example if the PolicyPool subscribes an excess loss policy with other company.
|
|
212
353
|
*
|
|
354
|
+
* Requirements:
|
|
355
|
+
* - The sender needs to approve the spending of `currency()` by this contract.
|
|
356
|
+
*
|
|
357
|
+
* Events:
|
|
358
|
+
* - Emits {WonPremiumsInOut} with moneyIn = true
|
|
359
|
+
*
|
|
360
|
+
* @param amount The amount to be transferred.
|
|
213
361
|
*/
|
|
214
362
|
function receiveGrant(uint256 amount) external {
|
|
215
|
-
currency().safeTransferFrom(msg.sender, address(this), amount);
|
|
216
363
|
_storePurePremiumWon(amount);
|
|
217
364
|
emit WonPremiumsInOut(true, amount);
|
|
365
|
+
currency().safeTransferFrom(_msgSender(), address(this), amount);
|
|
218
366
|
}
|
|
219
367
|
|
|
220
368
|
/**
|
|
221
369
|
*
|
|
222
|
-
* Withdraws excess premiums to
|
|
370
|
+
* Withdraws excess premiums (surplus) to the destination.
|
|
371
|
+
*
|
|
223
372
|
* This might be needed in some cases for example if we are deprecating the protocol or the excess premiums
|
|
224
373
|
* are needed to compensate something. Shouldn't be used. Can be disabled revoking role WITHDRAW_WON_PREMIUMS_ROLE
|
|
225
374
|
*
|
|
226
|
-
* returns The amount withdrawed
|
|
227
|
-
*
|
|
228
375
|
* Requirements:
|
|
229
|
-
*
|
|
230
376
|
* - onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
|
|
231
|
-
* -
|
|
377
|
+
* - _surplus > 0
|
|
378
|
+
*
|
|
379
|
+
* Events:
|
|
380
|
+
* - Emits {WonPremiumsInOut} with moneyIn = false
|
|
381
|
+
*
|
|
382
|
+
* @param amount The amount to withdraw
|
|
383
|
+
* @param destination The address that will receive the transferred funds.
|
|
384
|
+
* @return Returns the actual amount withdrawn.
|
|
232
385
|
*/
|
|
233
386
|
function withdrawWonPremiums(uint256 amount, address destination)
|
|
234
387
|
external
|
|
235
388
|
onlyGlobalOrComponentRole(WITHDRAW_WON_PREMIUMS_ROLE)
|
|
236
389
|
returns (uint256)
|
|
237
390
|
{
|
|
391
|
+
require(destination != address(0), "PremiumsAccount: destination cannot be the zero address");
|
|
238
392
|
if (_surplus <= 0) {
|
|
239
393
|
amount = 0;
|
|
240
394
|
} else {
|
|
@@ -283,6 +437,11 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
283
437
|
}
|
|
284
438
|
}
|
|
285
439
|
|
|
440
|
+
/**
|
|
441
|
+
* @dev Internal function that calls the eTokens to lock the solvency capital when the policy is created.
|
|
442
|
+
*
|
|
443
|
+
* @param policy The policy created
|
|
444
|
+
*/
|
|
286
445
|
function _unlockScr(Policy.PolicyData memory policy) internal {
|
|
287
446
|
if (policy.jrScr > 0) {
|
|
288
447
|
_juniorEtk.unlockScr(
|
|
@@ -300,12 +459,34 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
300
459
|
}
|
|
301
460
|
}
|
|
302
461
|
|
|
462
|
+
/**
|
|
463
|
+
* @dev Internal function that repays a loan taken (if any outstanding) from the an eToken
|
|
464
|
+
*
|
|
465
|
+
* @param purePremiumWon The amount earned and available for loan repayment.
|
|
466
|
+
* @param etk The eToken with the potential debt
|
|
467
|
+
* @return The excess amount of the purePremiumWon that wasn't used for the loan repayment.
|
|
468
|
+
*/
|
|
303
469
|
function _repayLoan(uint256 purePremiumWon, IEToken etk) internal returns (uint256) {
|
|
304
470
|
if (purePremiumWon < NEGLIGIBLE_AMOUNT) return purePremiumWon;
|
|
305
471
|
uint256 borrowedFromEtk = etk.getLoan(address(this));
|
|
306
472
|
if (borrowedFromEtk == 0) return purePremiumWon;
|
|
307
|
-
uint256 repayAmount =
|
|
308
|
-
|
|
473
|
+
uint256 repayAmount = Math.min(purePremiumWon, borrowedFromEtk);
|
|
474
|
+
|
|
475
|
+
// If not enough liquidity, it deinvests from the asset manager
|
|
476
|
+
if (currency().balanceOf(address(this)) < repayAmount) {
|
|
477
|
+
/**
|
|
478
|
+
* I send `repayAmount` because the IAssetManager expects the full amount that's needed, not the missing one.
|
|
479
|
+
* It uses the value of the full amount to optimize the deinvestment leaving more liquidity if possible to avoid
|
|
480
|
+
* future deinvestment. It will only fail if it can't refill `repayAmount - currency().balanceOf(address(this))`
|
|
481
|
+
*/
|
|
482
|
+
_refillWallet(repayAmount);
|
|
483
|
+
}
|
|
484
|
+
// Checks the allowance before repayment
|
|
485
|
+
if (currency().allowance(address(this), address(etk)) < repayAmount) {
|
|
486
|
+
// If I have to approve, I approve for all the pending debt (not just repayAmount), this way I avoid some
|
|
487
|
+
// future approvals.
|
|
488
|
+
currency().approve(address(etk), borrowedFromEtk);
|
|
489
|
+
}
|
|
309
490
|
etk.repayLoan(repayAmount, address(this));
|
|
310
491
|
return purePremiumWon - repayAmount;
|
|
311
492
|
}
|
|
@@ -334,4 +515,11 @@ contract PremiumsAccount is IPremiumsAccount, Reserve {
|
|
|
334
515
|
_storePurePremiumWon(purePremiumWon);
|
|
335
516
|
_unlockScr(policy);
|
|
336
517
|
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
521
|
+
* variables without shifting down storage in the inheritance chain.
|
|
522
|
+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
523
|
+
*/
|
|
524
|
+
uint256[47] private __gap;
|
|
337
525
|
}
|