GovernorTimelockControl.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
  2. const { expect } = require('chai');
  3. const Enums = require('../../helpers/enums');
  4. const {
  5. runGovernorWorkflow,
  6. } = require('../GovernorWorkflow.behavior');
  7. const {
  8. shouldSupportInterfaces,
  9. } = require('../../utils/introspection/SupportsInterface.behavior');
  10. const Token = artifacts.require('ERC20VotesMock');
  11. const Timelock = artifacts.require('TimelockController');
  12. const Governor = artifacts.require('GovernorTimelockControlMock');
  13. const CallReceiver = artifacts.require('CallReceiverMock');
  14. contract('GovernorTimelockControl', function (accounts) {
  15. const [ voter ] = accounts;
  16. const name = 'OZ-Governor';
  17. // const version = '1';
  18. const tokenName = 'MockToken';
  19. const tokenSymbol = 'MTKN';
  20. const tokenSupply = web3.utils.toWei('100');
  21. beforeEach(async function () {
  22. const [ deployer ] = await web3.eth.getAccounts();
  23. this.token = await Token.new(tokenName, tokenSymbol);
  24. this.timelock = await Timelock.new(3600, [], []);
  25. this.mock = await Governor.new(name, this.token.address, 4, 16, this.timelock.address, 0);
  26. this.receiver = await CallReceiver.new();
  27. // normal setup: governor is proposer, everyone is executor, timelock is its own admin
  28. await this.timelock.grantRole(await this.timelock.PROPOSER_ROLE(), this.mock.address);
  29. await this.timelock.grantRole(await this.timelock.EXECUTOR_ROLE(), constants.ZERO_ADDRESS);
  30. await this.timelock.revokeRole(await this.timelock.TIMELOCK_ADMIN_ROLE(), deployer);
  31. await this.token.mint(voter, tokenSupply);
  32. await this.token.delegate(voter, { from: voter });
  33. });
  34. shouldSupportInterfaces([
  35. 'ERC165',
  36. 'Governor',
  37. 'GovernorTimelock',
  38. ]);
  39. it('post deployment check', async function () {
  40. expect(await this.mock.name()).to.be.equal(name);
  41. expect(await this.mock.token()).to.be.equal(this.token.address);
  42. expect(await this.mock.votingDelay()).to.be.bignumber.equal('4');
  43. expect(await this.mock.votingPeriod()).to.be.bignumber.equal('16');
  44. expect(await this.mock.quorum(0)).to.be.bignumber.equal('0');
  45. expect(await this.mock.timelock()).to.be.equal(this.timelock.address);
  46. });
  47. describe('nominal', function () {
  48. beforeEach(async function () {
  49. this.settings = {
  50. proposal: [
  51. [ this.receiver.address ],
  52. [ web3.utils.toWei('0') ],
  53. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  54. '<proposal description>',
  55. ],
  56. voters: [
  57. { voter: voter, support: Enums.VoteType.For },
  58. ],
  59. steps: {
  60. queue: { delay: 3600 },
  61. },
  62. };
  63. });
  64. afterEach(async function () {
  65. const timelockid = await this.timelock.hashOperationBatch(
  66. ...this.settings.proposal.slice(0, 3),
  67. '0x0',
  68. this.descriptionHash,
  69. );
  70. expectEvent(
  71. this.receipts.propose,
  72. 'ProposalCreated',
  73. { proposalId: this.id },
  74. );
  75. expectEvent(
  76. this.receipts.queue,
  77. 'ProposalQueued',
  78. { proposalId: this.id },
  79. );
  80. await expectEvent.inTransaction(
  81. this.receipts.queue.transactionHash,
  82. this.timelock,
  83. 'CallScheduled',
  84. { id: timelockid },
  85. );
  86. expectEvent(
  87. this.receipts.execute,
  88. 'ProposalExecuted',
  89. { proposalId: this.id },
  90. );
  91. await expectEvent.inTransaction(
  92. this.receipts.execute.transactionHash,
  93. this.timelock,
  94. 'CallExecuted',
  95. { id: timelockid },
  96. );
  97. await expectEvent.inTransaction(
  98. this.receipts.execute.transactionHash,
  99. this.receiver,
  100. 'MockFunctionCalled',
  101. );
  102. });
  103. runGovernorWorkflow();
  104. });
  105. describe('executed by other proposer', function () {
  106. beforeEach(async function () {
  107. this.settings = {
  108. proposal: [
  109. [ this.receiver.address ],
  110. [ web3.utils.toWei('0') ],
  111. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  112. '<proposal description>',
  113. ],
  114. voters: [
  115. { voter: voter, support: Enums.VoteType.For },
  116. ],
  117. steps: {
  118. queue: { delay: 3600 },
  119. execute: { enable: false },
  120. },
  121. };
  122. });
  123. afterEach(async function () {
  124. await this.timelock.executeBatch(
  125. ...this.settings.proposal.slice(0, 3),
  126. '0x0',
  127. this.descriptionHash,
  128. );
  129. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Executed);
  130. await expectRevert(
  131. this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  132. 'Governor: proposal not successful',
  133. );
  134. });
  135. runGovernorWorkflow();
  136. });
  137. describe('not queued', function () {
  138. beforeEach(async function () {
  139. this.settings = {
  140. proposal: [
  141. [ this.receiver.address ],
  142. [ web3.utils.toWei('0') ],
  143. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  144. '<proposal description>',
  145. ],
  146. voters: [
  147. { voter: voter, support: Enums.VoteType.For },
  148. ],
  149. steps: {
  150. queue: { enable: false },
  151. execute: { error: 'TimelockController: operation is not ready' },
  152. },
  153. };
  154. });
  155. afterEach(async function () {
  156. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
  157. });
  158. runGovernorWorkflow();
  159. });
  160. describe('to early', function () {
  161. beforeEach(async function () {
  162. this.settings = {
  163. proposal: [
  164. [ this.receiver.address ],
  165. [ web3.utils.toWei('0') ],
  166. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  167. '<proposal description>',
  168. ],
  169. voters: [
  170. { voter: voter, support: Enums.VoteType.For },
  171. ],
  172. steps: {
  173. execute: { error: 'TimelockController: operation is not ready' },
  174. },
  175. };
  176. });
  177. afterEach(async function () {
  178. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
  179. });
  180. runGovernorWorkflow();
  181. });
  182. describe('re-queue / re-execute', function () {
  183. beforeEach(async function () {
  184. this.settings = {
  185. proposal: [
  186. [ this.receiver.address ],
  187. [ web3.utils.toWei('0') ],
  188. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  189. '<proposal description>',
  190. ],
  191. voters: [
  192. { voter: voter, support: Enums.VoteType.For },
  193. ],
  194. steps: {
  195. queue: { delay: 3600 },
  196. },
  197. };
  198. });
  199. afterEach(async function () {
  200. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Executed);
  201. await expectRevert(
  202. this.mock.queue(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  203. 'Governor: proposal not successful',
  204. );
  205. await expectRevert(
  206. this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  207. 'Governor: proposal not successful',
  208. );
  209. });
  210. runGovernorWorkflow();
  211. });
  212. describe('cancel before queue prevents scheduling', function () {
  213. beforeEach(async function () {
  214. this.settings = {
  215. proposal: [
  216. [ this.receiver.address ],
  217. [ web3.utils.toWei('0') ],
  218. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  219. '<proposal description>',
  220. ],
  221. voters: [
  222. { voter: voter, support: Enums.VoteType.For },
  223. ],
  224. steps: {
  225. queue: { enable: false },
  226. execute: { enable: false },
  227. },
  228. };
  229. });
  230. afterEach(async function () {
  231. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
  232. expectEvent(
  233. await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  234. 'ProposalCanceled',
  235. { proposalId: this.id },
  236. );
  237. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
  238. await expectRevert(
  239. this.mock.queue(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  240. 'Governor: proposal not successful',
  241. );
  242. });
  243. runGovernorWorkflow();
  244. });
  245. describe('cancel after queue prevents execution', function () {
  246. beforeEach(async function () {
  247. this.settings = {
  248. proposal: [
  249. [ this.receiver.address ],
  250. [ web3.utils.toWei('0') ],
  251. [ this.receiver.contract.methods.mockFunction().encodeABI() ],
  252. '<proposal description>',
  253. ],
  254. voters: [
  255. { voter: voter, support: Enums.VoteType.For },
  256. ],
  257. steps: {
  258. queue: { delay: 3600 },
  259. execute: { enable: false },
  260. },
  261. };
  262. });
  263. afterEach(async function () {
  264. const timelockid = await this.timelock.hashOperationBatch(
  265. ...this.settings.proposal.slice(0, 3),
  266. '0x0',
  267. this.descriptionHash,
  268. );
  269. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
  270. const receipt = await this.mock.cancel(...this.settings.proposal.slice(0, -1), this.descriptionHash);
  271. expectEvent(
  272. receipt,
  273. 'ProposalCanceled',
  274. { proposalId: this.id },
  275. );
  276. await expectEvent.inTransaction(
  277. receipt.receipt.transactionHash,
  278. this.timelock,
  279. 'Cancelled',
  280. { id: timelockid },
  281. );
  282. expect(await this.mock.state(this.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
  283. await expectRevert(
  284. this.mock.execute(...this.settings.proposal.slice(0, -1), this.descriptionHash),
  285. 'Governor: proposal not successful',
  286. );
  287. });
  288. runGovernorWorkflow();
  289. });
  290. describe('updateTimelock', function () {
  291. beforeEach(async function () {
  292. this.newTimelock = await Timelock.new(3600, [], []);
  293. });
  294. it('protected', async function () {
  295. await expectRevert(
  296. this.mock.updateTimelock(this.newTimelock.address),
  297. 'Governor: onlyGovernance',
  298. );
  299. });
  300. describe('using workflow', function () {
  301. beforeEach(async function () {
  302. this.settings = {
  303. proposal: [
  304. [ this.mock.address ],
  305. [ web3.utils.toWei('0') ],
  306. [ this.mock.contract.methods.updateTimelock(this.newTimelock.address).encodeABI() ],
  307. '<proposal description>',
  308. ],
  309. voters: [
  310. { voter: voter, support: Enums.VoteType.For },
  311. ],
  312. steps: {
  313. queue: { delay: 3600 },
  314. },
  315. };
  316. });
  317. afterEach(async function () {
  318. expectEvent(
  319. this.receipts.propose,
  320. 'ProposalCreated',
  321. { proposalId: this.id },
  322. );
  323. expectEvent(
  324. this.receipts.execute,
  325. 'ProposalExecuted',
  326. { proposalId: this.id },
  327. );
  328. expectEvent(
  329. this.receipts.execute,
  330. 'TimelockChange',
  331. { oldTimelock: this.timelock.address, newTimelock: this.newTimelock.address },
  332. );
  333. expect(await this.mock.timelock()).to.be.bignumber.equal(this.newTimelock.address);
  334. });
  335. runGovernorWorkflow();
  336. });
  337. });
  338. });