Skip to content

Commit

Permalink
Voting plugin work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
brickpop committed Dec 17, 2024
1 parent 1a2bfaf commit ac6e3eb
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 94 deletions.
2 changes: 1 addition & 1 deletion lib/osx
Submodule osx updated 473 files
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
@aragon/osx/=lib/osx/packages/contracts/src/
@aragon/osx-commons-contracts/=lib/osx-commons/contracts/src/
@ensdomains/ens-contracts/=lib/ens-contracts/
forge-std/=lib/forge-std/src/
97 changes: 38 additions & 59 deletions src/LockManager.sol
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

import {ILockManager, LockManagerSettings, UnlockMode} from "./interfaces/ILockManager.sol";
import {IDAO} from "@aragon/osx/core/dao/IDAO.sol";
import {DaoAuthorizable} from "@aragon/osx/core/plugin/dao-authorizable/DaoAuthorizable.sol";
import {ILockManager} from "./interfaces/ILockManager.sol";
import {ILockToVote} from "./interfaces/ILockToVote.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract LockManager is ILockManager, DaoAuthorizable {
/// @notice Defines whether locked funds can be unlocked at any time or not
enum UnlockMode {
STRICT,
EARLY
}
/// @notice The current LockManager settings
LockManagerSettings public settings;

/// @notice The struct containing the LockManager helper settings
struct Settings {
/// @param lockMode The mode defining whether funds can be unlocked at any time or not
UnlockMode unlockMode;
/// @param plugin The address of the lock to vote plugin to use
ILockToVote plugin;
/// @param token The address of the token contract
IERC20 token;
}
/// @notice The address of the lock to vote plugin to use
ILockToVote public immutable plugin;

/// @notice The current LockManager settings
Settings public settings;
/// @notice The address of the token contract
IERC20 public immutable token;

/// @notice Keeps track of the amount of tokens locked by address
mapping(address => uint256) lockedBalances;
Expand All @@ -52,7 +42,12 @@ contract LockManager is ILockManager, DaoAuthorizable {
/// @notice Raised when attempting to unlock while active votes are cast in strict mode
error LocksStillActive();

constructor(IDAO _dao, Settings memory _settings) DaoAuthorizable(_dao) {
constructor(
IDAO _dao,
LockManagerSettings memory _settings,
ILockToVote _plugin,
IERC20 _token
) DaoAuthorizable(_dao) {
if (
_settings.unlockMode != UnlockMode.STRICT &&
_settings.unlockMode != UnlockMode.EARLY
Expand All @@ -61,8 +56,8 @@ contract LockManager is ILockManager, DaoAuthorizable {
}

settings.unlockMode = _settings.unlockMode;
settings.plugin = _settings.plugin;
settings.token = _settings.token;
plugin = _plugin;
token = _token;
}

/// @inheritdoc ILockManager
Expand All @@ -83,10 +78,10 @@ contract LockManager is ILockManager, DaoAuthorizable {
address _voter
) external view returns (bool) {
uint256 availableBalance = lockedBalances[_voter] -
settings.plugin.usedVotingPower(_proposalId, _voter);
plugin.usedVotingPower(_proposalId, _voter);
if (availableBalance == 0) return false;

(bool open, bool executed, ) = settings.plugin.getProposal(_proposalId);
(bool open, bool executed, ) = plugin.getProposal(_proposalId);
return !executed && open;
}

Expand All @@ -102,30 +97,30 @@ contract LockManager is ILockManager, DaoAuthorizable {
}

if (settings.unlockMode == UnlockMode.STRICT) {
if (hasActiveLocks()) revert LocksStillActive();
if (_hasActiveLocks()) revert LocksStillActive();
} else {
withdrawActiveVotingPower();
_withdrawActiveVotingPower();
}

// All votes clear

uint256 _refundableBalance = lockedBalances[msg.sender];
lockedBalances[msg.sender] = 0;

// Refund
settings.token.transfer(msg.sender, _refundableBalance);
// Withdraw
token.transfer(msg.sender, _refundableBalance);
emit BalanceUnlocked(msg.sender, _refundableBalance);
}

/// @inheritdoc ILockManager
function proposalEnded(uint256 _proposalId) public {
if (msg.sender != address(settings.plugin)) {
if (msg.sender != address(plugin)) {
revert InvalidPluginAddress();
}

for (uint _i; _i < knownProposalIds.length; ) {
if (knownProposalIds[_i] == _proposalId) {
removeKnownProposalId(_i);
_removeKnownProposalId(_i);
return;
}
}
Expand All @@ -134,15 +129,12 @@ contract LockManager is ILockManager, DaoAuthorizable {
// Internal

function _lock() internal {
uint256 _allowance = settings.token.allowance(
msg.sender,
address(this)
);
uint256 _allowance = token.allowance(msg.sender, address(this));
if (_allowance == 0) {
revert NoBalance();
}

settings.token.transferFrom(msg.sender, address(this), _allowance);
token.transferFrom(msg.sender, address(this), _allowance);
lockedBalances[msg.sender] += _allowance;
emit BalanceLocked(msg.sender, _allowance);
}
Expand All @@ -152,22 +144,20 @@ contract LockManager is ILockManager, DaoAuthorizable {
if (_newVotingPower == 0) {
revert NoBalance();
} else if (
_newVotingPower ==
settings.plugin.usedVotingPower(_proposalId, msg.sender)
_newVotingPower == plugin.usedVotingPower(_proposalId, msg.sender)
) {
return;
}

settings.plugin.vote(_proposalId, msg.sender, _newVotingPower);
plugin.vote(_proposalId, msg.sender, _newVotingPower);
}

function hasActiveLocks() internal returns (bool) {
function _hasActiveLocks() internal returns (bool) {
uint256 _proposalCount = knownProposalIds.length;
for (uint256 _i; _i < _proposalCount; ) {
// (bool open, ) = settings.plugin.getProposal(knownProposalIds[_i]);
bool open = true;
(bool open, ) = plugin.getProposal(knownProposalIds[_i]);
if (!open) {
removeKnownProposalId(_i);
_removeKnownProposalId(_i);
_proposalCount = knownProposalIds.length;

// Are we at the last item?
Expand All @@ -179,12 +169,7 @@ contract LockManager is ILockManager, DaoAuthorizable {
continue;
}

if (
settings.plugin.usedVotingPower(
knownProposalIds[_i],
msg.sender
) > 0
) {
if (plugin.usedVotingPower(knownProposalIds[_i], msg.sender) > 0) {
return true;
}

Expand All @@ -194,13 +179,12 @@ contract LockManager is ILockManager, DaoAuthorizable {
}
}

function withdrawActiveVotingPower() internal {
function _withdrawActiveVotingPower() internal {
uint256 _proposalCount = knownProposalIds.length;
for (uint256 _i; _i < _proposalCount; ) {
// (bool open, ) = settings.plugin.getProposal(knownProposalIds[_i]);
bool open = true;
(bool open, ) = plugin.getProposal(knownProposalIds[_i]);
if (!open) {
removeKnownProposalId(_i);
_removeKnownProposalId(_i);
_proposalCount = knownProposalIds.length;

// Are we at the last item?
Expand All @@ -212,13 +196,8 @@ contract LockManager is ILockManager, DaoAuthorizable {
continue;
}

if (
settings.plugin.usedVotingPower(
knownProposalIds[_i],
msg.sender
) > 0
) {
settings.plugin.clearVote(knownProposalIds[_i], msg.sender);
if (plugin.usedVotingPower(knownProposalIds[_i], msg.sender) > 0) {
plugin.clearVote(knownProposalIds[_i], msg.sender);
}

unchecked {
Expand All @@ -228,7 +207,7 @@ contract LockManager is ILockManager, DaoAuthorizable {
}

/// @dev Cleaning up ended proposals, otherwise they would pile up and make unlocks more and more gas costly over time
function removeKnownProposalId(uint _arrayIndex) internal {
function _removeKnownProposalId(uint _arrayIndex) internal {
uint _lastItemIdx = knownProposalIds.length - 1;

// Swap the current item with the last, if needed
Expand Down
Loading

0 comments on commit ac6e3eb

Please sign in to comment.