AccessManager.test.js 104 KB

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