TimelockController.test.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. const { constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
  2. const { ZERO_BYTES32 } = constants;
  3. const { expect } = require('chai');
  4. const TimelockController = artifacts.require('TimelockController');
  5. const CallReceiverMock = artifacts.require('CallReceiverMock');
  6. const Implementation2 = artifacts.require('Implementation2');
  7. const MINDELAY = time.duration.days(1);
  8. function genOperation (target, value, data, predecessor, salt) {
  9. const id = web3.utils.keccak256(web3.eth.abi.encodeParameters([
  10. 'address',
  11. 'uint256',
  12. 'bytes',
  13. 'uint256',
  14. 'bytes32',
  15. ], [
  16. target,
  17. value,
  18. data,
  19. predecessor,
  20. salt,
  21. ]));
  22. return { id, target, value, data, predecessor, salt };
  23. }
  24. function genOperationBatch (targets, values, payloads, predecessor, salt) {
  25. const id = web3.utils.keccak256(web3.eth.abi.encodeParameters([
  26. 'address[]',
  27. 'uint256[]',
  28. 'bytes[]',
  29. 'uint256',
  30. 'bytes32',
  31. ], [
  32. targets,
  33. values,
  34. payloads,
  35. predecessor,
  36. salt,
  37. ]));
  38. return { id, targets, values, payloads, predecessor, salt };
  39. }
  40. contract('TimelockController', function (accounts) {
  41. const [ admin, proposer, canceller, executor, other ] = accounts;
  42. const TIMELOCK_ADMIN_ROLE = web3.utils.soliditySha3('TIMELOCK_ADMIN_ROLE');
  43. const PROPOSER_ROLE = web3.utils.soliditySha3('PROPOSER_ROLE');
  44. const EXECUTOR_ROLE = web3.utils.soliditySha3('EXECUTOR_ROLE');
  45. const CANCELLER_ROLE = web3.utils.soliditySha3('CANCELLER_ROLE');
  46. beforeEach(async function () {
  47. // Deploy new timelock
  48. this.timelock = await TimelockController.new(
  49. MINDELAY,
  50. [ proposer ],
  51. [ executor ],
  52. { from: admin },
  53. );
  54. expect(await this.timelock.hasRole(CANCELLER_ROLE, proposer)).to.be.equal(true);
  55. await this.timelock.revokeRole(CANCELLER_ROLE, proposer, { from: admin });
  56. await this.timelock.grantRole(CANCELLER_ROLE, canceller, { from: admin });
  57. // Mocks
  58. this.callreceivermock = await CallReceiverMock.new({ from: admin });
  59. this.implementation2 = await Implementation2.new({ from: admin });
  60. });
  61. it('initial state', async function () {
  62. expect(await this.timelock.getMinDelay()).to.be.bignumber.equal(MINDELAY);
  63. expect(await this.timelock.TIMELOCK_ADMIN_ROLE()).to.be.equal(TIMELOCK_ADMIN_ROLE);
  64. expect(await this.timelock.PROPOSER_ROLE()).to.be.equal(PROPOSER_ROLE);
  65. expect(await this.timelock.EXECUTOR_ROLE()).to.be.equal(EXECUTOR_ROLE);
  66. expect(await this.timelock.CANCELLER_ROLE()).to.be.equal(CANCELLER_ROLE);
  67. expect(await Promise.all([ PROPOSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE ].map(role =>
  68. this.timelock.hasRole(role, proposer),
  69. ))).to.be.deep.equal([ true, false, false ]);
  70. expect(await Promise.all([ PROPOSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE ].map(role =>
  71. this.timelock.hasRole(role, canceller),
  72. ))).to.be.deep.equal([ false, true, false ]);
  73. expect(await Promise.all([ PROPOSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE ].map(role =>
  74. this.timelock.hasRole(role, executor),
  75. ))).to.be.deep.equal([ false, false, true ]);
  76. });
  77. describe('methods', function () {
  78. describe('operation hashing', function () {
  79. it('hashOperation', async function () {
  80. this.operation = genOperation(
  81. '0x29cebefe301c6ce1bb36b58654fea275e1cacc83',
  82. '0xf94fdd6e21da21d2',
  83. '0xa3bc5104',
  84. '0xba41db3be0a9929145cfe480bd0f1f003689104d275ae912099f925df424ef94',
  85. '0x60d9109846ab510ed75c15f979ae366a8a2ace11d34ba9788c13ac296db50e6e',
  86. );
  87. expect(await this.timelock.hashOperation(
  88. this.operation.target,
  89. this.operation.value,
  90. this.operation.data,
  91. this.operation.predecessor,
  92. this.operation.salt,
  93. )).to.be.equal(this.operation.id);
  94. });
  95. it('hashOperationBatch', async function () {
  96. this.operation = genOperationBatch(
  97. Array(8).fill('0x2d5f21620e56531c1d59c2df9b8e95d129571f71'),
  98. Array(8).fill('0x2b993cfce932ccee'),
  99. Array(8).fill('0xcf51966b'),
  100. '0xce8f45069cc71d25f71ba05062de1a3974f9849b004de64a70998bca9d29c2e7',
  101. '0x8952d74c110f72bfe5accdf828c74d53a7dfb71235dfa8a1e8c75d8576b372ff',
  102. );
  103. expect(await this.timelock.hashOperationBatch(
  104. this.operation.targets,
  105. this.operation.values,
  106. this.operation.payloads,
  107. this.operation.predecessor,
  108. this.operation.salt,
  109. )).to.be.equal(this.operation.id);
  110. });
  111. });
  112. describe('simple', function () {
  113. describe('schedule', function () {
  114. beforeEach(async function () {
  115. this.operation = genOperation(
  116. '0x31754f590B97fD975Eb86938f18Cc304E264D2F2',
  117. 0,
  118. '0x3bf92ccc',
  119. ZERO_BYTES32,
  120. '0x025e7b0be353a74631ad648c667493c0e1cd31caa4cc2d3520fdc171ea0cc726',
  121. );
  122. });
  123. it('proposer can schedule', async function () {
  124. const receipt = await this.timelock.schedule(
  125. this.operation.target,
  126. this.operation.value,
  127. this.operation.data,
  128. this.operation.predecessor,
  129. this.operation.salt,
  130. MINDELAY,
  131. { from: proposer },
  132. );
  133. expectEvent(receipt, 'CallScheduled', {
  134. id: this.operation.id,
  135. index: web3.utils.toBN(0),
  136. target: this.operation.target,
  137. value: web3.utils.toBN(this.operation.value),
  138. data: this.operation.data,
  139. predecessor: this.operation.predecessor,
  140. delay: MINDELAY,
  141. });
  142. const block = await web3.eth.getBlock(receipt.receipt.blockHash);
  143. expect(await this.timelock.getTimestamp(this.operation.id))
  144. .to.be.bignumber.equal(web3.utils.toBN(block.timestamp).add(MINDELAY));
  145. });
  146. it('prevent overwriting active operation', async function () {
  147. await this.timelock.schedule(
  148. this.operation.target,
  149. this.operation.value,
  150. this.operation.data,
  151. this.operation.predecessor,
  152. this.operation.salt,
  153. MINDELAY,
  154. { from: proposer },
  155. );
  156. await expectRevert(
  157. this.timelock.schedule(
  158. this.operation.target,
  159. this.operation.value,
  160. this.operation.data,
  161. this.operation.predecessor,
  162. this.operation.salt,
  163. MINDELAY,
  164. { from: proposer },
  165. ),
  166. 'TimelockController: operation already scheduled',
  167. );
  168. });
  169. it('prevent non-proposer from commiting', async function () {
  170. await expectRevert(
  171. this.timelock.schedule(
  172. this.operation.target,
  173. this.operation.value,
  174. this.operation.data,
  175. this.operation.predecessor,
  176. this.operation.salt,
  177. MINDELAY,
  178. { from: other },
  179. ),
  180. `AccessControl: account ${other.toLowerCase()} is missing role ${PROPOSER_ROLE}`,
  181. );
  182. });
  183. it('enforce minimum delay', async function () {
  184. await expectRevert(
  185. this.timelock.schedule(
  186. this.operation.target,
  187. this.operation.value,
  188. this.operation.data,
  189. this.operation.predecessor,
  190. this.operation.salt,
  191. MINDELAY - 1,
  192. { from: proposer },
  193. ),
  194. 'TimelockController: insufficient delay',
  195. );
  196. });
  197. });
  198. describe('execute', function () {
  199. beforeEach(async function () {
  200. this.operation = genOperation(
  201. '0xAe22104DCD970750610E6FE15E623468A98b15f7',
  202. 0,
  203. '0x13e414de',
  204. ZERO_BYTES32,
  205. '0xc1059ed2dc130227aa1d1d539ac94c641306905c020436c636e19e3fab56fc7f',
  206. );
  207. });
  208. it('revert if operation is not scheduled', async function () {
  209. await expectRevert(
  210. this.timelock.execute(
  211. this.operation.target,
  212. this.operation.value,
  213. this.operation.data,
  214. this.operation.predecessor,
  215. this.operation.salt,
  216. { from: executor },
  217. ),
  218. 'TimelockController: operation is not ready',
  219. );
  220. });
  221. describe('with scheduled operation', function () {
  222. beforeEach(async function () {
  223. ({ receipt: this.receipt, logs: this.logs } = await this.timelock.schedule(
  224. this.operation.target,
  225. this.operation.value,
  226. this.operation.data,
  227. this.operation.predecessor,
  228. this.operation.salt,
  229. MINDELAY,
  230. { from: proposer },
  231. ));
  232. });
  233. it('revert if execution comes too early 1/2', async function () {
  234. await expectRevert(
  235. this.timelock.execute(
  236. this.operation.target,
  237. this.operation.value,
  238. this.operation.data,
  239. this.operation.predecessor,
  240. this.operation.salt,
  241. { from: executor },
  242. ),
  243. 'TimelockController: operation is not ready',
  244. );
  245. });
  246. it('revert if execution comes too early 2/2', async function () {
  247. const timestamp = await this.timelock.getTimestamp(this.operation.id);
  248. await time.increaseTo(timestamp - 5); // -1 is too tight, test sometime fails
  249. await expectRevert(
  250. this.timelock.execute(
  251. this.operation.target,
  252. this.operation.value,
  253. this.operation.data,
  254. this.operation.predecessor,
  255. this.operation.salt,
  256. { from: executor },
  257. ),
  258. 'TimelockController: operation is not ready',
  259. );
  260. });
  261. describe('on time', function () {
  262. beforeEach(async function () {
  263. const timestamp = await this.timelock.getTimestamp(this.operation.id);
  264. await time.increaseTo(timestamp);
  265. });
  266. it('executor can reveal', async function () {
  267. const receipt = await this.timelock.execute(
  268. this.operation.target,
  269. this.operation.value,
  270. this.operation.data,
  271. this.operation.predecessor,
  272. this.operation.salt,
  273. { from: executor },
  274. );
  275. expectEvent(receipt, 'CallExecuted', {
  276. id: this.operation.id,
  277. index: web3.utils.toBN(0),
  278. target: this.operation.target,
  279. value: web3.utils.toBN(this.operation.value),
  280. data: this.operation.data,
  281. });
  282. });
  283. it('prevent non-executor from revealing', async function () {
  284. await expectRevert(
  285. this.timelock.execute(
  286. this.operation.target,
  287. this.operation.value,
  288. this.operation.data,
  289. this.operation.predecessor,
  290. this.operation.salt,
  291. { from: other },
  292. ),
  293. `AccessControl: account ${other.toLowerCase()} is missing role ${EXECUTOR_ROLE}`,
  294. );
  295. });
  296. });
  297. });
  298. });
  299. });
  300. describe('batch', function () {
  301. describe('schedule', function () {
  302. beforeEach(async function () {
  303. this.operation = genOperationBatch(
  304. Array(8).fill('0xEd912250835c812D4516BBD80BdaEA1bB63a293C'),
  305. Array(8).fill(0),
  306. Array(8).fill('0x2fcb7a88'),
  307. ZERO_BYTES32,
  308. '0x6cf9d042ade5de78bed9ffd075eb4b2a4f6b1736932c2dc8af517d6e066f51f5',
  309. );
  310. });
  311. it('proposer can schedule', async function () {
  312. const receipt = await this.timelock.scheduleBatch(
  313. this.operation.targets,
  314. this.operation.values,
  315. this.operation.payloads,
  316. this.operation.predecessor,
  317. this.operation.salt,
  318. MINDELAY,
  319. { from: proposer },
  320. );
  321. for (const i in this.operation.targets) {
  322. expectEvent(receipt, 'CallScheduled', {
  323. id: this.operation.id,
  324. index: web3.utils.toBN(i),
  325. target: this.operation.targets[i],
  326. value: web3.utils.toBN(this.operation.values[i]),
  327. data: this.operation.payloads[i],
  328. predecessor: this.operation.predecessor,
  329. delay: MINDELAY,
  330. });
  331. }
  332. const block = await web3.eth.getBlock(receipt.receipt.blockHash);
  333. expect(await this.timelock.getTimestamp(this.operation.id))
  334. .to.be.bignumber.equal(web3.utils.toBN(block.timestamp).add(MINDELAY));
  335. });
  336. it('prevent overwriting active operation', async function () {
  337. await this.timelock.scheduleBatch(
  338. this.operation.targets,
  339. this.operation.values,
  340. this.operation.payloads,
  341. this.operation.predecessor,
  342. this.operation.salt,
  343. MINDELAY,
  344. { from: proposer },
  345. );
  346. await expectRevert(
  347. this.timelock.scheduleBatch(
  348. this.operation.targets,
  349. this.operation.values,
  350. this.operation.payloads,
  351. this.operation.predecessor,
  352. this.operation.salt,
  353. MINDELAY,
  354. { from: proposer },
  355. ),
  356. 'TimelockController: operation already scheduled',
  357. );
  358. });
  359. it('length of batch parameter must match #1', async function () {
  360. await expectRevert(
  361. this.timelock.scheduleBatch(
  362. this.operation.targets,
  363. [],
  364. this.operation.payloads,
  365. this.operation.predecessor,
  366. this.operation.salt,
  367. MINDELAY,
  368. { from: proposer },
  369. ),
  370. 'TimelockController: length mismatch',
  371. );
  372. });
  373. it('length of batch parameter must match #1', async function () {
  374. await expectRevert(
  375. this.timelock.scheduleBatch(
  376. this.operation.targets,
  377. this.operation.values,
  378. [],
  379. this.operation.predecessor,
  380. this.operation.salt,
  381. MINDELAY,
  382. { from: proposer },
  383. ),
  384. 'TimelockController: length mismatch',
  385. );
  386. });
  387. it('prevent non-proposer from commiting', async function () {
  388. await expectRevert(
  389. this.timelock.scheduleBatch(
  390. this.operation.targets,
  391. this.operation.values,
  392. this.operation.payloads,
  393. this.operation.predecessor,
  394. this.operation.salt,
  395. MINDELAY,
  396. { from: other },
  397. ),
  398. `AccessControl: account ${other.toLowerCase()} is missing role ${PROPOSER_ROLE}`,
  399. );
  400. });
  401. it('enforce minimum delay', async function () {
  402. await expectRevert(
  403. this.timelock.scheduleBatch(
  404. this.operation.targets,
  405. this.operation.values,
  406. this.operation.payloads,
  407. this.operation.predecessor,
  408. this.operation.salt,
  409. MINDELAY - 1,
  410. { from: proposer },
  411. ),
  412. 'TimelockController: insufficient delay',
  413. );
  414. });
  415. });
  416. describe('execute', function () {
  417. beforeEach(async function () {
  418. this.operation = genOperationBatch(
  419. Array(8).fill('0x76E53CcEb05131Ef5248553bEBDb8F70536830b1'),
  420. Array(8).fill(0),
  421. Array(8).fill('0x58a60f63'),
  422. ZERO_BYTES32,
  423. '0x9545eeabc7a7586689191f78a5532443698538e54211b5bd4d7dc0fc0102b5c7',
  424. );
  425. });
  426. it('revert if operation is not scheduled', async function () {
  427. await expectRevert(
  428. this.timelock.executeBatch(
  429. this.operation.targets,
  430. this.operation.values,
  431. this.operation.payloads,
  432. this.operation.predecessor,
  433. this.operation.salt,
  434. { from: executor },
  435. ),
  436. 'TimelockController: operation is not ready',
  437. );
  438. });
  439. describe('with scheduled operation', function () {
  440. beforeEach(async function () {
  441. ({ receipt: this.receipt, logs: this.logs } = await this.timelock.scheduleBatch(
  442. this.operation.targets,
  443. this.operation.values,
  444. this.operation.payloads,
  445. this.operation.predecessor,
  446. this.operation.salt,
  447. MINDELAY,
  448. { from: proposer },
  449. ));
  450. });
  451. it('revert if execution comes too early 1/2', async function () {
  452. await expectRevert(
  453. this.timelock.executeBatch(
  454. this.operation.targets,
  455. this.operation.values,
  456. this.operation.payloads,
  457. this.operation.predecessor,
  458. this.operation.salt,
  459. { from: executor },
  460. ),
  461. 'TimelockController: operation is not ready',
  462. );
  463. });
  464. it('revert if execution comes too early 2/2', async function () {
  465. const timestamp = await this.timelock.getTimestamp(this.operation.id);
  466. await time.increaseTo(timestamp - 5); // -1 is to tight, test sometime fails
  467. await expectRevert(
  468. this.timelock.executeBatch(
  469. this.operation.targets,
  470. this.operation.values,
  471. this.operation.payloads,
  472. this.operation.predecessor,
  473. this.operation.salt,
  474. { from: executor },
  475. ),
  476. 'TimelockController: operation is not ready',
  477. );
  478. });
  479. describe('on time', function () {
  480. beforeEach(async function () {
  481. const timestamp = await this.timelock.getTimestamp(this.operation.id);
  482. await time.increaseTo(timestamp);
  483. });
  484. it('executor can reveal', async function () {
  485. const receipt = await this.timelock.executeBatch(
  486. this.operation.targets,
  487. this.operation.values,
  488. this.operation.payloads,
  489. this.operation.predecessor,
  490. this.operation.salt,
  491. { from: executor },
  492. );
  493. for (const i in this.operation.targets) {
  494. expectEvent(receipt, 'CallExecuted', {
  495. id: this.operation.id,
  496. index: web3.utils.toBN(i),
  497. target: this.operation.targets[i],
  498. value: web3.utils.toBN(this.operation.values[i]),
  499. data: this.operation.payloads[i],
  500. });
  501. }
  502. });
  503. it('prevent non-executor from revealing', async function () {
  504. await expectRevert(
  505. this.timelock.executeBatch(
  506. this.operation.targets,
  507. this.operation.values,
  508. this.operation.payloads,
  509. this.operation.predecessor,
  510. this.operation.salt,
  511. { from: other },
  512. ),
  513. `AccessControl: account ${other.toLowerCase()} is missing role ${EXECUTOR_ROLE}`,
  514. );
  515. });
  516. it('length mismatch #1', async function () {
  517. await expectRevert(
  518. this.timelock.executeBatch(
  519. [],
  520. this.operation.values,
  521. this.operation.payloads,
  522. this.operation.predecessor,
  523. this.operation.salt,
  524. { from: executor },
  525. ),
  526. 'TimelockController: length mismatch',
  527. );
  528. });
  529. it('length mismatch #2', async function () {
  530. await expectRevert(
  531. this.timelock.executeBatch(
  532. this.operation.targets,
  533. [],
  534. this.operation.payloads,
  535. this.operation.predecessor,
  536. this.operation.salt,
  537. { from: executor },
  538. ),
  539. 'TimelockController: length mismatch',
  540. );
  541. });
  542. it('length mismatch #3', async function () {
  543. await expectRevert(
  544. this.timelock.executeBatch(
  545. this.operation.targets,
  546. this.operation.values,
  547. [],
  548. this.operation.predecessor,
  549. this.operation.salt,
  550. { from: executor },
  551. ),
  552. 'TimelockController: length mismatch',
  553. );
  554. });
  555. });
  556. });
  557. it('partial execution', async function () {
  558. const operation = genOperationBatch(
  559. [
  560. this.callreceivermock.address,
  561. this.callreceivermock.address,
  562. this.callreceivermock.address,
  563. ],
  564. [
  565. 0,
  566. 0,
  567. 0,
  568. ],
  569. [
  570. this.callreceivermock.contract.methods.mockFunction().encodeABI(),
  571. this.callreceivermock.contract.methods.mockFunctionThrows().encodeABI(),
  572. this.callreceivermock.contract.methods.mockFunction().encodeABI(),
  573. ],
  574. ZERO_BYTES32,
  575. '0x8ac04aa0d6d66b8812fb41d39638d37af0a9ab11da507afd65c509f8ed079d3e',
  576. );
  577. await this.timelock.scheduleBatch(
  578. operation.targets,
  579. operation.values,
  580. operation.payloads,
  581. operation.predecessor,
  582. operation.salt,
  583. MINDELAY,
  584. { from: proposer },
  585. );
  586. await time.increase(MINDELAY);
  587. await expectRevert(
  588. this.timelock.executeBatch(
  589. operation.targets,
  590. operation.values,
  591. operation.payloads,
  592. operation.predecessor,
  593. operation.salt,
  594. { from: executor },
  595. ),
  596. 'TimelockController: underlying transaction reverted',
  597. );
  598. });
  599. });
  600. });
  601. describe('cancel', function () {
  602. beforeEach(async function () {
  603. this.operation = genOperation(
  604. '0xC6837c44AA376dbe1d2709F13879E040CAb653ca',
  605. 0,
  606. '0x296e58dd',
  607. ZERO_BYTES32,
  608. '0xa2485763600634800df9fc9646fb2c112cf98649c55f63dd1d9c7d13a64399d9',
  609. );
  610. ({ receipt: this.receipt, logs: this.logs } = await this.timelock.schedule(
  611. this.operation.target,
  612. this.operation.value,
  613. this.operation.data,
  614. this.operation.predecessor,
  615. this.operation.salt,
  616. MINDELAY,
  617. { from: proposer },
  618. ));
  619. });
  620. it('canceller can cancel', async function () {
  621. const receipt = await this.timelock.cancel(this.operation.id, { from: canceller });
  622. expectEvent(receipt, 'Cancelled', { id: this.operation.id });
  623. });
  624. it('cannot cancel invalid operation', async function () {
  625. await expectRevert(
  626. this.timelock.cancel(constants.ZERO_BYTES32, { from: canceller }),
  627. 'TimelockController: operation cannot be cancelled',
  628. );
  629. });
  630. it('prevent non-canceller from canceling', async function () {
  631. await expectRevert(
  632. this.timelock.cancel(this.operation.id, { from: other }),
  633. `AccessControl: account ${other.toLowerCase()} is missing role ${CANCELLER_ROLE}`,
  634. );
  635. });
  636. });
  637. });
  638. describe('maintenance', function () {
  639. it('prevent unauthorized maintenance', async function () {
  640. await expectRevert(
  641. this.timelock.updateDelay(0, { from: other }),
  642. 'TimelockController: caller must be timelock',
  643. );
  644. });
  645. it('timelock scheduled maintenance', async function () {
  646. const newDelay = time.duration.hours(6);
  647. const operation = genOperation(
  648. this.timelock.address,
  649. 0,
  650. this.timelock.contract.methods.updateDelay(newDelay.toString()).encodeABI(),
  651. ZERO_BYTES32,
  652. '0xf8e775b2c5f4d66fb5c7fa800f35ef518c262b6014b3c0aee6ea21bff157f108',
  653. );
  654. await this.timelock.schedule(
  655. operation.target,
  656. operation.value,
  657. operation.data,
  658. operation.predecessor,
  659. operation.salt,
  660. MINDELAY,
  661. { from: proposer },
  662. );
  663. await time.increase(MINDELAY);
  664. const receipt = await this.timelock.execute(
  665. operation.target,
  666. operation.value,
  667. operation.data,
  668. operation.predecessor,
  669. operation.salt,
  670. { from: executor },
  671. );
  672. expectEvent(receipt, 'MinDelayChange', { newDuration: newDelay.toString(), oldDuration: MINDELAY });
  673. expect(await this.timelock.getMinDelay()).to.be.bignumber.equal(newDelay);
  674. });
  675. });
  676. describe('dependency', function () {
  677. beforeEach(async function () {
  678. this.operation1 = genOperation(
  679. '0xdE66bD4c97304200A95aE0AadA32d6d01A867E39',
  680. 0,
  681. '0x01dc731a',
  682. ZERO_BYTES32,
  683. '0x64e932133c7677402ead2926f86205e2ca4686aebecf5a8077627092b9bb2feb',
  684. );
  685. this.operation2 = genOperation(
  686. '0x3c7944a3F1ee7fc8c5A5134ba7c79D11c3A1FCa3',
  687. 0,
  688. '0x8f531849',
  689. this.operation1.id,
  690. '0x036e1311cac523f9548e6461e29fb1f8f9196b91910a41711ea22f5de48df07d',
  691. );
  692. await this.timelock.schedule(
  693. this.operation1.target,
  694. this.operation1.value,
  695. this.operation1.data,
  696. this.operation1.predecessor,
  697. this.operation1.salt,
  698. MINDELAY,
  699. { from: proposer },
  700. );
  701. await this.timelock.schedule(
  702. this.operation2.target,
  703. this.operation2.value,
  704. this.operation2.data,
  705. this.operation2.predecessor,
  706. this.operation2.salt,
  707. MINDELAY,
  708. { from: proposer },
  709. );
  710. await time.increase(MINDELAY);
  711. });
  712. it('cannot execute before dependency', async function () {
  713. await expectRevert(
  714. this.timelock.execute(
  715. this.operation2.target,
  716. this.operation2.value,
  717. this.operation2.data,
  718. this.operation2.predecessor,
  719. this.operation2.salt,
  720. { from: executor },
  721. ),
  722. 'TimelockController: missing dependency',
  723. );
  724. });
  725. it('can execute after dependency', async function () {
  726. await this.timelock.execute(
  727. this.operation1.target,
  728. this.operation1.value,
  729. this.operation1.data,
  730. this.operation1.predecessor,
  731. this.operation1.salt,
  732. { from: executor },
  733. );
  734. await this.timelock.execute(
  735. this.operation2.target,
  736. this.operation2.value,
  737. this.operation2.data,
  738. this.operation2.predecessor,
  739. this.operation2.salt,
  740. { from: executor },
  741. );
  742. });
  743. });
  744. describe('usage scenario', function () {
  745. this.timeout(10000);
  746. it('call', async function () {
  747. const operation = genOperation(
  748. this.implementation2.address,
  749. 0,
  750. this.implementation2.contract.methods.setValue(42).encodeABI(),
  751. ZERO_BYTES32,
  752. '0x8043596363daefc89977b25f9d9b4d06c3910959ef0c4d213557a903e1b555e2',
  753. );
  754. await this.timelock.schedule(
  755. operation.target,
  756. operation.value,
  757. operation.data,
  758. operation.predecessor,
  759. operation.salt,
  760. MINDELAY,
  761. { from: proposer },
  762. );
  763. await time.increase(MINDELAY);
  764. await this.timelock.execute(
  765. operation.target,
  766. operation.value,
  767. operation.data,
  768. operation.predecessor,
  769. operation.salt,
  770. { from: executor },
  771. );
  772. expect(await this.implementation2.getValue()).to.be.bignumber.equal(web3.utils.toBN(42));
  773. });
  774. it('call reverting', async function () {
  775. const operation = genOperation(
  776. this.callreceivermock.address,
  777. 0,
  778. this.callreceivermock.contract.methods.mockFunctionRevertsNoReason().encodeABI(),
  779. ZERO_BYTES32,
  780. '0xb1b1b276fdf1a28d1e00537ea73b04d56639128b08063c1a2f70a52e38cba693',
  781. );
  782. await this.timelock.schedule(
  783. operation.target,
  784. operation.value,
  785. operation.data,
  786. operation.predecessor,
  787. operation.salt,
  788. MINDELAY,
  789. { from: proposer },
  790. );
  791. await time.increase(MINDELAY);
  792. await expectRevert(
  793. this.timelock.execute(
  794. operation.target,
  795. operation.value,
  796. operation.data,
  797. operation.predecessor,
  798. operation.salt,
  799. { from: executor },
  800. ),
  801. 'TimelockController: underlying transaction reverted',
  802. );
  803. });
  804. it('call throw', async function () {
  805. const operation = genOperation(
  806. this.callreceivermock.address,
  807. 0,
  808. this.callreceivermock.contract.methods.mockFunctionThrows().encodeABI(),
  809. ZERO_BYTES32,
  810. '0xe5ca79f295fc8327ee8a765fe19afb58f4a0cbc5053642bfdd7e73bc68e0fc67',
  811. );
  812. await this.timelock.schedule(
  813. operation.target,
  814. operation.value,
  815. operation.data,
  816. operation.predecessor,
  817. operation.salt,
  818. MINDELAY,
  819. { from: proposer },
  820. );
  821. await time.increase(MINDELAY);
  822. await expectRevert(
  823. this.timelock.execute(
  824. operation.target,
  825. operation.value,
  826. operation.data,
  827. operation.predecessor,
  828. operation.salt,
  829. { from: executor },
  830. ),
  831. 'TimelockController: underlying transaction reverted',
  832. );
  833. });
  834. it('call out of gas', async function () {
  835. const operation = genOperation(
  836. this.callreceivermock.address,
  837. 0,
  838. this.callreceivermock.contract.methods.mockFunctionOutOfGas().encodeABI(),
  839. ZERO_BYTES32,
  840. '0xf3274ce7c394c5b629d5215723563a744b817e1730cca5587c567099a14578fd',
  841. );
  842. await this.timelock.schedule(
  843. operation.target,
  844. operation.value,
  845. operation.data,
  846. operation.predecessor,
  847. operation.salt,
  848. MINDELAY,
  849. { from: proposer },
  850. );
  851. await time.increase(MINDELAY);
  852. await expectRevert(
  853. this.timelock.execute(
  854. operation.target,
  855. operation.value,
  856. operation.data,
  857. operation.predecessor,
  858. operation.salt,
  859. { from: executor, gas: '70000' },
  860. ),
  861. 'TimelockController: underlying transaction reverted',
  862. );
  863. });
  864. it('call payable with eth', async function () {
  865. const operation = genOperation(
  866. this.callreceivermock.address,
  867. 1,
  868. this.callreceivermock.contract.methods.mockFunction().encodeABI(),
  869. ZERO_BYTES32,
  870. '0x5ab73cd33477dcd36c1e05e28362719d0ed59a7b9ff14939de63a43073dc1f44',
  871. );
  872. await this.timelock.schedule(
  873. operation.target,
  874. operation.value,
  875. operation.data,
  876. operation.predecessor,
  877. operation.salt,
  878. MINDELAY,
  879. { from: proposer },
  880. );
  881. await time.increase(MINDELAY);
  882. expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  883. expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  884. await this.timelock.execute(
  885. operation.target,
  886. operation.value,
  887. operation.data,
  888. operation.predecessor,
  889. operation.salt,
  890. { from: executor, value: 1 },
  891. );
  892. expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  893. expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(1));
  894. });
  895. it('call nonpayable with eth', async function () {
  896. const operation = genOperation(
  897. this.callreceivermock.address,
  898. 1,
  899. this.callreceivermock.contract.methods.mockFunctionNonPayable().encodeABI(),
  900. ZERO_BYTES32,
  901. '0xb78edbd920c7867f187e5aa6294ae5a656cfbf0dea1ccdca3751b740d0f2bdf8',
  902. );
  903. await this.timelock.schedule(
  904. operation.target,
  905. operation.value,
  906. operation.data,
  907. operation.predecessor,
  908. operation.salt,
  909. MINDELAY,
  910. { from: proposer },
  911. );
  912. await time.increase(MINDELAY);
  913. expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  914. expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  915. await expectRevert(
  916. this.timelock.execute(
  917. operation.target,
  918. operation.value,
  919. operation.data,
  920. operation.predecessor,
  921. operation.salt,
  922. { from: executor },
  923. ),
  924. 'TimelockController: underlying transaction reverted',
  925. );
  926. expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  927. expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  928. });
  929. it('call reverting with eth', async function () {
  930. const operation = genOperation(
  931. this.callreceivermock.address,
  932. 1,
  933. this.callreceivermock.contract.methods.mockFunctionRevertsNoReason().encodeABI(),
  934. ZERO_BYTES32,
  935. '0xdedb4563ef0095db01d81d3f2decf57cf83e4a72aa792af14c43a792b56f4de6',
  936. );
  937. await this.timelock.schedule(
  938. operation.target,
  939. operation.value,
  940. operation.data,
  941. operation.predecessor,
  942. operation.salt,
  943. MINDELAY,
  944. { from: proposer },
  945. );
  946. await time.increase(MINDELAY);
  947. expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  948. expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  949. await expectRevert(
  950. this.timelock.execute(
  951. operation.target,
  952. operation.value,
  953. operation.data,
  954. operation.predecessor,
  955. operation.salt,
  956. { from: executor },
  957. ),
  958. 'TimelockController: underlying transaction reverted',
  959. );
  960. expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  961. expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
  962. });
  963. });
  964. });