Skip to content

Commit

Permalink
DAO Builder and test dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
brickpop committed Dec 19, 2024
1 parent 27824f2 commit 60e2ceb
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 201 deletions.
27 changes: 18 additions & 9 deletions src/LockManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {DaoAuthorizable} from "@aragon/osx-commons-contracts/src/permission/auth/DaoAuthorizable.sol";
import {ILockToVote} from "./interfaces/ILockToVote.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/// @title LockManager
/// @author Aragon X 2024
/// @notice Helper contract acting as the vault for locked tokens used to vote on multiple plugins and proposals.
contract LockManager is ILockManager, DaoAuthorizable {
/// @notice The ID of the permission required to call the `updateVotingSettings` function.
bytes32 public constant UPDATE_SETTINGS_PERMISSION_ID = keccak256("UPDATE_SETTINGS_PERMISSION");

/// @notice The current LockManager settings
LockManagerSettings public settings;

/// @notice The address of the lock to vote plugin to use
ILockToVote public immutable plugin;
ILockToVote public plugin;

/// @notice The address of the token contract
IERC20 public immutable token;
Expand Down Expand Up @@ -48,19 +52,14 @@ contract LockManager is ILockManager, DaoAuthorizable {
/// @notice Raised when attempting to unlock while active votes are cast in strict mode
error LocksStillActive();

constructor(
IDAO _dao,
LockManagerSettings memory _settings,
ILockToVote _plugin,
IERC20 _token,
IERC20 _underlyingToken
) DaoAuthorizable(_dao) {
constructor(IDAO _dao, LockManagerSettings memory _settings, IERC20 _token, IERC20 _underlyingToken)
DaoAuthorizable(_dao)
{
if (_settings.unlockMode != UnlockMode.STRICT && _settings.unlockMode != UnlockMode.EARLY) {
revert InvalidUnlockMode();
}

settings.unlockMode = _settings.unlockMode;
plugin = _plugin;
token = _token;
underlyingTokenAddress = _underlyingToken;
}
Expand Down Expand Up @@ -123,13 +122,23 @@ contract LockManager is ILockManager, DaoAuthorizable {
}
}

/// @inheritdoc ILockManager
function underlyingToken() external view returns (IERC20) {
if (address(underlyingTokenAddress) == address(0)) {
return token;
}
return underlyingTokenAddress;
}

/// @inheritdoc ILockManager
function setPluginAddress(ILockToVote _plugin) public auth(UPDATE_SETTINGS_PERMISSION_ID) {
if (!IERC165(address(_plugin)).supportsInterface(type(ILockToVote).interfaceId)) {
revert InvalidPlugin();
}

plugin = _plugin;
}

// Internal

function _lock() internal {
Expand Down
6 changes: 6 additions & 0 deletions src/interfaces/ILockManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,10 @@ interface ILockManager {
/// @notice Called by the lock to vote plugin whenever a proposal is executed (or ended). It instructs the manager to remove the proposal from the list of active proposal locks.
/// @param proposalId The ID of the proposal that msg.sender is reporting as done.
function proposalEnded(uint256 proposalId) external;

/// @notice Defines the given plugin address as the target for voting
function setPluginAddress(ILockToVote _plugin) external;

/// @notice Thrown then trying to set an invalid contract as the plugin
error InvalidPlugin();
}
32 changes: 32 additions & 0 deletions src/util/proxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

function createProxyAndCall(address logic, bytes memory data) returns (address) {
return address(new ERC1967Proxy(logic, data));
}

function createSaltedProxyAndCall(address logic, bytes memory data, bytes32 salt) returns (address) {
return address(new ERC1967Proxy{salt: salt}(logic, data));
}

function predictProxyAddress(address factory, bytes32 salt, address logic, bytes memory data)
pure
returns (address predictedAddress)
{
predictedAddress = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
factory,
salt,
keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(logic, data)))
)
)
)
)
);
}
65 changes: 63 additions & 2 deletions test/LockManager.t.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {Test} from "forge-std/Test.sol";
import {AragonTest} from "./util/AragonTest.sol";
import {DaoBuilder} from "./util/DaoBuilder.sol";
import {DAO, IDAO} from "@aragon/osx/src/core/dao/DAO.sol";
import {createProxyAndCall} from "../src/util/proxy.sol";
import {IProposal} from "@aragon/osx-commons-contracts/src/plugin/extensions/proposal/IProposal.sol";
import {IPlugin} from "@aragon/osx-commons-contracts/src/plugin/IPlugin.sol";
import {LockToVotePlugin} from "../src/LockToVotePlugin.sol";
import {LockManagerSettings, UnlockMode} from "../src/interfaces/ILockManager.sol";
import {LockManager} from "../src/LockManager.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract LockManagerTest is AragonTest {
DaoBuilder builder;
DAO dao;
LockToVotePlugin plugin;
LockManager lockManager;
IERC20 lockableToken;
IERC20 underlyingToken;

address immutable LOCK_TO_VOTE_BASE = address(new LockToVotePlugin());
address immutable LOCK_MANAGER_BASE = address(
new LockManager(
IDAO(address(0)), LockManagerSettings(UnlockMode.STRICT), IERC20(address(0)), IERC20(address(0))
)
);

function setUp() public {
vm.startPrank(alice);
vm.warp(1 days);
vm.roll(100);

builder = new DaoBuilder();
(dao, plugin, lockManager, lockableToken, underlyingToken) = builder.withTokenHolder(alice, 1 ether)
.withTokenHolder(bob, 10 ether).withTokenHolder(carol, 10 ether).withTokenHolder(david, 15 ether).build();
}

contract LockManagerTest is Test {
modifier givenDeployingTheContract() {
_;
}
Expand Down Expand Up @@ -347,6 +380,34 @@ contract LockManagerTest is Test {
vm.skip(true);
}

function test_WhenCallingPlugin() external {
// It Should return the right address
vm.skip(true);
}

function test_WhenCallingToken() external {
// It Should return the right address
vm.skip(true);
}

modifier givenNoUnderlyingToken() {
_;
}

function test_WhenCallingUnderlyingTokenEmpty() external givenNoUnderlyingToken {
// It Should return the token address
vm.skip(true);
}

modifier givenUnderlyingTokenDefined() {
_;
}

function test_WhenCallingUnderlyingTokenSet() external givenUnderlyingTokenDefined {
// It Should return the right address
vm.skip(true);
}

function test_GivenPermissions() external {
// It Should revert if proposalEnded is called by an incompatible plugin
vm.skip(true);
Expand Down
20 changes: 20 additions & 0 deletions test/LockManager.t.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,26 @@ LockManagerTest:
- it: Should allow voters from that proposal to unlock right away
- it: Should revert on voters who have any other unreleased proposal votes

- when: Calling plugin()
then:
- it: Should return the right address

- when: Calling token()
then:
- it: Should return the right address

- given: No underlying token
then:
- when: Calling underlyingToken() empty
then:
- it: Should return the token address

- given: Underlying token defined
then:
- when: Calling underlyingToken() set
then:
- it: Should return the right address

- given: Permissions
then:
- it: Should revert if proposalEnded is called by an incompatible plugin
Loading

0 comments on commit 60e2ceb

Please sign in to comment.