AccessManager.test.js 105 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture, getStorageAt } = require('@nomicfoundation/hardhat-network-helpers');
  4. const { impersonate } = require('../../helpers/account');
  5. const { MAX_UINT48 } = require('../../helpers/constants');
  6. const { bigint: time } = require('../../helpers/time');
  7. const { selector } = require('../../helpers/methods');
  8. const {
  9. buildBaseRoles,
  10. formatAccess,
  11. EXPIRATION,
  12. MINSETBACK,
  13. EXECUTION_ID_STORAGE_SLOT,
  14. CONSUMING_SCHEDULE_STORAGE_SLOT,
  15. prepareOperation,
  16. hashOperation,
  17. } = require('../../helpers/access-manager');
  18. const {
  19. shouldBehaveLikeDelayedAdminOperation,
  20. shouldBehaveLikeNotDelayedAdminOperation,
  21. shouldBehaveLikeRoleAdminOperation,
  22. shouldBehaveLikeAManagedRestrictedOperation,
  23. } = require('./AccessManager.behavior');
  24. const {
  25. LIKE_COMMON_SCHEDULABLE,
  26. testAsClosable,
  27. testAsDelay,
  28. testAsSchedulableOperation,
  29. testAsCanCall,
  30. testAsHasRole,
  31. testAsGetAccess,
  32. } = require('./AccessManager.predicate');
  33. const { address: someAddress } = ethers.Wallet.createRandom();
  34. async function fixture() {
  35. const [admin, roleAdmin, roleGuardian, member, user, other] = await ethers.getSigners();
  36. // Build roles
  37. const roles = buildBaseRoles();
  38. // Add members
  39. roles.ADMIN.members = [admin];
  40. roles.SOME_ADMIN.members = [roleAdmin];
  41. roles.SOME_GUARDIAN.members = [roleGuardian];
  42. roles.SOME.members = [member];
  43. roles.PUBLIC.members = [admin, roleAdmin, roleGuardian, member, user, other];
  44. const manager = await ethers.deployContract('$AccessManager', [admin]);
  45. const target = await ethers.deployContract('$AccessManagedTarget', [manager]);
  46. for (const { id: roleId, admin, guardian, members } of Object.values(roles)) {
  47. if (roleId === roles.PUBLIC.id) continue; // Every address belong to public and is locked
  48. if (roleId === roles.ADMIN.id) continue; // Admin set during construction and is locked
  49. // Set admin role avoiding default
  50. if (admin.id !== roles.ADMIN.id) {
  51. await manager.$_setRoleAdmin(roleId, admin.id);
  52. }
  53. // Set guardian role avoiding default
  54. if (guardian.id !== roles.ADMIN.id) {
  55. await manager.$_setRoleGuardian(roleId, guardian.id);
  56. }
  57. // Grant role to members
  58. for (const member of members) {
  59. await manager.$_grantRole(roleId, member, 0, 0);
  60. }
  61. }
  62. return {
  63. // TODO: Check if all signers are actually used
  64. admin,
  65. roleAdmin,
  66. roleGuardian,
  67. member,
  68. user,
  69. other,
  70. roles,
  71. manager,
  72. target,
  73. };
  74. }
  75. // This test suite is made using the following tools:
  76. //
  77. // * Predicates: Functions with common conditional setups without assertions.
  78. // * Behaviors: Functions with common assertions.
  79. //
  80. // The behavioral tests are built by composing predicates and are used as templates
  81. // for testing access to restricted functions.
  82. //
  83. // Similarly, unit tests in this suite will use predicates to test subsets of these
  84. // behaviors and are helped by common assertions provided for some of the predicates.
  85. //
  86. // The predicates can be identified by the `testAs*` prefix while the behaviors
  87. // are prefixed with `shouldBehave*`. The common assertions for predicates are
  88. // defined as constants.
  89. contract('AccessManager', function () {
  90. // const [admin, manager, guardian, member, user, other] = accounts;
  91. beforeEach(async function () {
  92. Object.assign(this, await loadFixture(fixture));
  93. });
  94. describe('during construction', function () {
  95. it('grants admin role to initialAdmin', async function () {
  96. const manager = await ethers.deployContract('$AccessManager', [this.other]);
  97. expect(await manager.hasRole(this.roles.ADMIN.id, this.other).then(formatAccess)).to.be.deep.equal([true, '0']);
  98. });
  99. it('rejects zero address for initialAdmin', async function () {
  100. await expect(ethers.deployContract('$AccessManager', [ethers.ZeroAddress]))
  101. .to.be.revertedWithCustomError(this.manager, 'AccessManagerInvalidInitialAdmin')
  102. .withArgs(ethers.ZeroAddress);
  103. });
  104. it('initializes setup roles correctly', async function () {
  105. for (const { id: roleId, admin, guardian, members } of Object.values(this.roles)) {
  106. expect(await this.manager.getRoleAdmin(roleId)).to.equal(admin.id);
  107. expect(await this.manager.getRoleGuardian(roleId)).to.equal(guardian.id);
  108. for (const user of this.roles.PUBLIC.members) {
  109. expect(await this.manager.hasRole(roleId, user).then(formatAccess)).to.be.deep.equal([
  110. members.includes(user),
  111. '0',
  112. ]);
  113. }
  114. }
  115. });
  116. });
  117. describe('getters', function () {
  118. describe('#canCall', function () {
  119. beforeEach('set calldata', function () {
  120. this.calldata = '0x12345678';
  121. this.role = { id: 379204n };
  122. });
  123. testAsCanCall({
  124. closed() {
  125. it('should return false and no delay', async function () {
  126. const { immediate, delay } = await this.manager.canCall(
  127. someAddress,
  128. this.target,
  129. this.calldata.substring(0, 10),
  130. );
  131. expect(immediate).to.be.false;
  132. expect(delay).to.equal(0n);
  133. });
  134. },
  135. open: {
  136. callerIsTheManager: {
  137. executing() {
  138. it('should return true and no delay', async function () {
  139. const { immediate, delay } = await this.manager.canCall(
  140. this.caller,
  141. this.target,
  142. this.calldata.substring(0, 10),
  143. );
  144. expect(immediate).to.be.true;
  145. expect(delay).to.equal(0n);
  146. });
  147. },
  148. notExecuting() {
  149. it('should return false and no delay', async function () {
  150. const { immediate, delay } = await this.manager.canCall(
  151. this.caller,
  152. this.target,
  153. this.calldata.substring(0, 10),
  154. );
  155. expect(immediate).to.be.false;
  156. expect(delay).to.equal(0n);
  157. });
  158. },
  159. },
  160. callerIsNotTheManager: {
  161. publicRoleIsRequired() {
  162. it('should return true and no delay', async function () {
  163. const { immediate, delay } = await this.manager.canCall(
  164. this.caller,
  165. this.target,
  166. this.calldata.substring(0, 10),
  167. );
  168. expect(immediate).to.be.true;
  169. expect(delay).to.equal(0n);
  170. });
  171. },
  172. specificRoleIsRequired: {
  173. requiredRoleIsGranted: {
  174. roleGrantingIsDelayed: {
  175. callerHasAnExecutionDelay: {
  176. beforeGrantDelay: function self() {
  177. self.mineDelay = true;
  178. it('should return false and no execution delay', async function () {
  179. const { immediate, delay } = await this.manager.canCall(
  180. this.caller,
  181. this.target,
  182. this.calldata.substring(0, 10),
  183. );
  184. expect(immediate).to.be.false;
  185. expect(delay).to.equal(0n);
  186. });
  187. },
  188. afterGrantDelay: function self() {
  189. self.mineDelay = true;
  190. beforeEach('sets execution delay', function () {
  191. this.scheduleIn = this.executionDelay; // For testAsSchedulableOperation
  192. });
  193. testAsSchedulableOperation({
  194. scheduled: {
  195. before: function self() {
  196. self.mineDelay = true;
  197. it('should return false and execution delay', async function () {
  198. const { immediate, delay } = await this.manager.canCall(
  199. this.caller,
  200. this.target,
  201. this.calldata.substring(0, 10),
  202. );
  203. expect(immediate).to.be.false;
  204. expect(delay).to.equal(this.executionDelay);
  205. });
  206. },
  207. after: function self() {
  208. self.mineDelay = true;
  209. it('should return false and execution delay', async function () {
  210. const { immediate, delay } = await this.manager.canCall(
  211. this.caller,
  212. this.target,
  213. this.calldata.substring(0, 10),
  214. );
  215. expect(immediate).to.be.false;
  216. expect(delay).to.equal(this.executionDelay);
  217. });
  218. },
  219. expired: function self() {
  220. self.mineDelay = true;
  221. it('should return false and execution delay', async function () {
  222. const { immediate, delay } = await this.manager.canCall(
  223. this.caller,
  224. this.target,
  225. this.calldata.substring(0, 10),
  226. );
  227. expect(immediate).to.be.false;
  228. expect(delay).to.equal(this.executionDelay);
  229. });
  230. },
  231. },
  232. notScheduled() {
  233. it('should return false and execution delay', async function () {
  234. const { immediate, delay } = await this.manager.canCall(
  235. this.caller,
  236. this.target,
  237. this.calldata.substring(0, 10),
  238. );
  239. expect(immediate).to.be.false;
  240. expect(delay).to.equal(this.executionDelay);
  241. });
  242. },
  243. });
  244. },
  245. },
  246. callerHasNoExecutionDelay: {
  247. beforeGrantDelay: function self() {
  248. self.mineDelay = true;
  249. it('should return false and no execution delay', async function () {
  250. const { immediate, delay } = await this.manager.canCall(
  251. this.caller,
  252. this.target,
  253. this.calldata.substring(0, 10),
  254. );
  255. expect(immediate).to.be.false;
  256. expect(delay).to.equal(0n);
  257. });
  258. },
  259. afterGrantDelay: function self() {
  260. self.mineDelay = true;
  261. it('should return true and no execution delay', async function () {
  262. const { immediate, delay } = await this.manager.canCall(
  263. this.caller,
  264. this.target,
  265. this.calldata.substring(0, 10),
  266. );
  267. expect(immediate).to.be.true;
  268. expect(delay).to.equal(0n);
  269. });
  270. },
  271. },
  272. },
  273. roleGrantingIsNotDelayed: {
  274. callerHasAnExecutionDelay() {
  275. it('should return false and execution delay', async function () {
  276. const { immediate, delay } = await this.manager.canCall(
  277. this.caller,
  278. this.target,
  279. this.calldata.substring(0, 10),
  280. );
  281. expect(immediate).to.be.false;
  282. expect(delay).to.equal(this.executionDelay);
  283. });
  284. },
  285. callerHasNoExecutionDelay() {
  286. it('should return true and no execution delay', async function () {
  287. const { immediate, delay } = await this.manager.canCall(
  288. this.caller,
  289. this.target,
  290. this.calldata.substring(0, 10),
  291. );
  292. expect(immediate).to.be.true;
  293. expect(delay).to.equal(0n);
  294. });
  295. },
  296. },
  297. },
  298. requiredRoleIsNotGranted() {
  299. it('should return false and no execution delay', async function () {
  300. const { immediate, delay } = await this.manager.canCall(
  301. this.caller,
  302. this.target,
  303. this.calldata.substring(0, 10),
  304. );
  305. expect(immediate).to.be.false;
  306. expect(delay).to.equal(0n);
  307. });
  308. },
  309. },
  310. },
  311. },
  312. });
  313. });
  314. describe('#expiration', function () {
  315. it('has a 7 days default expiration', async function () {
  316. expect(await this.manager.expiration()).to.equal(EXPIRATION);
  317. });
  318. });
  319. describe('#minSetback', function () {
  320. it('has a 5 days default minimum setback', async function () {
  321. expect(await this.manager.minSetback()).to.equal(MINSETBACK);
  322. });
  323. });
  324. describe('#isTargetClosed', function () {
  325. testAsClosable({
  326. closed() {
  327. it('returns true', async function () {
  328. expect(await this.manager.isTargetClosed(this.target)).to.be.true;
  329. });
  330. },
  331. open() {
  332. it('returns false', async function () {
  333. expect(await this.manager.isTargetClosed(this.target)).to.be.false;
  334. });
  335. },
  336. });
  337. });
  338. describe('#getTargetFunctionRole', function () {
  339. const methodSelector = selector('something(address,bytes)');
  340. it('returns the target function role', async function () {
  341. const roleId = 21498n;
  342. await this.manager.$_setTargetFunctionRole(this.target, methodSelector, roleId);
  343. expect(await this.manager.getTargetFunctionRole(this.target, methodSelector)).to.equal(roleId);
  344. });
  345. it('returns the ADMIN role if not set', async function () {
  346. expect(await this.manager.getTargetFunctionRole(this.target, methodSelector)).to.equal(this.roles.ADMIN.id);
  347. });
  348. });
  349. describe('#getTargetAdminDelay', function () {
  350. describe('when the target admin delay is setup', function () {
  351. beforeEach('set target admin delay', async function () {
  352. this.oldDelay = await this.manager.getTargetAdminDelay(this.target);
  353. this.newDelay = time.duration.days(10);
  354. await this.manager.$_setTargetAdminDelay(this.target, this.newDelay);
  355. this.delay = MINSETBACK; // For testAsDelay
  356. });
  357. testAsDelay('effect', {
  358. before: function self() {
  359. self.mineDelay = true;
  360. it('returns the old target admin delay', async function () {
  361. expect(await this.manager.getTargetAdminDelay(this.target)).to.equal(this.oldDelay);
  362. });
  363. },
  364. after: function self() {
  365. self.mineDelay = true;
  366. it('returns the new target admin delay', async function () {
  367. expect(await this.manager.getTargetAdminDelay(this.target)).to.equal(this.newDelay);
  368. });
  369. },
  370. });
  371. });
  372. it('returns the 0 if not set', async function () {
  373. expect(await this.manager.getTargetAdminDelay(this.target)).to.equal(0n);
  374. });
  375. });
  376. describe('#getRoleAdmin', function () {
  377. const roleId = 5234907n;
  378. it('returns the role admin', async function () {
  379. const adminId = 789433n;
  380. await this.manager.$_setRoleAdmin(roleId, adminId);
  381. expect(await this.manager.getRoleAdmin(roleId)).to.equal(adminId);
  382. });
  383. it('returns the ADMIN role if not set', async function () {
  384. expect(await this.manager.getRoleAdmin(roleId)).to.equal(this.roles.ADMIN.id);
  385. });
  386. });
  387. describe('#getRoleGuardian', function () {
  388. const roleId = 5234907n;
  389. it('returns the role guardian', async function () {
  390. const guardianId = 789433n;
  391. await this.manager.$_setRoleGuardian(roleId, guardianId);
  392. expect(await this.manager.getRoleGuardian(roleId)).to.equal(guardianId);
  393. });
  394. it('returns the ADMIN role if not set', async function () {
  395. expect(await this.manager.getRoleGuardian(roleId)).to.equal(this.roles.ADMIN.id);
  396. });
  397. });
  398. describe('#getRoleGrantDelay', function () {
  399. const roleId = 9248439n;
  400. describe('when the grant admin delay is setup', function () {
  401. beforeEach('set grant admin delay', async function () {
  402. this.oldDelay = await this.manager.getRoleGrantDelay(roleId);
  403. this.newDelay = time.duration.days(11);
  404. await this.manager.$_setGrantDelay(roleId, this.newDelay);
  405. this.delay = MINSETBACK; // For testAsDelay
  406. });
  407. testAsDelay('grant', {
  408. before: function self() {
  409. self.mineDelay = true;
  410. it('returns the old role grant delay', async function () {
  411. expect(await this.manager.getRoleGrantDelay(roleId)).to.equal(this.oldDelay);
  412. });
  413. },
  414. after: function self() {
  415. self.mineDelay = true;
  416. it('returns the new role grant delay', async function () {
  417. expect(await this.manager.getRoleGrantDelay(roleId)).to.equal(this.newDelay);
  418. });
  419. },
  420. });
  421. });
  422. it('returns 0 if delay is not set', async function () {
  423. expect(await this.manager.getTargetAdminDelay(this.target)).to.equal(0n);
  424. });
  425. });
  426. describe('#getAccess', function () {
  427. beforeEach('set role', function () {
  428. this.role = { id: 9452n };
  429. this.caller = this.user;
  430. });
  431. testAsGetAccess({
  432. requiredRoleIsGranted: {
  433. roleGrantingIsDelayed: {
  434. callerHasAnExecutionDelay: {
  435. beforeGrantDelay: function self() {
  436. self.mineDelay = true;
  437. it('role is not in effect and execution delay is set', async function () {
  438. const access = await this.manager.getAccess(this.role.id, this.caller);
  439. expect(access[0]).to.equal(this.delayEffect); // inEffectSince
  440. expect(access[1]).to.equal(this.executionDelay); // currentDelay
  441. expect(access[2]).to.equal(0n); // pendingDelay
  442. expect(access[3]).to.equal(0n); // pendingDelayEffect
  443. // Not in effect yet
  444. expect(await time.clock.timestamp()).to.lt(access[0]);
  445. });
  446. },
  447. afterGrantDelay: function self() {
  448. self.mineDelay = true;
  449. it('access has role in effect and execution delay is set', async function () {
  450. const access = await this.manager.getAccess(this.role.id, this.caller);
  451. expect(access[0]).to.equal(this.delayEffect); // inEffectSince
  452. expect(access[1]).to.equal(this.executionDelay); // currentDelay
  453. expect(access[2]).to.equal(0n); // pendingDelay
  454. expect(access[3]).to.equal(0n); // pendingDelayEffect
  455. // Already in effect
  456. expect(await time.clock.timestamp()).to.equal(access[0]);
  457. });
  458. },
  459. },
  460. callerHasNoExecutionDelay: {
  461. beforeGrantDelay: function self() {
  462. self.mineDelay = true;
  463. it('access has role not in effect without execution delay', async function () {
  464. const access = await this.manager.getAccess(this.role.id, this.caller);
  465. expect(access[0]).to.equal(this.delayEffect); // inEffectSince
  466. expect(access[1]).to.equal(0n); // currentDelay
  467. expect(access[2]).to.equal(0n); // pendingDelay
  468. expect(access[3]).to.equal(0n); // pendingDelayEffect
  469. // Not in effect yet
  470. expect(await time.clock.timestamp()).to.lt(access[0]);
  471. });
  472. },
  473. afterGrantDelay: function self() {
  474. self.mineDelay = true;
  475. it('role is in effect without execution delay', async function () {
  476. const access = await this.manager.getAccess(this.role.id, this.caller);
  477. expect(access[0]).to.equal(this.delayEffect); // inEffectSince
  478. expect(access[1]).to.equal(0n); // currentDelay
  479. expect(access[2]).to.equal(0n); // pendingDelay
  480. expect(access[3]).to.equal(0n); // pendingDelayEffect
  481. // Already in effect
  482. expect(await time.clock.timestamp()).to.equal(access[0]);
  483. });
  484. },
  485. },
  486. },
  487. roleGrantingIsNotDelayed: {
  488. callerHasAnExecutionDelay() {
  489. it('access has role in effect and execution delay is set', async function () {
  490. const access = await this.manager.getAccess(this.role.id, this.caller);
  491. expect(access[0]).to.equal(await time.clock.timestamp()); // inEffectSince
  492. expect(access[1]).to.equal(this.executionDelay); // currentDelay
  493. expect(access[2]).to.equal(0n); // pendingDelay
  494. expect(access[3]).to.equal(0n); // pendingDelayEffect
  495. // Already in effect
  496. expect(await time.clock.timestamp()).to.equal(access[0]);
  497. });
  498. },
  499. callerHasNoExecutionDelay() {
  500. it('access has role in effect without execution delay', async function () {
  501. const access = await this.manager.getAccess(this.role.id, this.caller);
  502. expect(access[0]).to.equal(await time.clock.timestamp()); // inEffectSince
  503. expect(access[1]).to.equal(0n); // currentDelay
  504. expect(access[2]).to.equal(0n); // pendingDelay
  505. expect(access[3]).to.equal(0n); // pendingDelayEffect
  506. // Already in effect
  507. expect(await time.clock.timestamp()).to.equal(access[0]);
  508. });
  509. },
  510. },
  511. },
  512. requiredRoleIsNotGranted() {
  513. it('has empty access', async function () {
  514. const access = await this.manager.getAccess(this.role.id, this.caller);
  515. expect(access[0]).to.equal(0n); // inEffectSince
  516. expect(access[1]).to.equal(0n); // currentDelay
  517. expect(access[2]).to.equal(0n); // pendingDelay
  518. expect(access[3]).to.equal(0n); // pendingDelayEffect
  519. });
  520. },
  521. });
  522. });
  523. describe('#hasRole', function () {
  524. beforeEach('setup testAsHasRole', function () {
  525. this.role = { id: 49832n };
  526. this.calldata = '0x12345678';
  527. this.caller = this.user;
  528. });
  529. testAsHasRole({
  530. publicRoleIsRequired() {
  531. it('has PUBLIC role', async function () {
  532. const { isMember, executionDelay } = await this.manager.hasRole(this.role.id, this.caller);
  533. expect(isMember).to.be.true;
  534. expect(executionDelay).to.equal('0');
  535. });
  536. },
  537. specificRoleIsRequired: {
  538. requiredRoleIsGranted: {
  539. roleGrantingIsDelayed: {
  540. callerHasAnExecutionDelay: {
  541. beforeGrantDelay: function self() {
  542. self.mineDelay = true;
  543. it('does not have role but execution delay', async function () {
  544. const { isMember, executionDelay } = await this.manager.hasRole(this.role.id, this.caller);
  545. expect(isMember).to.be.false;
  546. expect(executionDelay).to.equal(this.executionDelay);
  547. });
  548. },
  549. afterGrantDelay: function self() {
  550. self.mineDelay = true;
  551. it('has role and execution delay', async function () {
  552. const { isMember, executionDelay } = await this.manager.hasRole(this.role.id, this.caller);
  553. expect(isMember).to.be.true;
  554. expect(executionDelay).to.equal(this.executionDelay);
  555. });
  556. },
  557. },
  558. callerHasNoExecutionDelay: {
  559. beforeGrantDelay: function self() {
  560. self.mineDelay = true;
  561. it('does not have role nor execution delay', async function () {
  562. const { isMember, executionDelay } = await this.manager.hasRole(this.role.id, this.caller);
  563. expect(isMember).to.be.false;
  564. expect(executionDelay).to.equal('0');
  565. });
  566. },
  567. afterGrantDelay: function self() {
  568. self.mineDelay = true;
  569. it('has role and no execution delay', async function () {
  570. const { isMember, executionDelay } = await this.manager.hasRole(this.role.id, this.caller);
  571. expect(isMember).to.be.true;
  572. expect(executionDelay).to.equal('0');
  573. });
  574. },
  575. },
  576. },
  577. roleGrantingIsNotDelayed: {
  578. callerHasAnExecutionDelay() {
  579. it('has role and execution delay', async function () {
  580. const { isMember, executionDelay } = await this.manager.hasRole(this.role.id, this.caller);
  581. expect(isMember).to.be.true;
  582. expect(executionDelay).to.equal(this.executionDelay);
  583. });
  584. },
  585. callerHasNoExecutionDelay() {
  586. it('has role and no execution delay', async function () {
  587. const { isMember, executionDelay } = await this.manager.hasRole(this.role.id, this.caller);
  588. expect(isMember).to.be.true;
  589. expect(executionDelay).to.equal('0');
  590. });
  591. },
  592. },
  593. },
  594. requiredRoleIsNotGranted() {
  595. it('has no role and no execution delay', async function () {
  596. const { isMember, executionDelay } = await this.manager.hasRole(this.role.id, this.caller);
  597. expect(isMember).to.be.false;
  598. expect(executionDelay).to.equal('0');
  599. });
  600. },
  601. },
  602. });
  603. });
  604. describe('#getSchedule', function () {
  605. beforeEach('set role and calldata', async function () {
  606. const fnRestricted = this.target.fnRestricted.getFragment().selector;
  607. this.caller = this.user;
  608. this.role = { id: 493590n };
  609. await this.manager.$_setTargetFunctionRole(this.target, fnRestricted, this.role.id);
  610. await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); // nonzero execution delay
  611. this.calldata = this.target.interface.encodeFunctionData(fnRestricted, []);
  612. this.scheduleIn = time.duration.days(10); // For testAsSchedulableOperation
  613. });
  614. testAsSchedulableOperation({
  615. scheduled: {
  616. before: function self() {
  617. self.mineDelay = true;
  618. it('returns schedule in the future', async function () {
  619. const schedule = await this.manager.getSchedule(this.operationId);
  620. expect(schedule).to.equal(this.scheduledAt + this.scheduleIn);
  621. expect(schedule).to.gt(await time.clock.timestamp());
  622. });
  623. },
  624. after: function self() {
  625. self.mineDelay = true;
  626. it('returns schedule', async function () {
  627. const schedule = await this.manager.getSchedule(this.operationId);
  628. expect(schedule).to.equal(this.scheduledAt + this.scheduleIn);
  629. expect(schedule).to.equal(await time.clock.timestamp());
  630. });
  631. },
  632. expired: function self() {
  633. self.mineDelay = true;
  634. it('returns 0', async function () {
  635. expect(await this.manager.getSchedule(this.operationId)).to.equal(0n);
  636. });
  637. },
  638. },
  639. notScheduled() {
  640. it('defaults to 0', async function () {
  641. expect(await this.manager.getSchedule(this.operationId)).to.equal(0n);
  642. });
  643. },
  644. });
  645. });
  646. describe('#getNonce', function () {
  647. describe('when operation is scheduled', function () {
  648. beforeEach('schedule operation', async function () {
  649. const fnRestricted = this.target.fnRestricted.getFragment().selector;
  650. this.caller = this.user;
  651. this.role = { id: 4209043n };
  652. await this.manager.$_setTargetFunctionRole(this.target, fnRestricted, this.role.id);
  653. await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); // nonzero execution delay
  654. this.calldata = this.target.interface.encodeFunctionData(fnRestricted, []);
  655. this.delay = time.duration.days(10);
  656. const { operationId, schedule } = await prepareOperation(this.manager, {
  657. caller: this.caller,
  658. target: this.target,
  659. calldata: this.calldata,
  660. delay: this.delay,
  661. });
  662. await schedule();
  663. this.operationId = operationId;
  664. });
  665. it('returns nonce', async function () {
  666. expect(await this.manager.getNonce(this.operationId)).to.equal(1n);
  667. });
  668. });
  669. describe('when is not scheduled', function () {
  670. it('returns default 0', async function () {
  671. expect(await this.manager.getNonce(ethers.id('operation'))).to.equal(0n);
  672. });
  673. });
  674. });
  675. describe('#hashOperation', function () {
  676. it('returns an operationId', async function () {
  677. const calldata = '0x123543';
  678. const address = someAddress;
  679. const args = [this.user.address, address, calldata];
  680. expect(await this.manager.hashOperation(...args)).to.equal(hashOperation(...args));
  681. });
  682. });
  683. });
  684. describe('admin operations', function () {
  685. beforeEach('set required role', function () {
  686. this.role = this.roles.ADMIN;
  687. });
  688. describe('subject to a delay', function () {
  689. describe('#labelRole', function () {
  690. describe('restrictions', function () {
  691. beforeEach('set method and args', function () {
  692. const args = [123443, 'TEST'];
  693. const method = this.manager.interface.getFunction('labelRole(uint64,string)');
  694. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  695. });
  696. shouldBehaveLikeDelayedAdminOperation();
  697. });
  698. it('emits an event with the label', async function () {
  699. await expect(this.manager.connect(this.admin).labelRole(this.roles.SOME.id, 'Some label'))
  700. .to.emit(this.manager, 'RoleLabel')
  701. .withArgs(this.roles.SOME.id, 'Some label');
  702. });
  703. it('updates label on a second call', async function () {
  704. await this.manager.connect(this.admin).labelRole(this.roles.SOME.id, 'Some label');
  705. await expect(this.manager.connect(this.admin).labelRole(this.roles.SOME.id, 'Updated label'))
  706. .to.emit(this.manager, 'RoleLabel')
  707. .withArgs(this.roles.SOME.id, 'Updated label');
  708. });
  709. it('reverts labeling PUBLIC_ROLE', async function () {
  710. await expect(this.manager.connect(this.admin).labelRole(this.roles.PUBLIC.id, 'Some label'))
  711. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  712. .withArgs(this.roles.PUBLIC.id);
  713. });
  714. it('reverts labeling ADMIN_ROLE', async function () {
  715. await expect(this.manager.connect(this.admin).labelRole(this.roles.ADMIN.id, 'Some label'))
  716. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  717. .withArgs(this.roles.ADMIN.id);
  718. });
  719. });
  720. describe('#setRoleAdmin', function () {
  721. describe('restrictions', function () {
  722. beforeEach('set method and args', function () {
  723. const args = [93445, 84532];
  724. const method = this.manager.interface.getFunction('setRoleAdmin(uint64,uint64)');
  725. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  726. });
  727. shouldBehaveLikeDelayedAdminOperation();
  728. });
  729. it("sets any role's admin if called by an admin", async function () {
  730. expect(await this.manager.getRoleAdmin(this.roles.SOME.id)).to.equal(this.roles.SOME_ADMIN.id);
  731. await expect(this.manager.connect(this.admin).setRoleAdmin(this.roles.SOME.id, this.roles.ADMIN.id))
  732. .to.emit(this.manager, 'RoleAdminChanged')
  733. .withArgs(this.roles.SOME.id, this.roles.ADMIN.id);
  734. expect(await this.manager.getRoleAdmin(this.roles.SOME.id)).to.equal(this.roles.ADMIN.id);
  735. });
  736. it('reverts setting PUBLIC_ROLE admin', async function () {
  737. await expect(this.manager.connect(this.admin).setRoleAdmin(this.roles.PUBLIC.id, this.roles.ADMIN.id))
  738. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  739. .withArgs(this.roles.PUBLIC.id);
  740. });
  741. it('reverts setting ADMIN_ROLE admin', async function () {
  742. await expect(this.manager.connect(this.admin).setRoleAdmin(this.roles.ADMIN.id, this.roles.ADMIN.id))
  743. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  744. .withArgs(this.roles.ADMIN.id);
  745. });
  746. });
  747. describe('#setRoleGuardian', function () {
  748. describe('restrictions', function () {
  749. beforeEach('set method and args', function () {
  750. const args = [93445, 84532];
  751. const method = this.manager.interface.getFunction('setRoleGuardian(uint64,uint64)');
  752. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  753. });
  754. shouldBehaveLikeDelayedAdminOperation();
  755. });
  756. it("sets any role's guardian if called by an admin", async function () {
  757. expect(await this.manager.getRoleGuardian(this.roles.SOME.id)).to.equal(this.roles.SOME_GUARDIAN.id);
  758. await expect(this.manager.connect(this.admin).setRoleGuardian(this.roles.SOME.id, this.roles.ADMIN.id))
  759. .to.emit(this.manager, 'RoleGuardianChanged')
  760. .withArgs(this.roles.SOME.id, this.roles.ADMIN.id);
  761. expect(await this.manager.getRoleGuardian(this.roles.SOME.id)).to.equal(this.roles.ADMIN.id);
  762. });
  763. it('reverts setting PUBLIC_ROLE admin', async function () {
  764. await expect(this.manager.connect(this.admin).setRoleGuardian(this.roles.PUBLIC.id, this.roles.ADMIN.id))
  765. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  766. .withArgs(this.roles.PUBLIC.id);
  767. });
  768. it('reverts setting ADMIN_ROLE admin', async function () {
  769. await expect(this.manager.connect(this.admin).setRoleGuardian(this.roles.ADMIN.id, this.roles.ADMIN.id))
  770. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  771. .withArgs(this.roles.ADMIN.id);
  772. });
  773. });
  774. describe('#setGrantDelay', function () {
  775. describe('restrictions', function () {
  776. beforeEach('set method and args', function () {
  777. const args = [984910, time.duration.days(2)];
  778. const method = this.manager.interface.getFunction('setGrantDelay(uint64,uint32)');
  779. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  780. });
  781. shouldBehaveLikeDelayedAdminOperation();
  782. });
  783. it('reverts setting grant delay for the PUBLIC_ROLE', function () {
  784. expect(this.manager.connect(this.admin).setGrantDelay(this.roles.PUBLIC.id, 69n))
  785. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  786. .withArgs(this.roles.PUBLIC.id);
  787. });
  788. describe('when increasing the delay', function () {
  789. const oldDelay = 10n;
  790. const newDelay = 100n;
  791. beforeEach('sets old delay', async function () {
  792. this.role = this.roles.SOME;
  793. await this.manager.$_setGrantDelay(this.role.id, oldDelay);
  794. await time.increaseBy.timestamp(MINSETBACK);
  795. expect(await this.manager.getRoleGrantDelay(this.role.id)).to.equal(oldDelay);
  796. });
  797. it('increases the delay after minsetback', async function () {
  798. const txResponse = await this.manager.connect(this.admin).setGrantDelay(this.role.id, newDelay);
  799. const setGrantDelayAt = await time.clockFromReceipt.timestamp(txResponse);
  800. expect(txResponse)
  801. .to.emit(this.manager, 'RoleGrantDelayChanged')
  802. .withArgs(this.role.id, newDelay, setGrantDelayAt + MINSETBACK);
  803. expect(await this.manager.getRoleGrantDelay(this.role.id)).to.equal(oldDelay);
  804. await time.increaseBy.timestamp(MINSETBACK);
  805. expect(await this.manager.getRoleGrantDelay(this.role.id)).to.equal(newDelay);
  806. });
  807. });
  808. describe('when reducing the delay', function () {
  809. const oldDelay = time.duration.days(10);
  810. beforeEach('sets old delay', async function () {
  811. this.role = this.roles.SOME;
  812. await this.manager.$_setGrantDelay(this.role.id, oldDelay);
  813. await time.increaseBy.timestamp(MINSETBACK);
  814. expect(await this.manager.getRoleGrantDelay(this.role.id)).to.equal(oldDelay);
  815. });
  816. describe('when the delay difference is shorter than minimum setback', function () {
  817. const newDelay = oldDelay - 1n;
  818. it('increases the delay after minsetback', async function () {
  819. const txResponse = await this.manager.connect(this.admin).setGrantDelay(this.role.id, newDelay);
  820. const setGrantDelayAt = await time.clockFromReceipt.timestamp(txResponse);
  821. expect(txResponse)
  822. .to.emit(this.manager, 'RoleGrantDelayChanged')
  823. .withArgs(this.role.id, newDelay, setGrantDelayAt + MINSETBACK);
  824. expect(await this.manager.getRoleGrantDelay(this.role.id)).to.equal(oldDelay);
  825. await time.increaseBy.timestamp(MINSETBACK);
  826. expect(await this.manager.getRoleGrantDelay(this.role.id)).to.equal(newDelay);
  827. });
  828. });
  829. describe('when the delay difference is longer than minimum setback', function () {
  830. const newDelay = 1n;
  831. beforeEach('assert delay difference is higher than minsetback', function () {
  832. expect(oldDelay - newDelay).to.gt(MINSETBACK);
  833. });
  834. it('increases the delay after delay difference', async function () {
  835. const setback = oldDelay - newDelay;
  836. const txResponse = await this.manager.connect(this.admin).setGrantDelay(this.role.id, newDelay);
  837. const setGrantDelayAt = await time.clockFromReceipt.timestamp(txResponse);
  838. expect(txResponse)
  839. .to.emit(this.manager, 'RoleGrantDelayChanged')
  840. .withArgs(this.role.id, newDelay, setGrantDelayAt + setback);
  841. expect(await this.manager.getRoleGrantDelay(this.role.id)).to.equal(oldDelay);
  842. await time.increaseBy.timestamp(setback);
  843. expect(await this.manager.getRoleGrantDelay(this.role.id)).to.equal(newDelay);
  844. });
  845. });
  846. });
  847. });
  848. describe('#setTargetAdminDelay', function () {
  849. describe('restrictions', function () {
  850. beforeEach('set method and args', function () {
  851. const args = [someAddress, time.duration.days(3)];
  852. const method = this.manager.interface.getFunction('setTargetAdminDelay(address,uint32)');
  853. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  854. });
  855. shouldBehaveLikeDelayedAdminOperation();
  856. });
  857. describe('when increasing the delay', function () {
  858. const oldDelay = time.duration.days(10);
  859. const newDelay = time.duration.days(11);
  860. const target = someAddress;
  861. beforeEach('sets old delay', async function () {
  862. await this.manager.$_setTargetAdminDelay(target, oldDelay);
  863. await time.increaseBy.timestamp(MINSETBACK);
  864. expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay);
  865. });
  866. it('increases the delay after minsetback', async function () {
  867. const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(target, newDelay);
  868. const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse);
  869. expect(txResponse)
  870. .to.emit(this.manager, 'TargetAdminDelayUpdated')
  871. .withArgs(target, newDelay, setTargetAdminDelayAt + MINSETBACK);
  872. expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay);
  873. await time.increaseBy.timestamp(MINSETBACK);
  874. expect(await this.manager.getTargetAdminDelay(target)).to.equal(newDelay);
  875. });
  876. });
  877. describe('when reducing the delay', function () {
  878. const oldDelay = time.duration.days(10);
  879. const target = someAddress;
  880. beforeEach('sets old delay', async function () {
  881. await this.manager.$_setTargetAdminDelay(target, oldDelay);
  882. await time.increaseBy.timestamp(MINSETBACK);
  883. expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay);
  884. });
  885. describe('when the delay difference is shorter than minimum setback', function () {
  886. const newDelay = oldDelay - 1n;
  887. it('increases the delay after minsetback', async function () {
  888. const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(target, newDelay);
  889. const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse);
  890. expect(txResponse)
  891. .to.emit(this.manager, 'TargetAdminDelayUpdated')
  892. .withArgs(target, newDelay, setTargetAdminDelayAt + MINSETBACK);
  893. expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay);
  894. await time.increaseBy.timestamp(MINSETBACK);
  895. expect(await this.manager.getTargetAdminDelay(target)).to.equal(newDelay);
  896. });
  897. });
  898. describe('when the delay difference is longer than minimum setback', function () {
  899. const newDelay = 1n;
  900. beforeEach('assert delay difference is higher than minsetback', function () {
  901. expect(oldDelay - newDelay).to.gt(MINSETBACK);
  902. });
  903. it('increases the delay after delay difference', async function () {
  904. const setback = oldDelay - newDelay;
  905. const txResponse = await this.manager.connect(this.admin).setTargetAdminDelay(target, newDelay);
  906. const setTargetAdminDelayAt = await time.clockFromReceipt.timestamp(txResponse);
  907. expect(txResponse)
  908. .to.emit(this.manager, 'TargetAdminDelayUpdated')
  909. .withArgs(target, newDelay, setTargetAdminDelayAt + setback);
  910. expect(await this.manager.getTargetAdminDelay(target)).to.equal(oldDelay);
  911. await time.increaseBy.timestamp(setback);
  912. expect(await this.manager.getTargetAdminDelay(target)).to.equal(newDelay);
  913. });
  914. });
  915. });
  916. });
  917. });
  918. describe('not subject to a delay', function () {
  919. describe('#updateAuthority', function () {
  920. beforeEach('create a target and a new authority', async function () {
  921. this.newAuthority = await ethers.deployContract('$AccessManager', [this.admin]);
  922. this.newManagedTarget = await ethers.deployContract('$AccessManagedTarget', [this.manager]);
  923. });
  924. describe('restrictions', function () {
  925. beforeEach('set method and args', function () {
  926. this.calldata = this.manager.interface.encodeFunctionData('updateAuthority(address,address)', [
  927. this.newManagedTarget.target,
  928. this.newAuthority.target,
  929. ]);
  930. });
  931. shouldBehaveLikeNotDelayedAdminOperation();
  932. });
  933. it('changes the authority', async function () {
  934. expect(await this.newManagedTarget.authority()).to.be.equal(this.manager.target);
  935. await expect(this.manager.connect(this.admin).updateAuthority(this.newManagedTarget, this.newAuthority))
  936. .to.emit(this.newManagedTarget, 'AuthorityUpdated') // Managed contract is responsible of notifying the change through an event
  937. .withArgs(this.newAuthority.target);
  938. expect(await this.newManagedTarget.authority()).to.be.equal(this.newAuthority.target);
  939. });
  940. });
  941. describe('#setTargetClosed', function () {
  942. describe('restrictions', function () {
  943. beforeEach('set method and args', function () {
  944. const args = [someAddress, true];
  945. const method = this.manager.interface.getFunction('setTargetClosed(address,bool)');
  946. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  947. });
  948. shouldBehaveLikeNotDelayedAdminOperation();
  949. });
  950. it('closes and opens a target', async function () {
  951. await expect(this.manager.connect(this.admin).setTargetClosed(this.target, true))
  952. .to.emit(this.manager, 'TargetClosed')
  953. .withArgs(this.target.target, true);
  954. expect(await this.manager.isTargetClosed(this.target)).to.be.true;
  955. await expect(this.manager.connect(this.admin).setTargetClosed(this.target, false))
  956. .to.emit(this.manager, 'TargetClosed')
  957. .withArgs(this.target.target, false);
  958. expect(await this.manager.isTargetClosed(this.target)).to.be.false;
  959. });
  960. it('reverts if closing the manager', async function () {
  961. await expect(this.manager.connect(this.admin).setTargetClosed(this.manager, true))
  962. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedAccount')
  963. .withArgs(this.manager.target);
  964. });
  965. });
  966. describe('#setTargetFunctionRole', function () {
  967. describe('restrictions', function () {
  968. beforeEach('set method and args', function () {
  969. const args = [someAddress, ['0x12345678'], 443342];
  970. const method = this.manager.interface.getFunction('setTargetFunctionRole(address,bytes4[],uint64)');
  971. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  972. });
  973. shouldBehaveLikeNotDelayedAdminOperation();
  974. });
  975. const sigs = ['someFunction()', 'someOtherFunction(uint256)', 'oneMoreFunction(address,uint8)'].map(selector);
  976. it('sets function roles', async function () {
  977. for (const sig of sigs) {
  978. expect(await this.manager.getTargetFunctionRole(this.target, sig)).to.equal(this.roles.ADMIN.id);
  979. }
  980. const allowRole = await this.manager
  981. .connect(this.admin)
  982. .setTargetFunctionRole(this.target, sigs, this.roles.SOME.id);
  983. for (const sig of sigs) {
  984. expect(allowRole)
  985. .to.emit(this.manager, 'TargetFunctionRoleUpdated')
  986. .withArgs(this.target.target, sig, this.roles.SOME.id);
  987. expect(await this.manager.getTargetFunctionRole(this.target, sig)).to.equal(this.roles.SOME.id);
  988. }
  989. await expect(
  990. this.manager.connect(this.admin).setTargetFunctionRole(this.target, [sigs[1]], this.roles.SOME_ADMIN.id),
  991. )
  992. .to.emit(this.manager, 'TargetFunctionRoleUpdated')
  993. .withArgs(this.target.target, sigs[1], this.roles.SOME_ADMIN.id);
  994. for (const sig of sigs) {
  995. expect(await this.manager.getTargetFunctionRole(this.target, sig)).to.equal(
  996. sig == sigs[1] ? this.roles.SOME_ADMIN.id : this.roles.SOME.id,
  997. );
  998. }
  999. });
  1000. });
  1001. describe('role admin operations', function () {
  1002. const ANOTHER_ADMIN = 0xdeadc0de1n;
  1003. const ANOTHER_ROLE = 0xdeadc0de2n;
  1004. beforeEach('set required role', async function () {
  1005. // Make admin a member of ANOTHER_ADMIN
  1006. await this.manager.$_grantRole(ANOTHER_ADMIN, this.admin, 0, 0);
  1007. await this.manager.$_setRoleAdmin(ANOTHER_ROLE, ANOTHER_ADMIN);
  1008. this.role = { id: ANOTHER_ADMIN };
  1009. await this.manager.$_grantRole(this.role.id, this.user, 0, 0);
  1010. });
  1011. describe('#grantRole', function () {
  1012. describe('restrictions', function () {
  1013. beforeEach('set method and args', function () {
  1014. const args = [ANOTHER_ROLE, someAddress, 0];
  1015. const method = this.manager.interface.getFunction('grantRole(uint64,address,uint32)');
  1016. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  1017. });
  1018. shouldBehaveLikeRoleAdminOperation(ANOTHER_ADMIN);
  1019. });
  1020. it('reverts when granting PUBLIC_ROLE', async function () {
  1021. await expect(this.manager.connect(this.admin).grantRole(this.roles.PUBLIC.id, this.user, 0))
  1022. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  1023. .withArgs(this.roles.PUBLIC.id);
  1024. });
  1025. describe('when the user is not a role member', function () {
  1026. describe('with grant delay', function () {
  1027. beforeEach('set grant delay and grant role', async function () {
  1028. // Delay granting
  1029. this.grantDelay = time.duration.weeks(2);
  1030. await this.manager.$_setGrantDelay(ANOTHER_ROLE, this.grantDelay);
  1031. await time.increaseBy.timestamp(MINSETBACK);
  1032. // Grant role
  1033. this.executionDelay = time.duration.days(3);
  1034. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1035. false,
  1036. '0',
  1037. ]);
  1038. this.txResponse = await this.manager
  1039. .connect(this.admin)
  1040. .grantRole(ANOTHER_ROLE, this.user, this.executionDelay);
  1041. this.delay = this.grantDelay; // For testAsDelay
  1042. });
  1043. testAsDelay('grant', {
  1044. before: function self() {
  1045. self.mineDelay = true;
  1046. it('does not grant role to the user yet', async function () {
  1047. const timestamp = await time.clockFromReceipt.timestamp(this.txResponse);
  1048. expect(this.txResponse)
  1049. .to.emit(this.manager, 'RoleGranted')
  1050. .withArgs(ANOTHER_ROLE, this.user, timestamp + this.grantDelay, this.executionDelay, true);
  1051. // Access is correctly stored
  1052. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1053. expect(access[0]).to.equal(timestamp + this.grantDelay); // inEffectSince
  1054. expect(access[1]).to.equal(this.executionDelay); // currentDelay
  1055. expect(access[2]).to.equal(0n); // pendingDelay
  1056. expect(access[3]).to.equal(0n); // pendingDelayEffect
  1057. // Not in effect yet
  1058. const currentTimestamp = await time.clock.timestamp();
  1059. expect(currentTimestamp).to.be.lt(access[0]);
  1060. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1061. false,
  1062. this.executionDelay.toString(),
  1063. ]);
  1064. });
  1065. },
  1066. after: function self() {
  1067. self.mineDelay = true;
  1068. it('grants role to the user', async function () {
  1069. const timestamp = await time.clockFromReceipt.timestamp(this.txResponse);
  1070. expect(this.txResponse)
  1071. .to.emit(this.manager, 'RoleAccessRequested')
  1072. .withArgs(ANOTHER_ROLE, this.user, timestamp + this.grantDelay, this.executionDelay, true);
  1073. // Access is correctly stored
  1074. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1075. expect(access[0]).to.equal(timestamp + this.grantDelay); // inEffectSince
  1076. expect(access[1]).to.equal(this.executionDelay); // currentDelay
  1077. expect(access[2]).to.equal(0n); // pendingDelay
  1078. expect(access[3]).to.equal(0n); // pendingDelayEffect
  1079. // Already in effect
  1080. const currentTimestamp = await time.clock.timestamp();
  1081. expect(currentTimestamp).to.be.equal(access[0]);
  1082. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1083. true,
  1084. this.executionDelay.toString(),
  1085. ]);
  1086. });
  1087. },
  1088. });
  1089. });
  1090. describe('without grant delay', function () {
  1091. beforeEach('set granting delay', async function () {
  1092. // Delay granting
  1093. this.grantDelay = 0;
  1094. await this.manager.$_setGrantDelay(ANOTHER_ROLE, this.grantDelay);
  1095. await time.increaseBy.timestamp(MINSETBACK);
  1096. });
  1097. it('immediately grants the role to the user', async function () {
  1098. const executionDelay = time.duration.days(6);
  1099. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1100. false,
  1101. '0',
  1102. ]);
  1103. const txResponse = await this.manager
  1104. .connect(this.admin)
  1105. .grantRole(ANOTHER_ROLE, this.user, executionDelay);
  1106. const grantedAt = await time.clockFromReceipt.timestamp(txResponse);
  1107. expect(txResponse)
  1108. .to.emit(this.manager, 'RoleGranted')
  1109. .withArgs(ANOTHER_ROLE, this.user.address, executionDelay, grantedAt, true);
  1110. // Access is correctly stored
  1111. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1112. expect(access[0]).to.equal(grantedAt); // inEffectSince
  1113. expect(access[1]).to.equal(executionDelay); // currentDelay
  1114. expect(access[2]).to.equal(0n); // pendingDelay
  1115. expect(access[3]).to.equal(0n); // pendingDelayEffect
  1116. // Already in effect
  1117. const currentTimestamp = await time.clock.timestamp();
  1118. expect(currentTimestamp).to.be.equal(access[0]);
  1119. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1120. true,
  1121. executionDelay.toString(),
  1122. ]);
  1123. });
  1124. });
  1125. });
  1126. describe('when the user is already a role member', function () {
  1127. beforeEach('make user role member', async function () {
  1128. this.previousExecutionDelay = time.duration.days(6);
  1129. await this.manager.$_grantRole(ANOTHER_ROLE, this.user, 0, this.previousExecutionDelay);
  1130. this.oldAccess = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1131. });
  1132. describe('with grant delay', function () {
  1133. beforeEach('set granting delay', async function () {
  1134. // Delay granting
  1135. const grantDelay = time.duration.weeks(2);
  1136. await this.manager.$_setGrantDelay(ANOTHER_ROLE, grantDelay);
  1137. await time.increaseBy.timestamp(MINSETBACK);
  1138. });
  1139. describe('when increasing the execution delay', function () {
  1140. beforeEach('set increased new execution delay', async function () {
  1141. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1142. true,
  1143. this.previousExecutionDelay.toString(),
  1144. ]);
  1145. this.newExecutionDelay = this.previousExecutionDelay + time.duration.days(4);
  1146. });
  1147. it('emits event and immediately changes the execution delay', async function () {
  1148. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1149. true,
  1150. this.previousExecutionDelay.toString(),
  1151. ]);
  1152. const txResponse = await this.manager
  1153. .connect(this.admin)
  1154. .grantRole(ANOTHER_ROLE, this.user, this.newExecutionDelay);
  1155. const timestamp = await time.clockFromReceipt.timestamp(txResponse);
  1156. expect(txResponse)
  1157. .to.emit(this.manager, 'RoleGranted')
  1158. .withArgs(ANOTHER_ROLE, this.user.address, timestamp, this.newExecutionDelay, false);
  1159. // Access is correctly stored
  1160. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1161. expect(access[0]).to.equal(this.oldAccess[0]); // inEffectSince
  1162. expect(access[1]).to.equal(this.newExecutionDelay); // currentDelay
  1163. expect(access[2]).to.equal(0n); // pendingDelay
  1164. expect(access[3]).to.equal(0n); // pendingDelayEffect
  1165. // Already in effect
  1166. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1167. true,
  1168. this.newExecutionDelay.toString(),
  1169. ]);
  1170. });
  1171. });
  1172. describe('when decreasing the execution delay', function () {
  1173. beforeEach('decrease execution delay', async function () {
  1174. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1175. true,
  1176. this.previousExecutionDelay.toString(),
  1177. ]);
  1178. this.newExecutionDelay = this.previousExecutionDelay - time.duration.days(4);
  1179. this.txResponse = await this.manager
  1180. .connect(this.admin)
  1181. .grantRole(ANOTHER_ROLE, this.user, this.newExecutionDelay);
  1182. this.grantTimestamp = await time.clockFromReceipt.timestamp(this.txResponse);
  1183. this.delay = this.previousExecutionDelay - this.newExecutionDelay; // For testAsDelay
  1184. });
  1185. it('emits event', function () {
  1186. expect(this.txResponse)
  1187. .to.emit(this.manager, 'RoleGranted')
  1188. .withArgs(
  1189. ANOTHER_ROLE,
  1190. this.user.address,
  1191. this.grantTimestamp + this.delay,
  1192. this.newExecutionDelay,
  1193. false,
  1194. );
  1195. });
  1196. testAsDelay('execution delay effect', {
  1197. before: function self() {
  1198. self.mineDelay = true;
  1199. it('does not change the execution delay yet', async function () {
  1200. // Access is correctly stored
  1201. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1202. expect(access[0]).to.equal(this.oldAccess[0]); // inEffectSince
  1203. expect(access[1]).to.equal(this.previousExecutionDelay); // currentDelay
  1204. expect(access[2]).to.equal(this.newExecutionDelay); // pendingDelay
  1205. expect(access[3]).to.equal(this.grantTimestamp + this.delay); // pendingDelayEffect
  1206. // Not in effect yet
  1207. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1208. true,
  1209. this.previousExecutionDelay.toString(),
  1210. ]);
  1211. });
  1212. },
  1213. after: function self() {
  1214. self.mineDelay = true;
  1215. it('changes the execution delay', async function () {
  1216. // Access is correctly stored
  1217. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1218. expect(access[0]).to.equal(this.oldAccess[0]); // inEffectSince
  1219. expect(access[1]).to.equal(this.newExecutionDelay); // currentDelay
  1220. expect(access[2]).to.equal(0n); // pendingDelay
  1221. expect(access[3]).to.equal(0n); // pendingDelayEffect
  1222. // Already in effect
  1223. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1224. true,
  1225. this.newExecutionDelay.toString(),
  1226. ]);
  1227. });
  1228. },
  1229. });
  1230. });
  1231. });
  1232. describe('without grant delay', function () {
  1233. beforeEach('set granting delay', async function () {
  1234. // Delay granting
  1235. const grantDelay = 0;
  1236. await this.manager.$_setGrantDelay(ANOTHER_ROLE, grantDelay);
  1237. await time.increaseBy.timestamp(MINSETBACK);
  1238. });
  1239. describe('when increasing the execution delay', function () {
  1240. beforeEach('set increased new execution delay', async function () {
  1241. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1242. true,
  1243. this.previousExecutionDelay.toString(),
  1244. ]);
  1245. this.newExecutionDelay = this.previousExecutionDelay + time.duration.days(4);
  1246. });
  1247. it('emits event and immediately changes the execution delay', async function () {
  1248. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1249. true,
  1250. this.previousExecutionDelay.toString(),
  1251. ]);
  1252. const txResponse = await this.manager
  1253. .connect(this.admin)
  1254. .grantRole(ANOTHER_ROLE, this.user, this.newExecutionDelay);
  1255. const timestamp = await time.clockFromReceipt.timestamp(txResponse);
  1256. expect(txResponse)
  1257. .to.emit(this.manager, 'RoleGranted')
  1258. .withArgs(ANOTHER_ROLE, this.user.address, timestamp, this.newExecutionDelay, false);
  1259. // Access is correctly stored
  1260. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1261. expect(access[0]).to.equal(this.oldAccess[0]); // inEffectSince
  1262. expect(access[1]).to.equal(this.newExecutionDelay); // currentDelay
  1263. expect(access[2]).to.equal(0n); // pendingDelay
  1264. expect(access[3]).to.equal(0n); // pendingDelayEffect
  1265. // Already in effect
  1266. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1267. true,
  1268. this.newExecutionDelay.toString(),
  1269. ]);
  1270. });
  1271. });
  1272. describe('when decreasing the execution delay', function () {
  1273. beforeEach('decrease execution delay', async function () {
  1274. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1275. true,
  1276. this.previousExecutionDelay.toString(),
  1277. ]);
  1278. this.newExecutionDelay = this.previousExecutionDelay - time.duration.days(4);
  1279. this.txResponse = await this.manager
  1280. .connect(this.admin)
  1281. .grantRole(ANOTHER_ROLE, this.user, this.newExecutionDelay);
  1282. this.grantTimestamp = await time.clockFromReceipt.timestamp(this.txResponse);
  1283. this.delay = this.previousExecutionDelay - this.newExecutionDelay; // For testAsDelay
  1284. });
  1285. it('emits event', function () {
  1286. expect(this.txResponse)
  1287. .to.emit(this.manager, 'RoleGranted')
  1288. .withArgs(
  1289. ANOTHER_ROLE,
  1290. this.user.address,
  1291. this.grantTimestamp + this.delay,
  1292. this.newExecutionDelay,
  1293. false,
  1294. );
  1295. });
  1296. testAsDelay('execution delay effect', {
  1297. before: function self() {
  1298. self.mineDelay = true;
  1299. it('does not change the execution delay yet', async function () {
  1300. // Access is correctly stored
  1301. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1302. expect(access[0]).to.equal(this.oldAccess[0]); // inEffectSince
  1303. expect(access[1]).to.equal(this.previousExecutionDelay); // currentDelay
  1304. expect(access[2]).to.equal(this.newExecutionDelay); // pendingDelay
  1305. expect(access[3]).to.equal(this.grantTimestamp + this.delay); // pendingDelayEffect
  1306. // Not in effect yet
  1307. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1308. true,
  1309. this.previousExecutionDelay.toString(),
  1310. ]);
  1311. });
  1312. },
  1313. after: function self() {
  1314. self.mineDelay = true;
  1315. it('changes the execution delay', async function () {
  1316. // Access is correctly stored
  1317. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1318. expect(access[0]).to.equal(this.oldAccess[0]); // inEffectSince
  1319. expect(access[1]).to.equal(this.newExecutionDelay); // currentDelay
  1320. expect(access[2]).to.equal(0n); // pendingDelay
  1321. expect(access[3]).to.equal(0n); // pendingDelayEffect
  1322. // Already in effect
  1323. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1324. true,
  1325. this.newExecutionDelay.toString(),
  1326. ]);
  1327. });
  1328. },
  1329. });
  1330. });
  1331. });
  1332. });
  1333. });
  1334. describe('#revokeRole', function () {
  1335. describe('restrictions', function () {
  1336. beforeEach('set method and args', async function () {
  1337. const args = [ANOTHER_ROLE, someAddress];
  1338. const method = this.manager.interface.getFunction('revokeRole(uint64,address)');
  1339. this.calldata = this.manager.interface.encodeFunctionData(method, args);
  1340. // Need to be set before revoking
  1341. await this.manager.$_grantRole(...args, 0, 0);
  1342. });
  1343. shouldBehaveLikeRoleAdminOperation(ANOTHER_ADMIN);
  1344. });
  1345. describe('when role has been granted', function () {
  1346. beforeEach('grant role with grant delay', async function () {
  1347. this.grantDelay = time.duration.weeks(1);
  1348. await this.manager.$_grantRole(ANOTHER_ROLE, this.user, this.grantDelay, 0);
  1349. this.delay = this.grantDelay; // For testAsDelay
  1350. });
  1351. testAsDelay('grant', {
  1352. before: function self() {
  1353. self.mineDelay = true;
  1354. it('revokes a granted role that will take effect in the future', async function () {
  1355. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1356. false,
  1357. '0',
  1358. ]);
  1359. await expect(this.manager.connect(this.admin).revokeRole(ANOTHER_ROLE, this.user))
  1360. .to.emit(this.manager, 'RoleRevoked')
  1361. .withArgs(ANOTHER_ROLE, this.user.address);
  1362. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1363. false,
  1364. '0',
  1365. ]);
  1366. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1367. expect(access[0]).to.equal(0n); // inRoleSince
  1368. expect(access[1]).to.equal(0n); // currentDelay
  1369. expect(access[2]).to.equal(0n); // pendingDelay
  1370. expect(access[3]).to.equal(0n); // effect
  1371. });
  1372. },
  1373. after: function self() {
  1374. self.mineDelay = true;
  1375. it('revokes a granted role that already took effect', async function () {
  1376. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1377. true,
  1378. '0',
  1379. ]);
  1380. await expect(this.manager.connect(this.admin).revokeRole(ANOTHER_ROLE, this.user))
  1381. .to.emit(this.manager, 'RoleRevoked')
  1382. .withArgs(ANOTHER_ROLE, this.user.address);
  1383. expect(await this.manager.hasRole(ANOTHER_ROLE, this.user).then(formatAccess)).to.be.deep.equal([
  1384. false,
  1385. '0',
  1386. ]);
  1387. const access = await this.manager.getAccess(ANOTHER_ROLE, this.user);
  1388. expect(access[0]).to.equal(0n); // inRoleSince
  1389. expect(access[1]).to.equal(0n); // currentDelay
  1390. expect(access[2]).to.equal(0n); // pendingDelay
  1391. expect(access[3]).to.equal(0n); // effect
  1392. });
  1393. },
  1394. });
  1395. });
  1396. describe('when role has not been granted', function () {
  1397. it('has no effect', async function () {
  1398. expect(await this.manager.hasRole(this.roles.SOME.id, this.user).then(formatAccess)).to.be.deep.equal([
  1399. false,
  1400. '0',
  1401. ]);
  1402. await expect(this.manager.connect(this.roleAdmin).revokeRole(this.roles.SOME.id, this.user)).to.not.emit(
  1403. this.manager,
  1404. 'RoleRevoked',
  1405. );
  1406. expect(await this.manager.hasRole(this.roles.SOME.id, this.user).then(formatAccess)).to.be.deep.equal([
  1407. false,
  1408. '0',
  1409. ]);
  1410. });
  1411. });
  1412. it('reverts revoking PUBLIC_ROLE', async function () {
  1413. await expect(this.manager.connect(this.admin).revokeRole(this.roles.PUBLIC.id, this.user))
  1414. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  1415. .withArgs(this.roles.PUBLIC.id);
  1416. });
  1417. });
  1418. });
  1419. describe('self role operations', function () {
  1420. describe('#renounceRole', function () {
  1421. beforeEach('grant role', async function () {
  1422. this.role = { id: 783164n };
  1423. this.caller = this.user;
  1424. await this.manager.$_grantRole(this.role.id, this.caller, 0, 0);
  1425. });
  1426. it('renounces a role', async function () {
  1427. expect(await this.manager.hasRole(this.role.id, this.caller).then(formatAccess)).to.be.deep.equal([
  1428. true,
  1429. '0',
  1430. ]);
  1431. await expect(this.manager.connect(this.caller).renounceRole(this.role.id, this.caller))
  1432. .to.emit(this.manager, 'RoleRevoked')
  1433. .withArgs(this.role.id, this.caller.address);
  1434. expect(await this.manager.hasRole(this.role.id, this.caller).then(formatAccess)).to.be.deep.equal([
  1435. false,
  1436. '0',
  1437. ]);
  1438. });
  1439. it('reverts if renouncing the PUBLIC_ROLE', async function () {
  1440. await expect(this.manager.connect(this.caller).renounceRole(this.roles.PUBLIC.id, this.caller))
  1441. .to.be.revertedWithCustomError(this.manager, 'AccessManagerLockedRole')
  1442. .withArgs(this.roles.PUBLIC.id);
  1443. });
  1444. it('reverts if renouncing with bad caller confirmation', async function () {
  1445. await expect(
  1446. this.manager.connect(this.caller).renounceRole(this.role.id, someAddress),
  1447. ).to.be.revertedWithCustomError(this.manager, 'AccessManagerBadConfirmation');
  1448. });
  1449. });
  1450. });
  1451. });
  1452. });
  1453. describe('access managed target operations', function () {
  1454. describe('when calling a restricted target function', function () {
  1455. beforeEach('set required role', function () {
  1456. this.method = this.target.fnRestricted.getFragment();
  1457. this.role = { id: 3597243n };
  1458. this.manager.$_setTargetFunctionRole(this.target, this.method.selector, this.role.id);
  1459. });
  1460. describe('restrictions', function () {
  1461. beforeEach('set method and args', function () {
  1462. this.calldata = this.target.interface.encodeFunctionData(this.method, []);
  1463. this.caller = this.user;
  1464. });
  1465. shouldBehaveLikeAManagedRestrictedOperation();
  1466. });
  1467. it('succeeds called by a role member', async function () {
  1468. await this.manager.$_grantRole(this.role.id, this.user, 0, 0);
  1469. await expect(
  1470. this.target.connect(this.user)[this.method.selector]({
  1471. data: this.calldata,
  1472. }),
  1473. )
  1474. .to.emit(this.target, 'CalledRestricted')
  1475. .withArgs(this.user.address);
  1476. });
  1477. });
  1478. describe('when calling a non-restricted target function', function () {
  1479. const method = 'fnUnrestricted()';
  1480. beforeEach('set required role', async function () {
  1481. this.role = { id: 879435n };
  1482. await this.manager.$_setTargetFunctionRole(
  1483. this.target,
  1484. this.target[method].getFragment().selector,
  1485. this.role.id,
  1486. );
  1487. });
  1488. it('succeeds called by anyone', async function () {
  1489. await expect(
  1490. this.target.connect(this.user)[method]({
  1491. data: this.calldata,
  1492. }),
  1493. )
  1494. .to.emit(this.target, 'CalledUnrestricted')
  1495. .withArgs(this.user.address);
  1496. });
  1497. });
  1498. });
  1499. describe('#schedule', function () {
  1500. beforeEach('set target function role', async function () {
  1501. this.method = this.target.fnRestricted.getFragment();
  1502. this.role = { id: 498305n };
  1503. this.caller = this.user;
  1504. await this.manager.$_setTargetFunctionRole(this.target, this.method.selector, this.role.id);
  1505. await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); // nonzero execution delay
  1506. this.calldata = this.target.interface.encodeFunctionData(this.method, []);
  1507. this.delay = time.duration.weeks(2);
  1508. });
  1509. describe('restrictions', function () {
  1510. testAsCanCall({
  1511. closed() {
  1512. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1513. const { schedule } = await prepareOperation(this.manager, {
  1514. caller: this.caller,
  1515. target: this.target,
  1516. calldata: this.calldata,
  1517. delay: this.delay,
  1518. });
  1519. await expect(schedule())
  1520. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1521. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1522. });
  1523. },
  1524. open: {
  1525. callerIsTheManager: {
  1526. executing() {
  1527. it.skip('is not reachable because schedule is not restrictable');
  1528. },
  1529. notExecuting() {
  1530. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1531. const { schedule } = await prepareOperation(this.manager, {
  1532. caller: this.caller,
  1533. target: this.target,
  1534. calldata: this.calldata,
  1535. delay: this.delay,
  1536. });
  1537. await expect(schedule())
  1538. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1539. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1540. });
  1541. },
  1542. },
  1543. callerIsNotTheManager: {
  1544. publicRoleIsRequired() {
  1545. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1546. // prepareOperation is not used here because it alters the next block timestamp
  1547. await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
  1548. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1549. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1550. });
  1551. },
  1552. specificRoleIsRequired: {
  1553. requiredRoleIsGranted: {
  1554. roleGrantingIsDelayed: {
  1555. callerHasAnExecutionDelay: {
  1556. beforeGrantDelay() {
  1557. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1558. // prepareOperation is not used here because it alters the next block timestamp
  1559. await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
  1560. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1561. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1562. });
  1563. },
  1564. afterGrantDelay() {
  1565. it('succeeds', async function () {
  1566. // prepareOperation is not used here because it alters the next block timestamp
  1567. await this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48);
  1568. });
  1569. },
  1570. },
  1571. callerHasNoExecutionDelay: {
  1572. beforeGrantDelay() {
  1573. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1574. // prepareOperation is not used here because it alters the next block timestamp
  1575. await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
  1576. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1577. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1578. });
  1579. },
  1580. afterGrantDelay() {
  1581. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1582. // prepareOperation is not used here because it alters the next block timestamp
  1583. await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
  1584. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1585. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1586. });
  1587. },
  1588. },
  1589. },
  1590. roleGrantingIsNotDelayed: {
  1591. callerHasAnExecutionDelay() {
  1592. it('succeeds', async function () {
  1593. const { schedule } = await prepareOperation(this.manager, {
  1594. caller: this.caller,
  1595. target: this.target,
  1596. calldata: this.calldata,
  1597. delay: this.delay,
  1598. });
  1599. await schedule();
  1600. });
  1601. },
  1602. callerHasNoExecutionDelay() {
  1603. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1604. // prepareOperation is not used here because it alters the next block timestamp
  1605. await expect(this.manager.connect(this.caller).schedule(this.target, this.calldata, MAX_UINT48))
  1606. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1607. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1608. });
  1609. },
  1610. },
  1611. },
  1612. requiredRoleIsNotGranted() {
  1613. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1614. const { schedule } = await prepareOperation(this.manager, {
  1615. caller: this.caller,
  1616. target: this.target,
  1617. calldata: this.calldata,
  1618. delay: this.delay,
  1619. });
  1620. await expect(schedule())
  1621. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1622. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1623. });
  1624. },
  1625. },
  1626. },
  1627. },
  1628. });
  1629. });
  1630. it('schedules an operation at the specified execution date if it is larger than caller execution delay', async function () {
  1631. const { operationId, scheduledAt, schedule } = await prepareOperation(this.manager, {
  1632. caller: this.caller,
  1633. target: this.target,
  1634. calldata: this.calldata,
  1635. delay: this.delay,
  1636. });
  1637. const txResponse = await schedule();
  1638. expect(await this.manager.getSchedule(operationId)).to.equal(scheduledAt + this.delay);
  1639. expect(txResponse)
  1640. .to.emit(this.manager, 'OperationScheduled')
  1641. .withArgs(operationId, '1', scheduledAt + this.delay, this.target.target, this.calldata);
  1642. });
  1643. it('schedules an operation at the minimum execution date if no specified execution date (when == 0)', async function () {
  1644. const executionDelay = await time.duration.hours(72);
  1645. await this.manager.$_grantRole(this.role.id, this.caller, 0, executionDelay);
  1646. const txResponse = await this.manager.connect(this.caller).schedule(this.target, this.calldata, 0);
  1647. const scheduledAt = await time.clockFromReceipt.timestamp(txResponse);
  1648. const operationId = await this.manager.hashOperation(this.caller, this.target, this.calldata);
  1649. expect(await this.manager.getSchedule(operationId)).to.equal(scheduledAt + executionDelay);
  1650. expect(txResponse)
  1651. .to.emit(this.manager, 'OperationScheduled')
  1652. .withArgs(operationId, '1', scheduledAt + executionDelay, this.target.target, this.calldata);
  1653. });
  1654. it('increases the nonce of an operation scheduled more than once', async function () {
  1655. // Setup and check initial nonce
  1656. const expectedOperationId = hashOperation(this.caller, this.target, this.calldata);
  1657. expect(await this.manager.getNonce(expectedOperationId)).to.equal('0');
  1658. // Schedule
  1659. const op1 = await prepareOperation(this.manager, {
  1660. caller: this.caller,
  1661. target: this.target,
  1662. calldata: this.calldata,
  1663. delay: this.delay,
  1664. });
  1665. await expect(op1.schedule())
  1666. .to.emit(this.manager, 'OperationScheduled')
  1667. .withArgs(
  1668. op1.operationId,
  1669. 1n,
  1670. op1.scheduledAt + this.delay,
  1671. this.caller.address,
  1672. this.target.target,
  1673. this.calldata,
  1674. );
  1675. expect(expectedOperationId).to.equal(op1.operationId);
  1676. // Consume
  1677. await time.increaseBy.timestamp(this.delay);
  1678. await this.manager.$_consumeScheduledOp(expectedOperationId);
  1679. // Check nonce
  1680. expect(await this.manager.getNonce(expectedOperationId)).to.equal('1');
  1681. // Schedule again
  1682. const op2 = await prepareOperation(this.manager, {
  1683. caller: this.caller,
  1684. target: this.target,
  1685. calldata: this.calldata,
  1686. delay: this.delay,
  1687. });
  1688. await expect(op2.schedule())
  1689. .to.emit(this.manager, 'OperationScheduled')
  1690. .withArgs(
  1691. op2.operationId,
  1692. 2n,
  1693. op2.scheduledAt + this.delay,
  1694. this.caller.address,
  1695. this.target.target,
  1696. this.calldata,
  1697. );
  1698. expect(expectedOperationId).to.equal(op2.operationId);
  1699. // Check final nonce
  1700. expect(await this.manager.getNonce(expectedOperationId)).to.equal('2');
  1701. });
  1702. it('reverts if the specified execution date is before the current timestamp + caller execution delay', async function () {
  1703. const executionDelay = time.duration.weeks(1) + this.delay;
  1704. await this.manager.$_grantRole(this.role.id, this.caller, 0, executionDelay);
  1705. const { schedule } = await prepareOperation(this.manager, {
  1706. caller: this.caller,
  1707. target: this.target,
  1708. calldata: this.calldata,
  1709. delay: this.delay,
  1710. });
  1711. await expect(schedule())
  1712. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1713. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1714. });
  1715. it('reverts if an operation is already schedule', async function () {
  1716. const op1 = await prepareOperation(this.manager, {
  1717. caller: this.caller,
  1718. target: this.target,
  1719. calldata: this.calldata,
  1720. delay: this.delay,
  1721. });
  1722. await op1.schedule();
  1723. const op2 = await prepareOperation(this.manager, {
  1724. caller: this.caller,
  1725. target: this.target,
  1726. calldata: this.calldata,
  1727. delay: this.delay,
  1728. });
  1729. await expect(op2.schedule())
  1730. .to.be.revertedWithCustomError(this.manager, 'AccessManagerAlreadyScheduled')
  1731. .withArgs(op1.operationId);
  1732. });
  1733. it('panics scheduling calldata with less than 4 bytes', async function () {
  1734. const calldata = '0x1234'; // 2 bytes
  1735. // Managed contract
  1736. const op1 = await prepareOperation(this.manager, {
  1737. caller: this.caller,
  1738. target: this.target,
  1739. calldata: calldata,
  1740. delay: this.delay,
  1741. });
  1742. await expect(op1.schedule()).to.be.revertedWithoutReason();
  1743. // Manager contract
  1744. const op2 = await prepareOperation(this.manager, {
  1745. caller: this.caller,
  1746. target: this.manager,
  1747. calldata: calldata,
  1748. delay: this.delay,
  1749. });
  1750. await expect(op2.schedule()).to.be.revertedWithoutReason();
  1751. });
  1752. it('reverts scheduling an unknown operation to the manager', async function () {
  1753. const calldata = '0x12345678';
  1754. const { schedule } = await prepareOperation(this.manager, {
  1755. caller: this.caller,
  1756. target: this.manager,
  1757. calldata,
  1758. delay: this.delay,
  1759. });
  1760. await expect(schedule())
  1761. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1762. .withArgs(this.caller.address, this.manager.target, calldata);
  1763. });
  1764. });
  1765. describe('#execute', function () {
  1766. beforeEach('set target function role', async function () {
  1767. this.method = this.target.fnRestricted.getFragment();
  1768. this.role = { id: 9825430n };
  1769. this.caller = this.user;
  1770. await this.manager.$_setTargetFunctionRole(this.target, this.method.selector, this.role.id);
  1771. await this.manager.$_grantRole(this.role.id, this.caller, 0, 0);
  1772. this.calldata = this.target.interface.encodeFunctionData(this.method, []);
  1773. });
  1774. describe('restrictions', function () {
  1775. testAsCanCall({
  1776. closed() {
  1777. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1778. await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
  1779. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1780. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1781. });
  1782. },
  1783. open: {
  1784. callerIsTheManager: {
  1785. executing() {
  1786. it('succeeds', async function () {
  1787. await this.manager.connect(this.caller).execute(this.target, this.calldata);
  1788. });
  1789. },
  1790. notExecuting() {
  1791. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1792. await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
  1793. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1794. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1795. });
  1796. },
  1797. },
  1798. callerIsNotTheManager: {
  1799. publicRoleIsRequired() {
  1800. it('succeeds', async function () {
  1801. await this.manager.connect(this.caller).execute(this.target, this.calldata);
  1802. });
  1803. },
  1804. specificRoleIsRequired: {
  1805. requiredRoleIsGranted: {
  1806. roleGrantingIsDelayed: {
  1807. callerHasAnExecutionDelay: {
  1808. beforeGrantDelay() {
  1809. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1810. await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
  1811. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1812. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1813. });
  1814. },
  1815. afterGrantDelay: function self() {
  1816. self.mineDelay = true;
  1817. beforeEach('define schedule delay', function () {
  1818. this.scheduleIn = time.duration.days(21); // For testAsSchedulableOperation
  1819. });
  1820. testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
  1821. },
  1822. },
  1823. callerHasNoExecutionDelay: {
  1824. beforeGrantDelay() {
  1825. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1826. await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
  1827. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1828. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1829. });
  1830. },
  1831. afterGrantDelay: function self() {
  1832. self.mineDelay = true;
  1833. it('succeeds', async function () {
  1834. await this.manager.connect(this.caller).execute(this.target, this.calldata);
  1835. });
  1836. },
  1837. },
  1838. },
  1839. roleGrantingIsNotDelayed: {
  1840. callerHasAnExecutionDelay() {
  1841. beforeEach('define schedule delay', function () {
  1842. this.scheduleIn = time.duration.days(15); // For testAsSchedulableOperation
  1843. });
  1844. testAsSchedulableOperation(LIKE_COMMON_SCHEDULABLE);
  1845. },
  1846. callerHasNoExecutionDelay() {
  1847. it('succeeds', async function () {
  1848. await this.manager.connect(this.caller).execute(this.target, this.calldata);
  1849. });
  1850. },
  1851. },
  1852. },
  1853. requiredRoleIsNotGranted() {
  1854. it('reverts as AccessManagerUnauthorizedCall', async function () {
  1855. await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
  1856. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  1857. .withArgs(this.caller.address, this.target.target, this.calldata.substring(0, 10));
  1858. });
  1859. },
  1860. },
  1861. },
  1862. },
  1863. });
  1864. });
  1865. it('executes with a delay consuming the scheduled operation', async function () {
  1866. const delay = time.duration.hours(4);
  1867. await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); // Execution delay is needed so the operation is consumed
  1868. const { operationId, schedule } = await prepareOperation(this.manager, {
  1869. caller: this.caller,
  1870. target: this.target,
  1871. calldata: this.calldata,
  1872. delay,
  1873. });
  1874. await schedule();
  1875. await time.increaseBy.timestamp(delay);
  1876. await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
  1877. .to.emit(this.manager, 'OperationExecuted')
  1878. .withArgs(operationId, 1n);
  1879. expect(await this.manager.getSchedule(operationId)).to.equal(0n);
  1880. });
  1881. it('executes with no delay consuming a scheduled operation', async function () {
  1882. const delay = time.duration.hours(4);
  1883. // give caller an execution delay
  1884. await this.manager.$_grantRole(this.role.id, this.caller, 0, 1);
  1885. const { operationId, schedule } = await prepareOperation(this.manager, {
  1886. caller: this.caller,
  1887. target: this.target,
  1888. calldata: this.calldata,
  1889. delay,
  1890. });
  1891. await schedule();
  1892. // remove the execution delay
  1893. await this.manager.$_grantRole(this.role.id, this.caller, 0, 0);
  1894. await time.increaseBy.timestamp(delay);
  1895. await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
  1896. .to.emit(this.manager, 'OperationExecuted')
  1897. .withArgs(operationId, 1n);
  1898. expect(await this.manager.getSchedule(operationId)).to.equal(0n);
  1899. });
  1900. it('keeps the original _executionId after finishing the call', async function () {
  1901. const executionIdBefore = await getStorageAt(this.manager.target, EXECUTION_ID_STORAGE_SLOT);
  1902. await this.manager.connect(this.caller).execute(this.target, this.calldata);
  1903. const executionIdAfter = await getStorageAt(this.manager.target, EXECUTION_ID_STORAGE_SLOT);
  1904. expect(executionIdBefore).to.equal(executionIdAfter);
  1905. });
  1906. it('reverts executing twice', async function () {
  1907. const delay = time.duration.hours(2);
  1908. await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); // Execution delay is needed so the operation is consumed
  1909. const { operationId, schedule } = await prepareOperation(this.manager, {
  1910. caller: this.caller,
  1911. target: this.target,
  1912. calldata: this.calldata,
  1913. delay,
  1914. });
  1915. await schedule();
  1916. await time.increaseBy.timestamp(delay);
  1917. await this.manager.connect(this.caller).execute(this.target, this.calldata);
  1918. await expect(this.manager.connect(this.caller).execute(this.target, this.calldata))
  1919. .to.be.revertedWithCustomError(this.manager, 'AccessManagerNotScheduled')
  1920. .withArgs(operationId);
  1921. });
  1922. });
  1923. describe('#consumeScheduledOp', function () {
  1924. beforeEach('define scheduling parameters', async function () {
  1925. const method = this.target.fnRestricted.getFragment();
  1926. this.caller = await ethers.getSigner(this.target.target);
  1927. await impersonate(this.caller.address);
  1928. this.calldata = this.target.interface.encodeFunctionData(method, []);
  1929. this.role = { id: 9834983n };
  1930. await this.manager.$_setTargetFunctionRole(this.target, method.selector, this.role.id);
  1931. await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); // nonzero execution delay
  1932. this.scheduleIn = time.duration.hours(10); // For testAsSchedulableOperation
  1933. });
  1934. describe('when caller is not consuming scheduled operation', function () {
  1935. beforeEach('set consuming false', async function () {
  1936. await this.target.setIsConsumingScheduledOp(false, ethers.toBeHex(CONSUMING_SCHEDULE_STORAGE_SLOT, 32));
  1937. });
  1938. it('reverts as AccessManagerUnauthorizedConsume', async function () {
  1939. await expect(this.manager.connect(this.caller).consumeScheduledOp(this.caller, this.calldata))
  1940. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedConsume')
  1941. .withArgs(this.caller.address);
  1942. });
  1943. });
  1944. describe('when caller is consuming scheduled operation', function () {
  1945. beforeEach('set consuming true', async function () {
  1946. await this.target.setIsConsumingScheduledOp(true, ethers.toBeHex(CONSUMING_SCHEDULE_STORAGE_SLOT, 32));
  1947. });
  1948. testAsSchedulableOperation({
  1949. scheduled: {
  1950. before() {
  1951. it('reverts as AccessManagerNotReady', async function () {
  1952. await expect(this.manager.connect(this.caller).consumeScheduledOp(this.caller, this.calldata))
  1953. .to.be.revertedWithCustomError(this.manager, 'AccessManagerNotReady')
  1954. .withArgs(this.operationId);
  1955. });
  1956. },
  1957. after() {
  1958. it('consumes the scheduled operation and resets timepoint', async function () {
  1959. expect(await this.manager.getSchedule(this.operationId)).to.equal(this.scheduledAt + this.scheduleIn);
  1960. await expect(this.manager.connect(this.caller).consumeScheduledOp(this.caller, this.calldata))
  1961. .to.emit(this.manager, 'OperationExecuted')
  1962. .withArgs(this.operationId, 1n);
  1963. expect(await this.manager.getSchedule(this.operationId)).to.equal(0n);
  1964. });
  1965. },
  1966. expired() {
  1967. it('reverts as AccessManagerExpired', async function () {
  1968. await expect(this.manager.connect(this.caller).consumeScheduledOp(this.caller, this.calldata))
  1969. .to.be.revertedWithCustomError(this.manager, 'AccessManagerExpired')
  1970. .withArgs(this.operationId);
  1971. });
  1972. },
  1973. },
  1974. notScheduled() {
  1975. it('reverts as AccessManagerNotScheduled', async function () {
  1976. await expect(this.manager.connect(this.caller).consumeScheduledOp(this.caller, this.calldata))
  1977. .to.be.revertedWithCustomError(this.manager, 'AccessManagerNotScheduled')
  1978. .withArgs(this.operationId);
  1979. });
  1980. },
  1981. });
  1982. });
  1983. });
  1984. describe('#cancelScheduledOp', function () {
  1985. beforeEach('setup scheduling', async function () {
  1986. this.method = this.target.fnRestricted.getFragment();
  1987. this.caller = this.roles.SOME.members[0];
  1988. await this.manager.$_setTargetFunctionRole(this.target, this.method.selector, this.roles.SOME.id);
  1989. await this.manager.$_grantRole(this.roles.SOME.id, this.caller, 0, 1); // nonzero execution delay
  1990. this.calldata = this.target.interface.encodeFunctionData(this.method, []);
  1991. this.scheduleIn = time.duration.days(10); // For testAsSchedulableOperation
  1992. });
  1993. testAsSchedulableOperation({
  1994. scheduled: {
  1995. before() {
  1996. describe('when caller is the scheduler', function () {
  1997. it('succeeds', async function () {
  1998. await this.manager.connect(this.caller).cancel(this.caller, this.target, this.calldata);
  1999. });
  2000. });
  2001. describe('when caller is an admin', function () {
  2002. it('succeeds', async function () {
  2003. await this.manager.connect(this.roles.ADMIN.members[0]).cancel(this.caller, this.target, this.calldata);
  2004. });
  2005. });
  2006. describe('when caller is the role guardian', function () {
  2007. it('succeeds', async function () {
  2008. await this.manager
  2009. .connect(this.roles.SOME_GUARDIAN.members[0])
  2010. .cancel(this.caller, this.target, this.calldata);
  2011. });
  2012. });
  2013. describe('when caller is any other account', function () {
  2014. it('reverts as AccessManagerUnauthorizedCancel', async function () {
  2015. await expect(this.manager.connect(this.other).cancel(this.caller, this.target, this.calldata))
  2016. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCancel')
  2017. .withArgs(this.other.address, this.caller.address, this.target.target, this.method.selector);
  2018. });
  2019. });
  2020. },
  2021. after() {
  2022. it('succeeds', async function () {
  2023. await this.manager.connect(this.caller).cancel(this.caller, this.target, this.calldata);
  2024. });
  2025. },
  2026. expired() {
  2027. it('succeeds', async function () {
  2028. await this.manager.connect(this.caller).cancel(this.caller, this.target, this.calldata);
  2029. });
  2030. },
  2031. },
  2032. notScheduled() {
  2033. it('reverts as AccessManagerNotScheduled', async function () {
  2034. await expect(this.manager.cancel(this.caller, this.target, this.calldata))
  2035. .to.be.revertedWithCustomError(this.manager, 'AccessManagerNotScheduled')
  2036. .withArgs(this.operationId);
  2037. });
  2038. },
  2039. });
  2040. it('cancels an operation and resets schedule', async function () {
  2041. const { operationId, schedule } = await prepareOperation(this.manager, {
  2042. caller: this.caller,
  2043. target: this.target,
  2044. calldata: this.calldata,
  2045. delay: this.scheduleIn,
  2046. });
  2047. await schedule();
  2048. await expect(this.manager.connect(this.caller).cancel(this.caller, this.target, this.calldata))
  2049. .to.emit(this.manager, 'OperationCanceled')
  2050. .withArgs(operationId, 1n);
  2051. expect(await this.manager.getSchedule(operationId)).to.equal('0');
  2052. });
  2053. });
  2054. describe('with Ownable target contract', function () {
  2055. const roleId = 1n;
  2056. beforeEach(async function () {
  2057. this.ownable = await ethers.deployContract('$Ownable', [this.manager]);
  2058. // add user to role
  2059. await this.manager.$_grantRole(roleId, this.user, 0, 0);
  2060. });
  2061. it('initial state', async function () {
  2062. expect(await this.ownable.owner()).to.be.equal(this.manager.target);
  2063. });
  2064. describe('Contract is closed', function () {
  2065. beforeEach(async function () {
  2066. await this.manager.$_setTargetClosed(this.ownable, true);
  2067. });
  2068. it('directly call: reverts', async function () {
  2069. await expect(this.ownable.connect(this.user).$_checkOwner())
  2070. .to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount')
  2071. .withArgs(this.user.address);
  2072. });
  2073. it('relayed call (with role): reverts', async function () {
  2074. await expect(
  2075. this.manager.connect(this.user).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector),
  2076. )
  2077. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  2078. .withArgs(this.user.address, this.ownable.target, this.ownable.$_checkOwner.getFragment().selector);
  2079. });
  2080. it('relayed call (without role): reverts', async function () {
  2081. await expect(
  2082. this.manager.connect(this.other).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector),
  2083. )
  2084. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  2085. .withArgs(this.other.address, this.ownable.target, this.ownable.$_checkOwner.getFragment().selector);
  2086. });
  2087. });
  2088. describe('Contract is managed', function () {
  2089. describe('function is open to specific role', function () {
  2090. beforeEach(async function () {
  2091. await this.manager.$_setTargetFunctionRole(
  2092. this.ownable,
  2093. this.ownable.$_checkOwner.getFragment().selector,
  2094. roleId,
  2095. );
  2096. });
  2097. it('directly call: reverts', async function () {
  2098. await expect(this.ownable.connect(this.user).$_checkOwner())
  2099. .to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount')
  2100. .withArgs(this.user.address);
  2101. });
  2102. it('relayed call (with role): success', async function () {
  2103. await this.manager.connect(this.user).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector);
  2104. });
  2105. it('relayed call (without role): reverts', async function () {
  2106. await expect(
  2107. this.manager.connect(this.other).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector),
  2108. )
  2109. .to.be.revertedWithCustomError(this.manager, 'AccessManagerUnauthorizedCall')
  2110. .withArgs(this.other.address, this.ownable.target, this.ownable.$_checkOwner.getFragment().selector);
  2111. });
  2112. });
  2113. describe('function is open to public role', function () {
  2114. beforeEach(async function () {
  2115. await this.manager.$_setTargetFunctionRole(
  2116. this.ownable,
  2117. this.ownable.$_checkOwner.getFragment().selector,
  2118. this.roles.PUBLIC.id,
  2119. );
  2120. });
  2121. it('directly call: reverts', async function () {
  2122. await expect(this.ownable.connect(this.user).$_checkOwner())
  2123. .to.be.revertedWithCustomError(this.ownable, 'OwnableUnauthorizedAccount')
  2124. .withArgs(this.user.address);
  2125. });
  2126. it('relayed call (with role): success', async function () {
  2127. await this.manager.connect(this.user).execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector);
  2128. });
  2129. it('relayed call (without role): success', async function () {
  2130. await this.manager
  2131. .connect(this.other)
  2132. .execute(this.ownable, this.ownable.$_checkOwner.getFragment().selector);
  2133. });
  2134. });
  2135. });
  2136. });
  2137. });