Bladeren bron

Prepare tests for hardhat-exposed transition (#3930)

Co-authored-by: Francisco <frangio.1@gmail.com>
Hadrien Croubois 2 jaren geleden
bovenliggende
commit
7c6e289782

+ 1 - 1
contracts/mocks/ERC721VotesMock.sol

@@ -15,7 +15,7 @@ contract ERC721VotesMock is ERC721Votes {
         _mint(account, tokenId);
     }
 
-    function burn(address, uint256 tokenId) public {
+    function burn(uint256 tokenId) public {
         _burn(tokenId);
     }
 

+ 8 - 11
contracts/mocks/VotesMock.sol

@@ -5,8 +5,7 @@ pragma solidity ^0.8.0;
 import "../governance/utils/Votes.sol";
 
 contract VotesMock is Votes {
-    mapping(address => uint256) private _balances;
-    mapping(uint256 => address) private _owners;
+    mapping(address => uint256) private _votingUnits;
 
     constructor(string memory name) EIP712(name, "1") {}
 
@@ -19,19 +18,17 @@ contract VotesMock is Votes {
     }
 
     function _getVotingUnits(address account) internal view override returns (uint256) {
-        return _balances[account];
+        return _votingUnits[account];
     }
 
-    function mint(address account, uint256 voteId) external {
-        _balances[account] += 1;
-        _owners[voteId] = account;
-        _transferVotingUnits(address(0), account, 1);
+    function mint(address account, uint256 votes) external {
+        _votingUnits[account] += votes;
+        _transferVotingUnits(address(0), account, votes);
     }
 
-    function burn(address, uint256 voteId) external {
-        address owner = _owners[voteId];
-        _balances[owner] -= 1;
-        _transferVotingUnits(owner, address(0), 1);
+    function burn(address account, uint256 votes) external {
+        _votingUnits[account] += votes;
+        _transferVotingUnits(account, address(0), votes);
     }
 
     function getChainId() external view returns (uint256) {

+ 36 - 87
package-lock.json

@@ -8,8 +8,8 @@
       "name": "openzeppelin-solidity",
       "version": "4.8.0",
       "license": "MIT",
-      "bin": {
-        "openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js"
+      "dependencies": {
+        "array.prototype.at": "^1.1.1"
       },
       "devDependencies": {
         "@nomicfoundation/hardhat-network-helpers": "^1.0.3",
@@ -2628,6 +2628,20 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/array.prototype.at": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.at/-/array.prototype.at-1.1.1.tgz",
+      "integrity": "sha512-n/wYNLJy/fVEU9EGPt2ww920hy1XX3XB2yTREFy1QsxctBgQV/tZIwg1G8jVxELna4pLCzg/xvvS/DDXtI4NNg==",
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/array.prototype.flat": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
@@ -3241,7 +3255,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
       "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
-      "dev": true,
       "dependencies": {
         "function-bind": "^1.1.1",
         "get-intrinsic": "^1.0.2"
@@ -4197,7 +4210,6 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
       "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
-      "dev": true,
       "dependencies": {
         "has-property-descriptors": "^1.0.0",
         "object-keys": "^1.1.1"
@@ -4506,7 +4518,6 @@
       "version": "1.20.5",
       "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz",
       "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "es-to-primitive": "^1.2.1",
@@ -4551,7 +4562,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
       "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
-      "dev": true,
       "dependencies": {
         "has": "^1.0.3"
       }
@@ -4560,7 +4570,6 @@
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
       "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
-      "dev": true,
       "dependencies": {
         "is-callable": "^1.1.4",
         "is-date-object": "^1.0.1",
@@ -6524,14 +6533,12 @@
     "node_modules/function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
     },
     "node_modules/function.prototype.name": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
       "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.3",
@@ -6555,7 +6562,6 @@
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
       "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
-      "dev": true,
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -6582,7 +6588,6 @@
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
       "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
-      "dev": true,
       "dependencies": {
         "function-bind": "^1.1.1",
         "has": "^1.0.3",
@@ -6617,7 +6622,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
       "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "get-intrinsic": "^1.1.1"
@@ -6889,7 +6893,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
       "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
-      "dev": true,
       "dependencies": {
         "get-intrinsic": "^1.1.3"
       },
@@ -7399,7 +7402,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
       "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
       "dependencies": {
         "function-bind": "^1.1.1"
       },
@@ -7411,7 +7413,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
       "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
-      "dev": true,
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -7429,7 +7430,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
       "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
-      "dev": true,
       "dependencies": {
         "get-intrinsic": "^1.1.1"
       },
@@ -7441,7 +7441,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
       "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
-      "dev": true,
       "engines": {
         "node": ">= 0.4"
       },
@@ -7453,7 +7452,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
       "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
-      "dev": true,
       "dependencies": {
         "has-symbols": "^1.0.2"
       },
@@ -7907,7 +7905,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
       "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==",
-      "dev": true,
       "dependencies": {
         "get-intrinsic": "^1.1.3",
         "has": "^1.0.3",
@@ -7979,7 +7976,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
       "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
-      "dev": true,
       "dependencies": {
         "has-bigints": "^1.0.1"
       },
@@ -8003,7 +7999,6 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
       "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "has-tostringtag": "^1.0.0"
@@ -8042,7 +8037,6 @@
       "version": "1.2.7",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
       "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
-      "dev": true,
       "engines": {
         "node": ">= 0.4"
       },
@@ -8066,7 +8060,6 @@
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
       "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
-      "dev": true,
       "dependencies": {
         "has-tostringtag": "^1.0.0"
       },
@@ -8160,7 +8153,6 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
       "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
-      "dev": true,
       "engines": {
         "node": ">= 0.4"
       },
@@ -8181,7 +8173,6 @@
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
       "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
-      "dev": true,
       "dependencies": {
         "has-tostringtag": "^1.0.0"
       },
@@ -8214,7 +8205,6 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
       "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "has-tostringtag": "^1.0.0"
@@ -8230,7 +8220,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
       "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2"
       },
@@ -8242,7 +8231,6 @@
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
       "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
-      "dev": true,
       "dependencies": {
         "has-tostringtag": "^1.0.0"
       },
@@ -8257,7 +8245,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
       "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
-      "dev": true,
       "dependencies": {
         "has-symbols": "^1.0.2"
       },
@@ -8324,7 +8311,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
       "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2"
       },
@@ -9724,7 +9710,6 @@
       "version": "1.12.2",
       "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
       "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
-      "dev": true,
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -9733,7 +9718,6 @@
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
       "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "dev": true,
       "engines": {
         "node": ">= 0.4"
       }
@@ -9742,7 +9726,6 @@
       "version": "4.1.4",
       "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
       "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
@@ -10622,7 +10605,6 @@
       "version": "1.4.3",
       "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
       "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.3",
@@ -11033,7 +11015,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
       "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "get-intrinsic": "^1.1.3",
@@ -11442,7 +11423,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
       "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.0",
         "get-intrinsic": "^1.0.2",
@@ -13235,7 +13215,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
       "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
@@ -13249,7 +13228,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
       "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
@@ -13867,7 +13845,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
       "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
-      "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
         "has-bigints": "^1.0.2",
@@ -14509,7 +14486,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
       "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
-      "dev": true,
       "dependencies": {
         "is-bigint": "^1.0.1",
         "is-boolean-object": "^1.1.0",
@@ -16797,6 +16773,17 @@
       "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
       "dev": true
     },
+    "array.prototype.at": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.at/-/array.prototype.at-1.1.1.tgz",
+      "integrity": "sha512-n/wYNLJy/fVEU9EGPt2ww920hy1XX3XB2yTREFy1QsxctBgQV/tZIwg1G8jVxELna4pLCzg/xvvS/DDXtI4NNg==",
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0"
+      }
+    },
     "array.prototype.flat": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
@@ -17308,7 +17295,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
       "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
-      "dev": true,
       "requires": {
         "function-bind": "^1.1.1",
         "get-intrinsic": "^1.0.2"
@@ -18079,7 +18065,6 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
       "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
-      "dev": true,
       "requires": {
         "has-property-descriptors": "^1.0.0",
         "object-keys": "^1.1.1"
@@ -18319,7 +18304,6 @@
       "version": "1.20.5",
       "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz",
       "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "es-to-primitive": "^1.2.1",
@@ -18358,7 +18342,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
       "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
-      "dev": true,
       "requires": {
         "has": "^1.0.3"
       }
@@ -18367,7 +18350,6 @@
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
       "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
-      "dev": true,
       "requires": {
         "is-callable": "^1.1.4",
         "is-date-object": "^1.0.1",
@@ -19964,14 +19946,12 @@
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
     },
     "function.prototype.name": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
       "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.3",
@@ -19988,8 +19968,7 @@
     "functions-have-names": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
-      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
-      "dev": true
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
     },
     "get-caller-file": {
       "version": "2.0.5",
@@ -20007,7 +19986,6 @@
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
       "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
-      "dev": true,
       "requires": {
         "function-bind": "^1.1.1",
         "has": "^1.0.3",
@@ -20030,7 +20008,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
       "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "get-intrinsic": "^1.1.1"
@@ -20247,7 +20224,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
       "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
-      "dev": true,
       "requires": {
         "get-intrinsic": "^1.1.3"
       }
@@ -20651,7 +20627,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
       "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
       "requires": {
         "function-bind": "^1.1.1"
       }
@@ -20659,8 +20634,7 @@
     "has-bigints": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
-      "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
-      "dev": true
+      "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ=="
     },
     "has-flag": {
       "version": "4.0.0",
@@ -20672,7 +20646,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
       "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
-      "dev": true,
       "requires": {
         "get-intrinsic": "^1.1.1"
       }
@@ -20680,14 +20653,12 @@
     "has-symbols": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
-      "dev": true
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
     },
     "has-tostringtag": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
       "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
-      "dev": true,
       "requires": {
         "has-symbols": "^1.0.2"
       }
@@ -21045,7 +21016,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
       "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==",
-      "dev": true,
       "requires": {
         "get-intrinsic": "^1.1.3",
         "has": "^1.0.3",
@@ -21099,7 +21069,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
       "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
-      "dev": true,
       "requires": {
         "has-bigints": "^1.0.1"
       }
@@ -21117,7 +21086,6 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
       "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "has-tostringtag": "^1.0.0"
@@ -21132,8 +21100,7 @@
     "is-callable": {
       "version": "1.2.7",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
-      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
-      "dev": true
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="
     },
     "is-core-module": {
       "version": "2.11.0",
@@ -21148,7 +21115,6 @@
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
       "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
-      "dev": true,
       "requires": {
         "has-tostringtag": "^1.0.0"
       }
@@ -21213,8 +21179,7 @@
     "is-negative-zero": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
-      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
-      "dev": true
+      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA=="
     },
     "is-number": {
       "version": "7.0.0",
@@ -21226,7 +21191,6 @@
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
       "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
-      "dev": true,
       "requires": {
         "has-tostringtag": "^1.0.0"
       }
@@ -21247,7 +21211,6 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
       "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "has-tostringtag": "^1.0.0"
@@ -21257,7 +21220,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
       "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2"
       }
@@ -21266,7 +21228,6 @@
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
       "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
-      "dev": true,
       "requires": {
         "has-tostringtag": "^1.0.0"
       }
@@ -21275,7 +21236,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
       "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
-      "dev": true,
       "requires": {
         "has-symbols": "^1.0.2"
       }
@@ -21324,7 +21284,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
       "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2"
       }
@@ -22439,20 +22398,17 @@
     "object-inspect": {
       "version": "1.12.2",
       "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
-      "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
-      "dev": true
+      "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
     },
     "object-keys": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "dev": true
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
     },
     "object.assign": {
       "version": "4.1.4",
       "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
       "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
@@ -23121,7 +23077,6 @@
       "version": "1.4.3",
       "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
       "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.3",
@@ -23403,7 +23358,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
       "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "get-intrinsic": "^1.1.3",
@@ -23739,7 +23693,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
       "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.0",
         "get-intrinsic": "^1.0.2",
@@ -25108,7 +25061,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
       "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
@@ -25119,7 +25071,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
       "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
@@ -25616,7 +25567,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
       "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
-      "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "has-bigints": "^1.0.2",
@@ -26163,7 +26113,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
       "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
-      "dev": true,
       "requires": {
         "is-bigint": "^1.0.1",
         "is-boolean-object": "^1.1.0",

+ 1 - 0
package.json

@@ -55,6 +55,7 @@
     "@nomiclabs/hardhat-web3": "^2.0.0",
     "@openzeppelin/docs-utils": "^0.1.3",
     "@openzeppelin/test-helpers": "^0.5.13",
+    "array.prototype.at": "^1.1.1",
     "chai": "^4.2.0",
     "eslint": "^7.32.0",
     "eslint-config-standard": "^16.0.3",

+ 2 - 3
test/finance/VestingWallet.test.js

@@ -1,14 +1,13 @@
 const { constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
 const { web3 } = require('@openzeppelin/test-helpers/src/setup');
 const { expect } = require('chai');
+const { BNmin } = require('../helpers/math');
 
 const ERC20Mock = artifacts.require('ERC20Mock');
 const VestingWallet = artifacts.require('VestingWallet');
 
 const { shouldBehaveLikeVesting } = require('./VestingWallet.behavior');
 
-const min = (...args) => args.slice(1).reduce((x, y) => x.lt(y) ? x : y, args[0]);
-
 contract('VestingWallet', function (accounts) {
   const [ sender, beneficiary ] = accounts;
 
@@ -36,7 +35,7 @@ contract('VestingWallet', function (accounts) {
   describe('vesting schedule', function () {
     beforeEach(async function () {
       this.schedule = Array(64).fill().map((_, i) => web3.utils.toBN(i).mul(duration).divn(60).add(this.start));
-      this.vestingFn = timestamp => min(amount, amount.mul(timestamp.sub(this.start)).div(duration));
+      this.vestingFn = timestamp => BNmin(amount, amount.mul(timestamp.sub(this.start)).div(duration));
     });
 
     describe('Eth vesting', function () {

+ 194 - 210
test/governance/utils/Votes.behavior.js

@@ -17,10 +17,12 @@ const Delegation = [
 
 const version = '1';
 
-function shouldBehaveLikeVotes () {
+function shouldBehaveLikeVotes (accounts, tokens, fungible = true) {
+  const getWeight = token => web3.utils.toBN(fungible ? token : 1);
+
   describe('run votes workflow', function () {
     it('initial nonce is 0', async function () {
-      expect(await this.votes.nonces(this.account1)).to.be.bignumber.equal('0');
+      expect(await this.votes.nonces(accounts[0])).to.be.bignumber.equal('0');
     });
 
     it('domain separator', async function () {
@@ -31,207 +33,207 @@ function shouldBehaveLikeVotes () {
       );
     });
 
-    describe('delegation with signature', function () {
-      const delegator = Wallet.generate();
-      const delegatorAddress = web3.utils.toChecksumAddress(delegator.getAddressString());
-      const nonce = 0;
-
-      const buildData = (chainId, verifyingContract, name, message) => ({
-        data: {
-          primaryType: 'Delegation',
-          types: { EIP712Domain, Delegation },
-          domain: { name, version, chainId, verifyingContract },
-          message,
-        },
-      });
+    describe('delegation', function () {
+      const token = tokens[0];
 
-      beforeEach(async function () {
-        await this.votes.mint(delegatorAddress, this.token0);
+      it('delegation without tokens', async function () {
+        expect(await this.votes.delegates(accounts[1])).to.be.equal(ZERO_ADDRESS);
+
+        const { receipt } = await this.votes.delegate(accounts[1], { from: accounts[1] });
+        expectEvent(receipt, 'DelegateChanged', {
+          delegator: accounts[1],
+          fromDelegate: ZERO_ADDRESS,
+          toDelegate: accounts[1],
+        });
+        expectEvent.notEmitted(receipt, 'DelegateVotesChanged');
+
+        expect(await this.votes.delegates(accounts[1])).to.be.equal(accounts[1]);
       });
 
-      it('accept signed delegation', async function () {
-        const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
-          delegator.getPrivateKey(),
-          buildData(this.chainId, this.votes.address, this.name, {
-            delegatee: delegatorAddress,
-            nonce,
-            expiry: MAX_UINT256,
-          }),
-        ));
+      it('delegation with tokens', async function () {
+        await this.votes.mint(accounts[1], token);
+        const weight = getWeight(token);
 
-        expect(await this.votes.delegates(delegatorAddress)).to.be.equal(ZERO_ADDRESS);
+        expect(await this.votes.delegates(accounts[1])).to.be.equal(ZERO_ADDRESS);
 
-        const { receipt } = await this.votes.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s);
+        const { receipt } = await this.votes.delegate(accounts[1], { from: accounts[1] });
         expectEvent(receipt, 'DelegateChanged', {
-          delegator: delegatorAddress,
+          delegator: accounts[1],
           fromDelegate: ZERO_ADDRESS,
-          toDelegate: delegatorAddress,
+          toDelegate: accounts[1],
         });
         expectEvent(receipt, 'DelegateVotesChanged', {
-          delegate: delegatorAddress,
+          delegate: accounts[1],
           previousBalance: '0',
-          newBalance: '1',
+          newBalance: weight,
         });
 
-        expect(await this.votes.delegates(delegatorAddress)).to.be.equal(delegatorAddress);
-
-        expect(await this.votes.getVotes(delegatorAddress)).to.be.bignumber.equal('1');
-        expect(await this.votes.getPastVotes(delegatorAddress, receipt.blockNumber - 1)).to.be.bignumber.equal('0');
+        expect(await this.votes.delegates(accounts[1])).to.be.equal(accounts[1]);
+        expect(await this.votes.getVotes(accounts[1])).to.be.bignumber.equal(weight);
+        expect(await this.votes.getPastVotes(accounts[1], receipt.blockNumber - 1)).to.be.bignumber.equal('0');
         await time.advanceBlock();
-        expect(await this.votes.getPastVotes(delegatorAddress, receipt.blockNumber)).to.be.bignumber.equal('1');
+        expect(await this.votes.getPastVotes(accounts[1], receipt.blockNumber)).to.be.bignumber.equal(weight);
       });
 
-      it('rejects reused signature', async function () {
-        const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
-          delegator.getPrivateKey(),
-          buildData(this.chainId, this.votes.address, this.name, {
-            delegatee: delegatorAddress,
-            nonce,
-            expiry: MAX_UINT256,
-          }),
-        ));
+      it('delegation update', async function () {
+        await this.votes.delegate(accounts[1], { from: accounts[1] });
+        await this.votes.mint(accounts[1], token);
+        const weight = getWeight(token);
 
-        await this.votes.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s);
+        expect(await this.votes.delegates(accounts[1])).to.be.equal(accounts[1]);
+        expect(await this.votes.getVotes(accounts[1])).to.be.bignumber.equal(weight);
+        expect(await this.votes.getVotes(accounts[2])).to.be.bignumber.equal('0');
 
-        await expectRevert(
-          this.votes.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s),
-          'Votes: invalid nonce',
-        );
-      });
+        const { receipt } = await this.votes.delegate(accounts[2], { from: accounts[1] });
+        expectEvent(receipt, 'DelegateChanged', {
+          delegator: accounts[1],
+          fromDelegate: accounts[1],
+          toDelegate: accounts[2],
+        });
+        expectEvent(receipt, 'DelegateVotesChanged', {
+          delegate: accounts[1],
+          previousBalance: weight,
+          newBalance: '0',
+        });
+        expectEvent(receipt, 'DelegateVotesChanged', {
+          delegate: accounts[2],
+          previousBalance: '0',
+          newBalance: weight,
+        });
 
-      it('rejects bad delegatee', async function () {
-        const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
-          delegator.getPrivateKey(),
-          buildData(this.chainId, this.votes.address, this.name, {
-            delegatee: delegatorAddress,
-            nonce,
-            expiry: MAX_UINT256,
-          }),
-        ));
-
-        const receipt = await this.votes.delegateBySig(this.account1Delegatee, nonce, MAX_UINT256, v, r, s);
-        const { args } = receipt.logs.find(({ event }) => event === 'DelegateChanged');
-        expect(args.delegator).to.not.be.equal(delegatorAddress);
-        expect(args.fromDelegate).to.be.equal(ZERO_ADDRESS);
-        expect(args.toDelegate).to.be.equal(this.account1Delegatee);
-      });
+        expect(await this.votes.delegates(accounts[1])).to.be.equal(accounts[2]);
+        expect(await this.votes.getVotes(accounts[1])).to.be.bignumber.equal('0');
+        expect(await this.votes.getVotes(accounts[2])).to.be.bignumber.equal(weight);
 
-      it('rejects bad nonce', async function () {
-        const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
-          delegator.getPrivateKey(),
-          buildData(this.chainId, this.votes.address, this.name, {
-            delegatee: delegatorAddress,
-            nonce,
-            expiry: MAX_UINT256,
-          }),
-        ));
-        await expectRevert(
-          this.votes.delegateBySig(delegatorAddress, nonce + 1, MAX_UINT256, v, r, s),
-          'Votes: invalid nonce',
-        );
+        expect(await this.votes.getPastVotes(accounts[1], receipt.blockNumber - 1)).to.be.bignumber.equal(weight);
+        expect(await this.votes.getPastVotes(accounts[2], receipt.blockNumber - 1)).to.be.bignumber.equal('0');
+        await time.advanceBlock();
+        expect(await this.votes.getPastVotes(accounts[1], receipt.blockNumber)).to.be.bignumber.equal('0');
+        expect(await this.votes.getPastVotes(accounts[2], receipt.blockNumber)).to.be.bignumber.equal(weight);
       });
 
-      it('rejects expired permit', async function () {
-        const expiry = (await time.latest()) - time.duration.weeks(1);
-        const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
-          delegator.getPrivateKey(),
-          buildData(this.chainId, this.votes.address, this.name, {
-            delegatee: delegatorAddress,
-            nonce,
-            expiry,
-          }),
-        ));
+      describe('with signature', function () {
+        const delegator = Wallet.generate();
+        const [delegatee, other] = accounts;
+        const nonce = 0;
+        delegator.address = web3.utils.toChecksumAddress(delegator.getAddressString());
+
+        const buildData = (chainId, verifyingContract, name, message) => ({
+          data: {
+            primaryType: 'Delegation',
+            types: { EIP712Domain, Delegation },
+            domain: { name, version, chainId, verifyingContract },
+            message,
+          },
+        });
 
-        await expectRevert(
-          this.votes.delegateBySig(delegatorAddress, nonce, expiry, v, r, s),
-          'Votes: signature expired',
-        );
-      });
-    });
+        it('accept signed delegation', async function () {
+          await this.votes.mint(delegator.address, token);
+          const weight = getWeight(token);
 
-    describe('set delegation', function () {
-      describe('call', function () {
-        it('delegation with tokens', async function () {
-          await this.votes.mint(this.account1, this.token0);
-          expect(await this.votes.delegates(this.account1)).to.be.equal(ZERO_ADDRESS);
+          const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
+            delegator.getPrivateKey(),
+            buildData(this.chainId, this.votes.address, this.name, {
+              delegatee,
+              nonce,
+              expiry: MAX_UINT256,
+            }),
+          ));
 
-          const { receipt } = await this.votes.delegate(this.account1, { from: this.account1 });
+          expect(await this.votes.delegates(delegator.address)).to.be.equal(ZERO_ADDRESS);
+
+          const { receipt } = await this.votes.delegateBySig(delegatee, nonce, MAX_UINT256, v, r, s);
           expectEvent(receipt, 'DelegateChanged', {
-            delegator: this.account1,
+            delegator: delegator.address,
             fromDelegate: ZERO_ADDRESS,
-            toDelegate: this.account1,
+            toDelegate: delegatee,
           });
           expectEvent(receipt, 'DelegateVotesChanged', {
-            delegate: this.account1,
+            delegate: delegatee,
             previousBalance: '0',
-            newBalance: '1',
+            newBalance: weight,
           });
 
-          expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1);
-
-          expect(await this.votes.getVotes(this.account1)).to.be.bignumber.equal('1');
-          expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber - 1)).to.be.bignumber.equal('0');
+          expect(await this.votes.delegates(delegator.address)).to.be.equal(delegatee);
+          expect(await this.votes.getVotes(delegator.address)).to.be.bignumber.equal('0');
+          expect(await this.votes.getVotes(delegatee)).to.be.bignumber.equal(weight);
+          expect(await this.votes.getPastVotes(delegatee, receipt.blockNumber - 1)).to.be.bignumber.equal('0');
           await time.advanceBlock();
-          expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber)).to.be.bignumber.equal('1');
+          expect(await this.votes.getPastVotes(delegatee, receipt.blockNumber)).to.be.bignumber.equal(weight);
         });
 
-        it('delegation without tokens', async function () {
-          expect(await this.votes.delegates(this.account1)).to.be.equal(ZERO_ADDRESS);
+        it('rejects reused signature', async function () {
+          const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
+            delegator.getPrivateKey(),
+            buildData(this.chainId, this.votes.address, this.name, {
+              delegatee,
+              nonce,
+              expiry: MAX_UINT256,
+            }),
+          ));
 
-          const { receipt } = await this.votes.delegate(this.account1, { from: this.account1 });
-          expectEvent(receipt, 'DelegateChanged', {
-            delegator: this.account1,
-            fromDelegate: ZERO_ADDRESS,
-            toDelegate: this.account1,
-          });
-          expectEvent.notEmitted(receipt, 'DelegateVotesChanged');
+          await this.votes.delegateBySig(delegatee, nonce, MAX_UINT256, v, r, s);
+
+          await expectRevert(
+            this.votes.delegateBySig(delegatee, nonce, MAX_UINT256, v, r, s),
+            'Votes: invalid nonce',
+          );
+        });
 
-          expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1);
+        it('rejects bad delegatee', async function () {
+          const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
+            delegator.getPrivateKey(),
+            buildData(this.chainId, this.votes.address, this.name, {
+              delegatee,
+              nonce,
+              expiry: MAX_UINT256,
+            }),
+          ));
+
+          const receipt = await this.votes.delegateBySig(other, nonce, MAX_UINT256, v, r, s);
+          const { args } = receipt.logs.find(({ event }) => event === 'DelegateChanged');
+          expect(args.delegator).to.not.be.equal(delegator.address);
+          expect(args.fromDelegate).to.be.equal(ZERO_ADDRESS);
+          expect(args.toDelegate).to.be.equal(other);
         });
-      });
-    });
 
-    describe('change delegation', function () {
-      beforeEach(async function () {
-        await this.votes.mint(this.account1, this.token0);
-        await this.votes.delegate(this.account1, { from: this.account1 });
-      });
+        it('rejects bad nonce', async function () {
+          const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
+            delegator.getPrivateKey(),
+            buildData(this.chainId, this.votes.address, this.name, {
+              delegatee,
+              nonce: nonce + 1,
+              expiry: MAX_UINT256,
+            }),
+          ));
+          await expectRevert(
+            this.votes.delegateBySig(delegatee, nonce + 1, MAX_UINT256, v, r, s),
+            'Votes: invalid nonce',
+          );
+        });
 
-      it('call', async function () {
-        expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1);
+        it('rejects expired permit', async function () {
+          const expiry = (await time.latest()) - time.duration.weeks(1);
+          const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
+            delegator.getPrivateKey(),
+            buildData(this.chainId, this.votes.address, this.name, {
+              delegatee,
+              nonce,
+              expiry,
+            }),
+          ));
 
-        const { receipt } = await this.votes.delegate(this.account1Delegatee, { from: this.account1 });
-        expectEvent(receipt, 'DelegateChanged', {
-          delegator: this.account1,
-          fromDelegate: this.account1,
-          toDelegate: this.account1Delegatee,
-        });
-        expectEvent(receipt, 'DelegateVotesChanged', {
-          delegate: this.account1,
-          previousBalance: '1',
-          newBalance: '0',
-        });
-        expectEvent(receipt, 'DelegateVotesChanged', {
-          delegate: this.account1Delegatee,
-          previousBalance: '0',
-          newBalance: '1',
+          await expectRevert(
+            this.votes.delegateBySig(delegatee, nonce, expiry, v, r, s),
+            'Votes: signature expired',
+          );
         });
-        const prevBlock = receipt.blockNumber - 1;
-        expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1Delegatee);
-
-        expect(await this.votes.getVotes(this.account1)).to.be.bignumber.equal('0');
-        expect(await this.votes.getVotes(this.account1Delegatee)).to.be.bignumber.equal('1');
-        expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber - 1)).to.be.bignumber.equal('1');
-        expect(await this.votes.getPastVotes(this.account1Delegatee, prevBlock)).to.be.bignumber.equal('0');
-        await time.advanceBlock();
-        expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber)).to.be.bignumber.equal('0');
-        expect(await this.votes.getPastVotes(this.account1Delegatee, receipt.blockNumber)).to.be.bignumber.equal('1');
       });
     });
 
     describe('getPastTotalSupply', function () {
       beforeEach(async function () {
-        await this.votes.delegate(this.account1, { from: this.account1 });
+        await this.votes.delegate(accounts[1], { from: accounts[1] });
       });
 
       it('reverts if block number >= current block', async function () {
@@ -245,95 +247,77 @@ function shouldBehaveLikeVotes () {
         expect(await this.votes.getPastTotalSupply(0)).to.be.bignumber.equal('0');
       });
 
-      it('returns the latest block if >= last checkpoint block', async function () {
-        const t1 = await this.votes.mint(this.account1, this.token0);
-        await time.advanceBlock();
-        await time.advanceBlock();
+      it('returns the correct checkpointed total supply', async function () {
+        const blockNumber = Number(await time.latestBlock());
 
-        expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
-        expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
-      });
-
-      it('returns zero if < first checkpoint block', async function () {
-        await time.advanceBlock();
-        const t2 = await this.votes.mint(this.account1, this.token1);
-        await time.advanceBlock();
-        await time.advanceBlock();
-
-        expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
-        expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
-      });
-
-      it('generally returns the voting balance at the appropriate checkpoint', async function () {
-        const t1 = await this.votes.mint(this.account1, this.token1);
-        await time.advanceBlock();
-        await time.advanceBlock();
-        const t2 = await this.votes.burn(this.account1, this.token1);
-        await time.advanceBlock();
-        await time.advanceBlock();
-        const t3 = await this.votes.mint(this.account1, this.token2);
+        await this.votes.mint(accounts[1], tokens[0]); // mint 0
         await time.advanceBlock();
+        await this.votes.mint(accounts[1], tokens[1]); // mint 1
         await time.advanceBlock();
-        const t4 = await this.votes.burn(this.account1, this.token2);
+        await this.votes.burn(...(fungible ? [accounts[1]] : []), tokens[1]); // burn 1
         await time.advanceBlock();
+        await this.votes.mint(accounts[1], tokens[2]); // mint 2
         await time.advanceBlock();
-        const t5 = await this.votes.mint(this.account1, this.token3);
+        await this.votes.burn(...(fungible ? [accounts[1]] : []), tokens[0]); // burn 0
         await time.advanceBlock();
+        await this.votes.burn(...(fungible ? [accounts[1]] : []), tokens[2]); // burn 2
         await time.advanceBlock();
 
-        expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
-        expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber)).to.be.bignumber.equal('1');
-        expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
-        expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber)).to.be.bignumber.equal('0');
-        expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber + 1)).to.be.bignumber.equal('0');
-        expect(await this.votes.getPastTotalSupply(t3.receipt.blockNumber)).to.be.bignumber.equal('1');
-        expect(await this.votes.getPastTotalSupply(t3.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
-        expect(await this.votes.getPastTotalSupply(t4.receipt.blockNumber)).to.be.bignumber.equal('0');
-        expect(await this.votes.getPastTotalSupply(t4.receipt.blockNumber + 1)).to.be.bignumber.equal('0');
-        expect(await this.votes.getPastTotalSupply(t5.receipt.blockNumber)).to.be.bignumber.equal('1');
-        expect(await this.votes.getPastTotalSupply(t5.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
+        const weight = tokens.map(getWeight);
+
+        expect(await this.votes.getPastTotalSupply(blockNumber)).to.be.bignumber.equal('0');
+        expect(await this.votes.getPastTotalSupply(blockNumber + 1)).to.be.bignumber.equal(weight[0]);
+        expect(await this.votes.getPastTotalSupply(blockNumber + 2)).to.be.bignumber.equal(weight[0]);
+        expect(await this.votes.getPastTotalSupply(blockNumber + 3)).to.be.bignumber.equal(weight[0].add(weight[1]));
+        expect(await this.votes.getPastTotalSupply(blockNumber + 4)).to.be.bignumber.equal(weight[0].add(weight[1]));
+        expect(await this.votes.getPastTotalSupply(blockNumber + 5)).to.be.bignumber.equal(weight[0]);
+        expect(await this.votes.getPastTotalSupply(blockNumber + 6)).to.be.bignumber.equal(weight[0]);
+        expect(await this.votes.getPastTotalSupply(blockNumber + 7)).to.be.bignumber.equal(weight[0].add(weight[2]));
+        expect(await this.votes.getPastTotalSupply(blockNumber + 8)).to.be.bignumber.equal(weight[0].add(weight[2]));
+        expect(await this.votes.getPastTotalSupply(blockNumber + 9)).to.be.bignumber.equal(weight[2]);
+        expect(await this.votes.getPastTotalSupply(blockNumber + 10)).to.be.bignumber.equal(weight[2]);
+        expect(await this.votes.getPastTotalSupply(blockNumber + 11)).to.be.bignumber.equal('0');
+        await expectRevert(this.votes.getPastTotalSupply(blockNumber + 12), 'Votes: block not yet mined');
       });
     });
 
-    // The following tests are a adaptation of
+    // The following tests are an adaptation of
     // https://github.com/compound-finance/compound-protocol/blob/master/tests/Governance/CompTest.js.
     describe('Compound test suite', function () {
       beforeEach(async function () {
-        await this.votes.mint(this.account1, this.token0);
-        await this.votes.mint(this.account1, this.token1);
-        await this.votes.mint(this.account1, this.token2);
-        await this.votes.mint(this.account1, this.token3);
+        await this.votes.mint(accounts[1], tokens[0]);
+        await this.votes.mint(accounts[1], tokens[1]);
+        await this.votes.mint(accounts[1], tokens[2]);
       });
 
       describe('getPastVotes', function () {
         it('reverts if block number >= current block', async function () {
           await expectRevert(
-            this.votes.getPastVotes(this.account2, 5e10),
+            this.votes.getPastVotes(accounts[2], 5e10),
             'block not yet mined',
           );
         });
 
         it('returns 0 if there are no checkpoints', async function () {
-          expect(await this.votes.getPastVotes(this.account2, 0)).to.be.bignumber.equal('0');
+          expect(await this.votes.getPastVotes(accounts[2], 0)).to.be.bignumber.equal('0');
         });
 
         it('returns the latest block if >= last checkpoint block', async function () {
-          const t1 = await this.votes.delegate(this.account2, { from: this.account1 });
+          const tx = await this.votes.delegate(accounts[2], { from: accounts[1] });
           await time.advanceBlock();
           await time.advanceBlock();
-          const latest = await this.votes.getVotes(this.account2);
-          const nextBlock = t1.receipt.blockNumber + 1;
-          expect(await this.votes.getPastVotes(this.account2, t1.receipt.blockNumber)).to.be.bignumber.equal(latest);
-          expect(await this.votes.getPastVotes(this.account2, nextBlock)).to.be.bignumber.equal(latest);
+          const latest = await this.votes.getVotes(accounts[2]);
+          expect(await this.votes.getPastVotes(accounts[2], tx.receipt.blockNumber)).to.be.bignumber.equal(latest);
+          expect(await this.votes.getPastVotes(accounts[2], tx.receipt.blockNumber + 1)).to.be.bignumber.equal(latest);
         });
 
         it('returns zero if < first checkpoint block', async function () {
           await time.advanceBlock();
-          const t1 = await this.votes.delegate(this.account2, { from: this.account1 });
+          const tx = await this.votes.delegate(accounts[2], { from: accounts[1] });
           await time.advanceBlock();
           await time.advanceBlock();
 
-          expect(await this.votes.getPastVotes(this.account2, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
+          expect(await this.votes.getPastVotes(accounts[2], tx.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
         });
       });
     });

+ 43 - 17
test/governance/utils/Votes.test.js

@@ -1,6 +1,8 @@
-const { expectRevert, BN } = require('@openzeppelin/test-helpers');
-
+const { constants, expectRevert } = require('@openzeppelin/test-helpers');
 const { expect } = require('chai');
+const { BNsum } = require('../../helpers/math');
+
+require('array.prototype.at/auto');
 
 const {
   shouldBehaveLikeVotes,
@@ -10,6 +12,12 @@ const Votes = artifacts.require('VotesMock');
 
 contract('Votes', function (accounts) {
   const [ account1, account2, account3 ] = accounts;
+  const amounts = {
+    [account1]: web3.utils.toBN('10000000000000000000000000'),
+    [account2]: web3.utils.toBN('10'),
+    [account3]: web3.utils.toBN('20'),
+  };
+
   beforeEach(async function () {
     this.name = 'My Vote';
     this.votes = await Votes.new(this.name);
@@ -21,41 +29,59 @@ contract('Votes', function (accounts) {
 
   describe('performs voting operations', function () {
     beforeEach(async function () {
-      this.tx1 = await this.votes.mint(account1, 1);
-      this.tx2 = await this.votes.mint(account2, 1);
-      this.tx3 = await this.votes.mint(account3, 1);
+      this.txs = [];
+      for (const [account, amount] of Object.entries(amounts)) {
+        this.txs.push(await this.votes.mint(account, amount));
+      }
     });
 
     it('reverts if block number >= current block', async function () {
       await expectRevert(
-        this.votes.getPastTotalSupply(this.tx3.receipt.blockNumber + 1),
+        this.votes.getPastTotalSupply(this.txs.at(-1).receipt.blockNumber + 1),
         'Votes: block not yet mined',
       );
     });
 
     it('delegates', async function () {
-      await this.votes.delegate(account3, account2);
+      expect(await this.votes.getVotes(account1)).to.be.bignumber.equal('0');
+      expect(await this.votes.getVotes(account2)).to.be.bignumber.equal('0');
+      expect(await this.votes.delegates(account1)).to.be.equal(constants.ZERO_ADDRESS);
+      expect(await this.votes.delegates(account2)).to.be.equal(constants.ZERO_ADDRESS);
+
+      await this.votes.delegate(account1, account1);
+
+      expect(await this.votes.getVotes(account1)).to.be.bignumber.equal(amounts[account1]);
+      expect(await this.votes.getVotes(account2)).to.be.bignumber.equal('0');
+      expect(await this.votes.delegates(account1)).to.be.equal(account1);
+      expect(await this.votes.delegates(account2)).to.be.equal(constants.ZERO_ADDRESS);
+
+      await this.votes.delegate(account2, account1);
+
+      expect(await this.votes.getVotes(account1)).to.be.bignumber.equal(amounts[account1].add(amounts[account2]));
+      expect(await this.votes.getVotes(account2)).to.be.bignumber.equal('0');
+      expect(await this.votes.delegates(account1)).to.be.equal(account1);
+      expect(await this.votes.delegates(account2)).to.be.equal(account1);
+    });
+
+    it('cross delegates', async function () {
+      await this.votes.delegate(account1, account2);
+      await this.votes.delegate(account2, account1);
 
-      expect(await this.votes.delegates(account3)).to.be.equal(account2);
+      expect(await this.votes.getVotes(account1)).to.be.bignumber.equal(amounts[account2]);
+      expect(await this.votes.getVotes(account2)).to.be.bignumber.equal(amounts[account1]);
     });
 
     it('returns total amount of votes', async function () {
-      expect(await this.votes.getTotalSupply()).to.be.bignumber.equal('3');
+      const totalSupply = BNsum(...Object.values(amounts));
+      expect(await this.votes.getTotalSupply()).to.be.bignumber.equal(totalSupply);
     });
   });
 
   describe('performs voting workflow', function () {
     beforeEach(async function () {
       this.chainId = await this.votes.getChainId();
-      this.account1 = account1;
-      this.account2 = account2;
-      this.account1Delegatee = account2;
-      this.token0 = new BN('10000000000000000000000000');
-      this.token1 = new BN('10');
-      this.token2 = new BN('20');
-      this.token3 = new BN('30');
     });
 
-    shouldBehaveLikeVotes();
+    shouldBehaveLikeVotes(accounts, Object.values(amounts));
   });
 });

+ 1 - 3
test/helpers/enums.js

@@ -1,7 +1,5 @@
-const { BN } = require('@openzeppelin/test-helpers');
-
 function Enum (...options) {
-  return Object.fromEntries(options.map((key, i) => [ key, new BN(i) ]));
+  return Object.fromEntries(options.map((key, i) => [ key, web3.utils.toBN(i) ]));
 }
 
 module.exports = {

+ 11 - 0
test/helpers/math.js

@@ -0,0 +1,11 @@
+module.exports = {
+  // sum of integer / bignumber
+  sum: (...args) => args.reduce((acc, n) => acc + n, 0),
+  BNsum: (...args) => args.reduce((acc, n) => acc.add(n), web3.utils.toBN(0)),
+  // min of integer / bignumber
+  min: (...args) => args.slice(1).reduce((x, y) => x < y ? x : y, args[0]),
+  BNmin: (...args) => args.slice(1).reduce((x, y) => x.lt(y) ? x : y, args[0]),
+  // max of integer / bignumber
+  max: (...args) => args.slice(1).reduce((x, y) => x > y ? x : y, args[0]),
+  BNmax: (...args) => args.slice(1).reduce((x, y) => x.gt(y) ? x : y, args[0]),
+};

+ 4 - 11
test/token/ERC20/extensions/ERC20Votes.test.js

@@ -348,7 +348,7 @@ contract('ERC20Votes', function (accounts) {
 
         const t1 = await this.token.delegate(other1, { from: recipient });
         expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('1');
-        
+
         const t2 = await this.token.transfer(other2, 10, { from: recipient });
         expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('2');
 
@@ -518,17 +518,10 @@ contract('ERC20Votes', function (accounts) {
 
   describe('Voting workflow', function () {
     beforeEach(async function () {
-      this.account1 = holder;
-      this.account1Delegatee = holderDelegatee;
-      this.account2 = recipient;
-      this.name = 'My Token';
-      this.votes = this.token
-      this.token0 = 1;      
-      this.token1 = 1;
-      this.token2 = 1;
-      this.token3 = 1;
+      this.name = name;
+      this.votes = this.token;
     });
 
-    shouldBehaveLikeVotes();
+    shouldBehaveLikeVotes(accounts, [ 1, 17, 42]);
   });
 });

+ 4 - 11
test/token/ERC20/extensions/ERC20VotesComp.test.js

@@ -374,7 +374,7 @@ contract('ERC20VotesComp', function (accounts) {
         expect(await this.token.checkpoints(other1, 1)).to.be.deep.equal([ t4.receipt.blockNumber.toString(), '100' ]);
       });
     });
-  
+
     describe('getPriorVotes', function () {
       it('reverts if block number >= current block', async function () {
         await expectRevert(
@@ -497,17 +497,10 @@ contract('ERC20VotesComp', function (accounts) {
 
   describe('Voting workflow', function () {
     beforeEach(async function () {
-      this.account1 = holder;
-      this.account1Delegatee = holderDelegatee;
-      this.account2 = recipient;
-      this.name = 'My Token';
-      this.votes = this.token
-      this.token0 = 1;      
-      this.token1 = 1;
-      this.token2 = 1;
-      this.token3 = 1;
+      this.name = name;
+      this.votes = this.token;
     });
 
-    shouldBehaveLikeVotes();
+    shouldBehaveLikeVotes(accounts, [1, 17, 42]);
   });
 });

+ 9 - 7
test/token/ERC721/extensions/ERC721Consecutive.test.js

@@ -1,5 +1,6 @@
 const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
 const { expect } = require('chai');
+const { sum } = require('../../../helpers/math');
 
 const ERC721ConsecutiveMock = artifacts.require('ERC721ConsecutiveMock');
 const ERC721ConsecutiveEnumerableMock = artifacts.require('ERC721ConsecutiveEnumerableMock');
@@ -60,10 +61,11 @@ contract('ERC721Consecutive', function (accounts) {
 
       it('balance & voting power are set', async function () {
         for (const account of accounts) {
-          const balance = batches
-            .filter(({ receiver }) => receiver === account)
-            .map(({ amount }) => amount)
-            .reduce((a, b) => a + b, 0);
+          const balance = sum(
+            ...batches
+              .filter(({ receiver }) => receiver === account)
+              .map(({ amount }) => amount),
+          );
 
           expect(await this.token.balanceOf(account))
             .to.be.bignumber.equal(web3.utils.toBN(balance));
@@ -92,7 +94,7 @@ contract('ERC721Consecutive', function (accounts) {
       });
 
       it('simple minting is possible after construction', async function () {
-        const tokenId = batches.reduce((acc, { amount }) => acc + amount, 0);
+        const tokenId = sum(...batches.map(b => b.amount));
 
         expect(await this.token.exists(tokenId)).to.be.equal(false);
 
@@ -104,7 +106,7 @@ contract('ERC721Consecutive', function (accounts) {
       });
 
       it('cannot mint a token that has been batched minted', async function () {
-        const tokenId = batches.reduce((acc, { amount }) => acc + amount, 0) - 1;
+        const tokenId = sum(...batches.map(b => b.amount)) - 1;
 
         expect(await this.token.exists(tokenId)).to.be.equal(true);
 
@@ -141,7 +143,7 @@ contract('ERC721Consecutive', function (accounts) {
       });
 
       it('tokens can be burned and re-minted #2', async function () {
-        const tokenId = batches.reduce((acc, { amount }) => acc.addn(amount), web3.utils.toBN(0));
+        const tokenId = web3.utils.toBN(sum(...batches.map(({ amount }) => amount)));
 
         expect(await this.token.exists(tokenId)).to.be.equal(false);
         await expectRevert(this.token.ownerOf(tokenId), 'ERC721: invalid token ID');

+ 30 - 32
test/token/ERC721/extensions/ERC721Votes.test.js

@@ -1,6 +1,6 @@
 /* eslint-disable */
 
-const { BN, expectEvent, time } = require('@openzeppelin/test-helpers');
+const { expectEvent, time } = require('@openzeppelin/test-helpers');
 const { expect } = require('chai');
 
 const ERC721VotesMock = artifacts.require('ERC721VotesMock');
@@ -9,8 +9,14 @@ const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behav
 
 contract('ERC721Votes', function (accounts) {
   const [ account1, account2, account1Delegatee, other1, other2 ] = accounts;
-  this.name = 'My Vote';
+  const name = 'My Vote';
   const symbol = 'MTKN';
+  const tokens = [
+    '10000000000000000000000000',
+    '10',
+    '20',
+    '30',
+  ].map(n => web3.utils.toBN(n));
 
   beforeEach(async function () {
     this.votes = await ERC721VotesMock.new(name, symbol);
@@ -19,19 +25,14 @@ contract('ERC721Votes', function (accounts) {
     // from within the EVM as from the JSON RPC interface.
     // See https://github.com/trufflesuite/ganache-core/issues/515
     this.chainId = await this.votes.getChainId();
-
-    this.token0 = new BN('10000000000000000000000000');
-    this.token1 = new BN('10');
-    this.token2 = new BN('20');
-    this.token3 = new BN('30');
   });
 
   describe('balanceOf', function () {
     beforeEach(async function () {
-      await this.votes.mint(account1, this.token0);
-      await this.votes.mint(account1, this.token1);
-      await this.votes.mint(account1, this.token2);
-      await this.votes.mint(account1, this.token3);
+      await this.votes.mint(account1, tokens[0]);
+      await this.votes.mint(account1, tokens[1]);
+      await this.votes.mint(account1, tokens[2]);
+      await this.votes.mint(account1, tokens[3]);
     });
 
     it('grants to initial account', async function () {
@@ -41,12 +42,12 @@ contract('ERC721Votes', function (accounts) {
 
   describe('transfers', function () {
     beforeEach(async function () {
-      await this.votes.mint(account1, this.token0);
+      await this.votes.mint(account1, tokens[0]);
     });
 
     it('no delegation', async function () {
-      const { receipt } = await this.votes.transferFrom(account1, account2, this.token0, { from: account1 });
-      expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.token0 });
+      const { receipt } = await this.votes.transferFrom(account1, account2, tokens[0], { from: account1 });
+      expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: tokens[0] });
       expectEvent.notEmitted(receipt, 'DelegateVotesChanged');
 
       this.account1Votes = '0';
@@ -56,8 +57,8 @@ contract('ERC721Votes', function (accounts) {
     it('sender delegation', async function () {
       await this.votes.delegate(account1, { from: account1 });
 
-      const { receipt } = await this.votes.transferFrom(account1, account2, this.token0, { from: account1 });
-      expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.token0 });
+      const { receipt } = await this.votes.transferFrom(account1, account2, tokens[0], { from: account1 });
+      expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: tokens[0] });
       expectEvent(receipt, 'DelegateVotesChanged', { delegate: account1, previousBalance: '1', newBalance: '0' });
 
       const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer');
@@ -70,8 +71,8 @@ contract('ERC721Votes', function (accounts) {
     it('receiver delegation', async function () {
       await this.votes.delegate(account2, { from: account2 });
 
-      const { receipt } = await this.votes.transferFrom(account1, account2, this.token0, { from: account1 });
-      expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.token0 });
+      const { receipt } = await this.votes.transferFrom(account1, account2, tokens[0], { from: account1 });
+      expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: tokens[0] });
       expectEvent(receipt, 'DelegateVotesChanged', { delegate: account2, previousBalance: '0', newBalance: '1' });
 
       const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer');
@@ -85,8 +86,8 @@ contract('ERC721Votes', function (accounts) {
       await this.votes.delegate(account1, { from: account1 });
       await this.votes.delegate(account2, { from: account2 });
 
-      const { receipt } = await this.votes.transferFrom(account1, account2, this.token0, { from: account1 });
-      expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.token0 });
+      const { receipt } = await this.votes.transferFrom(account1, account2, tokens[0], { from: account1 });
+      expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: tokens[0] });
       expectEvent(receipt, 'DelegateVotesChanged', { delegate: account1, previousBalance: '1', newBalance: '0'});
       expectEvent(receipt, 'DelegateVotesChanged', { delegate: account2, previousBalance: '0', newBalance: '1' });
 
@@ -100,7 +101,7 @@ contract('ERC721Votes', function (accounts) {
     it('returns the same total supply on transfers', async function () {
       await this.votes.delegate(account1, { from: account1 });
 
-      const { receipt } = await this.votes.transferFrom(account1, account2, this.token0, { from: account1 });
+      const { receipt } = await this.votes.transferFrom(account1, account2, tokens[0], { from: account1 });
 
       await time.advanceBlock();
       await time.advanceBlock();
@@ -113,22 +114,22 @@ contract('ERC721Votes', function (accounts) {
     });
 
     it('generally returns the voting balance at the appropriate checkpoint', async function () {
-      await this.votes.mint(account1, this.token1);
-      await this.votes.mint(account1, this.token2);
-      await this.votes.mint(account1, this.token3);
+      await this.votes.mint(account1, tokens[1]);
+      await this.votes.mint(account1, tokens[2]);
+      await this.votes.mint(account1, tokens[3]);
 
       const total = await this.votes.balanceOf(account1);
 
       const t1 = await this.votes.delegate(other1, { from: account1 });
       await time.advanceBlock();
       await time.advanceBlock();
-      const t2 = await this.votes.transferFrom(account1, other2, this.token0, { from: account1 });
+      const t2 = await this.votes.transferFrom(account1, other2, tokens[0], { from: account1 });
       await time.advanceBlock();
       await time.advanceBlock();
-      const t3 = await this.votes.transferFrom(account1, other2, this.token2, { from: account1 });
+      const t3 = await this.votes.transferFrom(account1, other2, tokens[2], { from: account1 });
       await time.advanceBlock();
       await time.advanceBlock();
-      const t4 = await this.votes.transferFrom(other2, account1, this.token2, { from: other2 });
+      const t4 = await this.votes.transferFrom(other2, account1, tokens[2], { from: other2 });
       await time.advanceBlock();
       await time.advanceBlock();
 
@@ -160,12 +161,9 @@ contract('ERC721Votes', function (accounts) {
 
   describe('Voting workflow', function () {
     beforeEach(async function () {
-      this.account1 = account1;
-      this.account1Delegatee = account1Delegatee;
-      this.account2 = account2;
-      this.name = 'My Vote';
+      this.name = name;
     });
 
-    shouldBehaveLikeVotes();
+    shouldBehaveLikeVotes(accounts, tokens, false);
   });
 });