mirror of openzeppelin-contracts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openzeppelin-contracts/test/access/manager/AccessManager.behavior.js

213 lines
7.5 KiB

const { mine } = require('@nomicfoundation/hardhat-network-helpers');
const { expectRevertCustomError } = require('../../helpers/customError');
const {
LIKE_COMMON_IS_EXECUTING,
LIKE_COMMON_GET_ACCESS,
LIKE_COMMON_SCHEDULABLE,
testAsSchedulableOperation,
testAsRestrictedOperation,
testAsDelayedOperation,
testAsCanCall,
testAsHasRole,
} = require('./AccessManager.predicate');
// ============ ADMIN OPERATION ============
/**
* @requires this.{manager,roles,calldata,role}
*/
function shouldBehaveLikeDelayedAdminOperation() {
const getAccessPath = LIKE_COMMON_GET_ACCESS;
getAccessPath.requiredRoleIsGranted.roleGrantingIsDelayed.callerHasAnExecutionDelay.afterGrantDelay = function () {
beforeEach('consume previously set grant delay', async function () {
// Consume previously set delay
await mine();
});
testAsDelayedOperation();
};
getAccessPath.requiredRoleIsGranted.roleGrantingIsNotDelayed.callerHasAnExecutionDelay = function () {
beforeEach('set execution delay', async function () {
this.scheduleIn = this.executionDelay; // For testAsDelayedOperation
});
testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
};
beforeEach('set target as manager', function () {
this.target = this.manager;
});
testAsRestrictedOperation({
callerIsTheManager: LIKE_COMMON_IS_EXECUTING,
callerIsNotTheManager() {
testAsHasRole({
publicRoleIsRequired() {
it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expectRevertCustomError(
web3.eth.sendTransaction({ to: this.target.address, data: this.calldata, from: this.caller }),
'AccessManagerUnauthorizedAccount',
[
this.caller,
this.roles.ADMIN.id, // Although PUBLIC is required, target function role doesn't apply to admin ops
],
);
});
},
specificRoleIsRequired: getAccessPath,
});
},
});
}
/**
* @requires this.{manager,roles,calldata,role}
*/
function shouldBehaveLikeNotDelayedAdminOperation() {
const getAccessPath = LIKE_COMMON_GET_ACCESS;
getAccessPath.requiredRoleIsGranted.roleGrantingIsDelayed.callerHasAnExecutionDelay.afterGrantDelay = function () {
beforeEach('set execution delay', async function () {
await mine();
this.scheduleIn = this.executionDelay; // For testAsSchedulableOperation
});
testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
};
getAccessPath.requiredRoleIsGranted.roleGrantingIsNotDelayed.callerHasAnExecutionDelay = function () {
beforeEach('set execution delay', async function () {
this.scheduleIn = this.executionDelay; // For testAsSchedulableOperation
});
testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
};
beforeEach('set target as manager', function () {
this.target = this.manager;
});
testAsRestrictedOperation({
callerIsTheManager: LIKE_COMMON_IS_EXECUTING,
callerIsNotTheManager() {
testAsHasRole({
publicRoleIsRequired() {
it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expectRevertCustomError(
web3.eth.sendTransaction({ to: this.target.address, data: this.calldata, from: this.caller }),
'AccessManagerUnauthorizedAccount',
[this.caller, this.roles.ADMIN.id], // Although PUBLIC_ROLE is required, admin ops are not subject to target function roles
);
});
},
specificRoleIsRequired: getAccessPath,
});
},
});
}
/**
* @requires this.{manager,roles,calldata,role}
*/
function shouldBehaveLikeRoleAdminOperation(roleAdmin) {
const getAccessPath = LIKE_COMMON_GET_ACCESS;
getAccessPath.requiredRoleIsGranted.roleGrantingIsDelayed.callerHasAnExecutionDelay.afterGrantDelay = function () {
beforeEach('set operation delay', async function () {
await mine();
this.scheduleIn = this.executionDelay; // For testAsSchedulableOperation
});
testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
};
getAccessPath.requiredRoleIsGranted.roleGrantingIsNotDelayed.callerHasAnExecutionDelay = function () {
beforeEach('set execution delay', async function () {
this.scheduleIn = this.executionDelay; // For testAsSchedulableOperation
});
testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
};
beforeEach('set target as manager', function () {
this.target = this.manager;
});
testAsRestrictedOperation({
callerIsTheManager: LIKE_COMMON_IS_EXECUTING,
callerIsNotTheManager() {
testAsHasRole({
publicRoleIsRequired() {
it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expectRevertCustomError(
web3.eth.sendTransaction({ to: this.target.address, data: this.calldata, from: this.caller }),
'AccessManagerUnauthorizedAccount',
[this.caller, roleAdmin], // Role admin ops require the role's admin
);
});
},
specificRoleIsRequired: getAccessPath,
});
},
});
}
// ============ RESTRICTED OPERATION ============
/**
* @requires this.{manager,roles,calldata,role}
*/
function shouldBehaveLikeAManagedRestrictedOperation() {
function revertUnauthorized() {
it('reverts as AccessManagedUnauthorized', async function () {
await expectRevertCustomError(
web3.eth.sendTransaction({ to: this.target.address, data: this.calldata, from: this.caller }),
'AccessManagedUnauthorized',
[this.caller],
);
});
}
const getAccessPath = LIKE_COMMON_GET_ACCESS;
getAccessPath.requiredRoleIsGranted.roleGrantingIsDelayed.callerHasAnExecutionDelay.beforeGrantDelay =
revertUnauthorized;
getAccessPath.requiredRoleIsGranted.roleGrantingIsDelayed.callerHasNoExecutionDelay.beforeGrantDelay =
revertUnauthorized;
getAccessPath.requiredRoleIsNotGranted = revertUnauthorized;
getAccessPath.requiredRoleIsGranted.roleGrantingIsDelayed.callerHasAnExecutionDelay.afterGrantDelay = function () {
beforeEach('consume previously set grant delay', async function () {
// Consume previously set delay
await mine();
this.scheduleIn = this.executionDelay; // For testAsSchedulableOperation
});
testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
};
getAccessPath.requiredRoleIsGranted.roleGrantingIsNotDelayed.callerHasAnExecutionDelay = function () {
beforeEach('consume previously set grant delay', async function () {
this.scheduleIn = this.executionDelay; // For testAsSchedulableOperation
});
testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
};
const isExecutingPath = LIKE_COMMON_IS_EXECUTING;
isExecutingPath.notExecuting = revertUnauthorized;
testAsCanCall({
closed: revertUnauthorized,
open: {
callerIsTheManager: isExecutingPath,
callerIsNotTheManager: {
publicRoleIsRequired() {
it('succeeds called directly', async function () {
await web3.eth.sendTransaction({ to: this.target.address, data: this.calldata, from: this.caller });
});
it('succeeds via execute', async function () {
await this.manager.execute(this.target.address, this.calldata, { from: this.caller });
});
},
specificRoleIsRequired: getAccessPath,
},
},
});
}
module.exports = {
shouldBehaveLikeDelayedAdminOperation,
shouldBehaveLikeNotDelayedAdminOperation,
shouldBehaveLikeRoleAdminOperation,
shouldBehaveLikeAManagedRestrictedOperation,
};