TimelockController.test.js 33 KB

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