TimelockController.test.js 36 KB

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