VotesExtended.test.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers');
  4. const { sum } = require('../../helpers/math');
  5. const { zip } = require('../../helpers/iterate');
  6. const time = require('../../helpers/time');
  7. const { shouldBehaveLikeVotes } = require('./Votes.behavior');
  8. const MODES = {
  9. blocknumber: '$VotesExtendedMock',
  10. timestamp: '$VotesExtendedTimestampMock',
  11. };
  12. const AMOUNTS = [ethers.parseEther('10000000'), 10n, 20n];
  13. describe('VotesExtended', function () {
  14. for (const [mode, artifact] of Object.entries(MODES)) {
  15. const fixture = async () => {
  16. const accounts = await ethers.getSigners();
  17. const amounts = Object.fromEntries(
  18. zip(
  19. accounts.slice(0, AMOUNTS.length).map(({ address }) => address),
  20. AMOUNTS,
  21. ),
  22. );
  23. const name = 'Override Votes';
  24. const version = '1';
  25. const votes = await ethers.deployContract(artifact, [name, version]);
  26. return { accounts, amounts, votes, name, version };
  27. };
  28. describe(`vote with ${mode}`, function () {
  29. beforeEach(async function () {
  30. Object.assign(this, await loadFixture(fixture));
  31. });
  32. shouldBehaveLikeVotes(AMOUNTS, { mode, fungible: true });
  33. it('starts with zero votes', async function () {
  34. expect(await this.votes.getTotalSupply()).to.equal(0n);
  35. });
  36. describe('performs voting operations', function () {
  37. beforeEach(async function () {
  38. this.txs = [];
  39. for (const [account, amount] of Object.entries(this.amounts)) {
  40. this.txs.push(await this.votes.$_mint(account, amount));
  41. }
  42. });
  43. it('reverts if block number >= current block', async function () {
  44. const lastTxTimepoint = await time.clockFromReceipt[mode](this.txs.at(-1));
  45. const clock = await this.votes.clock();
  46. await expect(this.votes.getPastTotalSupply(lastTxTimepoint))
  47. .to.be.revertedWithCustomError(this.votes, 'ERC5805FutureLookup')
  48. .withArgs(lastTxTimepoint, clock);
  49. });
  50. it('delegates', async function () {
  51. expect(await this.votes.getVotes(this.accounts[0])).to.equal(0n);
  52. expect(await this.votes.getVotes(this.accounts[1])).to.equal(0n);
  53. expect(await this.votes.delegates(this.accounts[0])).to.equal(ethers.ZeroAddress);
  54. expect(await this.votes.delegates(this.accounts[1])).to.equal(ethers.ZeroAddress);
  55. await this.votes.delegate(this.accounts[0], ethers.Typed.address(this.accounts[0]));
  56. expect(await this.votes.getVotes(this.accounts[0])).to.equal(this.amounts[this.accounts[0].address]);
  57. expect(await this.votes.getVotes(this.accounts[1])).to.equal(0n);
  58. expect(await this.votes.delegates(this.accounts[0])).to.equal(this.accounts[0]);
  59. expect(await this.votes.delegates(this.accounts[1])).to.equal(ethers.ZeroAddress);
  60. await this.votes.delegate(this.accounts[1], ethers.Typed.address(this.accounts[0]));
  61. expect(await this.votes.getVotes(this.accounts[0])).to.equal(
  62. this.amounts[this.accounts[0].address] + this.amounts[this.accounts[1].address],
  63. );
  64. expect(await this.votes.getVotes(this.accounts[1])).to.equal(0n);
  65. expect(await this.votes.delegates(this.accounts[0])).to.equal(this.accounts[0]);
  66. expect(await this.votes.delegates(this.accounts[1])).to.equal(this.accounts[0]);
  67. });
  68. it('cross delegates', async function () {
  69. await this.votes.delegate(this.accounts[0], ethers.Typed.address(this.accounts[1]));
  70. await this.votes.delegate(this.accounts[1], ethers.Typed.address(this.accounts[0]));
  71. expect(await this.votes.getVotes(this.accounts[0])).to.equal(this.amounts[this.accounts[1].address]);
  72. expect(await this.votes.getVotes(this.accounts[1])).to.equal(this.amounts[this.accounts[0].address]);
  73. });
  74. it('returns total amount of votes', async function () {
  75. const totalSupply = sum(...Object.values(this.amounts));
  76. expect(await this.votes.getTotalSupply()).to.equal(totalSupply);
  77. });
  78. });
  79. });
  80. describe(`checkpoint delegates with ${mode}`, function () {
  81. beforeEach(async function () {
  82. Object.assign(this, await loadFixture(fixture));
  83. });
  84. it('checkpoint delegates', async function () {
  85. const tx = await this.votes.delegate(this.accounts[0], ethers.Typed.address(this.accounts[1]));
  86. const timepoint = await time.clockFromReceipt[mode](tx);
  87. await mine(2);
  88. expect(await this.votes.getPastDelegate(this.accounts[0], timepoint - 1n)).to.equal(ethers.ZeroAddress);
  89. expect(await this.votes.getPastDelegate(this.accounts[0], timepoint)).to.equal(this.accounts[1].address);
  90. expect(await this.votes.getPastDelegate(this.accounts[0], timepoint + 1n)).to.equal(this.accounts[1].address);
  91. });
  92. it('reverts if current timepoint <= timepoint', async function () {
  93. const tx = await this.votes.delegate(this.accounts[0], ethers.Typed.address(this.accounts[1]));
  94. const timepoint = await time.clockFromReceipt[mode](tx);
  95. await expect(this.votes.getPastDelegate(this.accounts[0], timepoint + 1n))
  96. .to.be.revertedWithCustomError(this.votes, 'ERC5805FutureLookup')
  97. .withArgs(timepoint + 1n, timepoint);
  98. });
  99. });
  100. describe(`checkpoint balances with ${mode}`, function () {
  101. beforeEach(async function () {
  102. Object.assign(this, await loadFixture(fixture));
  103. });
  104. it('checkpoint balances', async function () {
  105. const tx = await this.votes.$_mint(this.accounts[0].address, 100n);
  106. const timepoint = await time.clockFromReceipt[mode](tx);
  107. await mine(2);
  108. expect(await this.votes.getPastBalanceOf(this.accounts[0].address, timepoint - 1n)).to.equal(0n);
  109. expect(await this.votes.getPastBalanceOf(this.accounts[0].address, timepoint)).to.equal(100n);
  110. expect(await this.votes.getPastBalanceOf(this.accounts[0].address, timepoint + 1n)).to.equal(100n);
  111. });
  112. it('reverts if current timepoint <= timepoint', async function () {
  113. const tx = await this.votes.$_mint(this.accounts[0].address, 100n);
  114. const timepoint = await time.clockFromReceipt[mode](tx);
  115. await expect(this.votes.getPastBalanceOf(this.accounts[0], timepoint + 1n))
  116. .to.be.revertedWithCustomError(this.votes, 'ERC5805FutureLookup')
  117. .withArgs(timepoint + 1n, timepoint);
  118. });
  119. });
  120. }
  121. });