@ensuro/core 2.9.0 → 2.9.1
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/build-info.json +1 -0
- package/js/deploy.js +1 -1
- package/js/test-utils.js +2 -91
- package/js/utils.js +2 -208
- package/package.json +1 -1
- package/build/contracts/mocks/TestNFT.sol/TestNFT.json +0 -388
- package/contracts/mocks/TestNFT.sol +0 -23
- package/js/constants.js +0 -13
package/js/deploy.js
CHANGED
|
@@ -3,7 +3,7 @@ const fs = require("fs");
|
|
|
3
3
|
const ethers = require("ethers");
|
|
4
4
|
const { task, types } = require("hardhat/config");
|
|
5
5
|
const { WhitelistStatus } = require("../js/enums");
|
|
6
|
-
const { amountFunction, _W, grantRole, grantComponentRole, getDefaultSigner } = require("
|
|
6
|
+
const { amountFunction, _W, grantRole, grantComponentRole, getDefaultSigner } = require("@ensuro/utils/js/utils");
|
|
7
7
|
|
|
8
8
|
const reservesOpts = {
|
|
9
9
|
unsafeAllow: ["delegatecall"],
|
package/js/test-utils.js
CHANGED
|
@@ -1,48 +1,10 @@
|
|
|
1
1
|
const hre = require("hardhat");
|
|
2
|
-
const { _W, grantRole, getTransactionEvent } = require("
|
|
3
|
-
const { RiskModuleParameter } = require("
|
|
4
|
-
const helpers = require("@nomicfoundation/hardhat-network-helpers");
|
|
2
|
+
const { _W, grantRole, getTransactionEvent } = require("@ensuro/utils/js/utils");
|
|
3
|
+
const { RiskModuleParameter } = require("./enums");
|
|
5
4
|
|
|
6
5
|
const { ethers } = hre;
|
|
7
6
|
const { ZeroAddress } = ethers;
|
|
8
7
|
|
|
9
|
-
async function initCurrency(options, initial_targets, initial_balances) {
|
|
10
|
-
const Currency = await ethers.getContractFactory(options.contractClass || "TestCurrency");
|
|
11
|
-
let currency = await Currency.deploy(
|
|
12
|
-
options.name || "Test Currency",
|
|
13
|
-
options.symbol || "TEST",
|
|
14
|
-
options.initial_supply,
|
|
15
|
-
options.decimals || 18
|
|
16
|
-
);
|
|
17
|
-
initial_targets = initial_targets || [];
|
|
18
|
-
await Promise.all(
|
|
19
|
-
initial_targets.map(async function (user, index) {
|
|
20
|
-
await currency.transfer(user, initial_balances[index]);
|
|
21
|
-
})
|
|
22
|
-
);
|
|
23
|
-
return currency;
|
|
24
|
-
}
|
|
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.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, initialBalances[index]);
|
|
41
|
-
})
|
|
42
|
-
);
|
|
43
|
-
return currency;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
8
|
async function createRiskModule(
|
|
47
9
|
pool,
|
|
48
10
|
premiumsAccount,
|
|
@@ -258,52 +220,6 @@ async function makePolicy(pool, rm, cust, payout, premium, lossProb, expiration,
|
|
|
258
220
|
return newPolicyEvt;
|
|
259
221
|
}
|
|
260
222
|
|
|
261
|
-
/**
|
|
262
|
-
* Resets hardhat network to fork on the specified block and url
|
|
263
|
-
*/
|
|
264
|
-
async function setupChain(block, alchemyUrlEnv = "ALCHEMY_URL") {
|
|
265
|
-
const alchemyUrl = process.env[alchemyUrlEnv];
|
|
266
|
-
if (alchemyUrl === undefined) throw new Error(`Define envvar ${alchemyUrlEnv} for this test`);
|
|
267
|
-
|
|
268
|
-
if (block === undefined) throw new Error("Block can't be undefined use null for the current block");
|
|
269
|
-
if (block === null) block = undefined;
|
|
270
|
-
return hre.network.provider.request({
|
|
271
|
-
method: "hardhat_reset",
|
|
272
|
-
params: [
|
|
273
|
-
{
|
|
274
|
-
forking: {
|
|
275
|
-
jsonRpcUrl: alchemyUrl,
|
|
276
|
-
blockNumber: block,
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
],
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const skipForkTests = process.env.SKIP_FORK_TESTS === "true";
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Chai test case wrapper for tests that require forking a live chain.
|
|
287
|
-
*
|
|
288
|
-
* It validates that the chain node URL is set, forks the chain at the specified block and adds the
|
|
289
|
-
* block number to the test name.
|
|
290
|
-
*/
|
|
291
|
-
const fork = {
|
|
292
|
-
it: (name, blockNumber, test, alchemyUrlEnv = "ALCHEMY_URL") => {
|
|
293
|
-
const fullName = `[FORK ${blockNumber}] ${name}`;
|
|
294
|
-
|
|
295
|
-
// eslint-disable-next-line func-style
|
|
296
|
-
const wrapped = async (...args) => {
|
|
297
|
-
await setupChain(blockNumber, alchemyUrlEnv);
|
|
298
|
-
|
|
299
|
-
return test(...args);
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
return (skipForkTests ? it.skip : it)(fullName, wrapped);
|
|
303
|
-
},
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
if (process.env.ENABLE_HH_WARNINGS !== "yes" && hre.upgrades !== undefined) hre.upgrades.silenceWarnings();
|
|
307
223
|
|
|
308
224
|
module.exports = {
|
|
309
225
|
addEToken,
|
|
@@ -312,10 +228,5 @@ module.exports = {
|
|
|
312
228
|
createRiskModule,
|
|
313
229
|
deployPool,
|
|
314
230
|
deployPremiumsAccount,
|
|
315
|
-
fork,
|
|
316
|
-
initCurrency,
|
|
317
|
-
initForkCurrency,
|
|
318
231
|
makePolicy,
|
|
319
|
-
setupChain,
|
|
320
|
-
skipForkTests,
|
|
321
232
|
};
|
package/js/utils.js
CHANGED
|
@@ -1,182 +1,7 @@
|
|
|
1
|
-
const { findAll } = require("solidity-ast/utils");
|
|
2
1
|
const ethers = require("ethers");
|
|
3
2
|
const helpers = require("@nomicfoundation/hardhat-network-helpers");
|
|
4
|
-
const { DAY
|
|
5
|
-
|
|
6
|
-
const _E = ethers.parseEther;
|
|
7
|
-
const WAD = 10n ** 18n; // 1e18
|
|
8
|
-
const RAY = 10n ** 27n; // 1e27
|
|
9
|
-
|
|
10
|
-
function getAddress(addressable) {
|
|
11
|
-
return addressable.address || addressable.target || addressable;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function getStorageLayout(hre, contractSrc, contractName) {
|
|
15
|
-
const buildInfo = await hre.artifacts.getBuildInfo(`${contractSrc}:${contractName}`);
|
|
16
|
-
if (buildInfo === undefined) throw new Error(`Contract ${contractSrc}:${contractName} not in artifacts`);
|
|
17
|
-
|
|
18
|
-
const solcOutput = buildInfo.output;
|
|
19
|
-
|
|
20
|
-
const storageLayouts = {};
|
|
21
|
-
|
|
22
|
-
for (const def of findAll("ContractDefinition", solcOutput.sources[contractSrc].ast)) {
|
|
23
|
-
storageLayouts[def.name] = solcOutput.contracts[contractSrc][def.name].storageLayout;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return storageLayouts[contractName];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Creates a fixed-point conversion function for the desired number of decimals
|
|
31
|
-
* @param decimals The number of decimals. Must be >= 6.
|
|
32
|
-
* @returns The amount function created. The function can receive strings (recommended),
|
|
33
|
-
* floats/doubles (not recommended) and integers.
|
|
34
|
-
*
|
|
35
|
-
* Floats will be rounded to 6 decimal before scaling.
|
|
36
|
-
*/
|
|
37
|
-
function amountFunction(decimals) {
|
|
38
|
-
return function (value) {
|
|
39
|
-
if (value === undefined) return undefined;
|
|
40
|
-
|
|
41
|
-
if (typeof value === "string" || value instanceof String) {
|
|
42
|
-
return ethers.parseUnits(value, decimals);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (!Number.isInteger(value)) {
|
|
46
|
-
return BigInt(Math.round(value * 1e6).toString()) * BigInt(Math.pow(10, decimals - 6).toString());
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return BigInt(value.toString()) * BigInt("10") ** BigInt(decimals.toString());
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Wad function */
|
|
54
|
-
const _W = amountFunction(18);
|
|
55
|
-
|
|
56
|
-
/** Ray function */
|
|
57
|
-
const _R = amountFunction(27);
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Returns a role identifier by computing the keccak of the role name.
|
|
61
|
-
*/
|
|
62
|
-
function getRole(role) {
|
|
63
|
-
if (role.startsWith("0x")) return role;
|
|
64
|
-
return role === "DEFAULT_ADMIN_ROLE" ? ethers.ZeroHash : ethers.keccak256(ethers.toUtf8Bytes(role));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Builds the component role identifier
|
|
69
|
-
*
|
|
70
|
-
* Mimics the behaviour of the AccessManager.getComponentRole method
|
|
71
|
-
*
|
|
72
|
-
* Component roles are roles created doing XOR between the component
|
|
73
|
-
* address and the original role.
|
|
74
|
-
*
|
|
75
|
-
* Example:
|
|
76
|
-
* getComponentRole("0xc6e7DF5E7b4f2A278906862b61205850344D4e7d", "ORACLE_ADMIN_ROLE")
|
|
77
|
-
* // "0x05e01b185238b49f750d03d945e38a7f6c3be8b54de0ee42d481eb7814f0d3a8"
|
|
78
|
-
*/
|
|
79
|
-
function getComponentRole(componentAddress, role) {
|
|
80
|
-
if (!role.startsWith("0x")) role = getRole(role);
|
|
81
|
-
|
|
82
|
-
// 32 byte array
|
|
83
|
-
const bytesRole = ethers.getBytes(role);
|
|
84
|
-
|
|
85
|
-
// 20 byte array
|
|
86
|
-
const bytesAddress = ethers.getBytes(componentAddress);
|
|
87
|
-
|
|
88
|
-
// xor each byte, padding bytesAddress with zeros at the end
|
|
89
|
-
// eslint-disable-next-line no-bitwise
|
|
90
|
-
return ethers.hexlify(bytesRole.map((elem, idx) => elem ^ (bytesAddress[idx] || 0)));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function getDefaultSigner(hre) {
|
|
94
|
-
const signers = await hre.ethers.getSigners();
|
|
95
|
-
return signers[0];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Grant a component role to a user.
|
|
100
|
-
*/
|
|
101
|
-
// eslint-disable-next-line no-empty-function
|
|
102
|
-
async function grantComponentRole(hre, contract, component, role, user, txOverrides = {}, log = () => {}) {
|
|
103
|
-
let userAddress;
|
|
104
|
-
if (user === undefined) {
|
|
105
|
-
user = await getDefaultSigner(hre);
|
|
106
|
-
userAddress = user.address;
|
|
107
|
-
} else {
|
|
108
|
-
userAddress = user.address === undefined ? user : user.address;
|
|
109
|
-
}
|
|
110
|
-
const roleHex = getRole(role);
|
|
111
|
-
const componentAddress = component.address || component;
|
|
112
|
-
const componentRole = await contract.getComponentRole(componentAddress, roleHex);
|
|
113
|
-
if (!(await contract.hasRole(componentRole, userAddress))) {
|
|
114
|
-
await contract.grantComponentRole(componentAddress, roleHex, userAddress, txOverrides);
|
|
115
|
-
log(`Role ${role} (${roleHex}) Component ${componentAddress} granted to ${userAddress}`);
|
|
116
|
-
} else {
|
|
117
|
-
log(`Role ${role} (${roleHex}) Component ${componentAddress} already granted to ${userAddress}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Grant a role to a user
|
|
123
|
-
*/
|
|
124
|
-
// eslint-disable-next-line no-empty-function
|
|
125
|
-
async function grantRole(hre, contract, role, user, txOverrides = {}, log = () => {}) {
|
|
126
|
-
let userAddress;
|
|
127
|
-
if (user === undefined) {
|
|
128
|
-
user = await getDefaultSigner(hre);
|
|
129
|
-
userAddress = user.address;
|
|
130
|
-
} else {
|
|
131
|
-
userAddress = user.address === undefined ? user : user.address;
|
|
132
|
-
}
|
|
133
|
-
const roleHex = getRole(role);
|
|
134
|
-
if (!(await contract.hasRole(roleHex, userAddress))) {
|
|
135
|
-
await contract.grantRole(roleHex, userAddress, txOverrides);
|
|
136
|
-
log(`Role ${role} (${roleHex}) granted to ${userAddress}`);
|
|
137
|
-
} else {
|
|
138
|
-
log(`Role ${role} (${roleHex}) already granted to ${userAddress}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Finds an event in the receipt
|
|
144
|
-
* @param {Interface} interface The interface of the contract that contains the requested event
|
|
145
|
-
* @param {TransactionReceipt} receipt Transaction receipt containing the events in the logs
|
|
146
|
-
* @param {String} eventName The name of the event we are interested in
|
|
147
|
-
* @param {Boolean} firstOnly If false, returns all the events matching the name
|
|
148
|
-
* @param {String} contractAddress If not null, if returns only those events generated by the specified contract
|
|
149
|
-
* @returns {LogDescription}
|
|
150
|
-
*/
|
|
151
|
-
function getTransactionEvent(interface, receipt, eventName, firstOnly = true, contractAddress = null) {
|
|
152
|
-
const ret = [];
|
|
153
|
-
// for each log in the transaction receipt
|
|
154
|
-
for (const log of receipt.logs) {
|
|
155
|
-
let parsedLog;
|
|
156
|
-
if (contractAddress !== null && contractAddress !== log.address) continue;
|
|
157
|
-
try {
|
|
158
|
-
parsedLog = interface.parseLog(log);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
if (parsedLog?.name == eventName) {
|
|
163
|
-
if (firstOnly) return parsedLog;
|
|
164
|
-
ret.push(parsedLog);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return firstOnly ? null : ret;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Builds AccessControl error message for comparison in tests
|
|
172
|
-
*/
|
|
173
|
-
function accessControlMessage(user, component, role) {
|
|
174
|
-
const userAddr = getAddress(user);
|
|
175
|
-
const compAddr = component !== null ? getAddress(component) : component;
|
|
176
|
-
const roleHash = component !== null ? getComponentRole(compAddr, role) : getRole(role);
|
|
177
|
-
|
|
178
|
-
return `AccessControl: account ${userAddr.toLowerCase()} is missing role ${roleHash}`;
|
|
179
|
-
}
|
|
3
|
+
const { DAY } = require("@ensuro/utils/js/constants");
|
|
4
|
+
const { amountFunction, getAddress, _W } = require("@ensuro/utils/js/utils");
|
|
180
5
|
|
|
181
6
|
/**
|
|
182
7
|
* Create a policy id by combining the riskmodule address and the internal id.
|
|
@@ -339,47 +164,16 @@ function defaultBucketParams({ moc, jrCollRatio, collRatio, ensuroPpFee, ensuroC
|
|
|
339
164
|
};
|
|
340
165
|
}
|
|
341
166
|
|
|
342
|
-
async function readImplementationAddress(hre, contractAddress) {
|
|
343
|
-
const implStorage = await hre.ethers.provider.getStorageAt(contractAddress, IMPLEMENTATION_SLOT);
|
|
344
|
-
return ethers.utils.getAddress(ethers.utils.hexDataSlice(implStorage, 12));
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Converts a string value to uint256(keccak(value))
|
|
349
|
-
* @param {string} value
|
|
350
|
-
* @returns {ethers.BigNumber}
|
|
351
|
-
*/
|
|
352
|
-
function uintKeccak(value) {
|
|
353
|
-
return BigInt(ethers.keccak256(ethers.toUtf8Bytes(value)));
|
|
354
|
-
}
|
|
355
|
-
|
|
356
167
|
module.exports = {
|
|
357
|
-
_E,
|
|
358
|
-
_R,
|
|
359
|
-
_W,
|
|
360
|
-
accessControlMessage,
|
|
361
|
-
amountFunction,
|
|
362
168
|
defaultPolicyParams,
|
|
363
169
|
defaultPolicyParamsWithBucket,
|
|
364
170
|
defaultPolicyParamsWithParams,
|
|
365
171
|
defaultBucketParams,
|
|
366
|
-
getAddress,
|
|
367
|
-
getComponentRole,
|
|
368
|
-
getDefaultSigner,
|
|
369
|
-
getRole,
|
|
370
|
-
getStorageLayout,
|
|
371
|
-
getTransactionEvent,
|
|
372
|
-
grantComponentRole,
|
|
373
|
-
grantRole,
|
|
374
172
|
makeBucketQuoteMessage,
|
|
375
173
|
makeFullQuoteMessage,
|
|
376
174
|
paramsAsUint256,
|
|
377
175
|
makePolicyId,
|
|
378
176
|
makeQuoteMessage,
|
|
379
177
|
makeSignedQuote,
|
|
380
|
-
RAY,
|
|
381
|
-
readImplementationAddress,
|
|
382
178
|
recoverAddress,
|
|
383
|
-
uintKeccak,
|
|
384
|
-
WAD,
|
|
385
179
|
};
|