@ensuro/core 2.0.0-beta6 → 2.0.0-beta8
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/build/contracts/AccessManager.sol/AccessManager.json +74 -2
- package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +213 -0
- package/build/contracts/EToken.sol/EToken.json +114 -45
- package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
- package/build/contracts/LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json +197 -0
- package/build/contracts/Policy.sol/Policy.json +2 -2
- package/build/contracts/PolicyPool.sol/PolicyPool.json +324 -100
- package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +122 -2
- package/build/contracts/Reserve.sol/Reserve.json +71 -0
- package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +3 -3
- package/build/contracts/interfaces/IAccessManager.sol/IAccessManager.json +96 -0
- package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +103 -0
- package/build/contracts/interfaces/IEToken.sol/IEToken.json +13 -0
- package/build/contracts/interfaces/IPolicyPool.sol/IPolicyPool.json +6 -14
- package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +13 -0
- package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
- package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +7 -15
- package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
- package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +674 -0
- package/contracts/AccessManager.sol +39 -8
- package/contracts/ERC4626AssetManager.sol +49 -0
- package/contracts/EToken.sol +72 -52
- package/contracts/LiquidityThresholdAssetManager.sol +159 -0
- package/contracts/PolicyPool.sol +155 -135
- package/contracts/PolicyPoolComponent.sol +29 -19
- package/contracts/PremiumsAccount.sol +125 -62
- package/contracts/Reserve.sol +86 -2
- package/contracts/RiskModule.sol +17 -6
- package/contracts/TrustfulRiskModule.sol +17 -2
- package/contracts/interfaces/IAccessManager.sol +23 -5
- package/contracts/interfaces/IAssetManager.sol +22 -0
- package/contracts/interfaces/IEToken.sol +21 -1
- package/contracts/interfaces/IPolicyPool.sol +6 -11
- package/contracts/interfaces/IPremiumsAccount.sol +5 -0
- package/contracts/mocks/FixedRateVault.sol +85 -0
- package/contracts/mocks/PolicyPoolMock.sol +2 -5
- package/contracts/mocks/RiskModuleMock.sol +69 -0
- package/js/test-utils.js +35 -26
- package/package.json +1 -1
- package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -583
- package/build/contracts/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
- package/contracts/PolicyNFT.sol +0 -96
- package/contracts/interfaces/IPolicyNFT.sol +0 -15
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
5
|
+
import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
|
|
6
|
+
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
7
|
+
import {IMintableERC20} from "./IMintableERC20.sol";
|
|
8
|
+
import {TimeScaled} from "../TimeScaled.sol";
|
|
9
|
+
|
|
10
|
+
contract FixedRateVault is ERC4626 {
|
|
11
|
+
using TimeScaled for TimeScaled.ScaledAmount;
|
|
12
|
+
|
|
13
|
+
uint256 internal _interestRate;
|
|
14
|
+
TimeScaled.ScaledAmount internal _totalAssets;
|
|
15
|
+
bool internal _broken;
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
string memory name_,
|
|
19
|
+
string memory symbol_,
|
|
20
|
+
IERC20Metadata asset_,
|
|
21
|
+
uint256 interestRate_
|
|
22
|
+
) ERC20(name_, symbol_) ERC4626(asset_) {
|
|
23
|
+
_interestRate = interestRate_;
|
|
24
|
+
_totalAssets.init();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @dev See {IERC4262-totalAssets}. */
|
|
28
|
+
function totalAssets() public view virtual override returns (uint256) {
|
|
29
|
+
require(!_broken, "Vault is broken");
|
|
30
|
+
return _totalAssets.getScaledAmount(_interestRate);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @dev Deposit/mint common workflow.
|
|
35
|
+
*/
|
|
36
|
+
function _deposit(
|
|
37
|
+
address caller,
|
|
38
|
+
address receiver,
|
|
39
|
+
uint256 assets,
|
|
40
|
+
uint256 shares
|
|
41
|
+
) internal virtual override {
|
|
42
|
+
require(!_broken, "Vault is broken");
|
|
43
|
+
_totalAssets.add(assets, _interestRate);
|
|
44
|
+
super._deposit(caller, receiver, assets, shares);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @dev Withdraw/redeem common workflow.
|
|
49
|
+
*/
|
|
50
|
+
function _withdraw(
|
|
51
|
+
address caller,
|
|
52
|
+
address receiver,
|
|
53
|
+
address owner,
|
|
54
|
+
uint256 assets,
|
|
55
|
+
uint256 shares
|
|
56
|
+
) internal virtual override {
|
|
57
|
+
require(!_broken, "Vault is broken");
|
|
58
|
+
_totalAssets.sub(assets, _interestRate);
|
|
59
|
+
uint256 balance = IERC20Metadata(asset()).balanceOf(address(this));
|
|
60
|
+
if (balance < assets) {
|
|
61
|
+
// Here comes the magic! Free money!
|
|
62
|
+
IMintableERC20(asset()).mint(address(this), assets - balance);
|
|
63
|
+
}
|
|
64
|
+
super._withdraw(caller, receiver, owner, assets, shares);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* @dev Adds or remove assets not generated by deposits/withdraw - For testing discrete earnings/losses
|
|
69
|
+
*/
|
|
70
|
+
function discreteEarning(int256 assets) external {
|
|
71
|
+
if (assets > 0) {
|
|
72
|
+
_totalAssets.add(uint256(assets), _interestRate);
|
|
73
|
+
} else {
|
|
74
|
+
_totalAssets.sub(uint256(-assets), _interestRate);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function setBroken(bool broken_) external {
|
|
79
|
+
_broken = broken_;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function broken() external view returns (bool) {
|
|
83
|
+
return _broken;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -38,17 +38,14 @@ contract PolicyPoolMock is IPolicyPool {
|
|
|
38
38
|
return _access;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function policyNFT() external pure override returns (address) {
|
|
42
|
-
return address(0);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
41
|
function treasury() external pure override returns (address) {
|
|
46
42
|
return address(0);
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
function newPolicy(
|
|
50
46
|
Policy.PolicyData memory policy,
|
|
51
|
-
address, /*
|
|
47
|
+
address, /* caller */
|
|
48
|
+
address, /* policyHolder */
|
|
52
49
|
uint96 internalId
|
|
53
50
|
) external override returns (uint256) {
|
|
54
51
|
policy.id = (uint256(uint160(address(policy.riskModule))) << 96) + internalId;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
pragma solidity ^0.8.0;
|
|
4
|
+
|
|
5
|
+
import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
|
|
6
|
+
import {IPremiumsAccount} from "../interfaces/IPremiumsAccount.sol";
|
|
7
|
+
import {RiskModule} from "../RiskModule.sol";
|
|
8
|
+
import {Policy} from "../Policy.sol";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @title Trustful Risk Module
|
|
12
|
+
* @dev Risk Module without any validation, just the newPolicy and resolvePolicy need to be called by
|
|
13
|
+
authorized users
|
|
14
|
+
* @custom:security-contact security@ensuro.co
|
|
15
|
+
* @author Ensuro
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
contract RiskModuleMock is RiskModule {
|
|
19
|
+
bytes32 public constant PRICER_ROLE = keccak256("PRICER_ROLE");
|
|
20
|
+
bytes32 public constant RESOLVER_ROLE = keccak256("RESOLVER_ROLE");
|
|
21
|
+
|
|
22
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
23
|
+
// solhint-disable-next-line no-empty-blocks
|
|
24
|
+
constructor(IPolicyPool policyPool_, IPremiumsAccount premiumsAccount_)
|
|
25
|
+
RiskModule(policyPool_, premiumsAccount_)
|
|
26
|
+
{} // solhint-disable-line no-empty-blocks
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @dev Initializes the RiskModule
|
|
30
|
+
* @param name_ Name of the Risk Module
|
|
31
|
+
* @param collRatio_ Collateralization ratio to compute solvency requirement as % of payout (in ray)
|
|
32
|
+
* @param ensuroPpFee_ % of pure premium that will go for Ensuro treasury (in ray)
|
|
33
|
+
* @param srRoc_ return on capital paid to Senior LPs (annualized percentage - in ray)
|
|
34
|
+
* @param maxPayoutPerPolicy_ Maximum payout per policy (in wad)
|
|
35
|
+
* @param exposureLimit_ Max exposure (sum of payouts) to be allocated to this module (in wad)
|
|
36
|
+
* @param wallet_ Address of the RiskModule provider
|
|
37
|
+
*/
|
|
38
|
+
function initialize(
|
|
39
|
+
string memory name_,
|
|
40
|
+
uint256 collRatio_,
|
|
41
|
+
uint256 ensuroPpFee_,
|
|
42
|
+
uint256 srRoc_,
|
|
43
|
+
uint256 maxPayoutPerPolicy_,
|
|
44
|
+
uint256 exposureLimit_,
|
|
45
|
+
address wallet_
|
|
46
|
+
) public initializer {
|
|
47
|
+
__RiskModule_init(
|
|
48
|
+
name_,
|
|
49
|
+
collRatio_,
|
|
50
|
+
ensuroPpFee_,
|
|
51
|
+
srRoc_,
|
|
52
|
+
maxPayoutPerPolicy_,
|
|
53
|
+
exposureLimit_,
|
|
54
|
+
wallet_
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function newPolicy(
|
|
59
|
+
uint256 payout,
|
|
60
|
+
uint256 premium,
|
|
61
|
+
uint256 lossProb,
|
|
62
|
+
uint40 expiration,
|
|
63
|
+
address payer,
|
|
64
|
+
address onBehalfOf,
|
|
65
|
+
uint96 internalId
|
|
66
|
+
) external onlyComponentRole(PRICER_ROLE) returns (uint256) {
|
|
67
|
+
return _newPolicy(payout, premium, lossProb, expiration, payer, onBehalfOf, internalId).id;
|
|
68
|
+
}
|
|
69
|
+
}
|
package/js/test-utils.js
CHANGED
|
@@ -70,7 +70,7 @@ exports.addRiskModule = async function (
|
|
|
70
70
|
_W(scrInterestRate) || _W(0.1),
|
|
71
71
|
_A(maxScrPerPolicy) || _A(1000),
|
|
72
72
|
_A(scrLimit) || _A(1000000),
|
|
73
|
-
wallet || "
|
|
73
|
+
wallet || "0xdD2FD4581271e230360230F9337D5c0430Bf44C0", // Random address
|
|
74
74
|
...extraArgs,
|
|
75
75
|
],
|
|
76
76
|
{
|
|
@@ -86,7 +86,7 @@ exports.addRiskModule = async function (
|
|
|
86
86
|
moc = _W(moc);
|
|
87
87
|
await rm.setParam(0, moc);
|
|
88
88
|
}
|
|
89
|
-
await pool.
|
|
89
|
+
await pool.addComponent(rm.address, 2);
|
|
90
90
|
return rm;
|
|
91
91
|
};
|
|
92
92
|
|
|
@@ -114,7 +114,7 @@ exports.addEToken = async function (
|
|
|
114
114
|
);
|
|
115
115
|
|
|
116
116
|
await etk.deployed();
|
|
117
|
-
await pool.
|
|
117
|
+
await pool.addComponent(etk.address, 1);
|
|
118
118
|
return etk;
|
|
119
119
|
};
|
|
120
120
|
|
|
@@ -142,7 +142,7 @@ exports.impersonate = async function (address, setBalanceTo) {
|
|
|
142
142
|
* @param {String} eventName The name of the event we are interested in
|
|
143
143
|
* @returns {LogDescription}
|
|
144
144
|
*/
|
|
145
|
-
|
|
145
|
+
const getTransactionEvent = function (interface, receipt, eventName) {
|
|
146
146
|
// for each log in the transaction receipt
|
|
147
147
|
for (const log of receipt.events) {
|
|
148
148
|
let parsedLog;
|
|
@@ -158,22 +158,11 @@ exports.getTransactionEvent = function (interface, receipt, eventName) {
|
|
|
158
158
|
return null; // not found
|
|
159
159
|
};
|
|
160
160
|
|
|
161
|
+
exports.getTransactionEvent = getTransactionEvent;
|
|
162
|
+
|
|
161
163
|
exports.deployPool = async function (hre, options) {
|
|
162
164
|
const PolicyPool = await ethers.getContractFactory("PolicyPool");
|
|
163
165
|
const AccessManager = await ethers.getContractFactory("AccessManager");
|
|
164
|
-
const PolicyNFT = await ethers.getContractFactory("PolicyNFT");
|
|
165
|
-
|
|
166
|
-
// Deploy PolicyNFT
|
|
167
|
-
const policyNFT = await hre.upgrades.deployProxy(
|
|
168
|
-
PolicyNFT,
|
|
169
|
-
[
|
|
170
|
-
options.nftName || "Policy NFT",
|
|
171
|
-
options.nftSymbol || "EPOL",
|
|
172
|
-
options.policyPoolDetAddress || ethers.constants.AddressZero,
|
|
173
|
-
],
|
|
174
|
-
{ kind: "uups" }
|
|
175
|
-
);
|
|
176
|
-
await policyNFT.deployed();
|
|
177
166
|
|
|
178
167
|
// Deploy AccessManager
|
|
179
168
|
const accessManager = await hre.upgrades.deployProxy(AccessManager, [], { kind: "uups" });
|
|
@@ -182,9 +171,13 @@ exports.deployPool = async function (hre, options) {
|
|
|
182
171
|
|
|
183
172
|
const policyPool = await hre.upgrades.deployProxy(
|
|
184
173
|
PolicyPool,
|
|
185
|
-
[
|
|
174
|
+
[
|
|
175
|
+
options.nftName || "Policy NFT",
|
|
176
|
+
options.nftSymbol || "EPOL",
|
|
177
|
+
options.treasuryAddress || ethers.constants.AddressZero,
|
|
178
|
+
],
|
|
186
179
|
{
|
|
187
|
-
constructorArgs: [accessManager.address,
|
|
180
|
+
constructorArgs: [accessManager.address, options.currency],
|
|
188
181
|
kind: "uups",
|
|
189
182
|
unsafeAllow: ["delegatecall"],
|
|
190
183
|
}
|
|
@@ -215,7 +208,7 @@ exports.deployPremiumsAccount = async function (hre, pool, options) {
|
|
|
215
208
|
});
|
|
216
209
|
|
|
217
210
|
await premiumsAccount.deployed();
|
|
218
|
-
await pool.
|
|
211
|
+
await pool.addComponent(premiumsAccount.address, 3);
|
|
219
212
|
return premiumsAccount;
|
|
220
213
|
};
|
|
221
214
|
|
|
@@ -240,18 +233,18 @@ async function grantRole(hre, contract, role, user) {
|
|
|
240
233
|
|
|
241
234
|
exports.grantRole = grantRole;
|
|
242
235
|
|
|
243
|
-
async function grantComponentRole(hre,
|
|
236
|
+
async function grantComponentRole(hre, accessManager, component, role, user) {
|
|
244
237
|
let userAddress;
|
|
245
238
|
if (user === undefined) {
|
|
246
239
|
user = await _getDefaultSigner(hre);
|
|
247
240
|
userAddress = user.address;
|
|
248
241
|
} else {
|
|
249
|
-
userAddress = user;
|
|
242
|
+
userAddress = user.address === undefined ? user : user.address;
|
|
250
243
|
}
|
|
251
244
|
const roleHex = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(role));
|
|
252
|
-
const componentRole = await
|
|
253
|
-
if (!(await
|
|
254
|
-
await
|
|
245
|
+
const componentRole = await accessManager.getComponentRole(component.address, roleHex);
|
|
246
|
+
if (!(await accessManager.hasRole(componentRole, userAddress))) {
|
|
247
|
+
await accessManager.grantComponentRole(component.address, roleHex, userAddress);
|
|
255
248
|
}
|
|
256
249
|
}
|
|
257
250
|
|
|
@@ -301,7 +294,7 @@ Mimics the behaviour of the PolicyPoolConfig.getComponentRole method
|
|
|
301
294
|
Component roles are roles created doing XOR between the component
|
|
302
295
|
address and the original role.
|
|
303
296
|
|
|
304
|
-
Example:
|
|
297
|
+
Example:
|
|
305
298
|
getComponentRole("0xc6e7DF5E7b4f2A278906862b61205850344D4e7d", "ORACLE_ADMIN_ROLE")
|
|
306
299
|
// "0x05e01b185238b49f750d03d945e38a7f6c3be8b54de0ee42d481eb7814f0d3a8"
|
|
307
300
|
*/
|
|
@@ -332,3 +325,19 @@ function makePolicyId(rm, internalId) {
|
|
|
332
325
|
}
|
|
333
326
|
|
|
334
327
|
exports.makePolicyId = makePolicyId;
|
|
328
|
+
|
|
329
|
+
async function makePolicy(pool, rm, cust, payout, premium, lossProb, expiration, internalId) {
|
|
330
|
+
let tx = await rm.connect(cust).newPolicy(payout, premium, lossProb, expiration, cust.address, internalId);
|
|
331
|
+
let receipt = await tx.wait();
|
|
332
|
+
const newPolicyEvt = getTransactionEvent(pool.interface, receipt, "NewPolicy");
|
|
333
|
+
|
|
334
|
+
return newPolicyEvt;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
exports.makePolicy = makePolicy;
|
|
338
|
+
|
|
339
|
+
async function blockchainNow(owner) {
|
|
340
|
+
return (await owner.provider.getBlock("latest")).timestamp;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
exports.blockchainNow = blockchainNow;
|