Replace revert strings with custom errors (#4261)
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com> Co-authored-by: Francisco <fg@frang.io>
This commit is contained in:
@ -4,6 +4,8 @@ const { ZERO_ADDRESS, ZERO_BYTES32 } = constants;
|
||||
const { expect } = require('chai');
|
||||
|
||||
const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior');
|
||||
const { expectRevertCustomError } = require('../helpers/customError');
|
||||
const { OperationState } = require('../helpers/enums');
|
||||
|
||||
const TimelockController = artifacts.require('TimelockController');
|
||||
const CallReceiverMock = artifacts.require('CallReceiverMock');
|
||||
@ -182,7 +184,7 @@ contract('TimelockController', function (accounts) {
|
||||
{ from: proposer },
|
||||
);
|
||||
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.schedule(
|
||||
this.operation.target,
|
||||
this.operation.value,
|
||||
@ -192,12 +194,13 @@ contract('TimelockController', function (accounts) {
|
||||
MINDELAY,
|
||||
{ from: proposer },
|
||||
),
|
||||
'TimelockController: operation already scheduled',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[this.operation.id, OperationState.Unset],
|
||||
);
|
||||
});
|
||||
|
||||
it('prevent non-proposer from committing', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.schedule(
|
||||
this.operation.target,
|
||||
this.operation.value,
|
||||
@ -207,12 +210,13 @@ contract('TimelockController', function (accounts) {
|
||||
MINDELAY,
|
||||
{ from: other },
|
||||
),
|
||||
`AccessControl: account ${other.toLowerCase()} is missing role ${PROPOSER_ROLE}`,
|
||||
`AccessControlUnauthorizedAccount`,
|
||||
[other, PROPOSER_ROLE],
|
||||
);
|
||||
});
|
||||
|
||||
it('enforce minimum delay', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.schedule(
|
||||
this.operation.target,
|
||||
this.operation.value,
|
||||
@ -222,7 +226,8 @@ contract('TimelockController', function (accounts) {
|
||||
MINDELAY - 1,
|
||||
{ from: proposer },
|
||||
),
|
||||
'TimelockController: insufficient delay',
|
||||
'TimelockInsufficientDelay',
|
||||
[MINDELAY, MINDELAY - 1],
|
||||
);
|
||||
});
|
||||
|
||||
@ -252,7 +257,7 @@ contract('TimelockController', function (accounts) {
|
||||
});
|
||||
|
||||
it('revert if operation is not scheduled', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(
|
||||
this.operation.target,
|
||||
this.operation.value,
|
||||
@ -261,7 +266,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: operation is not ready',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[this.operation.id, OperationState.Ready],
|
||||
);
|
||||
});
|
||||
|
||||
@ -279,7 +285,7 @@ contract('TimelockController', function (accounts) {
|
||||
});
|
||||
|
||||
it('revert if execution comes too early 1/2', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(
|
||||
this.operation.target,
|
||||
this.operation.value,
|
||||
@ -288,7 +294,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: operation is not ready',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[this.operation.id, OperationState.Ready],
|
||||
);
|
||||
});
|
||||
|
||||
@ -296,7 +303,7 @@ contract('TimelockController', function (accounts) {
|
||||
const timestamp = await this.mock.getTimestamp(this.operation.id);
|
||||
await time.increaseTo(timestamp - 5); // -1 is too tight, test sometime fails
|
||||
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(
|
||||
this.operation.target,
|
||||
this.operation.value,
|
||||
@ -305,7 +312,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: operation is not ready',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[this.operation.id, OperationState.Ready],
|
||||
);
|
||||
});
|
||||
|
||||
@ -334,7 +342,7 @@ contract('TimelockController', function (accounts) {
|
||||
});
|
||||
|
||||
it('prevent non-executor from revealing', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(
|
||||
this.operation.target,
|
||||
this.operation.value,
|
||||
@ -343,7 +351,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: other },
|
||||
),
|
||||
`AccessControl: account ${other.toLowerCase()} is missing role ${EXECUTOR_ROLE}`,
|
||||
`AccessControlUnauthorizedAccount`,
|
||||
[other, EXECUTOR_ROLE],
|
||||
);
|
||||
});
|
||||
|
||||
@ -389,7 +398,7 @@ contract('TimelockController', function (accounts) {
|
||||
await reentrant.enableRentrancy(this.mock.address, data);
|
||||
|
||||
// Expect to fail
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(
|
||||
reentrantOperation.target,
|
||||
reentrantOperation.value,
|
||||
@ -398,7 +407,8 @@ contract('TimelockController', function (accounts) {
|
||||
reentrantOperation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: operation is not ready',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[reentrantOperation.id, OperationState.Ready],
|
||||
);
|
||||
|
||||
// Disable reentrancy
|
||||
@ -484,7 +494,7 @@ contract('TimelockController', function (accounts) {
|
||||
{ from: proposer },
|
||||
);
|
||||
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.scheduleBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -494,12 +504,13 @@ contract('TimelockController', function (accounts) {
|
||||
MINDELAY,
|
||||
{ from: proposer },
|
||||
),
|
||||
'TimelockController: operation already scheduled',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[this.operation.id, OperationState.Unset],
|
||||
);
|
||||
});
|
||||
|
||||
it('length of batch parameter must match #1', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.scheduleBatch(
|
||||
this.operation.targets,
|
||||
[],
|
||||
@ -509,12 +520,13 @@ contract('TimelockController', function (accounts) {
|
||||
MINDELAY,
|
||||
{ from: proposer },
|
||||
),
|
||||
'TimelockController: length mismatch',
|
||||
'TimelockInvalidOperationLength',
|
||||
[this.operation.targets.length, this.operation.payloads.length, 0],
|
||||
);
|
||||
});
|
||||
|
||||
it('length of batch parameter must match #1', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.scheduleBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -524,12 +536,13 @@ contract('TimelockController', function (accounts) {
|
||||
MINDELAY,
|
||||
{ from: proposer },
|
||||
),
|
||||
'TimelockController: length mismatch',
|
||||
'TimelockInvalidOperationLength',
|
||||
[this.operation.targets.length, 0, this.operation.payloads.length],
|
||||
);
|
||||
});
|
||||
|
||||
it('prevent non-proposer from committing', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.scheduleBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -539,12 +552,13 @@ contract('TimelockController', function (accounts) {
|
||||
MINDELAY,
|
||||
{ from: other },
|
||||
),
|
||||
`AccessControl: account ${other.toLowerCase()} is missing role ${PROPOSER_ROLE}`,
|
||||
`AccessControlUnauthorizedAccount`,
|
||||
[other, PROPOSER_ROLE],
|
||||
);
|
||||
});
|
||||
|
||||
it('enforce minimum delay', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.scheduleBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -554,7 +568,8 @@ contract('TimelockController', function (accounts) {
|
||||
MINDELAY - 1,
|
||||
{ from: proposer },
|
||||
),
|
||||
'TimelockController: insufficient delay',
|
||||
'TimelockInsufficientDelay',
|
||||
[MINDELAY, MINDELAY - 1],
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -571,7 +586,7 @@ contract('TimelockController', function (accounts) {
|
||||
});
|
||||
|
||||
it('revert if operation is not scheduled', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -580,7 +595,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: operation is not ready',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[this.operation.id, OperationState.Ready],
|
||||
);
|
||||
});
|
||||
|
||||
@ -598,7 +614,7 @@ contract('TimelockController', function (accounts) {
|
||||
});
|
||||
|
||||
it('revert if execution comes too early 1/2', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -607,7 +623,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: operation is not ready',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[this.operation.id, OperationState.Ready],
|
||||
);
|
||||
});
|
||||
|
||||
@ -615,7 +632,7 @@ contract('TimelockController', function (accounts) {
|
||||
const timestamp = await this.mock.getTimestamp(this.operation.id);
|
||||
await time.increaseTo(timestamp - 5); // -1 is to tight, test sometime fails
|
||||
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -624,7 +641,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: operation is not ready',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[this.operation.id, OperationState.Ready],
|
||||
);
|
||||
});
|
||||
|
||||
@ -655,7 +673,7 @@ contract('TimelockController', function (accounts) {
|
||||
});
|
||||
|
||||
it('prevent non-executor from revealing', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -664,12 +682,13 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: other },
|
||||
),
|
||||
`AccessControl: account ${other.toLowerCase()} is missing role ${EXECUTOR_ROLE}`,
|
||||
`AccessControlUnauthorizedAccount`,
|
||||
[other, EXECUTOR_ROLE],
|
||||
);
|
||||
});
|
||||
|
||||
it('length mismatch #1', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
[],
|
||||
this.operation.values,
|
||||
@ -678,12 +697,13 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: length mismatch',
|
||||
'TimelockInvalidOperationLength',
|
||||
[0, this.operation.payloads.length, this.operation.values.length],
|
||||
);
|
||||
});
|
||||
|
||||
it('length mismatch #2', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
this.operation.targets,
|
||||
[],
|
||||
@ -692,12 +712,13 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: length mismatch',
|
||||
'TimelockInvalidOperationLength',
|
||||
[this.operation.targets.length, this.operation.payloads.length, 0],
|
||||
);
|
||||
});
|
||||
|
||||
it('length mismatch #3', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
this.operation.targets,
|
||||
this.operation.values,
|
||||
@ -706,7 +727,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: length mismatch',
|
||||
'TimelockInvalidOperationLength',
|
||||
[this.operation.targets.length, 0, this.operation.values.length],
|
||||
);
|
||||
});
|
||||
|
||||
@ -752,7 +774,7 @@ contract('TimelockController', function (accounts) {
|
||||
await reentrant.enableRentrancy(this.mock.address, data);
|
||||
|
||||
// Expect to fail
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
reentrantBatchOperation.targets,
|
||||
reentrantBatchOperation.values,
|
||||
@ -761,7 +783,8 @@ contract('TimelockController', function (accounts) {
|
||||
reentrantBatchOperation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: operation is not ready',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[reentrantBatchOperation.id, OperationState.Ready],
|
||||
);
|
||||
|
||||
// Disable reentrancy
|
||||
@ -796,7 +819,7 @@ contract('TimelockController', function (accounts) {
|
||||
[0, 0, 0],
|
||||
[
|
||||
this.callreceivermock.contract.methods.mockFunction().encodeABI(),
|
||||
this.callreceivermock.contract.methods.mockFunctionThrows().encodeABI(),
|
||||
this.callreceivermock.contract.methods.mockFunctionRevertsNoReason().encodeABI(),
|
||||
this.callreceivermock.contract.methods.mockFunction().encodeABI(),
|
||||
],
|
||||
ZERO_BYTES32,
|
||||
@ -813,7 +836,7 @@ contract('TimelockController', function (accounts) {
|
||||
{ from: proposer },
|
||||
);
|
||||
await time.increase(MINDELAY);
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.executeBatch(
|
||||
operation.targets,
|
||||
operation.values,
|
||||
@ -822,7 +845,8 @@ contract('TimelockController', function (accounts) {
|
||||
operation.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: underlying transaction reverted',
|
||||
'FailedInnerCall',
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -854,16 +878,18 @@ contract('TimelockController', function (accounts) {
|
||||
});
|
||||
|
||||
it('cannot cancel invalid operation', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.cancel(constants.ZERO_BYTES32, { from: canceller }),
|
||||
'TimelockController: operation cannot be cancelled',
|
||||
'TimelockUnexpectedOperationState',
|
||||
[constants.ZERO_BYTES32, OperationState.Pending],
|
||||
);
|
||||
});
|
||||
|
||||
it('prevent non-canceller from canceling', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.cancel(this.operation.id, { from: other }),
|
||||
`AccessControl: account ${other.toLowerCase()} is missing role ${CANCELLER_ROLE}`,
|
||||
`AccessControlUnauthorizedAccount`,
|
||||
[other, CANCELLER_ROLE],
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -871,7 +897,7 @@ contract('TimelockController', function (accounts) {
|
||||
|
||||
describe('maintenance', function () {
|
||||
it('prevent unauthorized maintenance', async function () {
|
||||
await expectRevert(this.mock.updateDelay(0, { from: other }), 'TimelockController: caller must be timelock');
|
||||
await expectRevertCustomError(this.mock.updateDelay(0, { from: other }), 'TimelockUnauthorizedCaller', [other]);
|
||||
});
|
||||
|
||||
it('timelock scheduled maintenance', async function () {
|
||||
@ -946,7 +972,7 @@ contract('TimelockController', function (accounts) {
|
||||
});
|
||||
|
||||
it('cannot execute before dependency', async function () {
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(
|
||||
this.operation2.target,
|
||||
this.operation2.value,
|
||||
@ -955,7 +981,8 @@ contract('TimelockController', function (accounts) {
|
||||
this.operation2.salt,
|
||||
{ from: executor },
|
||||
),
|
||||
'TimelockController: missing dependency',
|
||||
'TimelockUnexecutedPredecessor',
|
||||
[this.operation1.id],
|
||||
);
|
||||
});
|
||||
|
||||
@ -1032,11 +1059,12 @@ contract('TimelockController', function (accounts) {
|
||||
{ from: proposer },
|
||||
);
|
||||
await time.increase(MINDELAY);
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(operation.target, operation.value, operation.data, operation.predecessor, operation.salt, {
|
||||
from: executor,
|
||||
}),
|
||||
'TimelockController: underlying transaction reverted',
|
||||
'FailedInnerCall',
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
@ -1059,11 +1087,11 @@ contract('TimelockController', function (accounts) {
|
||||
{ from: proposer },
|
||||
);
|
||||
await time.increase(MINDELAY);
|
||||
await expectRevert(
|
||||
// Targeted function reverts with a panic code (0x1) + the timelock bubble the panic code
|
||||
await expectRevert.unspecified(
|
||||
this.mock.execute(operation.target, operation.value, operation.data, operation.predecessor, operation.salt, {
|
||||
from: executor,
|
||||
}),
|
||||
'TimelockController: underlying transaction reverted',
|
||||
);
|
||||
});
|
||||
|
||||
@ -1086,12 +1114,13 @@ contract('TimelockController', function (accounts) {
|
||||
{ from: proposer },
|
||||
);
|
||||
await time.increase(MINDELAY);
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(operation.target, operation.value, operation.data, operation.predecessor, operation.salt, {
|
||||
from: executor,
|
||||
gas: '70000',
|
||||
gas: '100000',
|
||||
}),
|
||||
'TimelockController: underlying transaction reverted',
|
||||
'FailedInnerCall',
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
@ -1154,11 +1183,12 @@ contract('TimelockController', function (accounts) {
|
||||
expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
|
||||
expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
|
||||
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(operation.target, operation.value, operation.data, operation.predecessor, operation.salt, {
|
||||
from: executor,
|
||||
}),
|
||||
'TimelockController: underlying transaction reverted',
|
||||
'FailedInnerCall',
|
||||
[],
|
||||
);
|
||||
|
||||
expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
|
||||
@ -1188,11 +1218,12 @@ contract('TimelockController', function (accounts) {
|
||||
expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
|
||||
expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
|
||||
|
||||
await expectRevert(
|
||||
await expectRevertCustomError(
|
||||
this.mock.execute(operation.target, operation.value, operation.data, operation.predecessor, operation.salt, {
|
||||
from: executor,
|
||||
}),
|
||||
'TimelockController: underlying transaction reverted',
|
||||
'FailedInnerCall',
|
||||
[],
|
||||
);
|
||||
|
||||
expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
|
||||
|
||||
Reference in New Issue
Block a user