TimelockController.test.js 36 KB

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