@ensuro/core 2.6.1 → 2.7.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 (26) hide show
  1. package/build/contracts/AccessManager.sol/AccessManager.json +2 -2
  2. package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +2 -2
  3. package/build/contracts/EToken.sol/EToken.json +2 -2
  4. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
  5. package/build/contracts/PolicyPool.sol/PolicyPool.json +15 -2
  6. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +2 -2
  7. package/build/contracts/SignedBucketRiskModule.sol/SignedBucketRiskModule.json +2 -2
  8. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +2 -2
  9. package/build/contracts/TieredSignedQuoteRiskModule.sol/TieredSignedQuoteRiskModule.json +2 -2
  10. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +2 -2
  11. package/build/contracts/migration/LPManualWhitelistV20Upgraded.sol/LPManualWhitelistV20Upgraded.json +2 -2
  12. package/build/contracts/mocks/InterfaceIdCalculator.sol/InterfaceIdCalculator.json +2 -2
  13. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +102 -11
  14. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +2 -2
  15. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +2 -2
  16. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +2 -2
  17. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +2 -2
  18. package/contracts/PolicyPool.sol +57 -29
  19. package/contracts/interfaces/IAccessManager.sol +1 -1
  20. package/contracts/mocks/PolicyHolderMock.sol +78 -24
  21. package/js/constants.js +8 -2
  22. package/js/deploy.js +24 -26
  23. package/js/enums.js +26 -8
  24. package/js/test-utils.js +78 -7
  25. package/js/utils.js +19 -2
  26. package/package.json +1 -1
@@ -3,18 +3,36 @@ pragma solidity ^0.8.0;
3
3
 
4
4
  import {IPolicyHolder} from "../interfaces/IPolicyHolder.sol";
5
5
  import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
6
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
6
7
 
7
8
  contract PolicyHolderMock is IPolicyHolder {
8
9
  uint256 public policyId;
9
10
  uint256 public payout;
10
11
  bool public fail;
12
+ bool public emptyRevert;
11
13
  bool public notImplemented;
14
+ bool public badlyImplemented;
15
+ bool public noERC165;
12
16
 
13
- event NotificationReceived(uint256 kind, uint256 policyId, address operator, address from);
17
+ enum NotificationKind {
18
+ PolicyReceived,
19
+ PayoutReceived,
20
+ PolicyExpired
21
+ }
14
22
 
15
- constructor(bool fail_) {
16
- fail = fail_;
23
+ event NotificationReceived(
24
+ NotificationKind kind,
25
+ uint256 policyId,
26
+ address operator,
27
+ address from
28
+ );
29
+
30
+ constructor() {
31
+ fail = false;
17
32
  notImplemented = false;
33
+ badlyImplemented = false;
34
+ emptyRevert = false;
35
+ noERC165 = false;
18
36
  payout = type(uint256).max;
19
37
  }
20
38
 
@@ -26,20 +44,50 @@ contract PolicyHolderMock is IPolicyHolder {
26
44
  notImplemented = notImplemented_;
27
45
  }
28
46
 
47
+ function setBadlyImplemented(bool badlyImplemented_) external {
48
+ badlyImplemented = badlyImplemented_;
49
+ }
50
+
51
+ function setNoERC165(bool noERC165_) external {
52
+ noERC165 = noERC165_;
53
+ }
54
+
55
+ function setEmptyRevert(bool emptyRevert_) external {
56
+ emptyRevert = emptyRevert_;
57
+ }
58
+
59
+ /**
60
+ * @dev See {IERC165-supportsInterface}.
61
+ */
62
+ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
63
+ if (noERC165)
64
+ assembly {
65
+ revert(0, 0)
66
+ }
67
+ if (notImplemented) return false;
68
+ return
69
+ interfaceId == type(IPolicyHolder).interfaceId || interfaceId == type(IERC165).interfaceId;
70
+ }
71
+
29
72
  function onERC721Received(
30
73
  address operator,
31
74
  address from,
32
75
  uint256 policyId_,
33
76
  bytes calldata
34
77
  ) external override returns (bytes4) {
35
- if (notImplemented)
36
- // solhint-disable-next-line no-inline-assembly
37
- assembly {
38
- revert(0, 0)
39
- }
40
- if (fail) revert("onERC721Received: They told me I have to fail");
78
+ if (fail)
79
+ if (emptyRevert)
80
+ assembly {
81
+ revert(0, 0)
82
+ }
83
+ else revert("onERC721Received: They told me I have to fail");
84
+
41
85
  policyId = policyId_;
42
- emit NotificationReceived(0, policyId_, operator, from);
86
+ payout = type(uint256).max;
87
+ emit NotificationReceived(NotificationKind.PolicyReceived, policyId_, operator, from);
88
+
89
+ if (badlyImplemented) return bytes4(0x0badf00d);
90
+
43
91
  return IERC721Receiver.onERC721Received.selector;
44
92
  }
45
93
 
@@ -48,15 +96,18 @@ contract PolicyHolderMock is IPolicyHolder {
48
96
  address from,
49
97
  uint256 policyId_
50
98
  ) external override returns (bytes4) {
51
- if (notImplemented)
52
- // solhint-disable-next-line no-inline-assembly
53
- assembly {
54
- revert(0, 0)
55
- }
56
- if (fail) revert("onPolicyExpired: They told me I have to fail");
99
+ if (fail)
100
+ if (emptyRevert)
101
+ assembly {
102
+ revert(0, 0)
103
+ }
104
+ else revert("onPolicyExpired: They told me I have to fail");
57
105
  policyId = policyId_;
58
106
  payout = 0;
59
- emit NotificationReceived(1, policyId_, operator, from);
107
+ emit NotificationReceived(NotificationKind.PolicyExpired, policyId_, operator, from);
108
+
109
+ if (badlyImplemented) return bytes4(0x0badf00d);
110
+
60
111
  return IPolicyHolder.onPolicyExpired.selector;
61
112
  }
62
113
 
@@ -75,15 +126,18 @@ contract PolicyHolderMock is IPolicyHolder {
75
126
  uint256 policyId_,
76
127
  uint256 amount
77
128
  ) external override returns (bytes4) {
78
- if (notImplemented)
79
- // solhint-disable-next-line no-inline-assembly
80
- assembly {
81
- revert(0, 0)
82
- }
83
- if (fail) revert("onPayoutReceived: They told me I have to fail");
129
+ if (fail)
130
+ if (emptyRevert)
131
+ assembly {
132
+ revert(0, 0)
133
+ }
134
+ else revert("onPayoutReceived: They told me I have to fail");
84
135
  policyId = policyId_;
85
136
  payout = amount;
86
- emit NotificationReceived(2, policyId_, operator, from);
137
+ emit NotificationReceived(NotificationKind.PayoutReceived, policyId_, operator, from);
138
+
139
+ if (badlyImplemented) return bytes4(0x0badf00d);
140
+
87
141
  return IPolicyHolder.onPayoutReceived.selector;
88
142
  }
89
143
  }
package/js/constants.js CHANGED
@@ -1,7 +1,13 @@
1
- const WEEK = 3600 * 24 * 7;
2
- const DAY = 3600 * 24;
1
+ const HOUR = 3600;
2
+ const WEEK = HOUR * 24 * 7;
3
+ const DAY = HOUR * 24;
4
+
5
+ // From https://eips.ethereum.org/EIPS/eip-1967
6
+ const IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
3
7
 
4
8
  module.exports = {
5
9
  WEEK,
6
10
  DAY,
11
+ HOUR,
12
+ IMPLEMENTATION_SLOT,
7
13
  };
package/js/deploy.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const upgrades_core = require("@openzeppelin/upgrades-core");
2
2
  const helpers = require("@nomicfoundation/hardhat-network-helpers");
3
3
  const fs = require("fs");
4
- const { request } = require("undici");
5
4
  const ethers = require("ethers");
6
5
  const { task, types } = require("hardhat/config");
7
6
  const { WhitelistStatus } = require("../js/enums");
@@ -78,39 +77,33 @@ async function logContractCreated(hre, contractName, address) {
78
77
  async function verifyContract(hre, contract, isProxy, constructorArguments) {
79
78
  if (isProxy === undefined) isProxy = false;
80
79
  if (constructorArguments === undefined) constructorArguments = [];
81
- let address = contract.address;
82
- if (isProxy) address = await upgrades_core.getImplementationAddress(hre.network.provider, address);
80
+ const address = isProxy
81
+ ? await upgrades_core.getImplementationAddress(hre.network.provider, contract.address)
82
+ : contract.address;
83
83
  try {
84
84
  await hre.run("verify:verify", {
85
85
  address: address,
86
86
  constructorArguments: constructorArguments,
87
87
  });
88
+ } catch (error) {
89
+ console.log("Error verifying contract implementation: ", error);
90
+ console.log("You can retry with:");
91
+ console.log(
92
+ ` npx hardhat verify --network '${hre.network.name}' '${address}' ${constructorArguments
93
+ .map((a) => `'${a.toString()}'`)
94
+ .join(" ")}`
95
+ );
96
+ }
97
+ try {
88
98
  if (isProxy) {
89
- // the following should work but it fails with the current @openzeppelin/hardhat-upgrades version (1.20.0)
90
- // await hre.run("verify:verify", {
91
- // address: contract.address,
92
- // });
93
-
94
- // so we use the following workaround
95
- const endpoints = await hre.run("verify:get-etherscan-endpoint");
96
- const params = new URLSearchParams({
97
- module: "contract",
98
- action: "verifyproxycontract",
99
- apiKey: hre.config.etherscan.apiKey,
99
+ await hre.run("verify:verify", {
100
100
  address: contract.address,
101
101
  });
102
- const response = await request(endpoints.urls.apiURL, {
103
- method: "POST",
104
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
105
- body: params.toString(),
106
- });
107
- if (response.statusCode != 200)
108
- throw new Error(`Etherscan replied with ${response.statusCode}: ${await response.body.text()}`);
109
- const body = await response.body.json();
110
- if (body.status != 1) throw new Error(`Etherscan replied with ${body}`);
111
102
  }
112
103
  } catch (error) {
113
- console.log("Error verifying contract", error);
104
+ console.log("Error verifying contract proxy: ", error);
105
+ const endpoints = await hre.run("verify:get-etherscan-endpoint");
106
+ console.log(`You should do it manually at ${endpoints.urls.browserURL}/proxyContractChecker?a=${contract.address}`);
114
107
  }
115
108
  }
116
109
 
@@ -264,8 +257,11 @@ async function deployRiskModule(
264
257
  },
265
258
  hre
266
259
  ) {
267
- extraArgs = extraArgs || [];
268
- extraConstructorArgs = extraConstructorArgs || [];
260
+ extraArgs = typeof extraArgs === "string" ? JSON.parse(extraArgs) : extraArgs || [];
261
+
262
+ extraConstructorArgs =
263
+ typeof extraConstructorArgs === "string" ? JSON.parse(extraConstructorArgs) : extraConstructorArgs || [];
264
+
269
265
  const { contract } = await deployProxyContract(
270
266
  {
271
267
  contractClass: rmClass,
@@ -583,6 +579,8 @@ function add_task() {
583
579
  .addOptionalParam("exposureLimit", "Exposure (sum of payouts) limit for the RM", 1e6, types.float)
584
580
  .addOptionalParam("maxDuration", "Maximum policy duration in hours", 24 * 365, types.int)
585
581
  .addOptionalParam("moc", "Margin of Conservativism", 1.0, types.float)
582
+ .addOptionalParam("extraConstructorArgs", "Additional constructor args", undefined, types.str)
583
+ .addOptionalParam("extraArgs", "Additional initializer args", undefined, types.str)
586
584
  .addParam("wallet", "RM address", types.address)
587
585
  .setAction(deployRiskModule);
588
586
 
package/js/enums.js CHANGED
@@ -4,7 +4,29 @@ const WhitelistStatus = {
4
4
  blacklisted: 2,
5
5
  };
6
6
 
7
+ const ComponentKind = {
8
+ eToken: 1,
9
+ riskModule: 2,
10
+ premiumsAccount: 3,
11
+ };
12
+
13
+ const ComponentStatus = {
14
+ inactive: 0,
15
+ active: 1,
16
+ deprecated: 2,
17
+ suspended: 3,
18
+ };
19
+
20
+ const ETokenParameter = {
21
+ // From IEToken.Parameter
22
+ liquidityRequirement: 0,
23
+ minUtilizationRate: 1,
24
+ maxUtilizationRate: 2,
25
+ internalLoanInterestRate: 3,
26
+ };
27
+
7
28
  const RiskModuleParameter = {
29
+ // From IRiskModule.Parameter
8
30
  moc: 0,
9
31
  jrCollRatio: 1,
10
32
  collRatio: 2,
@@ -17,14 +39,10 @@ const RiskModuleParameter = {
17
39
  maxDuration: 9,
18
40
  };
19
41
 
20
- const ComponentKind = {
21
- eToken: 1,
22
- riskModule: 2,
23
- premiumsAccount: 3,
24
- };
25
-
26
42
  module.exports = {
27
- WhitelistStatus,
28
- RiskModuleParameter,
29
43
  ComponentKind,
44
+ ComponentStatus,
45
+ ETokenParameter,
46
+ RiskModuleParameter,
47
+ WhitelistStatus,
30
48
  };
package/js/test-utils.js CHANGED
@@ -1,11 +1,13 @@
1
1
  const hre = require("hardhat");
2
2
  const { _W, grantRole, getTransactionEvent } = require("../js/utils");
3
3
  const { RiskModuleParameter } = require("../js/enums");
4
+ const helpers = require("@nomicfoundation/hardhat-network-helpers");
4
5
 
5
- const { AddressZero } = hre.ethers.constants;
6
+ const { ethers } = hre;
7
+ const { AddressZero } = ethers.constants;
6
8
 
7
9
  async function initCurrency(options, initial_targets, initial_balances) {
8
- const Currency = await hre.ethers.getContractFactory("TestCurrency");
10
+ const Currency = await ethers.getContractFactory(options.contractClass || "TestCurrency");
9
11
  let currency = await Currency.deploy(
10
12
  options.name || "Test Currency",
11
13
  options.symbol || "TEST",
@@ -21,6 +23,26 @@ async function initCurrency(options, initial_targets, initial_balances) {
21
23
  return currency;
22
24
  }
23
25
 
26
+ /**
27
+ *
28
+ * @param currencyAddress The currency contract address, for example the USDC address
29
+ * @param currencyOrigin An account that holds at least sum(initialBalances) of currency tokens
30
+ * @param initialTargets Array of addresses that will receive the initial balances
31
+ * @param initialBalances Initial balances for each address
32
+ */
33
+ async function initForkCurrency(currencyAddress, currencyOrigin, initialTargets, initialBalances) {
34
+ const currency = await ethers.getContractAt("IERC20", currencyAddress);
35
+ await helpers.impersonateAccount(currencyOrigin);
36
+ await helpers.setBalance(currencyOrigin, ethers.utils.parseEther("100"));
37
+ const whale = await ethers.getSigner(currencyOrigin);
38
+ await Promise.all(
39
+ initialTargets.map(async function (user, index) {
40
+ await currency.connect(whale).transfer(user.address, initialBalances[index]);
41
+ })
42
+ );
43
+ return currency;
44
+ }
45
+
24
46
  async function createRiskModule(
25
47
  pool,
26
48
  premiumsAccount,
@@ -106,7 +128,7 @@ async function createEToken(
106
128
  pool,
107
129
  { etkName, etkSymbol, maxUtilizationRate, poolLoanInterestRate, extraArgs, extraConstructorArgs }
108
130
  ) {
109
- const EToken = await hre.ethers.getContractFactory("EToken");
131
+ const EToken = await ethers.getContractFactory("EToken");
110
132
  extraArgs = extraArgs || [];
111
133
  extraConstructorArgs = extraConstructorArgs || [];
112
134
  const etk = await hre.upgrades.deployProxy(
@@ -162,8 +184,8 @@ const randomAddress = "0x89cDb70Fee571251a66E34caa1673cE40f7549Dc";
162
184
  * - .dontGrantL123Roles: if specified, doesn't grants LEVEL1, 2 and 3 roles.
163
185
  */
164
186
  async function deployPool(options) {
165
- const PolicyPool = await hre.ethers.getContractFactory("PolicyPool");
166
- const AccessManager = await hre.ethers.getContractFactory("AccessManager");
187
+ const PolicyPool = await ethers.getContractFactory("PolicyPool");
188
+ const AccessManager = await ethers.getContractFactory("AccessManager");
167
189
 
168
190
  let accessManager;
169
191
 
@@ -172,7 +194,7 @@ async function deployPool(options) {
172
194
  accessManager = await hre.upgrades.deployProxy(AccessManager, [], { kind: "uups" });
173
195
  await accessManager.deployed();
174
196
  } else {
175
- accessManager = await hre.ethers.getContractAt("AccessManager", options.access);
197
+ accessManager = await ethers.getContractAt("AccessManager", options.access);
176
198
  }
177
199
 
178
200
  const policyPool = await hre.upgrades.deployProxy(
@@ -204,7 +226,7 @@ async function deployPool(options) {
204
226
  }
205
227
 
206
228
  async function deployPremiumsAccount(pool, options, addToPool = true) {
207
- const PremiumsAccount = await hre.ethers.getContractFactory("PremiumsAccount");
229
+ const PremiumsAccount = await ethers.getContractFactory("PremiumsAccount");
208
230
  const premiumsAccount = await hre.upgrades.deployProxy(PremiumsAccount, [], {
209
231
  constructorArgs: [pool.address, options.jrEtkAddr || AddressZero, options.srEtkAddr || AddressZero],
210
232
  kind: "uups",
@@ -226,6 +248,51 @@ async function makePolicy(pool, rm, cust, payout, premium, lossProb, expiration,
226
248
  return newPolicyEvt;
227
249
  }
228
250
 
251
+ /**
252
+ * Resets hardhat network to fork on the specified block and url
253
+ */
254
+ async function setupChain(block, alchemyUrlEnv = "ALCHEMY_URL") {
255
+ const alchemyUrl = process.env[alchemyUrlEnv];
256
+ if (alchemyUrl === undefined) throw new Error(`Define envvar ${alchemyUrlEnv} for this test`);
257
+
258
+ if (block === undefined) throw new Error("Block can't be undefined use null for the current block");
259
+ if (block === null) block = undefined;
260
+ return hre.network.provider.request({
261
+ method: "hardhat_reset",
262
+ params: [
263
+ {
264
+ forking: {
265
+ jsonRpcUrl: alchemyUrl,
266
+ blockNumber: block,
267
+ },
268
+ },
269
+ ],
270
+ });
271
+ }
272
+
273
+ const skipForkTests = process.env.SKIP_FORK_TESTS === "true";
274
+
275
+ /**
276
+ * Chai test case wrapper for tests that require forking a live chain.
277
+ *
278
+ * It validates that the chain node URL is set, forks the chain at the specified block and adds the
279
+ * block number to the test name.
280
+ */
281
+ const fork = {
282
+ it: (name, blockNumber, test, alchemyUrlEnv = "ALCHEMY_URL") => {
283
+ const fullName = `[FORK ${blockNumber}] ${name}`;
284
+
285
+ // eslint-disable-next-line func-style
286
+ const wrapped = async (...args) => {
287
+ await setupChain(blockNumber, alchemyUrlEnv);
288
+
289
+ return test(...args);
290
+ };
291
+
292
+ return (skipForkTests ? it.skip : it)(fullName, wrapped);
293
+ },
294
+ };
295
+
229
296
  if (process.env.ENABLE_HH_WARNINGS !== "yes") hre.upgrades.silenceWarnings();
230
297
 
231
298
  module.exports = {
@@ -235,6 +302,10 @@ module.exports = {
235
302
  createRiskModule,
236
303
  deployPool,
237
304
  deployPremiumsAccount,
305
+ fork,
238
306
  initCurrency,
307
+ initForkCurrency,
239
308
  makePolicy,
309
+ setupChain,
310
+ skipForkTests,
240
311
  };
package/js/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { findAll } = require("solidity-ast/utils");
2
2
  const ethers = require("ethers");
3
3
  const helpers = require("@nomicfoundation/hardhat-network-helpers");
4
+ const { DAY, IMPLEMENTATION_SLOT } = require("./constants");
4
5
 
5
6
  const _E = ethers.utils.parseEther;
6
7
  const _BN = ethers.BigNumber.from;
@@ -234,12 +235,26 @@ async function defaultPolicyParams(
234
235
  payout: payout || _A(1000),
235
236
  premium: premium || ethers.constants.MaxUint256,
236
237
  lossProb: lossProb || _W(0.1),
237
- expiration: expiration || now + 3600 * 24 * 30,
238
+ expiration: expiration || now + DAY * 30,
238
239
  policyData: policyData || ethers.utils.hexlify(ethers.utils.randomBytes(32)),
239
- validUntil: validUntil || now + 3600 * 24 * 30,
240
+ validUntil: validUntil || now + DAY * 30,
240
241
  };
241
242
  }
242
243
 
244
+ async function readImplementationAddress(contractAddress) {
245
+ const implStorage = await ethers.provider.getStorageAt(contractAddress, IMPLEMENTATION_SLOT);
246
+ return ethers.utils.getAddress(ethers.utils.hexDataSlice(implStorage, 12));
247
+ }
248
+
249
+ /**
250
+ * Converts a string value to uint256(keccak(value))
251
+ * @param {string} value
252
+ * @returns {ethers.BigNumber}
253
+ */
254
+ function uintKeccak(value) {
255
+ return ethers.BigNumber.from(ethers.utils.keccak256(ethers.utils.toUtf8Bytes(value)));
256
+ }
257
+
243
258
  module.exports = {
244
259
  _BN,
245
260
  _E,
@@ -260,5 +275,7 @@ module.exports = {
260
275
  makeQuoteMessage,
261
276
  makeSignedQuote,
262
277
  RAY,
278
+ readImplementationAddress,
279
+ uintKeccak,
263
280
  WAD,
264
281
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ensuro/core",
3
3
  "description": "Ensuro - Decentralized insurance protocol",
4
- "version": "2.6.1",
4
+ "version": "2.7.0",
5
5
  "files": [
6
6
  "**/*.sol",
7
7
  "/build/contracts/*.json",