ERC721Votes.test.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /* eslint-disable */
  2. const { BN, expectEvent, time } = require('@openzeppelin/test-helpers');
  3. const { expect } = require('chai');
  4. const { promisify } = require('util');
  5. const queue = promisify(setImmediate);
  6. const ERC721VotesMock = artifacts.require('ERC721VotesMock');
  7. const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behavior');
  8. contract('ERC721Votes', function (accounts) {
  9. const [ account1, account2, account1Delegatee, other1, other2 ] = accounts;
  10. this.name = 'My Vote';
  11. const symbol = 'MTKN';
  12. beforeEach(async function () {
  13. this.votes = await ERC721VotesMock.new(name, symbol);
  14. // We get the chain id from the contract because Ganache (used for coverage) does not return the same chain id
  15. // from within the EVM as from the JSON RPC interface.
  16. // See https://github.com/trufflesuite/ganache-core/issues/515
  17. this.chainId = await this.votes.getChainId();
  18. this.NFT0 = new BN('10000000000000000000000000');
  19. this.NFT1 = new BN('10');
  20. this.NFT2 = new BN('20');
  21. this.NFT3 = new BN('30');
  22. });
  23. describe('balanceOf', function () {
  24. beforeEach(async function () {
  25. await this.votes.mint(account1, this.NFT0);
  26. await this.votes.mint(account1, this.NFT1);
  27. await this.votes.mint(account1, this.NFT2);
  28. await this.votes.mint(account1, this.NFT3);
  29. });
  30. it('grants to initial account', async function () {
  31. expect(await this.votes.balanceOf(account1)).to.be.bignumber.equal('4');
  32. });
  33. });
  34. describe('transfers', function () {
  35. beforeEach(async function () {
  36. await this.votes.mint(account1, this.NFT0);
  37. });
  38. it('no delegation', async function () {
  39. const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 });
  40. expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.NFT0 });
  41. expectEvent.notEmitted(receipt, 'DelegateVotesChanged');
  42. this.account1Votes = '0';
  43. this.account2Votes = '0';
  44. });
  45. it('sender delegation', async function () {
  46. await this.votes.delegate(account1, { from: account1 });
  47. const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 });
  48. expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.NFT0 });
  49. expectEvent(receipt, 'DelegateVotesChanged', { delegate: account1, previousBalance: '1', newBalance: '0' });
  50. const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer');
  51. expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true);
  52. this.account1Votes = '0';
  53. this.account2Votes = '0';
  54. });
  55. it('receiver delegation', async function () {
  56. await this.votes.delegate(account2, { from: account2 });
  57. const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 });
  58. expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.NFT0 });
  59. expectEvent(receipt, 'DelegateVotesChanged', { delegate: account2, previousBalance: '0', newBalance: '1' });
  60. const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer');
  61. expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true);
  62. this.account1Votes = '0';
  63. this.account2Votes = '1';
  64. });
  65. it('full delegation', async function () {
  66. await this.votes.delegate(account1, { from: account1 });
  67. await this.votes.delegate(account2, { from: account2 });
  68. const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 });
  69. expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.NFT0 });
  70. expectEvent(receipt, 'DelegateVotesChanged', { delegate: account1, previousBalance: '1', newBalance: '0'});
  71. expectEvent(receipt, 'DelegateVotesChanged', { delegate: account2, previousBalance: '0', newBalance: '1' });
  72. const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer');
  73. expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true);
  74. this.account1Votes = '0';
  75. this.account2Votes = '1';
  76. });
  77. it('returns the same total supply on transfers', async function () {
  78. await this.votes.delegate(account1, { from: account1 });
  79. const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 });
  80. await time.advanceBlock();
  81. await time.advanceBlock();
  82. expect(await this.votes.getPastTotalSupply(receipt.blockNumber - 1)).to.be.bignumber.equal('1');
  83. expect(await this.votes.getPastTotalSupply(receipt.blockNumber + 1)).to.be.bignumber.equal('1');
  84. this.account1Votes = '0';
  85. this.account2Votes = '0';
  86. });
  87. it('generally returns the voting balance at the appropriate checkpoint', async function () {
  88. await this.votes.mint(account1, this.NFT1);
  89. await this.votes.mint(account1, this.NFT2);
  90. await this.votes.mint(account1, this.NFT3);
  91. const total = await this.votes.balanceOf(account1);
  92. const t1 = await this.votes.delegate(other1, { from: account1 });
  93. await time.advanceBlock();
  94. await time.advanceBlock();
  95. const t2 = await this.votes.transferFrom(account1, other2, this.NFT0, { from: account1 });
  96. await time.advanceBlock();
  97. await time.advanceBlock();
  98. const t3 = await this.votes.transferFrom(account1, other2, this.NFT2, { from: account1 });
  99. await time.advanceBlock();
  100. await time.advanceBlock();
  101. const t4 = await this.votes.transferFrom(other2, account1, this.NFT2, { from: other2 });
  102. await time.advanceBlock();
  103. await time.advanceBlock();
  104. expect(await this.votes.getPastVotes(other1, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
  105. expect(await this.votes.getPastVotes(other1, t1.receipt.blockNumber)).to.be.bignumber.equal(total);
  106. expect(await this.votes.getPastVotes(other1, t1.receipt.blockNumber + 1)).to.be.bignumber.equal(total);
  107. expect(await this.votes.getPastVotes(other1, t2.receipt.blockNumber)).to.be.bignumber.equal('3');
  108. expect(await this.votes.getPastVotes(other1, t2.receipt.blockNumber + 1)).to.be.bignumber.equal('3');
  109. expect(await this.votes.getPastVotes(other1, t3.receipt.blockNumber)).to.be.bignumber.equal('2');
  110. expect(await this.votes.getPastVotes(other1, t3.receipt.blockNumber + 1)).to.be.bignumber.equal('2');
  111. expect(await this.votes.getPastVotes(other1, t4.receipt.blockNumber)).to.be.bignumber.equal('3');
  112. expect(await this.votes.getPastVotes(other1, t4.receipt.blockNumber + 1)).to.be.bignumber.equal('3');
  113. this.account1Votes = '0';
  114. this.account2Votes = '0';
  115. });
  116. afterEach(async function () {
  117. expect(await this.votes.getVotes(account1)).to.be.bignumber.equal(this.account1Votes);
  118. expect(await this.votes.getVotes(account2)).to.be.bignumber.equal(this.account2Votes);
  119. // need to advance 2 blocks to see the effect of a transfer on "getPastVotes"
  120. const blockNumber = await time.latestBlock();
  121. await time.advanceBlock();
  122. expect(await this.votes.getPastVotes(account1, blockNumber)).to.be.bignumber.equal(this.account1Votes);
  123. expect(await this.votes.getPastVotes(account2, blockNumber)).to.be.bignumber.equal(this.account2Votes);
  124. });
  125. });
  126. describe('Voting workflow', function () {
  127. beforeEach(async function () {
  128. this.account1 = account1;
  129. this.account1Delegatee = account1Delegatee;
  130. this.account2 = account2;
  131. this.name = 'My Vote';
  132. });
  133. shouldBehaveLikeVotes();
  134. });
  135. });