Refactor time helper and remove custom error helper. (#4803)
Co-authored-by: ernestognw <ernestognw@gmail.com>
This commit is contained in:
@ -1,9 +1,7 @@
|
||||
const {
|
||||
bigint: { MAX_UINT64 },
|
||||
} = require('./constants');
|
||||
const { ethers } = require('hardhat');
|
||||
const { MAX_UINT64 } = require('./constants');
|
||||
const { namespaceSlot } = require('./namespaced-storage');
|
||||
const { bigint: time } = require('./time');
|
||||
const { keccak256, AbiCoder } = require('ethers');
|
||||
|
||||
function buildBaseRoles() {
|
||||
const roles = {
|
||||
@ -54,9 +52,8 @@ const CONSUMING_SCHEDULE_STORAGE_SLOT = namespaceSlot('AccessManaged', 0n);
|
||||
* @requires this.{manager, caller, target, calldata}
|
||||
*/
|
||||
async function prepareOperation(manager, { caller, target, calldata, delay }) {
|
||||
const timestamp = await time.clock.timestamp();
|
||||
const scheduledAt = timestamp + 1n;
|
||||
await time.forward.timestamp(scheduledAt, false); // Fix next block timestamp for predictability
|
||||
const scheduledAt = (await time.clock.timestamp()) + 1n;
|
||||
await time.increaseTo.timestamp(scheduledAt, false); // Fix next block timestamp for predictability
|
||||
|
||||
return {
|
||||
schedule: () => manager.connect(caller).schedule(target, calldata, scheduledAt + delay),
|
||||
@ -68,8 +65,8 @@ async function prepareOperation(manager, { caller, target, calldata, delay }) {
|
||||
const lazyGetAddress = addressable => addressable.address ?? addressable.target ?? addressable;
|
||||
|
||||
const hashOperation = (caller, target, data) =>
|
||||
keccak256(
|
||||
AbiCoder.defaultAbiCoder().encode(
|
||||
ethers.keccak256(
|
||||
ethers.AbiCoder.defaultAbiCoder().encode(
|
||||
['address', 'address', 'bytes'],
|
||||
[lazyGetAddress(caller), lazyGetAddress(target), data],
|
||||
),
|
||||
|
||||
@ -1,12 +1,4 @@
|
||||
// TODO: deprecate the old version in favor of this one
|
||||
const bigint = {
|
||||
module.exports = {
|
||||
MAX_UINT48: 2n ** 48n - 1n,
|
||||
MAX_UINT64: 2n ** 64n - 1n,
|
||||
};
|
||||
|
||||
// TODO: remove toString() when bigint are supported
|
||||
module.exports = {
|
||||
MAX_UINT48: bigint.MAX_UINT48.toString(),
|
||||
MAX_UINT64: bigint.MAX_UINT64.toString(),
|
||||
bigint,
|
||||
};
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
// DEPRECATED: replace with hardhat-toolbox chai matchers.
|
||||
|
||||
const { expect } = require('chai');
|
||||
|
||||
/** Revert handler that supports custom errors. */
|
||||
async function expectRevertCustomError(promise, expectedErrorName, args) {
|
||||
if (!Array.isArray(args)) {
|
||||
expect.fail('Expected 3rd array parameter for error arguments');
|
||||
}
|
||||
|
||||
await promise.then(
|
||||
() => expect.fail("Expected promise to throw but it didn't"),
|
||||
({ message }) => {
|
||||
// The revert message for custom errors looks like:
|
||||
// VM Exception while processing transaction:
|
||||
// reverted with custom error 'InvalidAccountNonce("0x70997970C51812dc3A010C7d01b50e0d17dc79C8", 0)'
|
||||
|
||||
// Attempt to parse as a custom error
|
||||
const match = message.match(/custom error '(?<name>\w+)\((?<args>.*)\)'/);
|
||||
if (!match) {
|
||||
expect.fail(`Could not parse as custom error. ${message}`);
|
||||
}
|
||||
// Extract the error name and parameters
|
||||
const errorName = match.groups.name;
|
||||
const argMatches = [...match.groups.args.matchAll(/-?\w+/g)];
|
||||
|
||||
// Assert error name
|
||||
expect(errorName).to.be.equal(
|
||||
expectedErrorName,
|
||||
`Unexpected custom error name (with found args: [${argMatches.map(([a]) => a)}])`,
|
||||
);
|
||||
|
||||
// Coerce to string for comparison since `arg` can be either a number or hex.
|
||||
const sanitizedExpected = args.map(arg => arg.toString().toLowerCase());
|
||||
const sanitizedActual = argMatches.map(([arg]) => arg.toString().toLowerCase());
|
||||
|
||||
// Assert argument equality
|
||||
expect(sanitizedActual).to.have.members(sanitizedExpected, `Unexpected ${errorName} arguments`);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
expectRevertCustomError,
|
||||
};
|
||||
@ -1,7 +1,7 @@
|
||||
const { ethers } = require('hardhat');
|
||||
const { forward } = require('./time');
|
||||
const { ProposalState } = require('./enums');
|
||||
const { unique } = require('./iterate');
|
||||
const time = require('./time');
|
||||
|
||||
const timelockSalt = (address, descriptionHash) =>
|
||||
ethers.toBeHex((ethers.toBigInt(address) << 96n) ^ ethers.toBigInt(descriptionHash), 32);
|
||||
@ -131,17 +131,17 @@ class GovernorHelper {
|
||||
/// Clock helpers
|
||||
async waitForSnapshot(offset = 0n) {
|
||||
const timepoint = await this.governor.proposalSnapshot(this.id);
|
||||
return forward[this.mode](timepoint + offset);
|
||||
return time.increaseTo[this.mode](timepoint + offset);
|
||||
}
|
||||
|
||||
async waitForDeadline(offset = 0n) {
|
||||
const timepoint = await this.governor.proposalDeadline(this.id);
|
||||
return forward[this.mode](timepoint + offset);
|
||||
return time.increaseTo[this.mode](timepoint + offset);
|
||||
}
|
||||
|
||||
async waitForEta(offset = 0n) {
|
||||
const timestamp = await this.governor.proposalEta(this.id);
|
||||
return forward.timestamp(timestamp + offset);
|
||||
return time.increaseTo.timestamp(timestamp + offset);
|
||||
}
|
||||
|
||||
/// Other helpers
|
||||
|
||||
@ -1,21 +1,15 @@
|
||||
const { keccak256, id, toBeHex, MaxUint256 } = require('ethers');
|
||||
const { artifacts } = require('hardhat');
|
||||
const { ethers, artifacts } = require('hardhat');
|
||||
const { erc7201slot } = require('./erc1967');
|
||||
|
||||
function namespaceId(contractName) {
|
||||
return `openzeppelin.storage.${contractName}`;
|
||||
}
|
||||
|
||||
function namespaceLocation(value) {
|
||||
const hashIdBN = BigInt(id(value)) - 1n; // keccak256(id) - 1
|
||||
const mask = MaxUint256 - 0xffn; // ~0xff
|
||||
return BigInt(keccak256(toBeHex(hashIdBN, 32))) & mask;
|
||||
}
|
||||
|
||||
function namespaceSlot(contractName, offset) {
|
||||
try {
|
||||
// Try to get the artifact paths, will throw if it doesn't exist
|
||||
artifacts._getArtifactPathSync(`${contractName}Upgradeable`);
|
||||
return offset + namespaceLocation(namespaceId(contractName));
|
||||
return offset + ethers.toBigInt(erc7201slot(namespaceId(contractName)));
|
||||
} catch (_) {
|
||||
return offset;
|
||||
}
|
||||
@ -23,6 +17,6 @@ function namespaceSlot(contractName, offset) {
|
||||
|
||||
module.exports = {
|
||||
namespaceSlot,
|
||||
namespaceLocation,
|
||||
namespaceLocation: erc7201slot,
|
||||
namespaceId,
|
||||
};
|
||||
|
||||
@ -1,27 +1,39 @@
|
||||
const { ethers } = require('hardhat');
|
||||
const { time, mineUpTo } = require('@nomicfoundation/hardhat-network-helpers');
|
||||
const { time, mine, mineUpTo } = require('@nomicfoundation/hardhat-network-helpers');
|
||||
const { mapValues } = require('./iterate');
|
||||
|
||||
const clock = {
|
||||
blocknumber: () => time.latestBlock(),
|
||||
timestamp: () => time.latest(),
|
||||
};
|
||||
const clockFromReceipt = {
|
||||
blocknumber: receipt => Promise.resolve(receipt.blockNumber),
|
||||
timestamp: receipt => ethers.provider.getBlock(receipt.blockNumber).then(block => block.timestamp),
|
||||
};
|
||||
const increaseBy = {
|
||||
blockNumber: mine,
|
||||
timestamp: (delay, mine = true) =>
|
||||
time.latest().then(clock => increaseTo.timestamp(clock + ethers.toNumber(delay), mine)),
|
||||
};
|
||||
const increaseTo = {
|
||||
blocknumber: mineUpTo,
|
||||
timestamp: (to, mine = true) => (mine ? time.increaseTo(to) : time.setNextBlockTimestamp(to)),
|
||||
};
|
||||
const duration = time.duration;
|
||||
|
||||
module.exports = {
|
||||
clock: {
|
||||
blocknumber: () => time.latestBlock(),
|
||||
timestamp: () => time.latest(),
|
||||
},
|
||||
clockFromReceipt: {
|
||||
blocknumber: receipt => Promise.resolve(receipt.blockNumber),
|
||||
timestamp: receipt => ethers.provider.getBlock(receipt.blockNumber).then(block => block.timestamp),
|
||||
},
|
||||
forward: {
|
||||
blocknumber: mineUpTo,
|
||||
timestamp: (to, mine = true) => (mine ? time.increaseTo(to) : time.setNextBlockTimestamp(to)),
|
||||
},
|
||||
duration: time.duration,
|
||||
clock,
|
||||
clockFromReceipt,
|
||||
increaseBy,
|
||||
increaseTo,
|
||||
duration,
|
||||
};
|
||||
|
||||
// TODO: deprecate the old version in favor of this one
|
||||
module.exports.bigint = {
|
||||
clock: mapValues(module.exports.clock, fn => () => fn().then(ethers.toBigInt)),
|
||||
clockFromReceipt: mapValues(module.exports.clockFromReceipt, fn => receipt => fn(receipt).then(ethers.toBigInt)),
|
||||
forward: module.exports.forward,
|
||||
duration: mapValues(module.exports.duration, fn => n => ethers.toBigInt(fn(ethers.toNumber(n)))),
|
||||
clock: mapValues(clock, fn => () => fn().then(ethers.toBigInt)),
|
||||
clockFromReceipt: mapValues(clockFromReceipt, fn => receipt => fn(receipt).then(ethers.toBigInt)),
|
||||
increaseBy: increaseBy,
|
||||
increaseTo: increaseTo,
|
||||
duration: mapValues(duration, fn => n => ethers.toBigInt(fn(ethers.toNumber(n)))),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user