Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
1c79698
Improve docs
ernestognw Sep 15, 2023
0218e2c
Make role's admin role more specific
ernestognw Sep 15, 2023
7f3c162
First pass through tests
ernestognw Sep 15, 2023
d27cbdc
Add _canCallSelf
ernestognw Sep 15, 2023
327a5b5
Merge branch 'audit/wip/2a-2b' into access-manager/tests
ernestognw Sep 15, 2023
febdf2a
AccessManager _canCallSelf and _canCallExecuting
ernestognw Sep 18, 2023
acfc3aa
AccessManager _canCallSelf and _canCallExecuting
ernestognw Sep 18, 2023
64a1c6e
Codespell
ernestognw Sep 18, 2023
4a8920e
Merge branch 'audit/wip/2a-2b' into access-manager/coverage-review
ernestognw Sep 18, 2023
62ff89b
Testing preview
ernestognw Sep 18, 2023
1152dbb
Merge branch 'master' into access-manager/tests
ernestognw Sep 19, 2023
d4c6480
Checkpoint
ernestognw Sep 19, 2023
6adcbf3
Finish role admin functions
ernestognw Sep 19, 2023
9d601a7
Checkpoint
ernestognw Sep 19, 2023
e2190d6
Checkpoint
ernestognw Sep 20, 2023
4c08a9d
Removed on:
ernestognw Sep 20, 2023
1d118eb
Define common admin ops path
ernestognw Sep 20, 2023
778ac91
Checkpoint
ernestognw Sep 21, 2023
3aeb3c8
Checkpoint
ernestognw Sep 21, 2023
61eea90
Update contracts/access/manager/AccessManager.sol
ernestognw Sep 21, 2023
ba8b3ca
Remove .call(this)
ernestognw Sep 23, 2023
8e3583b
Improve test names
ernestognw Sep 23, 2023
dce308e
Improve test names
ernestognw Sep 23, 2023
d0b3824
Checkpoint
ernestognw Sep 25, 2023
3a216f7
Finish execute
ernestognw Sep 25, 2023
fb0374f
Finish AccessManager tests
ernestognw Sep 25, 2023
856aa0c
Make selector decoding consistent
ernestognw Sep 25, 2023
242b19e
Merge branch 'master' into access-manager/tests
ernestognw Sep 25, 2023
d8dfe3b
Fix codespell
ernestognw Sep 25, 2023
1b66afe
Cherry pick from #4624
ernestognw Sep 26, 2023
3112045
Revert _setGrantDelay PUBLIC_ROLE case
ernestognw Sep 27, 2023
51e499b
Revert canCall hasRole path return
ernestognw Sep 27, 2023
47995ef
Update contracts/access/manager/AccessManager.sol
ernestognw Sep 27, 2023
e9cc80c
Update contracts/access/manager/AccessManager.sol
ernestognw Sep 27, 2023
bd4ab08
Update contracts/access/manager/AccessManager.sol
ernestognw Sep 27, 2023
a870c6b
Update contracts/access/manager/IAccessManager.sol
ernestognw Sep 27, 2023
d4dafda
Update test/access/manager/AccessManager.test.js
ernestognw Sep 27, 2023
04c5a9b
Implement suggestion for writing the _consumingSchedule variable
ernestognw Sep 27, 2023
9c0b8ad
Update test/helpers/access-manager.js
ernestognw Sep 27, 2023
854fbfe
Lint
ernestognw Sep 27, 2023
91cea51
Update contracts/access/manager/AccessManager.sol
ernestognw Sep 27, 2023
efce54a
Update contracts/access/manager/AccessManager.sol
ernestognw Sep 27, 2023
e955172
Update contracts/utils/types/Time.sol
ernestognw Sep 27, 2023
c0e69ee
Lint
ernestognw Sep 27, 2023
ea0973a
Recovered 100% tests coverage
ernestognw Sep 27, 2023
ee5315a
Remove console
ernestognw Sep 27, 2023
172b11f
Fix .only
ernestognw Sep 28, 2023
42e8a1a
execute+schedule tweaks
frangio Sep 29, 2023
180ef6c
fix tests
frangio Sep 30, 2023
9edb313
refactor
frangio Sep 30, 2023
7cb374d
trigger ci
frangio Oct 2, 2023
41b6880
Merge branch 'master' into access-manager/tests
ernestognw Oct 3, 2023
58e9545
Merge branch 'master' into access-manager/tests
frangio Oct 3, 2023
9d493af
fix upgradeable tests
frangio Oct 3, 2023
9291007
replace syntax `foo: function () {` -> `foo() {
frangio Oct 3, 2023
2ab4382
replace syntax (continued)
frangio Oct 3, 2023
eb914a2
lint
frangio Oct 3, 2023
da3d343
Fix consumingScheduleOp
ernestognw Oct 4, 2023
033337b
Fix consumeScheduleOp again
ernestognw Oct 4, 2023
a1011be
Apply review suggestions
ernestognw Oct 4, 2023
74009db
Check also for AccessManagedUpgradeable artifact
ernestognw Oct 4, 2023
1f4e7ca
Lint
ernestognw Oct 4, 2023
6e397cc
Merge branch 'master' into access-manager/tests
ernestognw Oct 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Checkpoint
  • Loading branch information
ernestognw committed Sep 20, 2023
commit e2190d6a734843194efaa26c0d7a774d145a78a8
2 changes: 1 addition & 1 deletion contracts/access/manager/AccessManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ contract AccessManager is Context, Multicall, IAccessManager {
* - the caller must be an admin for the role (see {getRoleAdmin})
* - granted role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleGranted} event
* Emits a {RoleGranted} event.
*/
function grantRole(uint64 roleId, address account, uint32 executionDelay) public virtual onlyAuthorized {
_grantRole(roleId, account, getRoleGrantDelay(roleId), executionDelay);
Expand Down
7 changes: 7 additions & 0 deletions contracts/access/manager/IAccessManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ interface IAccessManager {
event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce);

event RoleLabel(uint64 indexed roleId, string label);
/**
* @dev Emitted when `account` is granted `roleId`.
*
* NOTE: The meaning of the `since` argument depends on the `newMember` argument.
* If the role is granted to a new member, the `since` argument indicates when the delay goes into effect,
* otherwise it indicates the time when the roleId comes into effect.
*/
event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember);
event RoleRevoked(uint64 indexed roleId, address indexed account);
event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);
Expand Down
175 changes: 109 additions & 66 deletions test/access/manager/AccessManager.behavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,51 @@ const { impersonate } = require('../../helpers/account');
const { expectRevertCustomError } = require('../../helpers/customError');
const { EXPIRATION } = require('../../helpers/access-manager');

// ============ MODE HELPERS ============

/**
* @requires this.{manager}
*/
function shouldBehaveLikeClosable({ on: { closed, open } }) {
describe('when closed', function () {
beforeEach('close', async function () {
await this.manager.$_setTargetClosed(this.target.address, true);
});

closed.call(this);
});

describe('when open', function () {
beforeEach('open', async function () {
await this.manager.$_setTargetClosed(this.target.address, false);
});

open.call(this);
});
}

// ============ DELAY HELPERS ============

/**
* @requires this.{delay}
*/
function shouldBehaveLikeDelay(type, { on: { before, after } }) {
beforeEach('set effect timestamp', async function () {
const timestamp = await time.latest();
this.delayEffect = timestamp.add(this.delay);
});

describe(`when ${type} delay has not passed`, function () {
beforeEach(`set ${type} delay passed`, async function () {
const timestamp = await time.latest();
await setNextBlockTimestamp(timestamp.add(this.delay).subn(1));
await setNextBlockTimestamp(this.delayEffect.subn(1));
});

before.call(this);
});

describe(`when ${type} delay has passed`, function () {
beforeEach(`set ${type} delay passed`, async function () {
const timestamp = await time.latest();
await setNextBlockTimestamp(timestamp.add(this.delay));
await setNextBlockTimestamp(this.delayEffect);
});

after.call(this);
Expand Down Expand Up @@ -81,7 +107,7 @@ function shouldBehaveLikeDelayedOperation({

describe('when operation is not scheduled', function () {
beforeEach('set expected operationId', async function () {
this.operationId = await this.manager.hashOperation(this.caller, this.target, this.calldata);
this.operationId = await this.manager.hashOperation(this.caller, this.target.address, this.calldata);
});

beforeEach('assert operation is not scheduled', async function () {
Expand All @@ -104,7 +130,7 @@ function shouldBehaveLikeCanCallSelf({
},
}) {
beforeEach('set target as manager', function () {
this.target = this.manager.address;
this.target = this.manager;
});

describe('when caller is the manager', function () {
Expand Down Expand Up @@ -134,9 +160,9 @@ function shouldBehaveLikeCanCallExecuting({ on: { executing, notExecuting } }) {
describe('when is executing', function () {
beforeEach('set _executionId flag from calldata and target', async function () {
const executionId = await web3.utils.keccak256(
web3.eth.abi.encodeParameters(['address', 'bytes4'], [this.target, this.calldata.substring(0, 10)]),
web3.eth.abi.encodeParameters(['address', 'bytes4'], [this.target.address, this.calldata.substring(0, 10)]),
);
await setStorageAt(this.target, 3, executionId);
await setStorageAt(this.target.address, 3, executionId);
});

executing.call(this);
Expand All @@ -151,7 +177,8 @@ function shouldBehaveLikeCanCallExecuting({ on: { executing, notExecuting } }) {
function shouldBehaveLikeHasRole({ on: { requiredPublicRole, notRequiredPublicRole } }) {
describe('when required role is PUBLIC_ROLE', function () {
beforeEach('set required role as PUBLIC_ROLE', async function () {
await this.manager.$_setTargetFunctionRole(this.target, this.calldata.substring(0, 10), this.roles.PUBLIC.id, {
this.role = this.roles.PUBLIC;
await this.manager.$_setTargetFunctionRole(this.target.address, this.calldata.substring(0, 10), this.role.id, {
from: this.roles.ADMIN.members[0],
});
});
Expand All @@ -161,7 +188,7 @@ function shouldBehaveLikeHasRole({ on: { requiredPublicRole, notRequiredPublicRo

describe('when required role is not PUBLIC_ROLE', function () {
beforeEach('set required role', async function () {
await this.manager.$_setTargetFunctionRole(this.target, this.calldata.substring(0, 10), this.role.id, {
await this.manager.$_setTargetFunctionRole(this.target.address, this.calldata.substring(0, 10), this.role.id, {
from: this.roles.ADMIN.members[0],
});
});
Expand Down Expand Up @@ -205,7 +232,7 @@ function shouldBehaveLikeGetAccess({
describe('without caller execution delay', function () {
beforeEach('set role and delays', async function () {
this.delay = this.grantDelay;
this.executionDelay = 0;
this.executionDelay = web3.utils.toBN(0);
await this.manager.$_grantRole(this.role.id, this.caller, this.grantDelay, this.executionDelay);
});

Expand All @@ -217,7 +244,7 @@ function shouldBehaveLikeGetAccess({

describe('without grant delay', function () {
beforeEach('set delay', function () {
this.grantDelay = 0;
this.grantDelay = web3.utils.toBN(0);
});

describe('with caller execution delay', function () {
Expand All @@ -227,14 +254,12 @@ function shouldBehaveLikeGetAccess({
this.delayedUntil = this.executionDelay;
});

shouldBehaveLikeDelayedOperation.call(this, {
on: case3,
});
case3.call(this);
});

describe('without caller execution delay', function () {
beforeEach('set role and delay ', async function () {
this.executionDelay = 0;
this.executionDelay = web3.utils.toBN(0);
await this.manager.$_grantRole(this.role.id, this.caller, this.grantDelay, this.executionDelay);
});

Expand Down Expand Up @@ -287,9 +312,10 @@ function shouldBehaveLikeAnAdminOperation({ on: { externalCaller } }) {
/**
* @requires this.{manager,roles,calldata,role}
*/
function shouldBehaveLikeOptionallyDelayedAdminOperation({
function shouldBehaveLikeCommonAdminOperation({
on: {
externalCaller: {
requiredPublicRole,
notRequiredPublicRole: {
roleGranted: {
roleWithGrantDelay: {
Expand All @@ -305,14 +331,7 @@ function shouldBehaveLikeOptionallyDelayedAdminOperation({
externalCaller: function () {
shouldBehaveLikeHasRole({
on: {
requiredPublicRole: function () {
it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expectRevertCustomError(callMethod.call(this), 'AccessManagerUnauthorizedAccount', [
this.caller,
this.role.id,
]);
});
},
requiredPublicRole,
notRequiredPublicRole: {
roleGranted: {
roleWithGrantDelay: {
Expand Down Expand Up @@ -351,35 +370,39 @@ function shouldBehaveLikeOptionallyDelayedAdminOperation({
},
},
roleWithoutGrantDelay: {
callerWithExecutionDelay: {
scheduled: {
before: function () {
it('reverts as AccessManagerNotReady', async function () {
await expectRevertCustomError(callMethod.call(this), 'AccessManagerNotReady', [
this.operationId,
]);
});
},
after: function () {
it('succeeds', function () {
callMethod.call(this);
});
},
expired: function () {
it('reverts as AccessManagerExpired', async function () {
await expectRevertCustomError(callMethod.call(this), 'AccessManagerExpired', [
this.operationId,
]);
});
callerWithExecutionDelay: function () {
shouldBehaveLikeDelayedOperation.call(this, {
on: {
scheduled: {
before: function () {
it('reverts as AccessManagerNotReady', async function () {
await expectRevertCustomError(callMethod.call(this), 'AccessManagerNotReady', [
this.operationId,
]);
});
},
after: function () {
it('succeeds', async function () {
await callMethod.call(this);
});
},
expired: function () {
it('reverts as AccessManagerExpired', async function () {
await expectRevertCustomError(callMethod.call(this), 'AccessManagerExpired', [
this.operationId,
]);
});
},
},
notScheduled: function () {
it('reverts as AccessManagerNotScheduled', async function () {
await expectRevertCustomError(callMethod.call(this), 'AccessManagerNotScheduled', [
this.operationId,
]);
});
},
},
},
notScheduled: function () {
it('reverts as AccessManagerNotScheduled', async function () {
await expectRevertCustomError(callMethod.call(this), 'AccessManagerNotScheduled', [
this.operationId,
]);
});
},
});
},
callerWithoutExecutionDelay: function () {
it('succeeds', async function () {
Expand Down Expand Up @@ -408,9 +431,17 @@ function shouldBehaveLikeOptionallyDelayedAdminOperation({
* @requires this.{manager,roles,calldata,role}
*/
function shouldBehaveLikeDelayedAdminOperation() {
shouldBehaveLikeOptionallyDelayedAdminOperation({
shouldBehaveLikeCommonAdminOperation({
on: {
externalCaller: {
requiredPublicRole: function () {
it('reverts as AccessManagerUnauthorizedAccount', async function () {
await expectRevertCustomError(callMethod.call(this), 'AccessManagerUnauthorizedAccount', [
this.caller,
this.roles.ADMIN.id, // Although PUBLIC is required, target function role doesn't apply to admin ops
]);
});
},
notRequiredPublicRole: {
roleGranted: {
roleWithGrantDelay: {
Expand All @@ -425,7 +456,7 @@ function shouldBehaveLikeDelayedAdminOperation() {
describe('when operation delay is greater than execution delay', function () {
beforeEach('set operation delay', async function () {
this.operationDelay = this.executionDelay.add(time.duration.hours(1));
await this.manager.$_setTargetAdminDelay(this.target, this.operationDelay);
await this.manager.$_setTargetAdminDelay(this.target.address, this.operationDelay);

this.delayedUntil = this.operationDelay;
});
Expand All @@ -442,7 +473,7 @@ function shouldBehaveLikeDelayedAdminOperation() {
},
after: function () {
it('succeeds', async function () {
callMethod.call(this);
await callMethod.call(this);
});
},
expired: function () {
Expand All @@ -467,7 +498,7 @@ function shouldBehaveLikeDelayedAdminOperation() {
describe('when operation delay is shorter than execution delay', function () {
beforeEach('set operation delay', async function () {
this.operationDelay = this.executionDelay.sub(time.duration.hours(1));
await this.manager.$_setTargetAdminDelay(this.target, this.operationDelay);
await this.manager.$_setTargetAdminDelay(this.target.address, this.operationDelay);

this.delayedUntil = this.executionDelay;
});
Expand Down Expand Up @@ -509,8 +540,8 @@ function shouldBehaveLikeDelayedAdminOperation() {

describe('without operation delay', function () {
beforeEach('set operation delay', async function () {
this.operationDelay = 0;
await this.manager.$_setTargetAdminDelay(this.target, this.operationDelay);
this.operationDelay = web3.utils.toBN(0);
await this.manager.$_setTargetAdminDelay(this.target.address, this.operationDelay);
this.delayedUntil = this.executionDelay;
});

Expand Down Expand Up @@ -560,10 +591,15 @@ function shouldBehaveLikeDelayedAdminOperation() {
/**
* @requires this.{manager,roles,calldata,role}
*/
function shouldBehaveLikeNotDelayedAdminOperation() {
shouldBehaveLikeOptionallyDelayedAdminOperation({
function shouldBehaveLikeNotDelayedAdminOperation({
on: {
externalCaller: { requiredPublicRole },
},
}) {
shouldBehaveLikeCommonAdminOperation({
on: {
externalCaller: {
requiredPublicRole: requiredPublicRole,
notRequiredPublicRole: {
roleGranted: {
roleWithGrantDelay: {
Expand Down Expand Up @@ -624,24 +660,31 @@ async function scheduleOperation() {
const timestamp = await time.latest();
const nextTimestamp = timestamp.addn(1);
await setNextBlockTimestamp(nextTimestamp); // Fix next block timestamp for predictability
await this.manager.schedule(this.target, this.calldata, nextTimestamp.add(this.delay), {
await this.manager.schedule(this.target.address, this.calldata, nextTimestamp.add(this.delay), {
from: this.caller,
});

return {
operationId: await this.manager.hashOperation(this.caller, this.target, this.calldata),
operationId: await this.manager.hashOperation(this.caller, this.target.address, this.calldata),
};
}

/**
* @requires this.{manager, calldata, caller}
* @requires this.{target, calldata, caller}
*/
function callMethod() {
return this.manager.sendTransaction({ data: this.calldata, from: this.caller });
return web3.eth.sendTransaction({ to: this.target.address, data: this.calldata, from: this.caller });
}

module.exports = {
shouldBehaveLikeAnAdminOperation,
// MODE HELPERS
shouldBehaveLikeClosable,
// DELAY HELPERS
shouldBehaveLikeDelay,
// METHOD HELPERS
shouldBehaveLikeGetAccess,
shouldBehaveLikeHasRole,
// ADMIN OPERATION HELPERS
shouldBehaveLikeDelayedAdminOperation,
shouldBehaveLikeNotDelayedAdminOperation,
};
Loading