AccessManager.test.js 106 KB

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