|
|
|
|
@ -1,4 +1,5 @@
|
|
|
|
|
const { expectEvent, expectRevert, constants, BN } = require('@openzeppelin/test-helpers');
|
|
|
|
|
const { expectEvent, constants, BN } = require('@openzeppelin/test-helpers');
|
|
|
|
|
const { expectRevertCustomError } = require('../helpers/customError');
|
|
|
|
|
const { expect } = require('chai');
|
|
|
|
|
|
|
|
|
|
const { time } = require('@nomicfoundation/hardhat-network-helpers');
|
|
|
|
|
@ -12,7 +13,7 @@ const ROLE = web3.utils.soliditySha3('ROLE');
|
|
|
|
|
const OTHER_ROLE = web3.utils.soliditySha3('OTHER_ROLE');
|
|
|
|
|
const ZERO = web3.utils.toBN(0);
|
|
|
|
|
|
|
|
|
|
function shouldBehaveLikeAccessControl(errorPrefix, admin, authorized, other, otherAdmin) {
|
|
|
|
|
function shouldBehaveLikeAccessControl(admin, authorized, other, otherAdmin) {
|
|
|
|
|
shouldSupportInterfaces(['AccessControl']);
|
|
|
|
|
|
|
|
|
|
describe('default admin', function () {
|
|
|
|
|
@ -35,9 +36,10 @@ function shouldBehaveLikeAccessControl(errorPrefix, admin, authorized, other, ot
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('non-admin cannot grant role to other accounts', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.grantRole(ROLE, authorized, { from: other }),
|
|
|
|
|
`${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[other, DEFAULT_ADMIN_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -69,9 +71,10 @@ function shouldBehaveLikeAccessControl(errorPrefix, admin, authorized, other, ot
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('non-admin cannot revoke role', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.revokeRole(ROLE, authorized, { from: other }),
|
|
|
|
|
`${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[other, DEFAULT_ADMIN_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -103,9 +106,10 @@ function shouldBehaveLikeAccessControl(errorPrefix, admin, authorized, other, ot
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('only the sender can renounce their roles', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.renounceRole(ROLE, authorized, { from: admin }),
|
|
|
|
|
`${errorPrefix}: can only renounce roles for self`,
|
|
|
|
|
'AccessControlBadConfirmation',
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -146,16 +150,18 @@ function shouldBehaveLikeAccessControl(errorPrefix, admin, authorized, other, ot
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("a role's previous admins no longer grant roles", async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.grantRole(ROLE, authorized, { from: admin }),
|
|
|
|
|
`${errorPrefix}: account ${admin.toLowerCase()} is missing role ${OTHER_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[admin.toLowerCase(), OTHER_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("a role's previous admins no longer revoke roles", async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.revokeRole(ROLE, authorized, { from: admin }),
|
|
|
|
|
`${errorPrefix}: account ${admin.toLowerCase()} is missing role ${OTHER_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[admin.toLowerCase(), OTHER_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
@ -170,22 +176,24 @@ function shouldBehaveLikeAccessControl(errorPrefix, admin, authorized, other, ot
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("revert if sender doesn't have role #1", async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.methods['$_checkRole(bytes32)'](ROLE, { from: other }),
|
|
|
|
|
`${errorPrefix}: account ${other.toLowerCase()} is missing role ${ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[other, ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("revert if sender doesn't have role #2", async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.methods['$_checkRole(bytes32)'](OTHER_ROLE, { from: authorized }),
|
|
|
|
|
`${errorPrefix}: account ${authorized.toLowerCase()} is missing role ${OTHER_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[authorized.toLowerCase(), OTHER_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function shouldBehaveLikeAccessControlEnumerable(errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) {
|
|
|
|
|
function shouldBehaveLikeAccessControlEnumerable(admin, authorized, other, otherAdmin, otherAuthorized) {
|
|
|
|
|
shouldSupportInterfaces(['AccessControlEnumerable']);
|
|
|
|
|
|
|
|
|
|
describe('enumerating', function () {
|
|
|
|
|
@ -215,18 +223,9 @@ function shouldBehaveLikeAccessControlEnumerable(errorPrefix, admin, authorized,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defaultAdmin, newDefaultAdmin, other) {
|
|
|
|
|
function shouldBehaveLikeAccessControlDefaultAdminRules(delay, defaultAdmin, newDefaultAdmin, other) {
|
|
|
|
|
shouldSupportInterfaces(['AccessControlDefaultAdminRules']);
|
|
|
|
|
|
|
|
|
|
function expectNoEvent(receipt, eventName) {
|
|
|
|
|
try {
|
|
|
|
|
expectEvent(receipt, eventName);
|
|
|
|
|
throw new Error(`${eventName} event found`);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
expect(err.message).to.eq(`No '${eventName}' events found: expected false to equal true`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const getter of ['owner', 'defaultAdmin']) {
|
|
|
|
|
describe(`${getter}()`, function () {
|
|
|
|
|
it('has a default set to the initial default admin', async function () {
|
|
|
|
|
@ -366,30 +365,34 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should revert if granting default admin role', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin, { from: defaultAdmin }),
|
|
|
|
|
`${errorPrefix}: can't directly grant default admin role`,
|
|
|
|
|
'AccessControlEnforcedDefaultAdminRules',
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should revert if revoking default admin role', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin, { from: defaultAdmin }),
|
|
|
|
|
`${errorPrefix}: can't directly revoke default admin role`,
|
|
|
|
|
'AccessControlEnforcedDefaultAdminRules',
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should revert if defaultAdmin's admin is changed", async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.$_setRoleAdmin(DEFAULT_ADMIN_ROLE, defaultAdmin),
|
|
|
|
|
`${errorPrefix}: can't violate default admin rules`,
|
|
|
|
|
'AccessControlEnforcedDefaultAdminRules',
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not grant the default admin role twice', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.$_grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin),
|
|
|
|
|
`${errorPrefix}: default admin already granted`,
|
|
|
|
|
'AccessControlEnforcedDefaultAdminRules',
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -398,9 +401,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
let acceptSchedule;
|
|
|
|
|
|
|
|
|
|
it('reverts if called by non default admin accounts', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.beginDefaultAdminTransfer(newDefaultAdmin, { from: other }),
|
|
|
|
|
`${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[other, DEFAULT_ADMIN_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -456,7 +460,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
await this.accessControl.acceptDefaultAdminTransfer({ from: newDefaultAdmin });
|
|
|
|
|
const receipt = await this.accessControl.beginDefaultAdminTransfer(other, { from: newDefaultAdmin });
|
|
|
|
|
|
|
|
|
|
expectNoEvent(receipt, 'DefaultAdminTransferCanceled');
|
|
|
|
|
expectEvent.notEmitted(receipt, 'DefaultAdminTransferCanceled');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -506,9 +510,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
|
|
|
|
|
it('should revert if caller is not pending default admin', async function () {
|
|
|
|
|
await time.setNextBlockTimestamp(acceptSchedule.addn(1));
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.acceptDefaultAdminTransfer({ from: other }),
|
|
|
|
|
`${errorPrefix}: pending admin must accept`,
|
|
|
|
|
'AccessControlInvalidDefaultAdmin',
|
|
|
|
|
[other],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -549,9 +554,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
]) {
|
|
|
|
|
it(`should revert if block.timestamp is ${tag} to schedule`, async function () {
|
|
|
|
|
await time.setNextBlockTimestamp(acceptSchedule.toNumber() + fromSchedule);
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.acceptDefaultAdminTransfer({ from: newDefaultAdmin }),
|
|
|
|
|
`${errorPrefix}: transfer delay not passed`,
|
|
|
|
|
'AccessControlEnforcedDefaultAdminDelay',
|
|
|
|
|
[acceptSchedule],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@ -560,9 +566,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
|
|
|
|
|
describe('cancels a default admin transfer', function () {
|
|
|
|
|
it('reverts if called by non default admin accounts', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.cancelDefaultAdminTransfer({ from: other }),
|
|
|
|
|
`${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[other, DEFAULT_ADMIN_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -600,9 +607,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
await time.setNextBlockTimestamp(acceptSchedule.addn(1));
|
|
|
|
|
|
|
|
|
|
// Previous pending default admin should not be able to accept after cancellation.
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.acceptDefaultAdminTransfer({ from: newDefaultAdmin }),
|
|
|
|
|
`${errorPrefix}: pending admin must accept`,
|
|
|
|
|
'AccessControlInvalidDefaultAdmin',
|
|
|
|
|
[newDefaultAdmin],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
@ -615,7 +623,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
expect(newAdmin).to.equal(constants.ZERO_ADDRESS);
|
|
|
|
|
expect(schedule).to.be.bignumber.equal(ZERO);
|
|
|
|
|
|
|
|
|
|
expectNoEvent(receipt, 'DefaultAdminTransferCanceled');
|
|
|
|
|
expectEvent.notEmitted(receipt, 'DefaultAdminTransferCanceled');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
@ -634,9 +642,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
|
|
|
|
|
it('reverts if caller is not default admin', async function () {
|
|
|
|
|
await time.setNextBlockTimestamp(delayPassed);
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.renounceRole(DEFAULT_ADMIN_ROLE, other, { from: defaultAdmin }),
|
|
|
|
|
`${errorPrefix}: can only renounce roles for self`,
|
|
|
|
|
'AccessControlBadConfirmation',
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -693,9 +702,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
]) {
|
|
|
|
|
it(`reverts if block.timestamp is ${tag} to schedule`, async function () {
|
|
|
|
|
await time.setNextBlockTimestamp(delayNotPassed.toNumber() + fromSchedule);
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.renounceRole(DEFAULT_ADMIN_ROLE, defaultAdmin, { from: defaultAdmin }),
|
|
|
|
|
`${errorPrefix}: only can renounce in two delayed steps`,
|
|
|
|
|
'AccessControlEnforcedDefaultAdminDelay',
|
|
|
|
|
[expectedSchedule],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@ -704,11 +714,12 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
|
|
|
|
|
describe('changes delay', function () {
|
|
|
|
|
it('reverts if called by non default admin accounts', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.changeDefaultAdminDelay(time.duration.hours(4), {
|
|
|
|
|
from: other,
|
|
|
|
|
}),
|
|
|
|
|
`${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[other, DEFAULT_ADMIN_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -792,7 +803,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
from: defaultAdmin,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const eventMatcher = passed ? expectNoEvent : expectEvent;
|
|
|
|
|
const eventMatcher = passed ? expectEvent.notEmitted : expectEvent;
|
|
|
|
|
eventMatcher(receipt, 'DefaultAdminDelayChangeCanceled');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@ -803,9 +814,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
|
|
|
|
|
describe('rollbacks a delay change', function () {
|
|
|
|
|
it('reverts if called by non default admin accounts', async function () {
|
|
|
|
|
await expectRevert(
|
|
|
|
|
await expectRevertCustomError(
|
|
|
|
|
this.accessControl.rollbackDefaultAdminDelay({ from: other }),
|
|
|
|
|
`${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`,
|
|
|
|
|
'AccessControlUnauthorizedAccount',
|
|
|
|
|
[other, DEFAULT_ADMIN_ROLE],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -841,7 +853,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules(errorPrefix, delay, defa
|
|
|
|
|
|
|
|
|
|
const receipt = await this.accessControl.rollbackDefaultAdminDelay({ from: defaultAdmin });
|
|
|
|
|
|
|
|
|
|
const eventMatcher = passed ? expectNoEvent : expectEvent;
|
|
|
|
|
const eventMatcher = passed ? expectEvent.notEmitted : expectEvent;
|
|
|
|
|
eventMatcher(receipt, 'DefaultAdminDelayChangeCanceled');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|