Skip to content

Commit

Permalink
Introduce VotingPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
k06a committed Oct 20, 2024
1 parent bb6c99c commit 4f73cee
Showing 1 changed file with 102 additions and 0 deletions.
102 changes: 102 additions & 0 deletions contracts/VotingPlugin.sol
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)));
}
}
}
}

0 comments on commit 4f73cee

Please sign in to comment.