diff --git a/test/LlamaPeripheryTestSetup.sol b/test/LlamaPeripheryTestSetup.sol index 83d086b..f034e5f 100644 --- a/test/LlamaPeripheryTestSetup.sol +++ b/test/LlamaPeripheryTestSetup.sol @@ -51,4 +51,9 @@ contract LlamaPeripheryTestSetup is Test { function setUp() public virtual { vm.createSelectFork(MAINNET_RPC_URL, 18_707_845); } + + function mineBlock() internal { + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + } } diff --git a/test/token-voting/LlamaTokenVotingFactory.t.sol b/test/token-voting/LlamaTokenVotingFactory.t.sol index 8424bf8..5883cff 100644 --- a/test/token-voting/LlamaTokenVotingFactory.t.sol +++ b/test/token-voting/LlamaTokenVotingFactory.t.sol @@ -3,14 +3,11 @@ pragma solidity ^0.8.23; import {Test, console2} from "forge-std/Test.sol"; -import {MockERC20Votes} from "test/mock/MockERC20Votes.sol"; -import {MockERC721Votes} from "test/mock/MockERC721Votes.sol"; +import {Clones} from "@openzeppelin/proxy/Clones.sol"; + import {LlamaTokenVotingTestSetup} from "test/token-voting/LlamaTokenVotingTestSetup.sol"; -import {Action, ActionInfo} from "src/lib/Structs.sol"; -import {RoleDescription} from "src/lib/UDVTs.sol"; -import {ERC20Votes} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol"; -import {ERC721Votes} from "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol"; +import {ActionInfo} from "src/lib/Structs.sol"; import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol"; import {LlamaTokenVotingFactory} from "src/token-voting/LlamaTokenVotingFactory.sol"; @@ -23,51 +20,24 @@ contract LlamaTokenVotingFactoryTest is LlamaTokenVotingTestSetup { event ERC721TokenholderCasterCreated( address caster, address indexed token, uint256 minApprovalPct, uint256 minDisapprovalPct ); - event RoleAssigned(address indexed policyholder, uint8 indexed role, uint64 expiration, uint96 quantity); - event RoleInitialized(uint8 indexed role, RoleDescription description); - - uint256 public constant CREATION_THRESHOLD = 100; - uint256 public constant MIN_APPROVAL_PCT = 1000; - uint256 public constant MIN_DISAPPROVAL_PCT = 1000; - - LlamaTokenVotingFactory public FACTORY; - ERC20Votes public ERC20TOKEN; - ERC721Votes public ERC721TOKEN; function setUp() public override { LlamaTokenVotingTestSetup.setUp(); + } - FACTORY = new LlamaTokenVotingFactory(); - MockERC20Votes mockERC20Votes = new MockERC20Votes(); - ERC20TOKEN = ERC20Votes(address(mockERC20Votes)); - MockERC721Votes mockERC721Votes = new MockERC721Votes(); - ERC721TOKEN = ERC721Votes(address(mockERC721Votes)); - - mockERC20Votes.mint(coreTeam1, 100); - mockERC20Votes.mint(coreTeam2, 100); - mockERC20Votes.mint(coreTeam3, 100); - mockERC20Votes.mint(coreTeam4, 100); - - mockERC721Votes.mint(coreTeam1, 0); - mockERC721Votes.mint(coreTeam2, 1); - mockERC721Votes.mint(coreTeam3, 2); - mockERC721Votes.mint(coreTeam4, 3); - - ILlamaPolicy.PermissionData memory newPermission1 = ILlamaPolicy.PermissionData( - address(FACTORY), LlamaTokenVotingFactory.deployTokenVotingModule.selector, address(STRATEGY) + function _setPermissionCreateApproveAndQueueAction(bytes memory data) internal returns (ActionInfo memory actionInfo) { + // Assign `deployTokenVotingModule` permission to the `CORE_TEAM_ROLE` role. + ILlamaPolicy.PermissionData memory deployTokenVotingPermission = ILlamaPolicy.PermissionData( + address(tokenVotingFactory), LlamaTokenVotingFactory.deployTokenVotingModule.selector, address(STRATEGY) ); - vm.startPrank(address(EXECUTOR)); - POLICY.setRolePermission(CORE_TEAM_ROLE, newPermission1, true); - vm.stopPrank(); - - vm.warp(block.timestamp + 1); - } + vm.prank(address(EXECUTOR)); + POLICY.setRolePermission(CORE_TEAM_ROLE, deployTokenVotingPermission, true); - function _createApproveAndQueueAction(bytes memory data) internal returns (ActionInfo memory actionInfo) { + // Create an action and queue it to deploy the token voting module. vm.prank(coreTeam4); - uint256 actionId = CORE.createAction(CORE_TEAM_ROLE, STRATEGY, address(FACTORY), 0, data, ""); - actionInfo = ActionInfo(actionId, coreTeam4, CORE_TEAM_ROLE, STRATEGY, address(FACTORY), 0, data); + uint256 actionId = CORE.createAction(CORE_TEAM_ROLE, STRATEGY, address(tokenVotingFactory), 0, data, ""); + actionInfo = ActionInfo(actionId, coreTeam4, CORE_TEAM_ROLE, STRATEGY, address(tokenVotingFactory), 0, data); vm.prank(coreTeam1); CORE.castApproval(CORE_TEAM_ROLE, actionInfo, ""); @@ -75,46 +45,75 @@ contract LlamaTokenVotingFactoryTest is LlamaTokenVotingTestSetup { CORE.castApproval(CORE_TEAM_ROLE, actionInfo, ""); vm.prank(coreTeam3); CORE.castApproval(CORE_TEAM_ROLE, actionInfo, ""); - - // vm.warp(block.timestamp + 1 days); - // CORE.queueAction(actionInfo); - // vm.warp(block.timestamp + 5 days); } } contract DeployTokenVotingModule is LlamaTokenVotingFactoryTest { function test_CanDeployERC20TokenVotingModule() public { + // Set up action to call `deployTokenVotingModule` with the ERC20 token. bytes memory data = abi.encodeWithSelector( LlamaTokenVotingFactory.deployTokenVotingModule.selector, - address(ERC20TOKEN), + address(erc20VotesToken), true, - CREATION_THRESHOLD, - MIN_APPROVAL_PCT, - MIN_DISAPPROVAL_PCT + ERC20_CREATION_THRESHOLD, + ERC20_MIN_APPROVAL_PCT, + ERC20_MIN_DISAPPROVAL_PCT ); + ActionInfo memory actionInfo = _setPermissionCreateApproveAndQueueAction(data); - ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + // Compute addresses of ERC20 Token Voting Module + address erc20TokenholderActionCreator = Clones.predictDeterministicAddress( + address(erc20TokenholderActionCreatorLogic), + keccak256(abi.encodePacked(address(erc20VotesToken), address(EXECUTOR))), // salt + address(tokenVotingFactory) // deployer + ); + address erc20TokenholderCaster = Clones.predictDeterministicAddress( + address(erc20TokenholderCasterLogic), + keccak256(abi.encodePacked(address(erc20VotesToken), address(EXECUTOR))), // salt + address(tokenVotingFactory) // deployer + ); + // Expect events to be emitted on call to `deployTokenVotingModule`. vm.expectEmit(); - emit ERC20TokenholderActionCreatorCreated(0x104fBc016F4bb334D775a19E8A6510109AC63E00, address(ERC20TOKEN)); + emit ERC20TokenholderActionCreatorCreated(erc20TokenholderActionCreator, address(erc20VotesToken)); vm.expectEmit(); emit ERC20TokenholderCasterCreated( - 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, address(ERC20TOKEN), MIN_APPROVAL_PCT, MIN_DISAPPROVAL_PCT + erc20TokenholderCaster, address(erc20VotesToken), ERC20_MIN_APPROVAL_PCT, ERC20_MIN_DISAPPROVAL_PCT ); CORE.executeAction(actionInfo); } function test_CanDeployERC721TokenVotingModule() public { + // Set up action to call `deployTokenVotingModule` with the ERC721 token. bytes memory data = abi.encodeWithSelector( - LlamaTokenVotingFactory.deployTokenVotingModule.selector, address(ERC721TOKEN), false, 1, 1, 1 + LlamaTokenVotingFactory.deployTokenVotingModule.selector, + address(erc721VotesToken), + false, + ERC721_CREATION_THRESHOLD, + ERC721_MIN_APPROVAL_PCT, + ERC721_MIN_DISAPPROVAL_PCT ); + ActionInfo memory actionInfo = _setPermissionCreateApproveAndQueueAction(data); - ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + // Compute addresses of ERC721 Token Voting Module + address erc721TokenholderActionCreator = Clones.predictDeterministicAddress( + address(erc721TokenholderActionCreatorLogic), + keccak256(abi.encodePacked(address(erc721VotesToken), address(EXECUTOR))), // salt + address(tokenVotingFactory) // deployer + ); + address erc721TokenholderCaster = Clones.predictDeterministicAddress( + address(erc721TokenholderCasterLogic), + keccak256(abi.encodePacked(address(erc721VotesToken), address(EXECUTOR))), // salt + address(tokenVotingFactory) // deployer + ); + // Expect events to be emitted on call to `deployTokenVotingModule`. vm.expectEmit(); - emit ERC721TokenholderActionCreatorCreated(0x104fBc016F4bb334D775a19E8A6510109AC63E00, address(ERC721TOKEN)); + emit ERC721TokenholderActionCreatorCreated(erc721TokenholderActionCreator, address(erc721VotesToken)); vm.expectEmit(); - emit ERC721TokenholderCasterCreated(0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, address(ERC721TOKEN), 1, 1); + emit ERC721TokenholderCasterCreated( + erc721TokenholderCaster, address(erc721VotesToken), ERC721_MIN_APPROVAL_PCT, ERC721_MIN_DISAPPROVAL_PCT + ); CORE.executeAction(actionInfo); } } diff --git a/test/token-voting/LlamaTokenVotingTestSetup.sol b/test/token-voting/LlamaTokenVotingTestSetup.sol index c95bdad..dbb82e7 100644 --- a/test/token-voting/LlamaTokenVotingTestSetup.sol +++ b/test/token-voting/LlamaTokenVotingTestSetup.sol @@ -2,18 +2,57 @@ pragma solidity ^0.8.23; import {Test, console2} from "forge-std/Test.sol"; - import {Vm} from "forge-std/Vm.sol"; +import {ERC20Votes} from "@openzeppelin/token/ERC20/extensions/ERC20Votes.sol"; +import {ERC721Votes} from "@openzeppelin/token/ERC721/extensions/ERC721Votes.sol"; + +import {MockERC20Votes} from "test/mock/MockERC20Votes.sol"; +import {MockERC721Votes} from "test/mock/MockERC721Votes.sol"; import {LlamaPeripheryTestSetup} from "test/LlamaPeripheryTestSetup.sol"; import {DeployLlamaTokenVotingFactory} from "script/DeployLlamaTokenVotingFactory.s.sol"; contract LlamaTokenVotingTestSetup is LlamaPeripheryTestSetup, DeployLlamaTokenVotingFactory { + // ERC20 Token Voting Constants. + uint256 public constant ERC20_CREATION_THRESHOLD = 100; + uint256 public constant ERC20_MIN_APPROVAL_PCT = 1000; + uint256 public constant ERC20_MIN_DISAPPROVAL_PCT = 1000; + + // ERC721 Token Voting Constants. + uint256 public constant ERC721_CREATION_THRESHOLD = 1; + uint256 public constant ERC721_MIN_APPROVAL_PCT = 1; + uint256 public constant ERC721_MIN_DISAPPROVAL_PCT = 1; + + // Tokens + MockERC20Votes public mockERC20Votes; + ERC20Votes public erc20VotesToken; + MockERC721Votes public mockERC721Votes; + ERC721Votes public erc721VotesToken; + function setUp() public virtual override { LlamaPeripheryTestSetup.setUp(); - // Deploy the Llama Token Voting factory and logic contracts + // Deploy the Llama Token Voting factory and logic contracts. DeployLlamaTokenVotingFactory.run(); + + // Deploy the ERC20 and ERC721 tokens. + mockERC20Votes = new MockERC20Votes(); + erc20VotesToken = ERC20Votes(address(mockERC20Votes)); + mockERC721Votes = new MockERC721Votes(); + erc721VotesToken = ERC721Votes(address(mockERC721Votes)); + + // Mint tokens to core team members. + mockERC20Votes.mint(coreTeam1, 100); + mockERC20Votes.mint(coreTeam2, 100); + mockERC20Votes.mint(coreTeam3, 100); + mockERC20Votes.mint(coreTeam4, 100); + mockERC721Votes.mint(coreTeam1, 0); + mockERC721Votes.mint(coreTeam2, 1); + mockERC721Votes.mint(coreTeam3, 2); + mockERC721Votes.mint(coreTeam4, 3); + + // Mine block so that the ERC20 and ERC721 supply will be available when doing a past timestamp check. + mineBlock(); } }