TimelockController.test.js 36 KB

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