ERC20Votes.sol 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. import "./draft-ERC20Permit.sol";
  4. import "./IERC20Votes.sol";
  5. import "../../../utils/math/Math.sol";
  6. import "../../../utils/math/SafeCast.sol";
  7. import "../../../utils/cryptography/ECDSA.sol";
  8. /**
  9. * @dev Extension of the ERC20 token contract to support Compound's voting and delegation.
  10. *
  11. * This extensions keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either
  12. * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting
  13. * power can be queried through the public accessors {getCurrentVotes} and {getPriorVotes}.
  14. *
  15. * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
  16. * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
  17. * Enabling self-delegation can easily be done by overriding the {delegates} function. Keep in mind however that this
  18. * will significantly increase the base gas cost of transfers.
  19. *
  20. * _Available since v4.2._
  21. */
  22. abstract contract ERC20Votes is IERC20Votes, ERC20Permit {
  23. bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
  24. mapping (address => address) private _delegates;
  25. mapping (address => Checkpoint[]) private _checkpoints;
  26. Checkpoint[] private _totalSupplyCheckpoints;
  27. /**
  28. * @dev Get the `pos`-th checkpoint for `account`.
  29. */
  30. function checkpoints(address account, uint32 pos) external view virtual override returns (Checkpoint memory) {
  31. return _checkpoints[account][pos];
  32. }
  33. /**
  34. * @dev Get number of checkpoints for `account`.
  35. */
  36. function numCheckpoints(address account) external view virtual override returns (uint32) {
  37. return SafeCast.toUint32(_checkpoints[account].length);
  38. }
  39. /**
  40. * @dev Get the address `account` is currently delegating to.
  41. */
  42. function delegates(address account) public view virtual override returns (address) {
  43. return _delegates[account];
  44. }
  45. /**
  46. * @dev Gets the current votes balance for `account`
  47. */
  48. function getCurrentVotes(address account) external view override returns (uint256) {
  49. uint256 pos = _checkpoints[account].length;
  50. return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
  51. }
  52. /**
  53. * @dev Retrieve the number of votes for `account` at the end of `blockNumber`.
  54. *
  55. * Requirements:
  56. *
  57. * - `blockNumber` must have been already mined
  58. */
  59. function getPriorVotes(address account, uint256 blockNumber) external view override returns (uint256) {
  60. require(blockNumber < block.number, "ERC20Votes: block not yet mined");
  61. return _checkpointsLookup(_checkpoints[account], blockNumber);
  62. }
  63. /**
  64. * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances.
  65. * It is but NOT the sum of all the delegated votes!
  66. *
  67. * Requirements:
  68. *
  69. * - `blockNumber` must have been already mined
  70. */
  71. function getPriorTotalSupply(uint256 blockNumber) external view override returns (uint256) {
  72. require(blockNumber < block.number, "ERC20Votes: block not yet mined");
  73. return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
  74. }
  75. /**
  76. * @dev Lookup a value in a list of (sorted) checkpoints.
  77. */
  78. function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) {
  79. // We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
  80. //
  81. // During the loop, the index of the wanted checkpoint remains in the range [low, high).
  82. // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
  83. // - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
  84. // - If the middle checkpoint is before `blockNumber`, we look in [mid+1, high)
  85. // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
  86. // out of bounds (in which case we're looking too far in the past and the result is 0).
  87. // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
  88. // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
  89. // the same.
  90. uint256 high = ckpts.length;
  91. uint256 low = 0;
  92. while (low < high) {
  93. uint256 mid = Math.average(low, high);
  94. if (ckpts[mid].fromBlock > blockNumber) {
  95. high = mid;
  96. } else {
  97. low = mid + 1;
  98. }
  99. }
  100. return high == 0 ? 0 : ckpts[high - 1].votes;
  101. }
  102. /**
  103. * @dev Delegate votes from the sender to `delegatee`.
  104. */
  105. function delegate(address delegatee) public virtual override {
  106. return _delegate(_msgSender(), delegatee);
  107. }
  108. /**
  109. * @dev Delegates votes from signer to `delegatee`
  110. */
  111. function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s)
  112. public virtual override
  113. {
  114. require(block.timestamp <= expiry, "ERC20Votes: signature expired");
  115. address signer = ECDSA.recover(
  116. _hashTypedDataV4(keccak256(abi.encode(
  117. _DELEGATION_TYPEHASH,
  118. delegatee,
  119. nonce,
  120. expiry
  121. ))),
  122. v, r, s
  123. );
  124. require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce");
  125. return _delegate(signer, delegatee);
  126. }
  127. /**
  128. * @dev snapshot the totalSupply after it has been increassed.
  129. */
  130. function _mint(address account, uint256 amount) internal virtual override {
  131. super._mint(account, amount);
  132. require(totalSupply() <= type(uint224).max, "ERC20Votes: total supply exceeds 2**224");
  133. _writeCheckpoint(_totalSupplyCheckpoints, add, amount);
  134. }
  135. /**
  136. * @dev snapshot the totalSupply after it has been decreased.
  137. */
  138. function _burn(address account, uint256 amount) internal virtual override {
  139. super._burn(account, amount);
  140. _writeCheckpoint(_totalSupplyCheckpoints, subtract, amount);
  141. }
  142. /**
  143. * @dev move voting power when tokens are transferred.
  144. */
  145. function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
  146. _moveVotingPower(delegates(from), delegates(to), amount);
  147. }
  148. /**
  149. * @dev Change delegation for `delegator` to `delegatee`.
  150. */
  151. function _delegate(address delegator, address delegatee) internal virtual {
  152. address currentDelegate = delegates(delegator);
  153. uint256 delegatorBalance = balanceOf(delegator);
  154. _delegates[delegator] = delegatee;
  155. emit DelegateChanged(delegator, currentDelegate, delegatee);
  156. _moveVotingPower(currentDelegate, delegatee, delegatorBalance);
  157. }
  158. function _moveVotingPower(address src, address dst, uint256 amount) private {
  159. if (src != dst && amount > 0) {
  160. if (src != address(0)) {
  161. (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], subtract, amount);
  162. emit DelegateVotesChanged(src, oldWeight, newWeight);
  163. }
  164. if (dst != address(0)) {
  165. (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], add, amount);
  166. emit DelegateVotesChanged(dst, oldWeight, newWeight);
  167. }
  168. }
  169. }
  170. function _writeCheckpoint(
  171. Checkpoint[] storage ckpts,
  172. function (uint256, uint256) view returns (uint256) op,
  173. uint256 delta
  174. )
  175. private returns (uint256 oldWeight, uint256 newWeight)
  176. {
  177. uint256 pos = ckpts.length;
  178. oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes;
  179. newWeight = op(oldWeight, delta);
  180. if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) {
  181. ckpts[pos - 1].votes = SafeCast.toUint224(newWeight);
  182. } else {
  183. ckpts.push(Checkpoint({
  184. fromBlock: SafeCast.toUint32(block.number),
  185. votes: SafeCast.toUint224(newWeight)
  186. }));
  187. }
  188. }
  189. function add(uint256 a, uint256 b) private pure returns (uint256) {
  190. return a + b;
  191. }
  192. function subtract(uint256 a, uint256 b) private pure returns (uint256) {
  193. return a - b;
  194. }
  195. }