AccessManager.test.js 104 KB

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