@ensuro/core 2.0.0-beta5 → 2.0.0-beta7
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 +219 -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 +203 -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 +109 -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 +50 -0
- package/contracts/EToken.sol +72 -52
- package/contracts/LiquidityThresholdAssetManager.sol +158 -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 +38 -31
- 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,43 +158,34 @@ 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
|
-
const accessManager = await hre.upgrades.deployProxy(AccessManager, [
|
|
180
|
-
options.policyPoolDetAddress || ethers.constants.AddressZero,
|
|
181
|
-
], {kind: 'uups'});
|
|
168
|
+
const accessManager = await hre.upgrades.deployProxy(AccessManager, [], { kind: "uups" });
|
|
182
169
|
|
|
183
170
|
await accessManager.deployed();
|
|
184
171
|
|
|
185
172
|
const policyPool = await hre.upgrades.deployProxy(
|
|
186
173
|
PolicyPool,
|
|
187
|
-
[
|
|
174
|
+
[
|
|
175
|
+
options.nftName || "Policy NFT",
|
|
176
|
+
options.nftSymbol || "EPOL",
|
|
177
|
+
options.treasuryAddress || ethers.constants.AddressZero,
|
|
178
|
+
],
|
|
188
179
|
{
|
|
189
|
-
constructorArgs: [accessManager.address,
|
|
190
|
-
kind:
|
|
180
|
+
constructorArgs: [accessManager.address, options.currency],
|
|
181
|
+
kind: "uups",
|
|
191
182
|
unsafeAllow: ["delegatecall"],
|
|
192
183
|
}
|
|
193
184
|
);
|
|
194
185
|
|
|
195
186
|
await policyPool.deployed();
|
|
196
187
|
|
|
197
|
-
for (const role of
|
|
188
|
+
for (const role of options.grantRoles || []) {
|
|
198
189
|
await grantRole(hre, accessManager, role);
|
|
199
190
|
}
|
|
200
191
|
|
|
@@ -217,7 +208,7 @@ exports.deployPremiumsAccount = async function (hre, pool, options) {
|
|
|
217
208
|
});
|
|
218
209
|
|
|
219
210
|
await premiumsAccount.deployed();
|
|
220
|
-
await pool.
|
|
211
|
+
await pool.addComponent(premiumsAccount.address, 3);
|
|
221
212
|
return premiumsAccount;
|
|
222
213
|
};
|
|
223
214
|
|
|
@@ -242,18 +233,18 @@ async function grantRole(hre, contract, role, user) {
|
|
|
242
233
|
|
|
243
234
|
exports.grantRole = grantRole;
|
|
244
235
|
|
|
245
|
-
async function grantComponentRole(hre,
|
|
236
|
+
async function grantComponentRole(hre, accessManager, component, role, user) {
|
|
246
237
|
let userAddress;
|
|
247
238
|
if (user === undefined) {
|
|
248
239
|
user = await _getDefaultSigner(hre);
|
|
249
240
|
userAddress = user.address;
|
|
250
241
|
} else {
|
|
251
|
-
userAddress = user;
|
|
242
|
+
userAddress = user.address === undefined ? user : user.address;
|
|
252
243
|
}
|
|
253
244
|
const roleHex = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(role));
|
|
254
|
-
const componentRole = await
|
|
255
|
-
if (!(await
|
|
256
|
-
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);
|
|
257
248
|
}
|
|
258
249
|
}
|
|
259
250
|
|
|
@@ -303,7 +294,7 @@ Mimics the behaviour of the PolicyPoolConfig.getComponentRole method
|
|
|
303
294
|
Component roles are roles created doing XOR between the component
|
|
304
295
|
address and the original role.
|
|
305
296
|
|
|
306
|
-
Example:
|
|
297
|
+
Example:
|
|
307
298
|
getComponentRole("0xc6e7DF5E7b4f2A278906862b61205850344D4e7d", "ORACLE_ADMIN_ROLE")
|
|
308
299
|
// "0x05e01b185238b49f750d03d945e38a7f6c3be8b54de0ee42d481eb7814f0d3a8"
|
|
309
300
|
*/
|
|
@@ -334,3 +325,19 @@ function makePolicyId(rm, internalId) {
|
|
|
334
325
|
}
|
|
335
326
|
|
|
336
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;
|