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:
Ernesto García
2023-06-12 17:41:52 -06:00
committed by GitHub
parent 08fd777f6d
commit b425a72240
138 changed files with 3220 additions and 1287 deletions

View File

@ -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));