VotesExtended.sol 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.2.0) (governance/utils/VotesExtended.sol)
  3. pragma solidity ^0.8.24;
  4. import {Checkpoints} from "../../utils/structs/Checkpoints.sol";
  5. import {Votes} from "./Votes.sol";
  6. import {SafeCast} from "../../utils/math/SafeCast.sol";
  7. /**
  8. * @dev Extension of {Votes} that adds checkpoints for delegations and balances.
  9. *
  10. * WARNING: While this contract extends {Votes}, valid uses of {Votes} may not be compatible with
  11. * {VotesExtended} without additional considerations. This implementation of {_transferVotingUnits} must
  12. * run AFTER the voting weight movement is registered, such that it is reflected on {_getVotingUnits}.
  13. *
  14. * Said differently, {VotesExtended} MUST be integrated in a way that calls {_transferVotingUnits} AFTER the
  15. * asset transfer is registered and balances are updated:
  16. *
  17. * ```solidity
  18. * contract VotingToken is Token, VotesExtended {
  19. * function transfer(address from, address to, uint256 tokenId) public override {
  20. * super.transfer(from, to, tokenId); // <- Perform the transfer first ...
  21. * _transferVotingUnits(from, to, 1); // <- ... then call _transferVotingUnits.
  22. * }
  23. *
  24. * function _getVotingUnits(address account) internal view override returns (uint256) {
  25. * return balanceOf(account);
  26. * }
  27. * }
  28. * ```
  29. *
  30. * {ERC20Votes} and {ERC721Votes} follow this pattern and are thus safe to use with {VotesExtended}.
  31. */
  32. abstract contract VotesExtended is Votes {
  33. using Checkpoints for Checkpoints.Trace160;
  34. using Checkpoints for Checkpoints.Trace208;
  35. mapping(address delegator => Checkpoints.Trace160) private _userDelegationCheckpoints;
  36. mapping(address account => Checkpoints.Trace208) private _userVotingUnitsCheckpoints;
  37. /**
  38. * @dev Returns the delegate of an `account` at a specific moment in the past. If the `clock()` is
  39. * configured to use block numbers, this will return the value at the end of the corresponding block.
  40. *
  41. * Requirements:
  42. *
  43. * - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
  44. */
  45. function getPastDelegate(address account, uint256 timepoint) public view virtual returns (address) {
  46. return address(_userDelegationCheckpoints[account].upperLookupRecent(_validateTimepoint(timepoint)));
  47. }
  48. /**
  49. * @dev Returns the `balanceOf` of an `account` at a specific moment in the past. If the `clock()` is
  50. * configured to use block numbers, this will return the value at the end of the corresponding block.
  51. *
  52. * Requirements:
  53. *
  54. * - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
  55. */
  56. function getPastBalanceOf(address account, uint256 timepoint) public view virtual returns (uint256) {
  57. return _userVotingUnitsCheckpoints[account].upperLookupRecent(_validateTimepoint(timepoint));
  58. }
  59. /// @inheritdoc Votes
  60. function _delegate(address account, address delegatee) internal virtual override {
  61. super._delegate(account, delegatee);
  62. _userDelegationCheckpoints[account].push(clock(), uint160(delegatee));
  63. }
  64. /// @inheritdoc Votes
  65. function _transferVotingUnits(address from, address to, uint256 amount) internal virtual override {
  66. super._transferVotingUnits(from, to, amount);
  67. if (from != to) {
  68. if (from != address(0)) {
  69. _userVotingUnitsCheckpoints[from].push(clock(), SafeCast.toUint208(_getVotingUnits(from)));
  70. }
  71. if (to != address(0)) {
  72. _userVotingUnitsCheckpoints[to].push(clock(), SafeCast.toUint208(_getVotingUnits(to)));
  73. }
  74. }
  75. }
  76. }