TimelockController.test.js 36 KB

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