AccessManager.test.js 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  1. const { web3 } = require('hardhat');
  2. const { constants, expectEvent, time } = require('@openzeppelin/test-helpers');
  3. const { expectRevertCustomError } = require('../../helpers/customError');
  4. const { selector } = require('../../helpers/methods');
  5. const { clockFromReceipt } = require('../../helpers/time');
  6. const { product } = require('../../helpers/iterate');
  7. const helpers = require('@nomicfoundation/hardhat-network-helpers');
  8. const AccessManager = artifacts.require('$AccessManager');
  9. const AccessManagedTarget = artifacts.require('$AccessManagedTarget');
  10. const Ownable = artifacts.require('$Ownable');
  11. const MAX_UINT64 = web3.utils.toBN((2n ** 64n - 1n).toString());
  12. const ROLES = {
  13. ADMIN: web3.utils.toBN(0),
  14. SOME_ADMIN: web3.utils.toBN(17),
  15. SOME: web3.utils.toBN(42),
  16. PUBLIC: MAX_UINT64,
  17. };
  18. Object.assign(ROLES, Object.fromEntries(Object.entries(ROLES).map(([key, value]) => [value, key])));
  19. const executeDelay = web3.utils.toBN(10);
  20. const grantDelay = web3.utils.toBN(10);
  21. const MINSETBACK = time.duration.days(5);
  22. const formatAccess = access => [access[0], access[1].toString()];
  23. contract('AccessManager', function (accounts) {
  24. const [admin, manager, member, user, other] = accounts;
  25. beforeEach(async function () {
  26. this.manager = await AccessManager.new(admin);
  27. // add member to role
  28. await this.manager.$_setRoleAdmin(ROLES.SOME, ROLES.SOME_ADMIN);
  29. await this.manager.$_setRoleGuardian(ROLES.SOME, ROLES.SOME_ADMIN);
  30. await this.manager.$_grantRole(ROLES.SOME_ADMIN, manager, 0, 0);
  31. await this.manager.$_grantRole(ROLES.SOME, member, 0, 0);
  32. });
  33. it('rejects zero address for initialAdmin', async function () {
  34. await expectRevertCustomError(AccessManager.new(constants.ZERO_ADDRESS), 'AccessManagerInvalidInitialAdmin', [
  35. constants.ZERO_ADDRESS,
  36. ]);
  37. });
  38. it('default minsetback is 1 day', async function () {
  39. expect(await this.manager.minSetback()).to.be.bignumber.equal(MINSETBACK);
  40. });
  41. it('roles are correctly initialized', async function () {
  42. // role admin
  43. expect(await this.manager.getRoleAdmin(ROLES.ADMIN)).to.be.bignumber.equal(ROLES.ADMIN);
  44. expect(await this.manager.getRoleAdmin(ROLES.SOME_ADMIN)).to.be.bignumber.equal(ROLES.ADMIN);
  45. expect(await this.manager.getRoleAdmin(ROLES.SOME)).to.be.bignumber.equal(ROLES.SOME_ADMIN);
  46. expect(await this.manager.getRoleAdmin(ROLES.PUBLIC)).to.be.bignumber.equal(ROLES.ADMIN);
  47. // role guardian
  48. expect(await this.manager.getRoleGuardian(ROLES.ADMIN)).to.be.bignumber.equal(ROLES.ADMIN);
  49. expect(await this.manager.getRoleGuardian(ROLES.SOME_ADMIN)).to.be.bignumber.equal(ROLES.ADMIN);
  50. expect(await this.manager.getRoleGuardian(ROLES.SOME)).to.be.bignumber.equal(ROLES.SOME_ADMIN);
  51. expect(await this.manager.getRoleGuardian(ROLES.PUBLIC)).to.be.bignumber.equal(ROLES.ADMIN);
  52. // role members
  53. expect(await this.manager.hasRole(ROLES.ADMIN, admin).then(formatAccess)).to.be.deep.equal([true, '0']);
  54. expect(await this.manager.hasRole(ROLES.ADMIN, manager).then(formatAccess)).to.be.deep.equal([false, '0']);
  55. expect(await this.manager.hasRole(ROLES.ADMIN, member).then(formatAccess)).to.be.deep.equal([false, '0']);
  56. expect(await this.manager.hasRole(ROLES.ADMIN, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  57. expect(await this.manager.hasRole(ROLES.SOME_ADMIN, admin).then(formatAccess)).to.be.deep.equal([false, '0']);
  58. expect(await this.manager.hasRole(ROLES.SOME_ADMIN, manager).then(formatAccess)).to.be.deep.equal([true, '0']);
  59. expect(await this.manager.hasRole(ROLES.SOME_ADMIN, member).then(formatAccess)).to.be.deep.equal([false, '0']);
  60. expect(await this.manager.hasRole(ROLES.SOME_ADMIN, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  61. expect(await this.manager.hasRole(ROLES.SOME, admin).then(formatAccess)).to.be.deep.equal([false, '0']);
  62. expect(await this.manager.hasRole(ROLES.SOME, manager).then(formatAccess)).to.be.deep.equal([false, '0']);
  63. expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']);
  64. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  65. expect(await this.manager.hasRole(ROLES.PUBLIC, admin).then(formatAccess)).to.be.deep.equal([true, '0']);
  66. expect(await this.manager.hasRole(ROLES.PUBLIC, manager).then(formatAccess)).to.be.deep.equal([true, '0']);
  67. expect(await this.manager.hasRole(ROLES.PUBLIC, member).then(formatAccess)).to.be.deep.equal([true, '0']);
  68. expect(await this.manager.hasRole(ROLES.PUBLIC, user).then(formatAccess)).to.be.deep.equal([true, '0']);
  69. });
  70. describe('Roles management', function () {
  71. describe('label role', function () {
  72. it('admin can emit a label event', async function () {
  73. expectEvent(await this.manager.labelRole(ROLES.SOME, 'Some label', { from: admin }), 'RoleLabel', {
  74. roleId: ROLES.SOME,
  75. label: 'Some label',
  76. });
  77. });
  78. it('admin can re-emit a label event', async function () {
  79. await this.manager.labelRole(ROLES.SOME, 'Some label', { from: admin });
  80. expectEvent(await this.manager.labelRole(ROLES.SOME, 'Updated label', { from: admin }), 'RoleLabel', {
  81. roleId: ROLES.SOME,
  82. label: 'Updated label',
  83. });
  84. });
  85. it('emitting a label is restricted', async function () {
  86. await expectRevertCustomError(
  87. this.manager.labelRole(ROLES.SOME, 'Invalid label', { from: other }),
  88. 'AccessManagerUnauthorizedAccount',
  89. [other, ROLES.ADMIN],
  90. );
  91. });
  92. });
  93. describe('grant role', function () {
  94. describe('without a grant delay', function () {
  95. it('without an execute delay', async function () {
  96. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  97. const { receipt } = await this.manager.grantRole(ROLES.SOME, user, 0, { from: manager });
  98. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  99. expectEvent(receipt, 'RoleGranted', {
  100. roleId: ROLES.SOME,
  101. account: user,
  102. since: timestamp,
  103. delay: '0',
  104. newMember: true,
  105. });
  106. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([true, '0']);
  107. const access = await this.manager.getAccess(ROLES.SOME, user);
  108. expect(access[0]).to.be.bignumber.equal(timestamp); // inRoleSince
  109. expect(access[1]).to.be.bignumber.equal('0'); // currentDelay
  110. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  111. expect(access[3]).to.be.bignumber.equal('0'); // effect
  112. });
  113. it('with an execute delay', async function () {
  114. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  115. const { receipt } = await this.manager.grantRole(ROLES.SOME, user, executeDelay, { from: manager });
  116. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  117. expectEvent(receipt, 'RoleGranted', {
  118. roleId: ROLES.SOME,
  119. account: user,
  120. since: timestamp,
  121. delay: executeDelay,
  122. newMember: true,
  123. });
  124. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([
  125. true,
  126. executeDelay.toString(),
  127. ]);
  128. const access = await this.manager.getAccess(ROLES.SOME, user);
  129. expect(access[0]).to.be.bignumber.equal(timestamp); // inRoleSince
  130. expect(access[1]).to.be.bignumber.equal(executeDelay); // currentDelay
  131. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  132. expect(access[3]).to.be.bignumber.equal('0'); // effect
  133. });
  134. it('to a user that is already in the role', async function () {
  135. expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']);
  136. await this.manager.grantRole(ROLES.SOME, member, 0, { from: manager });
  137. expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']);
  138. });
  139. it('to a user that is scheduled for joining the role', async function () {
  140. await this.manager.$_grantRole(ROLES.SOME, user, 10, 0); // grant delay 10
  141. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  142. await this.manager.grantRole(ROLES.SOME, user, 0, { from: manager });
  143. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  144. });
  145. it('grant role is restricted', async function () {
  146. await expectRevertCustomError(
  147. this.manager.grantRole(ROLES.SOME, user, 0, { from: other }),
  148. 'AccessManagerUnauthorizedAccount',
  149. [other, ROLES.SOME_ADMIN],
  150. );
  151. });
  152. });
  153. describe('with a grant delay', function () {
  154. beforeEach(async function () {
  155. await this.manager.$_setGrantDelay(ROLES.SOME, grantDelay);
  156. await time.increase(MINSETBACK);
  157. });
  158. it('granted role is not active immediately', async function () {
  159. const { receipt } = await this.manager.grantRole(ROLES.SOME, user, 0, { from: manager });
  160. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  161. expectEvent(receipt, 'RoleGranted', {
  162. roleId: ROLES.SOME,
  163. account: user,
  164. since: timestamp.add(grantDelay),
  165. delay: '0',
  166. newMember: true,
  167. });
  168. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  169. const access = await this.manager.getAccess(ROLES.SOME, user);
  170. expect(access[0]).to.be.bignumber.equal(timestamp.add(grantDelay)); // inRoleSince
  171. expect(access[1]).to.be.bignumber.equal('0'); // currentDelay
  172. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  173. expect(access[3]).to.be.bignumber.equal('0'); // effect
  174. });
  175. it('granted role is active after the delay', async function () {
  176. const { receipt } = await this.manager.grantRole(ROLES.SOME, user, 0, { from: manager });
  177. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  178. expectEvent(receipt, 'RoleGranted', {
  179. roleId: ROLES.SOME,
  180. account: user,
  181. since: timestamp.add(grantDelay),
  182. delay: '0',
  183. newMember: true,
  184. });
  185. await time.increase(grantDelay);
  186. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([true, '0']);
  187. const access = await this.manager.getAccess(ROLES.SOME, user);
  188. expect(access[0]).to.be.bignumber.equal(timestamp.add(grantDelay)); // inRoleSince
  189. expect(access[1]).to.be.bignumber.equal('0'); // currentDelay
  190. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  191. expect(access[3]).to.be.bignumber.equal('0'); // effect
  192. });
  193. });
  194. it('cannot grant public role', async function () {
  195. await expectRevertCustomError(
  196. this.manager.$_grantRole(ROLES.PUBLIC, other, 0, executeDelay, { from: manager }),
  197. 'AccessManagerLockedRole',
  198. [ROLES.PUBLIC],
  199. );
  200. });
  201. });
  202. describe('revoke role', function () {
  203. it('from a user that is already in the role', async function () {
  204. expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']);
  205. const { receipt } = await this.manager.revokeRole(ROLES.SOME, member, { from: manager });
  206. expectEvent(receipt, 'RoleRevoked', { roleId: ROLES.SOME, account: member });
  207. expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([false, '0']);
  208. const access = await this.manager.getAccess(ROLES.SOME, user);
  209. expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince
  210. expect(access[1]).to.be.bignumber.equal('0'); // currentDelay
  211. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  212. expect(access[3]).to.be.bignumber.equal('0'); // effect
  213. });
  214. it('from a user that is scheduled for joining the role', async function () {
  215. await this.manager.$_grantRole(ROLES.SOME, user, 10, 0); // grant delay 10
  216. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  217. const { receipt } = await this.manager.revokeRole(ROLES.SOME, user, { from: manager });
  218. expectEvent(receipt, 'RoleRevoked', { roleId: ROLES.SOME, account: user });
  219. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  220. const access = await this.manager.getAccess(ROLES.SOME, user);
  221. expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince
  222. expect(access[1]).to.be.bignumber.equal('0'); // currentDelay
  223. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  224. expect(access[3]).to.be.bignumber.equal('0'); // effect
  225. });
  226. it('from a user that is not in the role', async function () {
  227. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  228. await this.manager.revokeRole(ROLES.SOME, user, { from: manager });
  229. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  230. });
  231. it('revoke role is restricted', async function () {
  232. await expectRevertCustomError(
  233. this.manager.revokeRole(ROLES.SOME, member, { from: other }),
  234. 'AccessManagerUnauthorizedAccount',
  235. [other, ROLES.SOME_ADMIN],
  236. );
  237. });
  238. });
  239. describe('renounce role', function () {
  240. it('for a user that is already in the role', async function () {
  241. expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([true, '0']);
  242. const { receipt } = await this.manager.renounceRole(ROLES.SOME, member, { from: member });
  243. expectEvent(receipt, 'RoleRevoked', { roleId: ROLES.SOME, account: member });
  244. expect(await this.manager.hasRole(ROLES.SOME, member).then(formatAccess)).to.be.deep.equal([false, '0']);
  245. const access = await this.manager.getAccess(ROLES.SOME, member);
  246. expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince
  247. expect(access[1]).to.be.bignumber.equal('0'); // currentDelay
  248. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  249. expect(access[3]).to.be.bignumber.equal('0'); // effect
  250. });
  251. it('for a user that is schedule for joining the role', async function () {
  252. await this.manager.$_grantRole(ROLES.SOME, user, 10, 0); // grant delay 10
  253. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  254. const { receipt } = await this.manager.renounceRole(ROLES.SOME, user, { from: user });
  255. expectEvent(receipt, 'RoleRevoked', { roleId: ROLES.SOME, account: user });
  256. expect(await this.manager.hasRole(ROLES.SOME, user).then(formatAccess)).to.be.deep.equal([false, '0']);
  257. const access = await this.manager.getAccess(ROLES.SOME, user);
  258. expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince
  259. expect(access[1]).to.be.bignumber.equal('0'); // currentDelay
  260. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  261. expect(access[3]).to.be.bignumber.equal('0'); // effect
  262. });
  263. it('for a user that is not in the role', async function () {
  264. await this.manager.renounceRole(ROLES.SOME, user, { from: user });
  265. });
  266. it('bad user confirmation', async function () {
  267. await expectRevertCustomError(
  268. this.manager.renounceRole(ROLES.SOME, member, { from: user }),
  269. 'AccessManagerBadConfirmation',
  270. [],
  271. );
  272. });
  273. });
  274. describe('change role admin', function () {
  275. it("admin can set any role's admin", async function () {
  276. expect(await this.manager.getRoleAdmin(ROLES.SOME)).to.be.bignumber.equal(ROLES.SOME_ADMIN);
  277. const { receipt } = await this.manager.setRoleAdmin(ROLES.SOME, ROLES.ADMIN, { from: admin });
  278. expectEvent(receipt, 'RoleAdminChanged', { roleId: ROLES.SOME, admin: ROLES.ADMIN });
  279. expect(await this.manager.getRoleAdmin(ROLES.SOME)).to.be.bignumber.equal(ROLES.ADMIN);
  280. });
  281. it("setting a role's admin is restricted", async function () {
  282. await expectRevertCustomError(
  283. this.manager.setRoleAdmin(ROLES.SOME, ROLES.SOME, { from: manager }),
  284. 'AccessManagerUnauthorizedAccount',
  285. [manager, ROLES.ADMIN],
  286. );
  287. });
  288. });
  289. describe('change role guardian', function () {
  290. it("admin can set any role's admin", async function () {
  291. expect(await this.manager.getRoleGuardian(ROLES.SOME)).to.be.bignumber.equal(ROLES.SOME_ADMIN);
  292. const { receipt } = await this.manager.setRoleGuardian(ROLES.SOME, ROLES.ADMIN, { from: admin });
  293. expectEvent(receipt, 'RoleGuardianChanged', { roleId: ROLES.SOME, guardian: ROLES.ADMIN });
  294. expect(await this.manager.getRoleGuardian(ROLES.SOME)).to.be.bignumber.equal(ROLES.ADMIN);
  295. });
  296. it("setting a role's admin is restricted", async function () {
  297. await expectRevertCustomError(
  298. this.manager.setRoleGuardian(ROLES.SOME, ROLES.SOME, { from: other }),
  299. 'AccessManagerUnauthorizedAccount',
  300. [other, ROLES.ADMIN],
  301. );
  302. });
  303. });
  304. describe('change execution delay', function () {
  305. it('increasing the delay has immediate effect', async function () {
  306. const oldDelay = web3.utils.toBN(10);
  307. const newDelay = web3.utils.toBN(100);
  308. // role is already granted (with no delay) in the initial setup. this update takes time.
  309. await this.manager.$_grantRole(ROLES.SOME, member, 0, oldDelay);
  310. const accessBefore = await this.manager.getAccess(ROLES.SOME, member);
  311. expect(accessBefore[1]).to.be.bignumber.equal(oldDelay); // currentDelay
  312. expect(accessBefore[2]).to.be.bignumber.equal('0'); // pendingDelay
  313. expect(accessBefore[3]).to.be.bignumber.equal('0'); // effect
  314. const { receipt } = await this.manager.grantRole(ROLES.SOME, member, newDelay, {
  315. from: manager,
  316. });
  317. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  318. expectEvent(receipt, 'RoleGranted', {
  319. roleId: ROLES.SOME,
  320. account: member,
  321. since: timestamp,
  322. delay: newDelay,
  323. newMember: false,
  324. });
  325. // immediate effect
  326. const accessAfter = await this.manager.getAccess(ROLES.SOME, member);
  327. expect(accessAfter[1]).to.be.bignumber.equal(newDelay); // currentDelay
  328. expect(accessAfter[2]).to.be.bignumber.equal('0'); // pendingDelay
  329. expect(accessAfter[3]).to.be.bignumber.equal('0'); // effect
  330. });
  331. it('decreasing the delay takes time', async function () {
  332. const oldDelay = web3.utils.toBN(100);
  333. const newDelay = web3.utils.toBN(10);
  334. // role is already granted (with no delay) in the initial setup. this update takes time.
  335. await this.manager.$_grantRole(ROLES.SOME, member, 0, oldDelay);
  336. const accessBefore = await this.manager.getAccess(ROLES.SOME, member);
  337. expect(accessBefore[1]).to.be.bignumber.equal(oldDelay); // currentDelay
  338. expect(accessBefore[2]).to.be.bignumber.equal('0'); // pendingDelay
  339. expect(accessBefore[3]).to.be.bignumber.equal('0'); // effect
  340. const { receipt } = await this.manager.grantRole(ROLES.SOME, member, newDelay, {
  341. from: manager,
  342. });
  343. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  344. const setback = oldDelay.sub(newDelay);
  345. expectEvent(receipt, 'RoleGranted', {
  346. roleId: ROLES.SOME,
  347. account: member,
  348. since: timestamp.add(setback),
  349. delay: newDelay,
  350. newMember: false,
  351. });
  352. // no immediate effect
  353. const accessAfter = await this.manager.getAccess(ROLES.SOME, member);
  354. expect(accessAfter[1]).to.be.bignumber.equal(oldDelay); // currentDelay
  355. expect(accessAfter[2]).to.be.bignumber.equal(newDelay); // pendingDelay
  356. expect(accessAfter[3]).to.be.bignumber.equal(timestamp.add(setback)); // effect
  357. // delayed effect
  358. await time.increase(setback);
  359. const accessAfterSetback = await this.manager.getAccess(ROLES.SOME, member);
  360. expect(accessAfterSetback[1]).to.be.bignumber.equal(newDelay); // currentDelay
  361. expect(accessAfterSetback[2]).to.be.bignumber.equal('0'); // pendingDelay
  362. expect(accessAfterSetback[3]).to.be.bignumber.equal('0'); // effect
  363. });
  364. it('can set a user execution delay during the grant delay', async function () {
  365. await this.manager.$_grantRole(ROLES.SOME, other, 10, 0);
  366. // here: "other" is pending to get the role, but doesn't yet have it.
  367. const { receipt } = await this.manager.grantRole(ROLES.SOME, other, executeDelay, { from: manager });
  368. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  369. // increasing the execution delay from 0 to executeDelay is immediate
  370. expectEvent(receipt, 'RoleGranted', {
  371. roleId: ROLES.SOME,
  372. account: other,
  373. since: timestamp,
  374. delay: executeDelay,
  375. newMember: false,
  376. });
  377. });
  378. });
  379. describe('change grant delay', function () {
  380. it('increasing the delay has immediate effect', async function () {
  381. const oldDelay = web3.utils.toBN(10);
  382. const newDelay = web3.utils.toBN(100);
  383. await this.manager.$_setGrantDelay(ROLES.SOME, oldDelay);
  384. await time.increase(MINSETBACK);
  385. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay);
  386. const { receipt } = await this.manager.setGrantDelay(ROLES.SOME, newDelay, { from: admin });
  387. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  388. const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay));
  389. expect(setback).to.be.bignumber.equal(MINSETBACK);
  390. expectEvent(receipt, 'RoleGrantDelayChanged', {
  391. roleId: ROLES.SOME,
  392. delay: newDelay,
  393. since: timestamp.add(setback),
  394. });
  395. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay);
  396. await time.increase(setback);
  397. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(newDelay);
  398. });
  399. it('increasing the delay has delay effect #1', async function () {
  400. const oldDelay = web3.utils.toBN(100);
  401. const newDelay = web3.utils.toBN(10);
  402. await this.manager.$_setGrantDelay(ROLES.SOME, oldDelay);
  403. await time.increase(MINSETBACK);
  404. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay);
  405. const { receipt } = await this.manager.setGrantDelay(ROLES.SOME, newDelay, { from: admin });
  406. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  407. const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay));
  408. expect(setback).to.be.bignumber.equal(MINSETBACK);
  409. expectEvent(receipt, 'RoleGrantDelayChanged', {
  410. roleId: ROLES.SOME,
  411. delay: newDelay,
  412. since: timestamp.add(setback),
  413. });
  414. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay);
  415. await time.increase(setback);
  416. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(newDelay);
  417. });
  418. it('increasing the delay has delay effect #2', async function () {
  419. const oldDelay = time.duration.days(30); // more than the minsetback
  420. const newDelay = web3.utils.toBN(10);
  421. await this.manager.$_setGrantDelay(ROLES.SOME, oldDelay);
  422. await time.increase(MINSETBACK);
  423. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay);
  424. const { receipt } = await this.manager.setGrantDelay(ROLES.SOME, newDelay, { from: admin });
  425. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  426. const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay));
  427. expect(setback).to.be.bignumber.gt(MINSETBACK);
  428. expectEvent(receipt, 'RoleGrantDelayChanged', {
  429. roleId: ROLES.SOME,
  430. delay: newDelay,
  431. since: timestamp.add(setback),
  432. });
  433. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(oldDelay);
  434. await time.increase(setback);
  435. expect(await this.manager.getRoleGrantDelay(ROLES.SOME)).to.be.bignumber.equal(newDelay);
  436. });
  437. it('changing the grant delay is restricted', async function () {
  438. await expectRevertCustomError(
  439. this.manager.setGrantDelay(ROLES.SOME, grantDelay, { from: other }),
  440. 'AccessManagerUnauthorizedAccount',
  441. [ROLES.ADMIN, other],
  442. );
  443. });
  444. });
  445. });
  446. describe('with AccessManaged target contract', function () {
  447. beforeEach('deploy target contract', async function () {
  448. this.target = await AccessManagedTarget.new(this.manager.address);
  449. // helpers for indirect calls
  450. this.callData = selector('fnRestricted()');
  451. this.call = [this.target.address, this.callData];
  452. this.opId = web3.utils.keccak256(
  453. web3.eth.abi.encodeParameters(['address', 'address', 'bytes'], [user, ...this.call]),
  454. );
  455. this.direct = (opts = {}) => this.target.fnRestricted({ from: user, ...opts });
  456. this.schedule = (opts = {}) => this.manager.schedule(...this.call, 0, { from: user, ...opts });
  457. this.execute = (opts = {}) => this.manager.execute(...this.call, { from: user, ...opts });
  458. this.cancel = (opts = {}) => this.manager.cancel(user, ...this.call, { from: user, ...opts });
  459. });
  460. describe('Change function permissions', function () {
  461. const sigs = ['someFunction()', 'someOtherFunction(uint256)', 'oneMoreFunction(address,uint8)'].map(selector);
  462. it('admin can set function role', async function () {
  463. for (const sig of sigs) {
  464. expect(await this.manager.getTargetFunctionRole(this.target.address, sig)).to.be.bignumber.equal(ROLES.ADMIN);
  465. }
  466. const { receipt: receipt1 } = await this.manager.setTargetFunctionRole(this.target.address, sigs, ROLES.SOME, {
  467. from: admin,
  468. });
  469. for (const sig of sigs) {
  470. expectEvent(receipt1, 'TargetFunctionRoleUpdated', {
  471. target: this.target.address,
  472. selector: sig,
  473. roleId: ROLES.SOME,
  474. });
  475. expect(await this.manager.getTargetFunctionRole(this.target.address, sig)).to.be.bignumber.equal(ROLES.SOME);
  476. }
  477. const { receipt: receipt2 } = await this.manager.setTargetFunctionRole(
  478. this.target.address,
  479. [sigs[1]],
  480. ROLES.SOME_ADMIN,
  481. {
  482. from: admin,
  483. },
  484. );
  485. expectEvent(receipt2, 'TargetFunctionRoleUpdated', {
  486. target: this.target.address,
  487. selector: sigs[1],
  488. roleId: ROLES.SOME_ADMIN,
  489. });
  490. for (const sig of sigs) {
  491. expect(await this.manager.getTargetFunctionRole(this.target.address, sig)).to.be.bignumber.equal(
  492. sig == sigs[1] ? ROLES.SOME_ADMIN : ROLES.SOME,
  493. );
  494. }
  495. });
  496. it('non-admin cannot set function role', async function () {
  497. await expectRevertCustomError(
  498. this.manager.setTargetFunctionRole(this.target.address, sigs, ROLES.SOME, { from: other }),
  499. 'AccessManagerUnauthorizedAccount',
  500. [other, ROLES.ADMIN],
  501. );
  502. });
  503. });
  504. // WIP
  505. describe('Calling restricted & unrestricted functions', function () {
  506. for (const [callerRoles, fnRole, closed, delay] of product(
  507. [[], [ROLES.SOME]],
  508. [undefined, ROLES.ADMIN, ROLES.SOME, ROLES.PUBLIC],
  509. [false, true],
  510. [null, executeDelay],
  511. )) {
  512. // can we call with a delay ?
  513. const indirectSuccess = (fnRole == ROLES.PUBLIC || callerRoles.includes(fnRole)) && !closed;
  514. // can we call without a delay ?
  515. const directSuccess = (fnRole == ROLES.PUBLIC || (callerRoles.includes(fnRole) && !delay)) && !closed;
  516. const description = [
  517. 'Caller in roles',
  518. '[' + (callerRoles ?? []).map(roleId => ROLES[roleId]).join(', ') + ']',
  519. delay ? 'with a delay' : 'without a delay',
  520. '+',
  521. 'functions open to roles',
  522. '[' + (ROLES[fnRole] ?? '') + ']',
  523. closed ? `(closed)` : '',
  524. ].join(' ');
  525. describe(description, function () {
  526. beforeEach(async function () {
  527. // setup
  528. await Promise.all([
  529. this.manager.$_setTargetClosed(this.target.address, closed),
  530. fnRole && this.manager.$_setTargetFunctionRole(this.target.address, selector('fnRestricted()'), fnRole),
  531. fnRole && this.manager.$_setTargetFunctionRole(this.target.address, selector('fnUnrestricted()'), fnRole),
  532. ...callerRoles
  533. .filter(roleId => roleId != ROLES.PUBLIC)
  534. .map(roleId => this.manager.$_grantRole(roleId, user, 0, delay ?? 0)),
  535. ]);
  536. // post setup checks
  537. expect(await this.manager.isTargetClosed(this.target.address)).to.be.equal(closed);
  538. if (fnRole) {
  539. expect(
  540. await this.manager.getTargetFunctionRole(this.target.address, selector('fnRestricted()')),
  541. ).to.be.bignumber.equal(fnRole);
  542. expect(
  543. await this.manager.getTargetFunctionRole(this.target.address, selector('fnUnrestricted()')),
  544. ).to.be.bignumber.equal(fnRole);
  545. }
  546. for (const roleId of callerRoles) {
  547. const access = await this.manager.getAccess(roleId, user);
  548. if (roleId == ROLES.PUBLIC) {
  549. expect(access[0]).to.be.bignumber.equal('0'); // inRoleSince
  550. expect(access[1]).to.be.bignumber.equal('0'); // currentDelay
  551. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  552. expect(access[3]).to.be.bignumber.equal('0'); // effect
  553. } else {
  554. expect(access[0]).to.be.bignumber.gt('0'); // inRoleSince
  555. expect(access[1]).to.be.bignumber.eq(String(delay ?? 0)); // currentDelay
  556. expect(access[2]).to.be.bignumber.equal('0'); // pendingDelay
  557. expect(access[3]).to.be.bignumber.equal('0'); // effect
  558. }
  559. }
  560. });
  561. it('canCall', async function () {
  562. const result = await this.manager.canCall(user, this.target.address, selector('fnRestricted()'));
  563. expect(result[0]).to.be.equal(directSuccess);
  564. expect(result[1]).to.be.bignumber.equal(!directSuccess && indirectSuccess ? delay ?? '0' : '0');
  565. });
  566. it('Calling a non restricted function never revert', async function () {
  567. expectEvent(await this.target.fnUnrestricted({ from: user }), 'CalledUnrestricted', {
  568. caller: user,
  569. });
  570. });
  571. it(`Calling a restricted function directly should ${
  572. directSuccess ? 'succeed' : 'revert'
  573. }`, async function () {
  574. const promise = this.direct();
  575. if (directSuccess) {
  576. expectEvent(await promise, 'CalledRestricted', { caller: user });
  577. } else if (indirectSuccess) {
  578. await expectRevertCustomError(promise, 'AccessManagerNotScheduled', [this.opId]);
  579. } else {
  580. await expectRevertCustomError(promise, 'AccessManagedUnauthorized', [user]);
  581. }
  582. });
  583. it('Calling indirectly: only execute', async function () {
  584. // execute without schedule
  585. if (directSuccess) {
  586. const { receipt, tx } = await this.execute();
  587. expectEvent.notEmitted(receipt, 'OperationExecuted', { operationId: this.opId });
  588. await expectEvent.inTransaction(tx, this.target, 'CalledRestricted', { caller: this.manager.address });
  589. } else if (indirectSuccess) {
  590. await expectRevertCustomError(this.execute(), 'AccessManagerNotScheduled', [this.opId]);
  591. } else {
  592. await expectRevertCustomError(this.execute(), 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  593. }
  594. });
  595. it('Calling indirectly: schedule and execute', async function () {
  596. if (directSuccess || indirectSuccess) {
  597. const { receipt } = await this.schedule();
  598. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  599. expectEvent(receipt, 'OperationScheduled', {
  600. operationId: this.opId,
  601. caller: user,
  602. target: this.call[0],
  603. data: this.call[1],
  604. });
  605. // if can call directly, delay should be 0. Otherwise, the delay should be applied
  606. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal(
  607. timestamp.add(directSuccess ? web3.utils.toBN(0) : delay),
  608. );
  609. // execute without wait
  610. if (directSuccess) {
  611. const { receipt, tx } = await this.execute();
  612. await expectEvent.inTransaction(tx, this.target, 'CalledRestricted', { caller: this.manager.address });
  613. if (delay && fnRole !== ROLES.PUBLIC) {
  614. expectEvent(receipt, 'OperationExecuted', { operationId: this.opId });
  615. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal('0');
  616. }
  617. } else if (indirectSuccess) {
  618. await expectRevertCustomError(this.execute(), 'AccessManagerNotReady', [this.opId]);
  619. } else {
  620. await expectRevertCustomError(this.execute(), 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  621. }
  622. } else {
  623. await expectRevertCustomError(this.schedule(), 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  624. }
  625. });
  626. it('Calling indirectly: schedule wait and execute', async function () {
  627. if (directSuccess || indirectSuccess) {
  628. const { receipt } = await this.schedule();
  629. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  630. expectEvent(receipt, 'OperationScheduled', {
  631. operationId: this.opId,
  632. caller: user,
  633. target: this.call[0],
  634. data: this.call[1],
  635. });
  636. // if can call directly, delay should be 0. Otherwise, the delay should be applied
  637. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal(
  638. timestamp.add(directSuccess ? web3.utils.toBN(0) : delay),
  639. );
  640. // wait
  641. await time.increase(delay ?? 0);
  642. // execute without wait
  643. if (directSuccess || indirectSuccess) {
  644. const { receipt, tx } = await this.execute();
  645. await expectEvent.inTransaction(tx, this.target, 'CalledRestricted', { caller: this.manager.address });
  646. if (delay && fnRole !== ROLES.PUBLIC) {
  647. expectEvent(receipt, 'OperationExecuted', { operationId: this.opId });
  648. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal('0');
  649. }
  650. } else {
  651. await expectRevertCustomError(this.execute(), 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  652. }
  653. } else {
  654. await expectRevertCustomError(this.schedule(), 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  655. }
  656. });
  657. it('Calling directly: schedule and call', async function () {
  658. if (directSuccess || indirectSuccess) {
  659. const { receipt } = await this.schedule();
  660. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  661. expectEvent(receipt, 'OperationScheduled', {
  662. operationId: this.opId,
  663. caller: user,
  664. target: this.call[0],
  665. data: this.call[1],
  666. });
  667. // if can call directly, delay should be 0. Otherwise, the delay should be applied
  668. const schedule = timestamp.add(directSuccess ? web3.utils.toBN(0) : delay);
  669. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal(schedule);
  670. // execute without wait
  671. const promise = this.direct();
  672. if (directSuccess) {
  673. expectEvent(await promise, 'CalledRestricted', { caller: user });
  674. // schedule is not reset
  675. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal(schedule);
  676. } else if (indirectSuccess) {
  677. await expectRevertCustomError(promise, 'AccessManagerNotReady', [this.opId]);
  678. } else {
  679. await expectRevertCustomError(promise, 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  680. }
  681. } else {
  682. await expectRevertCustomError(this.schedule(), 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  683. }
  684. });
  685. it('Calling directly: schedule wait and call', async function () {
  686. if (directSuccess || indirectSuccess) {
  687. const { receipt } = await this.schedule();
  688. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  689. expectEvent(receipt, 'OperationScheduled', {
  690. operationId: this.opId,
  691. caller: user,
  692. target: this.call[0],
  693. data: this.call[1],
  694. });
  695. // if can call directly, delay should be 0. Otherwise, the delay should be applied
  696. const schedule = timestamp.add(directSuccess ? web3.utils.toBN(0) : delay);
  697. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal(schedule);
  698. // wait
  699. await time.increase(delay ?? 0);
  700. // execute without wait
  701. const promise = await this.direct();
  702. if (directSuccess) {
  703. expectEvent(await promise, 'CalledRestricted', { caller: user });
  704. // schedule is not reset
  705. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal(schedule);
  706. } else if (indirectSuccess) {
  707. const receipt = await promise;
  708. expectEvent(receipt, 'CalledRestricted', { caller: user });
  709. await expectEvent.inTransaction(receipt.tx, this.manager, 'OperationExecuted', {
  710. operationId: this.opId,
  711. });
  712. // schedule is reset
  713. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal('0');
  714. } else {
  715. await expectRevertCustomError(this.direct(), 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  716. }
  717. } else {
  718. await expectRevertCustomError(this.schedule(), 'AccessManagerUnauthorizedCall', [user, ...this.call]);
  719. }
  720. });
  721. it('Scheduling for later than needed'); // TODO
  722. });
  723. }
  724. });
  725. describe('Indirect execution corner-cases', async function () {
  726. beforeEach(async function () {
  727. await this.manager.$_setTargetFunctionRole(this.target.address, this.callData, ROLES.SOME);
  728. await this.manager.$_grantRole(ROLES.SOME, user, 0, executeDelay);
  729. });
  730. it('Checking canCall when caller is the manager depend on the _executionId', async function () {
  731. const result = await this.manager.canCall(this.manager.address, this.target.address, '0x00000000');
  732. expect(result[0]).to.be.false;
  733. expect(result[1]).to.be.bignumber.equal('0');
  734. });
  735. it('Cannot execute earlier', async function () {
  736. const { receipt } = await this.schedule();
  737. const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
  738. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal(timestamp.add(executeDelay));
  739. // too early
  740. await helpers.time.setNextBlockTimestamp(timestamp.add(executeDelay).subn(1));
  741. await expectRevertCustomError(this.execute(), 'AccessManagerNotReady', [this.opId]);
  742. // the revert happened one second before the execution delay expired
  743. expect(await time.latest()).to.be.bignumber.equal(timestamp.add(executeDelay).subn(1));
  744. // ok
  745. await helpers.time.setNextBlockTimestamp(timestamp.add(executeDelay));
  746. await this.execute();
  747. // the success happened when the delay was reached (earliest possible)
  748. expect(await time.latest()).to.be.bignumber.equal(timestamp.add(executeDelay));
  749. });
  750. it('Cannot schedule an already scheduled operation', async function () {
  751. const { receipt } = await this.schedule();
  752. expectEvent(receipt, 'OperationScheduled', {
  753. operationId: this.opId,
  754. caller: user,
  755. target: this.call[0],
  756. data: this.call[1],
  757. });
  758. await expectRevertCustomError(this.schedule(), 'AccessManagerAlreadyScheduled', [this.opId]);
  759. });
  760. it('Cannot cancel an operation that is not scheduled', async function () {
  761. await expectRevertCustomError(this.cancel(), 'AccessManagerNotScheduled', [this.opId]);
  762. });
  763. it('Cannot cancel an operation that is already executed', async function () {
  764. await this.schedule();
  765. await time.increase(executeDelay);
  766. await this.execute();
  767. await expectRevertCustomError(this.cancel(), 'AccessManagerNotScheduled', [this.opId]);
  768. });
  769. it('Scheduler can cancel', async function () {
  770. await this.schedule();
  771. expect(await this.manager.getSchedule(this.opId)).to.not.be.bignumber.equal('0');
  772. expectEvent(await this.cancel({ from: manager }), 'OperationCanceled', { operationId: this.opId });
  773. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal('0');
  774. });
  775. it('Guardian can cancel', async function () {
  776. await this.schedule();
  777. expect(await this.manager.getSchedule(this.opId)).to.not.be.bignumber.equal('0');
  778. expectEvent(await this.cancel({ from: manager }), 'OperationCanceled', { operationId: this.opId });
  779. expect(await this.manager.getSchedule(this.opId)).to.be.bignumber.equal('0');
  780. });
  781. it('Cancel is restricted', async function () {
  782. await this.schedule();
  783. expect(await this.manager.getSchedule(this.opId)).to.not.be.bignumber.equal('0');
  784. await expectRevertCustomError(this.cancel({ from: other }), 'AccessManagerUnauthorizedCancel', [
  785. other,
  786. user,
  787. ...this.call,
  788. ]);
  789. expect(await this.manager.getSchedule(this.opId)).to.not.be.bignumber.equal('0');
  790. });
  791. it('Can re-schedule after execution', async function () {
  792. await this.schedule();
  793. await time.increase(executeDelay);
  794. await this.execute();
  795. // reschedule
  796. const { receipt } = await this.schedule();
  797. expectEvent(receipt, 'OperationScheduled', {
  798. operationId: this.opId,
  799. caller: user,
  800. target: this.call[0],
  801. data: this.call[1],
  802. });
  803. });
  804. it('Can re-schedule after cancel', async function () {
  805. await this.schedule();
  806. await this.cancel();
  807. // reschedule
  808. const { receipt } = await this.schedule();
  809. expectEvent(receipt, 'OperationScheduled', {
  810. operationId: this.opId,
  811. caller: user,
  812. target: this.call[0],
  813. data: this.call[1],
  814. });
  815. });
  816. });
  817. });
  818. describe('with Ownable target contract', function () {
  819. const roleId = web3.utils.toBN(1);
  820. beforeEach(async function () {
  821. this.ownable = await Ownable.new(this.manager.address);
  822. // add user to role
  823. await this.manager.$_grantRole(roleId, user, 0, 0);
  824. });
  825. it('initial state', async function () {
  826. expect(await this.ownable.owner()).to.be.equal(this.manager.address);
  827. });
  828. describe('Contract is closed', function () {
  829. beforeEach(async function () {
  830. await this.manager.$_setTargetClosed(this.ownable.address, true);
  831. });
  832. it('directly call: reverts', async function () {
  833. await expectRevertCustomError(this.ownable.$_checkOwner({ from: user }), 'OwnableUnauthorizedAccount', [user]);
  834. });
  835. it('relayed call (with role): reverts', async function () {
  836. await expectRevertCustomError(
  837. this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: user }),
  838. 'AccessManagerUnauthorizedCall',
  839. [user, this.ownable.address, selector('$_checkOwner()')],
  840. );
  841. });
  842. it('relayed call (without role): reverts', async function () {
  843. await expectRevertCustomError(
  844. this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: other }),
  845. 'AccessManagerUnauthorizedCall',
  846. [other, this.ownable.address, selector('$_checkOwner()')],
  847. );
  848. });
  849. });
  850. describe('Contract is managed', function () {
  851. describe('function is open to specific role', function () {
  852. beforeEach(async function () {
  853. await this.manager.$_setTargetFunctionRole(this.ownable.address, selector('$_checkOwner()'), roleId);
  854. });
  855. it('directly call: reverts', async function () {
  856. await expectRevertCustomError(this.ownable.$_checkOwner({ from: user }), 'OwnableUnauthorizedAccount', [
  857. user,
  858. ]);
  859. });
  860. it('relayed call (with role): success', async function () {
  861. await this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: user });
  862. });
  863. it('relayed call (without role): reverts', async function () {
  864. await expectRevertCustomError(
  865. this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: other }),
  866. 'AccessManagerUnauthorizedCall',
  867. [other, this.ownable.address, selector('$_checkOwner()')],
  868. );
  869. });
  870. });
  871. describe('function is open to public role', function () {
  872. beforeEach(async function () {
  873. await this.manager.$_setTargetFunctionRole(this.ownable.address, selector('$_checkOwner()'), ROLES.PUBLIC);
  874. });
  875. it('directly call: reverts', async function () {
  876. await expectRevertCustomError(this.ownable.$_checkOwner({ from: user }), 'OwnableUnauthorizedAccount', [
  877. user,
  878. ]);
  879. });
  880. it('relayed call (with role): success', async function () {
  881. await this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: user });
  882. });
  883. it('relayed call (without role): success', async function () {
  884. await this.manager.execute(this.ownable.address, selector('$_checkOwner()'), { from: other });
  885. });
  886. });
  887. });
  888. });
  889. describe('authority update', function () {
  890. beforeEach(async function () {
  891. this.newManager = await AccessManager.new(admin);
  892. this.target = await AccessManagedTarget.new(this.manager.address);
  893. });
  894. it('admin can change authority', async function () {
  895. expect(await this.target.authority()).to.be.equal(this.manager.address);
  896. const { tx } = await this.manager.updateAuthority(this.target.address, this.newManager.address, { from: admin });
  897. await expectEvent.inTransaction(tx, this.target, 'AuthorityUpdated', { authority: this.newManager.address });
  898. expect(await this.target.authority()).to.be.equal(this.newManager.address);
  899. });
  900. it('cannot set an address without code as the authority', async function () {
  901. await expectRevertCustomError(
  902. this.manager.updateAuthority(this.target.address, user, { from: admin }),
  903. 'AccessManagedInvalidAuthority',
  904. [user],
  905. );
  906. });
  907. it('updateAuthority is restricted on manager', async function () {
  908. await expectRevertCustomError(
  909. this.manager.updateAuthority(this.target.address, this.newManager.address, { from: other }),
  910. 'AccessManagerUnauthorizedAccount',
  911. [other, ROLES.ADMIN],
  912. );
  913. });
  914. it('setAuthority is restricted on AccessManaged', async function () {
  915. await expectRevertCustomError(
  916. this.target.setAuthority(this.newManager.address, { from: admin }),
  917. 'AccessManagedUnauthorized',
  918. [admin],
  919. );
  920. });
  921. });
  922. // TODO:
  923. // - check opening/closing a contract
  924. // - check updating the contract delay
  925. // - check the delay applies to admin function
  926. describe.skip('contract modes', function () {});
  927. });