-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
102 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
import { Checkpoints } from "@openzeppelin/contracts/utils/Checkpoints.sol"; | ||
import { IERC20Plugins, DelegationPlugin } from "./DelegationPlugin.sol"; | ||
|
||
contract VotingPlugin is DelegationPlugin { | ||
using Checkpoints for Checkpoints.Trace224; | ||
|
||
error ERC5805FutureLookup(uint256 timepoint, uint48 clock); | ||
|
||
Checkpoints.Trace224 private _totalCheckpoints; | ||
mapping(address => Checkpoints.Trace224) private _delegateCheckpoints; | ||
|
||
constructor(string memory name, string memory symbol, IERC20Plugins token) | ||
DelegationPlugin(name, symbol, token) | ||
{} // solhint-disable-line no-empty-blocks | ||
|
||
/** | ||
* @dev Get number of checkpoints for `account`. | ||
*/ | ||
function numCheckpoints(address account) public view virtual returns (uint32) { | ||
return SafeCast.toUint32(_delegateCheckpoints[account].length()); | ||
} | ||
|
||
/** | ||
* @dev Get the `pos`-th checkpoint for `account`. | ||
*/ | ||
function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoints.Checkpoint224 memory) { | ||
return _delegateCheckpoints[account]._checkpoints[pos]; | ||
} | ||
|
||
/** | ||
* @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based | ||
* checkpoints (and voting), in which case {CLOCK_MODE} should be overridden as well to match. | ||
*/ | ||
function clock() public view virtual returns (uint48) { | ||
return SafeCast.toUint48(block.number); | ||
} | ||
|
||
/** | ||
* @dev Returns the current amount of votes that `account` has. | ||
*/ | ||
function getVotes(address account) public view virtual returns (uint256) { | ||
return _delegateCheckpoints[account].latest(); | ||
} | ||
|
||
/** | ||
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is | ||
* configured to use block numbers, this will return the value at the end of the corresponding block. | ||
* | ||
* Requirements: | ||
* | ||
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined. | ||
*/ | ||
function getPastVotes(address account, uint256 timepoint) public view virtual returns (uint256) { | ||
uint48 currentTimepoint = clock(); | ||
if (timepoint >= currentTimepoint) { | ||
revert ERC5805FutureLookup(timepoint, currentTimepoint); | ||
} | ||
return _delegateCheckpoints[account].upperLookupRecent(SafeCast.toUint32(timepoint)); | ||
} | ||
|
||
/** | ||
* @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is | ||
* configured to use block numbers, this will return the value at the end of the corresponding block. | ||
* | ||
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. | ||
* Votes that have not been delegated are still part of total supply, even though they would not participate in a | ||
* vote. | ||
* | ||
* Requirements: | ||
* | ||
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined. | ||
*/ | ||
function getPastTotalSupply(uint256 timepoint) public view virtual returns (uint256) { | ||
uint48 currentTimepoint = clock(); | ||
if (timepoint >= currentTimepoint) { | ||
revert ERC5805FutureLookup(timepoint, currentTimepoint); | ||
} | ||
return _totalCheckpoints.upperLookupRecent(SafeCast.toUint32(timepoint)); | ||
} | ||
|
||
function _updateBalances(address from, address to, address fromDelegatee, address toDelegatee, uint256 amount) internal virtual override { | ||
super._updateBalances(from, to, fromDelegatee, toDelegatee, amount); | ||
|
||
if (fromDelegatee != toDelegatee && amount > 0) { | ||
if (fromDelegatee == address(0)) { | ||
_delegateCheckpoints[toDelegatee].push(SafeCast.toUint32(clock()), SafeCast.toUint224(balanceOf(toDelegatee))); | ||
_totalCheckpoints.push(SafeCast.toUint32(clock()), SafeCast.toUint224(totalSupply())); | ||
} else if (toDelegatee == address(0)) { | ||
_delegateCheckpoints[fromDelegatee].push(SafeCast.toUint32(clock()), SafeCast.toUint224(balanceOf(fromDelegatee))); | ||
_totalCheckpoints.push(SafeCast.toUint32(clock()), SafeCast.toUint224(totalSupply())); | ||
} else { | ||
_delegateCheckpoints[fromDelegatee].push(SafeCast.toUint32(clock()), SafeCast.toUint224(balanceOf(fromDelegatee))); | ||
_delegateCheckpoints[toDelegatee].push(SafeCast.toUint32(clock()), SafeCast.toUint224(balanceOf(toDelegatee))); | ||
} | ||
} | ||
} | ||
} |