TimelockController.test.js 36 KB

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