diff --git a/src/LockManager.sol b/src/LockManager.sol index f941228..155ffc7 100644 --- a/src/LockManager.sol +++ b/src/LockManager.sol @@ -236,21 +236,21 @@ contract LockManager is ILockManager, DaoAuthorizable { ) { revert InvalidPlugin(); } - // Is it the right plugin type? + // Is it the right type of plugin? else if ( settings.pluginMode == PluginMode.Approval && !IERC165(address(_newPluginAddress)).supportsInterface( type(ILockToApprove).interfaceId ) ) { - revert InvalidPluginMode(); + revert InvalidPlugin(); } else if ( settings.pluginMode == PluginMode.Voting && !IERC165(address(_newPluginAddress)).supportsInterface( type(ILockToVote).interfaceId ) ) { - revert InvalidPluginMode(); + revert InvalidPlugin(); } plugin = _newPluginAddress; diff --git a/src/base/LockToVoteBase.sol b/src/base/LockToVoteBase.sol index 1b79f48..fc71120 100644 --- a/src/base/LockToVoteBase.sol +++ b/src/base/LockToVoteBase.sol @@ -12,13 +12,10 @@ import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/intro /// @title LockToVoteBase /// @author Aragon X 2024-2025 -abstract contract LockToVoteBase is - ILockToVoteBase, - IMembership, - ERC165Upgradeable -{ - error NoVotingPower(); +abstract contract LockToVoteBase is ILockToVoteBase, IMembership, ERC165Upgradeable { + event LockManagerDefined(ILockManager lockManager); + error NoVotingPower(); error LockManagerAlreadyDefined(); /// @inheritdoc ILockToVoteBase @@ -26,18 +23,14 @@ abstract contract LockToVoteBase is /// @notice Initializes the component to be used by inheriting contracts. /// @param _lockManager The address of the contract with the ability to manager votes on behalf of users. - function __LockToVoteBase_init( - ILockManager _lockManager - ) internal onlyInitializing { + function __LockToVoteBase_init(ILockManager _lockManager) internal onlyInitializing { _setLockManager(_lockManager); } /// @notice Checks if this or the parent contract supports an interface by its ID. /// @param _interfaceId The ID of the interface. /// @return Returns `true` if the interface is supported. - function supportsInterface( - bytes4 _interfaceId - ) public view virtual override(ERC165Upgradeable) returns (bool) { + function supportsInterface(bytes4 _interfaceId) public view virtual override(ERC165Upgradeable) returns (bool) { return _interfaceId == type(ILockToVoteBase).interfaceId || _interfaceId == type(IMembership).interfaceId || @@ -67,6 +60,8 @@ abstract contract LockToVoteBase is } lockManager = _lockManager; + + emit LockManagerDefined(_lockManager); } /// @notice This empty reserved space is put in place to allow future versions to add diff --git a/test/LockManager.t.sol b/test/LockManager.t.sol index 303fd4a..0b8b683 100644 --- a/test/LockManager.t.sol +++ b/test/LockManager.t.sol @@ -9,26 +9,34 @@ 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 {LockToApprovePlugin} from "../src/LockToApprovePlugin.sol"; +import {LockToVotePlugin, MajorityVotingBase} from "../src/LockToVotePlugin.sol"; import {ILockToVote} from "../src/interfaces/ILockToVote.sol"; -import {LockManagerSettings, UnlockMode} from "../src/interfaces/ILockManager.sol"; +import {LockManagerSettings, UnlockMode, PluginMode} from "../src/interfaces/ILockManager.sol"; +import {IMajorityVoting} from "../src/interfaces/IMajorityVoting.sol"; import {LockManager} from "../src/LockManager.sol"; +import {DaoUnauthorized} from "@aragon/osx-commons-contracts/src/permission/auth/auth.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract LockManagerTest is AragonTest { DaoBuilder builder; DAO dao; - LockToApprovePlugin plugin; + LockToApprovePlugin ltaPlugin; + LockToVotePlugin ltvPlugin; LockManager lockManager; IERC20 lockableToken; IERC20 underlyingToken; uint256 proposalId; address immutable LOCK_TO_VOTE_BASE = address(new LockToApprovePlugin()); - address immutable LOCK_MANAGER_BASE = address( - new LockManager( - IDAO(address(0)), LockManagerSettings(UnlockMode.Strict), IERC20(address(0)), IERC20(address(0)) - ) - ); + address immutable LOCK_MANAGER_BASE = + address( + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + IERC20(address(0)), + IERC20(address(0)) + ) + ); event BalanceLocked(address voter, uint256 amount); event BalanceUnlocked(address voter, uint256 amount); @@ -36,6 +44,7 @@ contract LockManagerTest is AragonTest { error InvalidUnlockMode(); error NoBalance(); + error NoNewBalance(); function setUp() public { vm.startPrank(alice); @@ -43,10 +52,14 @@ contract LockManagerTest is AragonTest { 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).withUnlockMode( - UnlockMode.Strict - ).build(); + (dao, ltaPlugin, ltvPlugin, lockManager, lockableToken, underlyingToken) = builder + .withTokenHolder(alice, 1 ether) + .withTokenHolder(bob, 10 ether) + .withTokenHolder(carol, 10 ether) + .withTokenHolder(david, 15 ether) + .withStrictUnlock() + .withApprovalPlugin() + .build(); } modifier givenDeployingTheContract() { @@ -55,21 +68,118 @@ contract LockManagerTest is AragonTest { function test_RevertWhen_ConstructorHasInvalidUnlockMode() external givenDeployingTheContract { // It Should revert - vm.expectRevert(); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidUnlockMode.selector)); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode(uint8(2)), PluginMode.Approval), + IERC20(address(0)), + IERC20(address(0)) + ); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidUnlockMode.selector)); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode(uint8(0)), PluginMode.Approval), + IERC20(address(0)), + IERC20(address(0)) + ); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidUnlockMode.selector)); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode(uint8(2)), PluginMode.Voting), + IERC20(address(0)), + IERC20(address(0)) + ); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidUnlockMode.selector)); new LockManager( - IDAO(address(0)), LockManagerSettings(UnlockMode(uint8(2))), IERC20(address(0)), IERC20(address(0)) + IDAO(address(0)), + LockManagerSettings(UnlockMode(uint8(0)), PluginMode.Voting), + IERC20(address(0)), + IERC20(address(0)) ); - vm.expectRevert(); + // OK + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + IERC20(address(0)), + IERC20(address(0)) + ); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Early, PluginMode.Approval), + IERC20(address(0)), + IERC20(address(0)) + ); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Strict, PluginMode.Voting), + IERC20(address(0)), + IERC20(address(0)) + ); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Early, PluginMode.Voting), + IERC20(address(0)), + IERC20(address(0)) + ); + } + + function test_RevertWhen_ConstructorHasInvalidPluginMode() external givenDeployingTheContract { + // It Should revert + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidPluginMode.selector)); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Strict, PluginMode(uint8(3))), + IERC20(address(0)), + IERC20(address(0)) + ); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidPluginMode.selector)); new LockManager( - IDAO(address(0)), LockManagerSettings(UnlockMode(uint8(0))), IERC20(address(0)), IERC20(address(0)) + IDAO(address(0)), + LockManagerSettings(UnlockMode.Early, PluginMode(uint8(0))), + IERC20(address(0)), + IERC20(address(0)) + ); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidPluginMode.selector)); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Strict, PluginMode(uint8(3))), + IERC20(address(0)), + IERC20(address(0)) + ); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidPluginMode.selector)); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Early, PluginMode(uint8(0))), + IERC20(address(0)), + IERC20(address(0)) ); // OK new LockManager( - IDAO(address(0)), LockManagerSettings(UnlockMode.Strict), IERC20(address(0)), IERC20(address(0)) + IDAO(address(0)), + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + IERC20(address(0)), + IERC20(address(0)) + ); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Early, PluginMode.Approval), + IERC20(address(0)), + IERC20(address(0)) + ); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Strict, PluginMode.Voting), + IERC20(address(0)), + IERC20(address(0)) + ); + new LockManager( + IDAO(address(0)), + LockManagerSettings(UnlockMode.Early, PluginMode.Voting), + IERC20(address(0)), + IERC20(address(0)) ); - new LockManager(IDAO(address(0)), LockManagerSettings(UnlockMode.Early), IERC20(address(0)), IERC20(address(0))); } function test_WhenConstructorWithValidParams() external givenDeployingTheContract { @@ -79,19 +189,30 @@ contract LockManagerTest is AragonTest { // 1 lockManager = new LockManager( - IDAO(address(1234)), LockManagerSettings(UnlockMode.Strict), IERC20(address(2345)), IERC20(address(3456)) + IDAO(address(1234)), + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + IERC20(address(2345)), + IERC20(address(3456)) ); assertEq(address(lockManager.dao()), address(1234)); assertEq(address(lockManager.token()), address(2345)); assertEq(address(lockManager.underlyingToken()), address(3456)); + (UnlockMode um, PluginMode pm) = lockManager.settings(); + assertEq(uint8(um), uint8(UnlockMode.Strict)); + assertEq(uint8(pm), uint8(PluginMode.Approval)); // 2 lockManager = new LockManager( - IDAO(address(5555)), LockManagerSettings(UnlockMode.Early), IERC20(address(6666)), IERC20(address(7777)) + IDAO(address(5555)), + LockManagerSettings(UnlockMode.Early, PluginMode.Voting), + IERC20(address(6666)), + IERC20(address(7777)) ); assertEq(address(lockManager.dao()), address(5555)); assertEq(address(lockManager.token()), address(6666)); assertEq(address(lockManager.underlyingToken()), address(7777)); + assertEq(uint8(um), uint8(UnlockMode.Early)); + assertEq(uint8(pm), uint8(PluginMode.Voting)); } modifier whenCallingSetPluginAddress() { @@ -101,68 +222,172 @@ contract LockManagerTest is AragonTest { function test_RevertGiven_InvalidPlugin() external whenCallingSetPluginAddress { // It should revert - lockManager = new LockManager(dao, LockManagerSettings(UnlockMode.Strict), lockableToken, underlyingToken); + // 1 + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + lockableToken, + underlyingToken + ); dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); - vm.expectRevert(); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidPlugin.selector)); + lockManager.setPluginAddress(LockToApprovePlugin(address(0x5555))); + + // 2 + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Early, PluginMode.Voting), + lockableToken, + underlyingToken + ); + dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidPlugin.selector)); lockManager.setPluginAddress(LockToApprovePlugin(address(0x5555))); } + function test_RevertGiven_InvalidPluginInterface() external whenCallingSetPluginAddress { + // It should revert + + // 1 + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + lockableToken, + underlyingToken + ); + dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidPlugin.selector)); + lockManager.setPluginAddress(new LockToVotePlugin()); + + // 2 + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Early, PluginMode.Voting), + lockableToken, + underlyingToken + ); + dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); + vm.expectRevert(abi.encodeWithSelector(LockManager.InvalidPlugin.selector)); + lockManager.setPluginAddress(new LockToApprovePlugin()); + + // ok + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Early, PluginMode.Approval), + lockableToken, + underlyingToken + ); + dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); + lockManager.setPluginAddress(new LockToApprovePlugin()); + } + function test_RevertWhen_SetPluginAddressWithoutThePermission() external whenCallingSetPluginAddress { // It should revert - (, LockToApprovePlugin plugin2,,,) = builder.build(); - (, LockToApprovePlugin plugin3,,,) = builder.build(); + (, LockToApprovePlugin ltaPlugin2, , , , ) = builder.build(); + (, LockToApprovePlugin ltaPlugin3, , , , ) = builder.build(); + (, , LockToVotePlugin ltvPlugin2, , , ) = builder.withVotingPlugin().build(); - lockManager = new LockManager(dao, LockManagerSettings(UnlockMode.Strict), lockableToken, underlyingToken); + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + lockableToken, + underlyingToken + ); // 1 - vm.expectRevert(); - lockManager.setPluginAddress(plugin2); + vm.expectRevert( + abi.encodeWithSelector( + DaoUnauthorized.selector, + address(dao), + address(lockManager), + alice, + lockManager.UPDATE_SETTINGS_PERMISSION_ID() + ) + ); + lockManager.setPluginAddress(ltaPlugin2); // 2 - vm.expectRevert(); - lockManager.setPluginAddress(plugin3); + vm.expectRevert( + abi.encodeWithSelector( + DaoUnauthorized.selector, + address(dao), + address(lockManager), + alice, + lockManager.UPDATE_SETTINGS_PERMISSION_ID() + ) + ); + lockManager.setPluginAddress(ltaPlugin3); // OK dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); - lockManager.setPluginAddress(plugin2); + lockManager.setPluginAddress(ltaPlugin2); // OK 2 - lockManager = new LockManager(dao, LockManagerSettings(UnlockMode.Strict), lockableToken, underlyingToken); + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Voting), + lockableToken, + underlyingToken + ); dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); - lockManager.setPluginAddress(plugin3); + lockManager.setPluginAddress(ltvPlugin2); } function test_WhenSetPluginAddressWithThePermission() external whenCallingSetPluginAddress { // It should update the address // It should revert if trying to update it later - (, LockToApprovePlugin plugin2,,,) = builder.build(); - (, LockToApprovePlugin plugin3,,,) = builder.build(); + (, LockToApprovePlugin ltaPlugin2, , , , ) = builder.build(); + (, , LockToVotePlugin ltvPlugin2, , , ) = builder.withVotingPlugin().build(); - lockManager = new LockManager(dao, LockManagerSettings(UnlockMode.Strict), lockableToken, underlyingToken); + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + lockableToken, + underlyingToken + ); + assertEq(address(lockManager.plugin()), address(0)); dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); - lockManager.setPluginAddress(plugin2); - assertEq(address(lockManager.plugin()), address(plugin2)); + lockManager.setPluginAddress(ltaPlugin2); + assertEq(address(lockManager.plugin()), address(ltaPlugin2)); // OK 2 - lockManager = new LockManager(dao, LockManagerSettings(UnlockMode.Strict), lockableToken, underlyingToken); + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Voting), + lockableToken, + underlyingToken + ); + assertEq(address(lockManager.plugin()), address(0)); dao.grant(address(lockManager), alice, lockManager.UPDATE_SETTINGS_PERMISSION_ID()); - lockManager.setPluginAddress(plugin3); - assertEq(address(lockManager.plugin()), address(plugin3)); + lockManager.setPluginAddress(ltvPlugin2); + assertEq(address(lockManager.plugin()), address(ltvPlugin2)); // Attempt to set when already defined vm.expectRevert(abi.encodeWithSelector(LockManager.SetPluginAddressForbidden.selector)); - lockManager.setPluginAddress(plugin2); + lockManager.setPluginAddress(ltaPlugin2); } - modifier givenNoLockedTokens() { + modifier givenProposalOnLockToApprove() { + Action[] memory _actions = new Action[](0); + proposalId = ltaPlugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); + + _; + } + + modifier givenProposalOnLockToVote() { + (dao, ltaPlugin, ltvPlugin, lockManager, lockableToken, underlyingToken) = builder.withVotingPlugin().build(); Action[] memory _actions = new Action[](0); - proposalId = plugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); + proposalId = ltvPlugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); + + _; + } + modifier givenNoLockedTokens() { _; } @@ -206,49 +431,124 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(address(lockManager)), 0.5 ether); } - function test_RevertWhen_CallingLockAndVote1() external givenNoLockedTokens givenNoTokenAllowanceNoLocked { + function test_RevertWhen_CallingLockAndApprove1() + external + givenProposalOnLockToApprove + givenNoLockedTokens + givenNoTokenAllowanceNoLocked + { + // It Should revert + + Action[] memory _actions = new Action[](0); + proposalId = ltaPlugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); + + vm.startPrank(randomWallet); + assertEq(lockableToken.balanceOf(randomWallet), 0); + assertEq(lockableToken.allowance(randomWallet, address(lockManager)), 0); + + vm.expectRevert(NoBalance.selector); + lockManager.lockAndApprove(proposalId); + + vm.startPrank(address(0x1234)); + assertEq(lockableToken.balanceOf(address(0x1234)), 0); + assertEq(lockableToken.allowance(address(0x1234), address(lockManager)), 0); + + vm.expectRevert(NoBalance.selector); + lockManager.lockAndApprove(proposalId); + + // vm.startPrank(alice); + assertEq(lockableToken.balanceOf(alice), 1 ether); + assertEq(lockableToken.allowance(alice, address(lockManager)), 0); + + vm.expectRevert(NoBalance.selector); + lockManager.lockAndApprove(proposalId); + } + + function test_RevertWhen_CallingApprove1() + external + givenProposalOnLockToApprove + givenNoLockedTokens + givenNoTokenAllowanceNoLocked + { + // It Should revert + + Action[] memory _actions = new Action[](0); + proposalId = ltaPlugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); + + vm.startPrank(randomWallet); + assertEq(lockManager.lockedBalances(randomWallet), 0); + + vm.expectRevert(NoBalance.selector); + lockManager.approve(proposalId); + + vm.startPrank(address(0x1234)); + assertEq(lockManager.lockedBalances(address(0x1234)), 0); + + vm.expectRevert(NoBalance.selector); + lockManager.approve(proposalId); + + // vm.startPrank(alice); + assertEq(lockManager.lockedBalances(alice), 0); + + vm.expectRevert(NoBalance.selector); + lockManager.approve(proposalId); + } + + function test_RevertWhen_CallingLockAndVote1() + external + givenProposalOnLockToVote + givenNoLockedTokens + givenNoTokenAllowanceNoLocked + { // It Should revert + vm.startPrank(randomWallet); assertEq(lockableToken.balanceOf(randomWallet), 0); assertEq(lockableToken.allowance(randomWallet, address(lockManager)), 0); vm.expectRevert(NoBalance.selector); - lockManager.lockAndVote(proposalId); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); vm.startPrank(address(0x1234)); assertEq(lockableToken.balanceOf(address(0x1234)), 0); assertEq(lockableToken.allowance(address(0x1234), address(lockManager)), 0); vm.expectRevert(NoBalance.selector); - lockManager.lockAndVote(proposalId); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.No); // vm.startPrank(alice); assertEq(lockableToken.balanceOf(alice), 1 ether); assertEq(lockableToken.allowance(alice, address(lockManager)), 0); vm.expectRevert(NoBalance.selector); - lockManager.lockAndVote(proposalId); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Abstain); } - function test_RevertWhen_CallingVote1() external givenNoLockedTokens givenNoTokenAllowanceNoLocked { + function test_RevertWhen_CallingVote1() + external + givenProposalOnLockToVote + givenNoLockedTokens + givenNoTokenAllowanceNoLocked + { // It Should revert + vm.startPrank(randomWallet); assertEq(lockManager.lockedBalances(randomWallet), 0); vm.expectRevert(NoBalance.selector); - lockManager.vote(proposalId); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); vm.startPrank(address(0x1234)); assertEq(lockManager.lockedBalances(address(0x1234)), 0); vm.expectRevert(NoBalance.selector); - lockManager.vote(proposalId); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); // vm.startPrank(alice); assertEq(lockManager.lockedBalances(alice), 0); vm.expectRevert(NoBalance.selector); - lockManager.vote(proposalId); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); } modifier givenWithTokenAllowanceNoLocked() { @@ -287,7 +587,68 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(address(lockManager)), 0.2 ether); } - function test_WhenCallingLockAndVote2() external givenNoLockedTokens givenWithTokenAllowanceNoLocked { + function test_WhenCallingLockAndApprove2() + external + givenProposalOnLockToApprove + givenNoLockedTokens + givenWithTokenAllowanceNoLocked + { + // It Should allow any token holder to lock + // It Should approve with the full token balance + // It The allocated token balance should have the full new balance + // It Should emit an event + + // vm.startPrank(alice); + uint256 initialBalance = lockableToken.balanceOf(alice); + uint256 allowance = lockableToken.allowance(alice, address(lockManager)); + vm.expectEmit(); + emit BalanceLocked(alice, allowance); + lockManager.lockAndApprove(proposalId); + assertEq(lockableToken.balanceOf(alice), initialBalance - allowance); + assertEq(lockManager.lockedBalances(alice), allowance); + assertEq(lockableToken.balanceOf(address(lockManager)), allowance); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), allowance); + + vm.startPrank(bob); + initialBalance = lockableToken.balanceOf(bob); + allowance = lockableToken.allowance(bob, address(lockManager)); + vm.expectEmit(); + emit BalanceLocked(bob, allowance); + lockManager.lockAndApprove(proposalId); + assertEq(lockableToken.balanceOf(bob), initialBalance - allowance); + assertEq(lockManager.lockedBalances(bob), allowance); + assertEq(lockableToken.balanceOf(address(lockManager)), 0.2 ether); + assertEq(ltaPlugin.usedVotingPower(proposalId, bob), allowance); + } + + function test_RevertWhen_CallingApprove2() + external + givenProposalOnLockToApprove + givenNoLockedTokens + givenWithTokenAllowanceNoLocked + { + // It Should revert + + Action[] memory _actions = new Action[](0); + proposalId = ltaPlugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); + + // vm.startPrank(alice); + assertEq(lockManager.lockedBalances(alice), 0); + vm.expectRevert(NoBalance.selector); + lockManager.approve(proposalId); + + vm.startPrank(bob); + assertEq(lockManager.lockedBalances(bob), 0); + vm.expectRevert(NoBalance.selector); + lockManager.approve(proposalId); + } + + function test_WhenCallingLockAndVote2() + external + givenProposalOnLockToVote + givenNoLockedTokens + givenWithTokenAllowanceNoLocked + { // It Should allow any token holder to lock // It Should approve with the full token balance // It The allocated token balance should have the full new balance @@ -298,45 +659,47 @@ contract LockManagerTest is AragonTest { uint256 allowance = lockableToken.allowance(alice, address(lockManager)); vm.expectEmit(); emit BalanceLocked(alice, allowance); - lockManager.lockAndVote(proposalId); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); assertEq(lockableToken.balanceOf(alice), initialBalance - allowance); assertEq(lockManager.lockedBalances(alice), allowance); assertEq(lockableToken.balanceOf(address(lockManager)), allowance); - assertEq(plugin.usedVotingPower(proposalId, alice), allowance); + assertEq(ltvPlugin.usedVotingPower(proposalId, alice), allowance); vm.startPrank(bob); initialBalance = lockableToken.balanceOf(bob); allowance = lockableToken.allowance(bob, address(lockManager)); vm.expectEmit(); emit BalanceLocked(bob, allowance); - lockManager.lockAndVote(proposalId); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.No); assertEq(lockableToken.balanceOf(bob), initialBalance - allowance); assertEq(lockManager.lockedBalances(bob), allowance); assertEq(lockableToken.balanceOf(address(lockManager)), 0.2 ether); - assertEq(plugin.usedVotingPower(proposalId, bob), allowance); + assertEq(ltvPlugin.usedVotingPower(proposalId, bob), allowance); } - function test_RevertWhen_CallingVote2() external givenNoLockedTokens givenWithTokenAllowanceNoLocked { + function test_RevertWhen_CallingVote2() + external + givenProposalOnLockToVote + givenNoLockedTokens + givenWithTokenAllowanceNoLocked + { // It Should revert // vm.startPrank(alice); assertEq(lockManager.lockedBalances(alice), 0); vm.expectRevert(NoBalance.selector); - lockManager.vote(proposalId); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); vm.startPrank(bob); assertEq(lockManager.lockedBalances(bob), 0); vm.expectRevert(NoBalance.selector); - lockManager.vote(proposalId); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.No); } modifier givenLockedTokens() { lockableToken.approve(address(lockManager), 0.1 ether); lockManager.lock(); - Action[] memory _actions = new Action[](0); - proposalId = plugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); - _; } @@ -363,16 +726,21 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(address(lockManager)), 0.6 ether); } - function test_RevertWhen_CallingLockAndVote3() external givenLockedTokens givenNoTokenAllowanceSomeLocked { + function test_RevertWhen_CallingLockAndApprove3() + external + givenProposalOnLockToApprove + givenLockedTokens + givenNoTokenAllowanceSomeLocked + { // It Should revert assertEq(lockableToken.allowance(alice, address(lockManager)), 0); vm.expectRevert(NoBalance.selector); - lockManager.lockAndVote(proposalId); + lockManager.lockAndApprove(proposalId); lockableToken.approve(address(lockManager), 0.5 ether); - lockManager.lockAndVote(proposalId); + lockManager.lockAndApprove(proposalId); assertEq(lockableToken.balanceOf(alice), 0.4 ether); assertEq(lockableToken.allowance(alice, address(lockManager)), 0); @@ -380,23 +748,33 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(address(lockManager)), 0.6 ether); } - function test_RevertWhen_CallingVoteSameBalance3() external givenLockedTokens givenNoTokenAllowanceSomeLocked { - // Prior vote - lockManager.vote(proposalId); + function test_RevertWhen_CallingApproveSameBalance3() + external + givenProposalOnLockToApprove + givenLockedTokens + givenNoTokenAllowanceSomeLocked + { + // Prior approval + lockManager.approve(proposalId); // It Should revert - vm.expectRevert(); - lockManager.vote(proposalId); + vm.expectRevert(NoNewBalance.selector); + lockManager.approve(proposalId); } - function test_WhenCallingVoteMoreLockedBalance3() external givenLockedTokens givenNoTokenAllowanceSomeLocked { + function test_WhenCallingApproveMoreLockedBalance3() + external + givenProposalOnLockToApprove + givenLockedTokens + givenNoTokenAllowanceSomeLocked + { // It Should approve with the full token balance // It Should emit an event // vm.startPrank(alice); - lockManager.vote(proposalId); - assertEq(plugin.usedVotingPower(proposalId, alice), 0.1 ether); + lockManager.approve(proposalId); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.1 ether); // More assertEq(lockManager.lockedBalances(alice), 0.1 ether); @@ -409,59 +787,130 @@ contract LockManagerTest is AragonTest { assertEq(lockManager.lockedBalances(alice), 0.6 ether); - lockManager.vote(proposalId); - assertEq(plugin.usedVotingPower(proposalId, alice), 0.6 ether); + lockManager.approve(proposalId); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.6 ether); } - modifier givenWithTokenAllowanceSomeLocked() { + function test_RevertWhen_CallingLockAndVote3() + external + givenProposalOnLockToVote + givenLockedTokens + givenNoTokenAllowanceSomeLocked + { + // It Should revert + + assertEq(lockableToken.allowance(alice, address(lockManager)), 0); + + vm.expectRevert(NoBalance.selector); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); + lockableToken.approve(address(lockManager), 0.5 ether); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.No); - _; + assertEq(lockableToken.balanceOf(alice), 0.4 ether); + assertEq(lockableToken.allowance(alice, address(lockManager)), 0); + + assertEq(lockableToken.balanceOf(address(lockManager)), 0.6 ether); } - function test_WhenCallingLock4() external givenLockedTokens givenWithTokenAllowanceSomeLocked { - // It Should allow any token holder to lock - // It Should approve with the full token balance - // It Should increase the locked amount - // It Should emit an event + function test_RevertWhen_CallingVoteSameBalance3() + external + givenProposalOnLockToVote + givenLockedTokens + givenNoTokenAllowanceSomeLocked + { + // Prior vote + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); - // vm.startPrank(alice); - uint256 initialBalance = lockableToken.balanceOf(alice); - uint256 allowance = lockableToken.allowance(alice, address(lockManager)); - vm.expectEmit(); - emit BalanceLocked(alice, allowance); - lockManager.lock(); + // It Should revert - assertEq(lockableToken.balanceOf(alice), initialBalance - allowance); - assertEq(lockManager.lockedBalances(alice), 0.1 ether + allowance); - assertEq(lockableToken.balanceOf(address(lockManager)), 0.1 ether + allowance); + vm.expectRevert(NoNewBalance.selector); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.No); } - function test_WhenCallingLockAndVoteNoPriorVotes4() external givenLockedTokens givenWithTokenAllowanceSomeLocked { - // It Should allow any token holder to lock + function test_WhenCallingVoteMoreLockedBalance3() + external + givenProposalOnLockToVote + givenLockedTokens + givenNoTokenAllowanceSomeLocked + { // It Should approve with the full token balance - // It Should increase the locked amount - // It The allocated token balance should have the full new balance // It Should emit an event // vm.startPrank(alice); - uint256 initialBalance = lockableToken.balanceOf(alice); - uint256 allowance = lockableToken.allowance(alice, address(lockManager)); - vm.expectEmit(); - emit BalanceLocked(alice, allowance); - lockManager.lockAndVote(proposalId); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); + assertEq(ltvPlugin.usedVotingPower(proposalId, alice), 0.1 ether); + + // More + assertEq(lockManager.lockedBalances(alice), 0.1 ether); + + lockableToken.approve(address(lockManager), 0.5 ether); + + vm.expectEmit(); + emit BalanceLocked(alice, 0.5 ether); + lockManager.lock(); + + assertEq(lockManager.lockedBalances(alice), 0.6 ether); + + lockManager.vote(proposalId, IMajorityVoting.VoteOption.No); + assertEq(ltvPlugin.usedVotingPower(proposalId, alice), 0.6 ether); + } + + modifier givenWithTokenAllowanceSomeLocked() { + lockableToken.approve(address(lockManager), 0.5 ether); + + _; + } + + function test_WhenCallingLock4() external givenLockedTokens givenWithTokenAllowanceSomeLocked { + // It Should allow any token holder to lock + // It Should approve with the full token balance + // It Should increase the locked amount + // It Should emit an event + + // vm.startPrank(alice); + uint256 initialBalance = lockableToken.balanceOf(alice); + uint256 allowance = lockableToken.allowance(alice, address(lockManager)); + vm.expectEmit(); + emit BalanceLocked(alice, allowance); + lockManager.lock(); + + assertEq(lockableToken.balanceOf(alice), initialBalance - allowance); + assertEq(lockManager.lockedBalances(alice), 0.1 ether + allowance); + assertEq(lockableToken.balanceOf(address(lockManager)), 0.1 ether + allowance); + } + + function test_WhenCallingLockAndApproveNoPriorPower4() + external + givenProposalOnLockToApprove + givenLockedTokens + givenWithTokenAllowanceSomeLocked + { + // It Should allow any token holder to lock + // It Should approve with the full token balance + // It Should increase the locked amount + // It The allocated token balance should have the full new balance + // It Should emit an event + + // vm.startPrank(alice); + uint256 initialBalance = lockableToken.balanceOf(alice); + uint256 allowance = lockableToken.allowance(alice, address(lockManager)); + vm.expectEmit(); + emit BalanceLocked(alice, allowance); + lockManager.lockAndApprove(proposalId); assertEq(lockableToken.balanceOf(alice), initialBalance - allowance); assertEq(lockManager.lockedBalances(alice), 0.1 ether + allowance); assertEq(lockableToken.balanceOf(address(lockManager)), 0.1 ether + allowance); - assertEq(plugin.usedVotingPower(proposalId, alice), 0.1 ether + allowance); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.1 ether + allowance); } - function test_WhenCallingLockAndVoteWithPriorVotes4() + function test_WhenCallingLockAndApproveWithPriorPower4() external + givenProposalOnLockToApprove givenLockedTokens givenWithTokenAllowanceSomeLocked { - lockManager.vote(proposalId); + lockManager.approve(proposalId); // It Should allow any token holder to lock // It Should approve with the full token balance @@ -474,25 +923,35 @@ contract LockManagerTest is AragonTest { uint256 allowance = lockableToken.allowance(alice, address(lockManager)); vm.expectEmit(); emit BalanceLocked(alice, allowance); - lockManager.lockAndVote(proposalId); + lockManager.lockAndApprove(proposalId); assertEq(lockableToken.balanceOf(alice), initialBalance - allowance); assertEq(lockManager.lockedBalances(alice), 0.1 ether + allowance); assertEq(lockableToken.balanceOf(address(lockManager)), 0.1 ether + allowance); - assertEq(plugin.usedVotingPower(proposalId, alice), 0.1 ether + allowance); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.1 ether + allowance); } - function test_RevertWhen_CallingVoteSameBalance4() external givenLockedTokens givenWithTokenAllowanceSomeLocked { + function test_RevertWhen_CallingApproveSameBalance4() + external + givenProposalOnLockToApprove + givenLockedTokens + givenWithTokenAllowanceSomeLocked + { // It Should revert // vm.startPrank(alice); - lockManager.vote(proposalId); + lockManager.approve(proposalId); vm.expectRevert(); - lockManager.vote(proposalId); + lockManager.approve(proposalId); } - function test_WhenCallingVoteMoreLockedBalance4() external givenLockedTokens givenWithTokenAllowanceSomeLocked { - lockManager.vote(proposalId); + function test_WhenCallingApproveMoreLockedBalance4() + external + givenProposalOnLockToApprove + givenLockedTokens + givenWithTokenAllowanceSomeLocked + { + lockManager.approve(proposalId); // It Should approve with the full token balance // It Should emit an event @@ -505,45 +964,170 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(alice), initialBalance - 0.5 ether); assertEq(lockManager.lockedBalances(alice), 0.6 ether); - lockManager.vote(proposalId); - assertEq(plugin.usedVotingPower(proposalId, alice), 0.6 ether); + lockManager.approve(proposalId); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.6 ether); } - modifier givenCallingLockOrLockToVote() { - Action[] memory _actions = new Action[](0); - proposalId = plugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); + function test_WhenCallingLockAndVoteNoPriorPower4() + external + givenProposalOnLockToVote + givenLockedTokens + givenWithTokenAllowanceSomeLocked + { + // It Should allow any token holder to lock + // It Should approve with the full token balance + // It Should increase the locked amount + // It The allocated token balance should have the full new balance + // It Should emit an event + // vm.startPrank(alice); + uint256 initialBalance = lockableToken.balanceOf(alice); + uint256 allowance = lockableToken.allowance(alice, address(lockManager)); + vm.expectEmit(); + emit BalanceLocked(alice, allowance); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); + assertEq(lockableToken.balanceOf(alice), initialBalance - allowance); + assertEq(lockManager.lockedBalances(alice), 0.1 ether + allowance); + assertEq(lockableToken.balanceOf(address(lockManager)), 0.1 ether + allowance); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.1 ether + allowance); + } + + function test_WhenCallingLockAndVoteWithPriorPower4() + external + givenProposalOnLockToVote + givenLockedTokens + givenWithTokenAllowanceSomeLocked + { + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); + + // It Should allow any token holder to lock + // It Should approve with the full token balance + // It Should increase the locked amount + // It The allocated token balance should have the full new balance + // It Should emit an event + + // vm.startPrank(alice); + uint256 initialBalance = lockableToken.balanceOf(alice); + uint256 allowance = lockableToken.allowance(alice, address(lockManager)); + vm.expectEmit(); + emit BalanceLocked(alice, allowance); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); + assertEq(lockableToken.balanceOf(alice), initialBalance - allowance); + assertEq(lockManager.lockedBalances(alice), 0.1 ether + allowance); + assertEq(lockableToken.balanceOf(address(lockManager)), 0.1 ether + allowance); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.1 ether + allowance); + } + + function test_RevertWhen_CallingVoteSameBalance4() + external + givenProposalOnLockToVote + givenLockedTokens + givenWithTokenAllowanceSomeLocked + { + // It Should revert + + // vm.startPrank(alice); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); + + vm.expectRevert(); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); + } + + function test_WhenCallingVoteMoreLockedBalance4() + external + givenProposalOnLockToVote + givenLockedTokens + givenWithTokenAllowanceSomeLocked + { + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); + + // It Should approve with the full token balance + // It Should emit an event + + // vm.startPrank(alice); + uint256 initialBalance = lockableToken.balanceOf(alice); + vm.expectEmit(); + emit BalanceLocked(alice, 0.5 ether); + lockManager.lock(); + + assertEq(lockableToken.balanceOf(alice), initialBalance - 0.5 ether); + assertEq(lockManager.lockedBalances(alice), 0.6 ether); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.6 ether); + } + + modifier givenCallingLockLockAndApproveOrLockAndVote() { _; } - function test_GivenEmptyPlugin() external givenCallingLockOrLockToVote { + function test_GivenEmptyPlugin() external givenCallingLockLockAndApproveOrLockAndVote { // It Locking and voting should revert - lockManager = new LockManager(dao, LockManagerSettings(UnlockMode.Strict), lockableToken, underlyingToken); + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + lockableToken, + underlyingToken + ); vm.expectRevert(); - lockManager.lockAndVote(proposalId); + lockManager.lockAndApprove(proposalId); vm.expectRevert(); - lockManager.vote(proposalId); + lockManager.approve(proposalId); + + // voting + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Voting), + lockableToken, + underlyingToken + ); + + vm.expectRevert(); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); + + vm.expectRevert(); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); } - function test_GivenInvalidToken() external givenCallingLockOrLockToVote { + function test_GivenInvalidToken() external givenCallingLockLockAndApproveOrLockAndVote { // It Locking should revert // It Locking and voting should revert // It Voting should revert - lockManager = - new LockManager(dao, LockManagerSettings(UnlockMode.Strict), IERC20(address(0x1234)), underlyingToken); + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), + IERC20(address(0x1234)), + underlyingToken + ); lockableToken.approve(address(lockManager), 0.1 ether); vm.expectRevert(); lockManager.lock(); vm.expectRevert(); - lockManager.lockAndVote(proposalId); + lockManager.lockAndApprove(proposalId); vm.expectRevert(); - lockManager.vote(proposalId); + lockManager.approve(proposalId); + + // Voting + lockManager = new LockManager( + dao, + LockManagerSettings(UnlockMode.Strict, PluginMode.Voting), + IERC20(address(0x1234)), + underlyingToken + ); + lockableToken.approve(address(lockManager), 0.1 ether); + vm.expectRevert(); + lockManager.lock(); + + vm.expectRevert(); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); + + vm.expectRevert(); + lockManager.vote(proposalId, IMajorityVoting.VoteOption.Yes); } modifier givenProposalCreatedIsCalled() { @@ -564,31 +1148,7 @@ contract LockManagerTest is AragonTest { function test_WhenTheCallerIsThePluginProposalCreated() external givenProposalCreatedIsCalled { // It Adds the proposal ID to the list of known proposals - vm.startPrank(address(plugin)); - lockManager.proposalCreated(1234); - lockManager.proposalCreated(2345); - lockManager.proposalCreated(3456); - - assertEq(lockManager.knownProposalIds(0), 1234); - assertEq(lockManager.knownProposalIds(1), 2345); - assertEq(lockManager.knownProposalIds(2), 3456); - } - - function test_RevertWhen_TheCallerIsNotThePlugin_ProposalCreated() external givenProposalCreatedIsCalled { - // It Should revert - - vm.expectRevert(); - lockManager.proposalCreated(12345); - - vm.startPrank(bob); - vm.expectRevert(); - lockManager.proposalCreated(2345); - } - - function test_WhenTheCallerIsThePlugin_ProposalCreated() external givenProposalCreatedIsCalled { - // It Removes the proposal ID from the list of known proposals - - vm.startPrank(address(plugin)); + vm.startPrank(address(ltaPlugin)); vm.expectRevert(); assertEq(lockManager.knownProposalIds(0), 0); @@ -611,7 +1171,7 @@ contract LockManagerTest is AragonTest { function test_RevertWhen_TheCallerIsNotThePluginProposalEnded() external givenProposalEndedIsCalled { // It Should revert - vm.startPrank(address(plugin)); + vm.startPrank(address(ltaPlugin)); lockManager.proposalCreated(1234); assertEq(lockManager.knownProposalIds(0), 1234); @@ -625,7 +1185,7 @@ contract LockManagerTest is AragonTest { function test_WhenTheCallerIsThePluginProposalEnded() external givenProposalEndedIsCalled { // It Removes the proposal ID from the list of known proposals - vm.startPrank(address(plugin)); + vm.startPrank(address(ltaPlugin)); lockManager.proposalCreated(1234); lockManager.proposalCreated(2345); lockManager.proposalCreated(3456); @@ -646,34 +1206,7 @@ contract LockManagerTest is AragonTest { lockManager.knownProposalIds(0); } - function test_RevertWhen_TheCallerIsNotThePlugin_ProposalEnded() external givenProposalEndedIsCalled { - // It Should revert - - vm.expectRevert(); - lockManager.proposalEnded(proposalId); - - vm.startPrank(bob); - vm.expectRevert(); - lockManager.proposalEnded(proposalId); - } - - function test_WhenTheCallerIsThePlugin_ProposalEnded() external givenProposalEndedIsCalled { - // It Removes the proposal ID from the list of known proposals - - vm.startPrank(address(plugin)); - lockManager.proposalCreated(1234); - assertEq(lockManager.knownProposalIds(0), 1234); - - lockManager.proposalEnded(1234); - - vm.expectRevert(); - lockManager.knownProposalIds(1234); - } - modifier givenStrictModeIsSet() { - Action[] memory _actions = new Action[](0); - proposalId = plugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); - _; } @@ -684,7 +1217,7 @@ contract LockManagerTest is AragonTest { function test_RevertWhen_TryingToUnlock1Strict() external givenStrictModeIsSet givenDidntLockAnythingStrict { // It Should revert - UnlockMode mode = lockManager.settings(); + (UnlockMode mode, ) = lockManager.settings(); assertEq(uint8(mode), uint8(UnlockMode.Strict)); // vm.startPrank(alice); @@ -692,11 +1225,30 @@ contract LockManagerTest is AragonTest { lockManager.unlock(); } + modifier givenLockedButDidntApproveAnywhereStrict() { + _; + } + + function test_WhenTryingToUnlock2ApprovalStrict() external givenProposalOnLockToApprove givenStrictModeIsSet givenLockedButDidntApproveAnywhereStrict { + // It Should unlock and refund the full amount right away + // It Should emit an event + + // vm.startPrank(alice); + lockableToken.approve(address(lockManager), 0.1 ether); + lockManager.lock(); + + uint256 initialBalance = lockableToken.balanceOf(alice); + vm.expectEmit(); + emit BalanceUnlocked(alice, 0.1 ether); + lockManager.unlock(); + assertEq(lockableToken.balanceOf(alice), initialBalance + 0.1 ether); + } + modifier givenLockedButDidntVoteAnywhereStrict() { _; } - function test_WhenTryingToUnlock2Strict() external givenStrictModeIsSet givenLockedButDidntVoteAnywhereStrict { + function test_WhenTryingToUnlock2VotingStrict() external givenProposalOnLockToVote givenStrictModeIsSet givenLockedButDidntVoteAnywhereStrict { // It Should unlock and refund the full amount right away // It Should emit an event @@ -711,12 +1263,43 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(alice), initialBalance + 0.1 ether); } + modifier givenLockedButApprovedEndedOrExecutedProposalsStrict() { + _; + } + + function test_WhenTryingToUnlock3ApprovedStrict() + external + givenProposalOnLockToApprove + givenStrictModeIsSet + givenLockedButApprovedEndedOrExecutedProposalsStrict + { + // It Should unlock and refund the full amount right away + // It Should emit an event + + lockableToken.approve(address(lockManager), 0.1 ether); + lockManager.lockAndApprove(proposalId); + + vm.expectRevert(LockManager.LocksStillActive.selector); + lockManager.unlock(); + + vm.startPrank(address(ltaPlugin)); + lockManager.proposalEnded(proposalId); + + vm.startPrank(alice); + uint256 initialBalance = lockableToken.balanceOf(alice); + vm.expectEmit(); + emit BalanceUnlocked(alice, 0.1 ether); + lockManager.unlock(); + assertEq(lockableToken.balanceOf(alice), initialBalance + 0.1 ether); + } + modifier givenLockedButVotedOnEndedOrExecutedProposalsStrict() { _; } - function test_WhenTryingToUnlock3Strict() + function test_WhenTryingToUnlock3VotedStrict() external + givenProposalOnLockToVote givenStrictModeIsSet givenLockedButVotedOnEndedOrExecutedProposalsStrict { @@ -724,12 +1307,12 @@ contract LockManagerTest is AragonTest { // It Should emit an event lockableToken.approve(address(lockManager), 0.1 ether); - lockManager.lockAndVote(proposalId); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); vm.expectRevert(LockManager.LocksStillActive.selector); lockManager.unlock(); - vm.startPrank(address(plugin)); + vm.startPrank(address(ltaPlugin)); lockManager.proposalEnded(proposalId); vm.startPrank(alice); @@ -740,12 +1323,33 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(alice), initialBalance + 0.1 ether); } + modifier givenLockedAndApprovedCurrentlyActiveProposalsStrict() { + _; + } + + function test_RevertWhen_TryingToUnlock4ApprovedStrict() + external + givenProposalOnLockToApprove + givenStrictModeIsSet + givenLockedAndApprovedCurrentlyActiveProposalsStrict + { + // It Should revert + + // vm.startPrank(alice); + lockableToken.approve(address(lockManager), 0.1 ether); + lockManager.lockAndApprove(proposalId); + + vm.expectRevert(LockManager.LocksStillActive.selector); + lockManager.unlock(); + } + modifier givenLockedAndVotedOnCurrentlyActiveProposalsStrict() { _; } - function test_RevertWhen_TryingToUnlock4Strict() + function test_RevertWhen_TryingToUnlock4VotedStrict() external + givenProposalOnLockToVote givenStrictModeIsSet givenLockedAndVotedOnCurrentlyActiveProposalsStrict { @@ -753,20 +1357,23 @@ contract LockManagerTest is AragonTest { // vm.startPrank(alice); lockableToken.approve(address(lockManager), 0.1 ether); - lockManager.lockAndVote(proposalId); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); vm.expectRevert(LockManager.LocksStillActive.selector); lockManager.unlock(); } modifier givenFlexibleModeIsSet() { - (dao, plugin, lockManager, lockableToken, underlyingToken) = builder.withTokenHolder(alice, 1 ether) - .withTokenHolder(bob, 10 ether).withTokenHolder(carol, 10 ether).withTokenHolder(david, 15 ether).withUnlockMode( - UnlockMode.Early - ).build(); + (dao, ltaPlugin, ltvPlugin, lockManager, lockableToken, underlyingToken) = builder + .withTokenHolder(alice, 1 ether) + .withTokenHolder(bob, 10 ether) + .withTokenHolder(carol, 10 ether) + .withTokenHolder(david, 15 ether) + .withEarlyUnlock() + .build(); Action[] memory _actions = new Action[](0); - proposalId = plugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); + proposalId = ltaPlugin.createProposal(bytes(""), _actions, 0, 0, bytes("")); _; } @@ -778,7 +1385,7 @@ contract LockManagerTest is AragonTest { function test_RevertWhen_TryingToUnlock1Flexible() external givenFlexibleModeIsSet givenDidntLockAnythingFlexible { // It Should revert - UnlockMode mode = lockManager.settings(); + (UnlockMode mode,) = lockManager.settings(); assertEq(uint8(mode), uint8(UnlockMode.Early)); // vm.startPrank(alice); @@ -786,12 +1393,37 @@ contract LockManagerTest is AragonTest { lockManager.unlock(); } + modifier givenLockedButDidntApproveAnywhereFlexible() { + _; + } + + function test_WhenTryingToUnlock2ApprovalFlexible() + external + givenProposalOnLockToApprove + givenFlexibleModeIsSet + givenLockedButDidntApproveAnywhereFlexible + { + // It Should unlock and refund the full amount right away + // It Should emit an event + + // vm.startPrank(alice); + lockableToken.approve(address(lockManager), 0.2 ether); + lockManager.lock(); + + uint256 initialBalance = lockableToken.balanceOf(alice); + vm.expectEmit(); + emit BalanceUnlocked(alice, 0.2 ether); + lockManager.unlock(); + assertEq(lockableToken.balanceOf(alice), initialBalance + 0.2 ether); + } + modifier givenLockedButDidntVoteAnywhereFlexible() { _; } - function test_WhenTryingToUnlock2Flexible() + function test_WhenTryingToUnlock2VotingFlexible() external + givenProposalOnLockToVote givenFlexibleModeIsSet givenLockedButDidntVoteAnywhereFlexible { @@ -809,12 +1441,40 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(alice), initialBalance + 0.2 ether); } + modifier givenLockedButApprovedOnEndedOrExecutedProposalsFlexible() { + _; + } + + function test_WhenTryingToUnlock3ApprovedFlexible() + external + givenProposalOnLockToApprove + givenFlexibleModeIsSet + givenLockedButApprovedOnEndedOrExecutedProposalsFlexible + { + // It Should unlock and refund the full amount right away + // It Should emit an event + + lockableToken.approve(address(lockManager), 0.3 ether); + lockManager.lockAndApprove(proposalId); + + vm.startPrank(address(ltaPlugin)); + lockManager.proposalEnded(proposalId); + + vm.startPrank(alice); + uint256 initialBalance = lockableToken.balanceOf(alice); + vm.expectEmit(); + emit BalanceUnlocked(alice, 0.3 ether); + lockManager.unlock(); + assertEq(lockableToken.balanceOf(alice), initialBalance + 0.3 ether); + } + modifier givenLockedButVotedOnEndedOrExecutedProposalsFlexible() { _; } - function test_WhenTryingToUnlock3Flexible() + function test_WhenTryingToUnlock3VotedFlexible() external + givenProposalOnLockToVote givenFlexibleModeIsSet givenLockedButVotedOnEndedOrExecutedProposalsFlexible { @@ -822,9 +1482,9 @@ contract LockManagerTest is AragonTest { // It Should emit an event lockableToken.approve(address(lockManager), 0.3 ether); - lockManager.lockAndVote(proposalId); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); - vm.startPrank(address(plugin)); + vm.startPrank(address(ltaPlugin)); lockManager.proposalEnded(proposalId); vm.startPrank(alice); @@ -835,12 +1495,41 @@ contract LockManagerTest is AragonTest { assertEq(lockableToken.balanceOf(alice), initialBalance + 0.3 ether); } + modifier givenLockedAndApprovedOnCurrentlyActiveProposalsFlexible() { + _; + } + + function test_WhenTryingToUnlock4ApprovalFlexible() + external + givenProposalOnLockToApprove + givenFlexibleModeIsSet + givenLockedAndApprovedOnCurrentlyActiveProposalsFlexible + { + // It Should deallocate the existing voting power from active proposals + // It Should unlock and refund the full amount + // It Should emit an event + + // vm.startPrank(alice); + lockableToken.approve(address(lockManager), 0.1 ether); + lockManager.lockAndApprove(proposalId); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.1 ether); + + uint256 initialBalance = lockableToken.balanceOf(alice); + vm.expectEmit(); + emit BalanceUnlocked(alice, 0.1 ether); + lockManager.unlock(); + + assertEq(lockableToken.balanceOf(alice), initialBalance + 0.1 ether); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0); + } + modifier givenLockedAndVotedOnCurrentlyActiveProposalsFlexible() { _; } - function test_WhenTryingToUnlock4Flexible() + function test_WhenTryingToUnlock4VotingFlexible() external + givenProposalOnLockToVote givenFlexibleModeIsSet givenLockedAndVotedOnCurrentlyActiveProposalsFlexible { @@ -850,8 +1539,8 @@ contract LockManagerTest is AragonTest { // vm.startPrank(alice); lockableToken.approve(address(lockManager), 0.1 ether); - lockManager.lockAndVote(proposalId); - assertEq(plugin.usedVotingPower(proposalId, alice), 0.1 ether); + lockManager.lockAndVote(proposalId, IMajorityVoting.VoteOption.Yes); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0.1 ether); uint256 initialBalance = lockableToken.balanceOf(alice); vm.expectEmit(); @@ -859,13 +1548,13 @@ contract LockManagerTest is AragonTest { lockManager.unlock(); assertEq(lockableToken.balanceOf(alice), initialBalance + 0.1 ether); - assertEq(plugin.usedVotingPower(proposalId, alice), 0); + assertEq(ltaPlugin.usedVotingPower(proposalId, alice), 0); } function test_WhenCallingPlugin() external view { // It Should return the right address - assertEq(address(lockManager.plugin()), address(plugin)); + assertEq(address(lockManager.plugin()), address(ltaPlugin)); } function test_WhenCallingToken() external view { @@ -883,7 +1572,7 @@ contract LockManagerTest is AragonTest { lockManager = new LockManager( dao, - LockManagerSettings(UnlockMode.Strict), + LockManagerSettings(UnlockMode.Strict, PluginMode.Approval), lockableToken, IERC20(address(0)) // underlying ); diff --git a/test/LockManager.t.yaml b/test/LockManager.t.yaml index 5d267a6..f5ee9a0 100644 --- a/test/LockManager.t.yaml +++ b/test/LockManager.t.yaml @@ -4,6 +4,9 @@ LockManagerTest: - when: Constructor has invalid unlock mode then: - it: Should revert + - when: Constructor has invalid plugin mode + then: + - it: Should revert - when: Constructor with valid params then: - it: Registers the DAO address @@ -15,6 +18,9 @@ LockManagerTest: - given: Invalid plugin and: - it: should revert + - given: Invalid plugin interface + and: + - it: should revert - when: setPluginAddress without the permission then: - it: should revert @@ -32,6 +38,12 @@ LockManagerTest: - when: Calling lock() 1 then: - it: Should revert + - when: Calling lockAndApprove() 1 + then: + - it: Should revert + - when: Calling approve() 1 + then: + - it: Should revert - when: Calling lockAndVote() 1 then: - it: Should revert @@ -45,12 +57,21 @@ LockManagerTest: - it: Should allow any token holder to lock - it: Should approve with the full token balance - it: Should emit an event - - when: Calling lockAndVote() 2 + - when: Calling lockAndApprove() 2 then: - it: Should allow any token holder to lock - it: Should approve with the full token balance - it: The allocated token balance should have the full new balance - it: Should emit an event + - when: Calling approve() 2 + then: + - it: Should revert + - when: Calling lockAndVote() 2 + then: + - it: Should allow any token holder to lock + - it: Should vote with the full token balance + - it: The allocated token balance should have the full new balance + - it: Should emit an event - when: Calling vote() 2 then: - it: Should revert @@ -62,6 +83,16 @@ LockManagerTest: - when: Calling lock() 3 then: - it: Should revert + - when: Calling lockAndApprove() 3 + then: + - it: Should revert + - when: Calling approve() [same balance] 3 + then: + - it: Should revert + - when: Calling approve() [more locked balance] 3 + then: + - it: Should approve with the full token balance + - it: Should emit an event - when: Calling lockAndVote() 3 then: - it: Should revert @@ -70,7 +101,7 @@ LockManagerTest: - it: Should revert - when: Calling vote() [more locked balance] 3 then: - - it: Should approve with the full token balance + - it: Should vote with the full token balance - it: Should emit an event - given: With token allowance [some locked] and: @@ -80,31 +111,52 @@ LockManagerTest: - it: Should approve with the full token balance - it: Should increase the locked amount - it: Should emit an event - - when: Calling lockAndVote() [no prior votes] 4 + - when: Calling lockAndApprove() [no prior power] 4 then: - it: Should allow any token holder to lock - it: Should approve with the full token balance - it: Should increase the locked amount - it: The allocated token balance should have the full new balance - it: Should emit an event - - when: Calling lockAndVote() [with prior votes] 4 + - when: Calling lockAndApprove() [with prior power] 4 then: - it: Should allow any token holder to lock - it: Should approve with the full token balance - it: Should increase the locked amount - it: The allocated token balance should have the full new balance - it: Should emit an event + - when: Calling approve() [same balance] 4 + then: + - it: Should revert + - when: Calling approve() [more locked balance] 4 + then: + - it: Should approve with the full token balance + - it: Should emit an event + - when: Calling lockAndVote() [no prior power] 4 + then: + - it: Should allow any token holder to lock + - it: Should vote with the full token balance + - it: Should increase the locked amount + - it: The allocated token balance should have the full new balance + - it: Should emit an event + - when: Calling lockAndVote() [with prior power] 4 + then: + - it: Should allow any token holder to lock + - it: Should vote with the full token balance + - it: Should increase the locked amount + - it: The allocated token balance should have the full new balance + - it: Should emit an event - when: Calling vote() [same balance] 4 then: - it: Should revert - when: Calling vote() [more locked balance] 4 then: - - it: Should approve with the full token balance + - it: Should vote with the full token balance - it: Should emit an event # Invalid config's - - given: Calling lock() or lockToVote() + - given: Calling lock(), lockAndApprove() or lockAndVote() then: - given: Empty plugin and: @@ -141,21 +193,38 @@ LockManagerTest: - when: Trying to unlock [1 strict] then: - it: Should revert + - given: Locked but didn't approve anywhere [strict] + then: + - when: Trying to unlock [2 approval strict] + then: + - it: Should unlock and refund the full amount right away + - it: Should emit an event - given: Locked but didn't vote anywhere [strict] then: - - when: Trying to unlock [2 strict] + - when: Trying to unlock [2 voting strict] + then: + - it: Should unlock and refund the full amount right away + - it: Should emit an event + - given: Locked but approved ended or executed proposals [strict] + then: + - when: Trying to unlock [3 approved strict] then: - it: Should unlock and refund the full amount right away - it: Should emit an event - given: Locked but voted on ended or executed proposals [strict] then: - - when: Trying to unlock [3 strict] + - when: Trying to unlock [3 voted strict] then: - it: Should unlock and refund the full amount right away - it: Should emit an event + - given: Locked and approved currently active proposals [strict] + then: + - when: Trying to unlock [4 voted strict] + then: + - it: Should revert - given: Locked and voted on currently active proposals [strict] then: - - when: Trying to unlock [4 strict] + - when: Trying to unlock [4 voted strict] then: - it: Should revert @@ -166,9 +235,21 @@ LockManagerTest: - when: Trying to unlock [1 flexible] then: - it: Should revert + - given: Locked but didn't approve anywhere [flexible] + then: + - when: Trying to unlock [2 approval flexible] + then: + - it: Should unlock and refund the full amount right away + - it: Should emit an event - given: Locked but didn't vote anywhere [flexible] then: - - when: Trying to unlock [2 flexible] + - when: Trying to unlock [2 voting flexible] + then: + - it: Should unlock and refund the full amount right away + - it: Should emit an event + - given: Locked but approved on ended or executed proposals [flexible] + then: + - when: Trying to unlock [3 approved flexible] then: - it: Should unlock and refund the full amount right away - it: Should emit an event @@ -178,9 +259,16 @@ LockManagerTest: then: - it: Should unlock and refund the full amount right away - it: Should emit an event + - given: Locked and approved currently active proposals [flexible] + then: + - when: Trying to unlock [4 approved flexible] + then: + - it: Should deallocate the existing voting power from active proposals + - it: Should unlock and refund the full amount + - it: Should emit an event - given: Locked and voted on currently active proposals [flexible] then: - - when: Trying to unlock [4 flexible] + - when: Trying to unlock [4 voted flexible] then: - it: Should deallocate the existing voting power from active proposals - it: Should unlock and refund the full amount diff --git a/test/util/DaoBuilder.sol b/test/util/DaoBuilder.sol index b0bf165..a76c4a2 100644 --- a/test/util/DaoBuilder.sol +++ b/test/util/DaoBuilder.sol @@ -6,8 +6,9 @@ import {DAO} from "@aragon/osx/src/core/dao/DAO.sol"; import {createProxyAndCall, createSaltedProxyAndCall, predictProxyAddress} from "../../src/util/proxy.sol"; import {ALICE_ADDRESS} from "../constants.sol"; import {LockToApprovePlugin} from "../../src/LockToApprovePlugin.sol"; +import {LockToVotePlugin, MajorityVotingBase} from "../../src/LockToVotePlugin.sol"; import {LockManager} from "../../src/LockManager.sol"; -import {LockManagerSettings, UnlockMode} from "../../src/interfaces/ILockManager.sol"; +import {LockManagerSettings, UnlockMode, PluginMode} from "../../src/interfaces/ILockManager.sol"; import {RATIO_BASE} from "@aragon/osx-commons-contracts/src/utils/math/Ratio.sol"; import {IPlugin} from "@aragon/osx-commons-contracts/src/plugin/IPlugin.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -15,7 +16,8 @@ import {TestToken} from "../mocks/TestToken.sol"; contract DaoBuilder is Test { address immutable DAO_BASE = address(new DAO()); - address immutable LOCK_TO_VOTE_BASE = address(new LockToApprovePlugin()); + address immutable LOCK_TO_APPROVE_BASE = address(new LockToApprovePlugin()); + address immutable LOCK_TO_VOTE_BASE = address(new LockToVotePlugin()); struct MintEntry { address tokenHolder; @@ -27,10 +29,17 @@ contract DaoBuilder is Test { address[] proposers; MintEntry[] tokenHolders; - bool onlyListed = true; + // Lock Manager + UnlockMode unlockMode = UnlockMode.Strict; + PluginMode pluginMode = PluginMode.Approval; + + // Voting + MajorityVotingBase.VotingMode votingMode = MajorityVotingBase.VotingMode.EarlyExecution; + uint32 supportThresholdRatio = 100_000; // 10% + uint32 minParticipationRatio = 100_000; // 10% + // Approval + voting uint32 minApprovalRatio = 100_000; // 10% uint32 proposalDuration = 10 days; - UnlockMode unlockMode = UnlockMode.Strict; function withDaoOwner(address newOwner) public returns (DaoBuilder) { owner = newOwner; @@ -42,6 +51,41 @@ contract DaoBuilder is Test { return this; } + function withStrictUnlock() public returns (DaoBuilder) { + unlockMode = UnlockMode.Strict; + return this; + } + + function withEarlyUnlock() public returns (DaoBuilder) { + unlockMode = UnlockMode.Early; + return this; + } + + function withApprovalPlugin() public returns (DaoBuilder) { + pluginMode = PluginMode.Approval; + return this; + } + + function withVotingPlugin() public returns (DaoBuilder) { + pluginMode = PluginMode.Voting; + return this; + } + + function withVotingMode(MajorityVotingBase.VotingMode newVotingMode) public returns (DaoBuilder) { + votingMode = newVotingMode; + return this; + } + + function withSupportThresholdRatio(uint32 newSupportThresholdRatio) public returns (DaoBuilder) { + supportThresholdRatio = newSupportThresholdRatio; + return this; + } + + function withMinParticipationRatio(uint32 newMinParticipationRatio) public returns (DaoBuilder) { + minParticipationRatio = newMinParticipationRatio; + return this; + } + function withMinApprovalRatio(uint32 newApprovalRatio) public returns (DaoBuilder) { if (newApprovalRatio > RATIO_BASE) revert("Approval ratio above 100%"); minApprovalRatio = newApprovalRatio; @@ -58,16 +102,18 @@ contract DaoBuilder is Test { return this; } - function withUnlockMode(UnlockMode newUnlockMode) public returns (DaoBuilder) { - unlockMode = newUnlockMode; - return this; - } - /// @dev Creates a DAO with the given orchestration settings. /// @dev The setup is done on block/timestamp 0 and tests should be made on block/timestamp 1 or later. function build() public - returns (DAO dao, LockToApprovePlugin plugin, LockManager helper, IERC20 lockableToken, IERC20 underlyingToken) + returns ( + DAO dao, + LockToApprovePlugin ltaPlugin, + LockToVotePlugin ltvPlugin, + LockManager lockManager, + IERC20 lockableToken, + IERC20 underlyingToken + ) { // Deploy the DAO with `this` as root dao = DAO( @@ -94,47 +140,74 @@ contract DaoBuilder is Test { { // Plugin and helper - helper = new LockManager(dao, LockManagerSettings(unlockMode), lockableToken, underlyingToken); - - LockToApprovePlugin.ApprovalSettings memory targetContractSettings = LockToApprovePlugin.ApprovalSettings({ - minApprovalRatio: minApprovalRatio, - proposalDuration: proposalDuration - }); + lockManager = new LockManager( + dao, + LockManagerSettings(unlockMode, pluginMode), + lockableToken, + underlyingToken + ); + bytes memory pluginMetadata = ""; IPlugin.TargetConfig memory targetConfig = IPlugin.TargetConfig({ target: address(dao), operation: IPlugin.Operation.Call }); - bytes memory pluginMetadata = ""; - plugin = LockToApprovePlugin( - createProxyAndCall( - address(LOCK_TO_VOTE_BASE), - abi.encodeCall( - LockToApprovePlugin.initialize, - (dao, helper, targetContractSettings, targetConfig, pluginMetadata) + if (pluginMode == PluginMode.Approval) { + LockToApprovePlugin.ApprovalSettings memory approvalSettings = LockToApprovePlugin.ApprovalSettings({ + minApprovalRatio: minApprovalRatio, + proposalDuration: proposalDuration, + minProposerVotingPower: 0 + }); + + ltaPlugin = LockToApprovePlugin( + createProxyAndCall( + address(LOCK_TO_APPROVE_BASE), + abi.encodeCall( + LockToApprovePlugin.initialize, + (dao, lockManager, approvalSettings, targetConfig, pluginMetadata) + ) ) - ) - ); + ); + } else { + MajorityVotingBase.VotingSettings memory votingSettings = MajorityVotingBase.VotingSettings({ + votingMode: votingMode, + supportThresholdRatio: supportThresholdRatio, + minParticipationRatio: minParticipationRatio, + minApprovalRatio: minApprovalRatio, + proposalDuration: proposalDuration, + minProposerVotingPower: 0 + }); + + ltvPlugin = LockToVotePlugin( + createProxyAndCall( + address(LOCK_TO_VOTE_BASE), + abi.encodeCall( + LockToVotePlugin.initialize, + (dao, lockManager, votingSettings, targetConfig, pluginMetadata) + ) + ) + ); + } - dao.grant(address(helper), address(this), helper.UPDATE_SETTINGS_PERMISSION_ID()); - helper.setPluginAddress(plugin); - dao.revoke(address(helper), address(this), helper.UPDATE_SETTINGS_PERMISSION_ID()); + dao.grant(address(lockManager), address(this), lockManager.UPDATE_SETTINGS_PERMISSION_ID()); + lockManager.setPluginAddress(ltaPlugin); + dao.revoke(address(lockManager), address(this), lockManager.UPDATE_SETTINGS_PERMISSION_ID()); } // The plugin can execute on the DAO - dao.grant(address(dao), address(plugin), dao.EXECUTE_PERMISSION_ID()); + dao.grant(address(dao), address(ltaPlugin), dao.EXECUTE_PERMISSION_ID()); // The LockManager can manage the plugin - dao.grant(address(plugin), address(helper), plugin.LOCK_MANAGER_PERMISSION_ID()); + dao.grant(address(ltaPlugin), address(lockManager), ltaPlugin.LOCK_MANAGER_PERMISSION_ID()); if (proposers.length > 0) { for (uint256 i = 0; i < proposers.length; i++) { - dao.grant(address(plugin), proposers[i], plugin.CREATE_PROPOSAL_PERMISSION_ID()); + dao.grant(address(ltaPlugin), proposers[i], ltaPlugin.CREATE_PROPOSAL_PERMISSION_ID()); } } else { // Ensure that at least the owner can propose - dao.grant(address(plugin), owner, plugin.CREATE_PROPOSAL_PERMISSION_ID()); + dao.grant(address(ltaPlugin), owner, ltaPlugin.CREATE_PROPOSAL_PERMISSION_ID()); } // Transfer ownership to the owner @@ -143,10 +216,11 @@ contract DaoBuilder is Test { // Labels vm.label(address(dao), "DAO"); - vm.label(address(plugin), "LockToVote plugin"); - vm.label(address(helper), "Lock Manager"); + vm.label(address(ltaPlugin), "LockToApprove"); + vm.label(address(ltaPlugin), "LockToVote"); + vm.label(address(lockManager), "LockManager"); vm.label(address(lockableToken), "VotingToken"); - vm.label(address(underlyingToken), "Underlying token"); + vm.label(address(underlyingToken), "UnderlyingToken"); // Moving forward to avoid proposal creations failing or getVotes() giving inconsistent values vm.roll(block.number + 1);