Skip to content
This repository has been archived by the owner on Nov 27, 2024. It is now read-only.

Commit

Permalink
test: Add tests for token voting action creator and caster (#16)
Browse files Browse the repository at this point in the history
**Motivation:**

Needed to migrate the token voting tests to the periphery repo to have
test coverage.

**Modifications:**

- added the test suite, and some interface contracts

**Result:**

- all of the token voting contracts (minus the factory) have coverage in
the periphery repo
  • Loading branch information
dd0sxx authored Dec 5, 2023
1 parent 9d94c69 commit 1de4f17
Show file tree
Hide file tree
Showing 13 changed files with 2,232 additions and 3 deletions.
34 changes: 34 additions & 0 deletions src/interfaces/ILlamaCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,27 @@ import {
/// @author Llama ([email protected])
/// @notice This is the interface for LlamaCore.
interface ILlamaCore {
error PolicyholderDoesNotHavePermission();

function actionGuard(address target, bytes4 selector) external view returns (address guard);

function actionsCount() external view returns (uint256);

function approvals(uint256 actionId, address policyholder) external view returns (bool hasApproved);

function authorizedAccountLogics(address accountLogic) external view returns (bool isAuthorized);

function authorizedScripts(address script) external view returns (bool isAuthorized);

function authorizedStrategyLogics(ILlamaStrategy strategyLogic) external view returns (bool isAuthorized);

function cancelAction(ActionInfo memory actionInfo) external;

function cancelActionBySig(address policyholder, ActionInfo memory actionInfo, uint8 v, bytes32 r, bytes32 s)
external;

function castApproval(uint8 role, ActionInfo memory actionInfo, string memory reason) external returns (uint96);

function castApprovalBySig(
address policyholder,
uint8 role,
Expand All @@ -39,7 +50,9 @@ interface ILlamaCore {
bytes32 r,
bytes32 s
) external returns (uint96);

function castDisapproval(uint8 role, ActionInfo memory actionInfo, string memory reason) external returns (uint96);

function castDisapprovalBySig(
address policyholder,
uint8 role,
Expand All @@ -49,7 +62,9 @@ interface ILlamaCore {
bytes32 r,
bytes32 s
) external returns (uint96);

function createAccounts(address llamaAccountLogic, bytes[] memory accountConfigs) external;

function createAction(
uint8 role,
ILlamaStrategy strategy,
Expand All @@ -58,6 +73,7 @@ interface ILlamaCore {
bytes memory data,
string memory description
) external returns (uint256 actionId);

function createActionBySig(
address policyholder,
uint8 role,
Expand All @@ -70,22 +86,40 @@ interface ILlamaCore {
bytes32 r,
bytes32 s
) external returns (uint256 actionId);

function createStrategies(address llamaStrategyLogic, bytes[] memory strategyConfigs) external;

function disapprovals(uint256 actionId, address policyholder) external view returns (bool hasDisapproved);

function executeAction(ActionInfo memory actionInfo) external payable;

function executor() external view returns (address);

function getAction(uint256 actionId) external view returns (Action memory);

function getActionState(ActionInfo memory actionInfo) external view returns (uint8);

function incrementNonce(bytes4 selector) external;

function initialize(LlamaInstanceConfig memory config, address policyLogic, address policyMetadataLogic) external;

function name() external view returns (string memory);

function nonces(address policyholder, bytes4 selector) external view returns (uint256 currentNonce);

function policy() external view returns (ILlamaPolicy);

function queueAction(ActionInfo memory actionInfo) external;

function setAccountLogicAuthorization(address accountLogic, bool authorized) external;

function setGuard(address target, bytes4 selector, address guard) external;

function setScriptAuthorization(address script, bool authorized) external;

function setStrategyAuthorization(ILlamaStrategy strategy, bool authorized) external;

function setStrategyLogicAuthorization(ILlamaStrategy strategyLogic, bool authorized) external;

function strategies(ILlamaStrategy strategy) external view returns (bool deployed, bool authorized);
}
42 changes: 42 additions & 0 deletions src/interfaces/ILlamaLens.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;

interface ILlamaLens {
struct PermissionData {
address target;
bytes4 selector;
address strategy;
}

function LLAMA_CORE_LOGIC() external view returns (address);

function LLAMA_FACTORY() external view returns (address);

function LLAMA_POLICY_LOGIC() external view returns (address);

function computeLlamaAccountAddress(address llamaAccountLogic, bytes memory accountConfig, address llamaCore)
external
pure
returns (address);

function computeLlamaCoreAddress(string memory name, address deployer) external view returns (address);

function computeLlamaExecutorAddress(address llamaCore) external pure returns (address);

function computeLlamaExecutorAddress(string memory name, address deployer) external view returns (address);

function computeLlamaPolicyAddress(string memory name, address deployer) external view returns (address);

function computeLlamaPolicyMetadataAddress(
address llamaPolicyMetadataLogic,
bytes memory metadataConfig,
address llamaPolicy
) external pure returns (address);

function computeLlamaStrategyAddress(address llamaStrategyLogic, bytes memory strategyConfig, address llamaCore)
external
pure
returns (address);

function computePermissionId(PermissionData memory permission) external pure returns (bytes32);
}
14 changes: 14 additions & 0 deletions src/interfaces/ILlamaRelativeStrategyBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ interface ILlamaRelativeStrategyBase {
bytes data;
}

struct Config {
uint64 approvalPeriod; // The length of time of the approval period.
uint64 queuingPeriod; // The length of time of the queuing period. The disapproval period is the queuing period when
// enabled.
uint64 expirationPeriod; // The length of time an action can be executed before it expires.
uint16 minApprovalPct; // Minimum percentage of total approval quantity / total approval supply.
uint16 minDisapprovalPct; // Minimum percentage of total disapproval quantity / total disapproval supply.
bool isFixedLengthApprovalPeriod; // Determines if an action be queued before approvalEndTime.
uint8 approvalRole; // Anyone with this role can cast approval of an action.
uint8 disapprovalRole; // Anyone with this role can cast disapproval of an action.
uint8[] forceApprovalRoles; // Anyone with this role can single-handedly approve an action.
uint8[] forceDisapprovalRoles; // Anyone with this role can single-handedly disapprove an action.
}

error CannotCancelInState(ActionState currentState);
error DisapprovalDisabled();
error InvalidActionInfo();
Expand Down
1 change: 0 additions & 1 deletion src/token-voting/ERC721TokenholderActionCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ contract ERC721TokenholderActionCreator is TokenholderActionCreator {
{
TOKEN = token;
if (!TOKEN.supportsInterface(type(IERC721).interfaceId)) revert InvalidTokenAddress();

uint256 totalSupply = TOKEN.getPastTotalSupply(block.timestamp - 1);
if (totalSupply == 0) revert InvalidTokenAddress();
if (_creationThreshold > totalSupply) revert InvalidCreationThreshold();
Expand Down
3 changes: 1 addition & 2 deletions src/token-voting/TokenholderActionCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,8 @@ abstract contract TokenholderActionCreator {
uint256 balance = _getPastVotes(tokenHolder, block.timestamp - 1);
if (balance < creationThreshold) revert InsufficientBalance(balance);

actionCreators[actionId] = tokenHolder;

actionId = LLAMA_CORE.createAction(role, strategy, target, value, data, description);
actionCreators[actionId] = tokenHolder;
emit ActionCreated(actionId, tokenHolder, role, strategy, target, value, data, description);
}

Expand Down
55 changes: 55 additions & 0 deletions test/PeripheryTestSetup.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Test, console2} from "forge-std/Test.sol";

import {Vm} from "forge-std/Vm.sol";

import {ILlamaAccount} from "src/interfaces/ILlamaAccount.sol";
import {ILlamaPolicyMetadata} from "src/interfaces/ILlamaPolicyMetadata.sol";
import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol";
import {ILlamaCore} from "src/interfaces/ILlamaCore.sol";
import {ILlamaExecutor} from "src/interfaces/ILlamaExecutor.sol";
import {ILlamaLens} from "src/interfaces/ILlamaLens.sol";
import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol";
import {ActionInfo, PermissionData, RoleHolderData} from "src/lib/Structs.sol";

contract PeripheryTestSetup is Test {
string MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL"); // can't use constant here

// Llama's Llama instance.
ILlamaCore constant CORE = ILlamaCore(0x688576dC6b1AbaEe1d7Ee2B7c41Ee5D81BFD293c);
ILlamaExecutor constant EXECUTOR = ILlamaExecutor(0xdAf00E9786cABB195a8a1Cf102730863aE94Dd75);
ILlamaPolicy constant POLICY = ILlamaPolicy(0x07CCDBF8bC642007D001CB1F412733A529bE3B04);
ILlamaAccount constant ACCOUNT = ILlamaAccount(0xf147B323B00bE213FBB4F5cC148FD611a02D8845);
ILlamaStrategy constant STRATEGY = ILlamaStrategy(0xeE1695FEbA09ADeC2bFb6ADd963A51cF119fF4Fb);
ILlamaLens constant LENS = ILlamaLens(0x1D74803D4939aFa3CC9fF1B8667bE4d119d925cB);
address constant RELATIVE_QUANTITY_QUORUM_LOGIC = 0x81F7D26fD7d814bFcEF78239a32c0BA5282C98Dc;

uint8 public constant CORE_TEAM_ROLE = 1;

// llama core team members.
address coreTeam1 = 0x2beC65F165cB63Ca2aa07CC14fE5915EAF6fc294;
address coreTeam2 = 0x475B3Ca8763e0Fa601dDC47162bD1F87dF465872;
address coreTeam3 = 0xe56f23CbD1B1071B0540E5068d699f3f071b75a4;
address coreTeam4 = 0x6b45E38c87bfCa15ee90AAe2AFe3CFC58cE08F75;
address coreTeam5 = 0xbdfcE43E5D2C7AA8599290d940c9932B8dBC94Ca;

// Function selectors used in tests.
bytes4 public constant SET_ROLE_HOLDER_SELECTOR = 0x2524842c; // pause(bool)

// Permission data for those selectors.
PermissionData setRoleHolderPermission = PermissionData(address(POLICY), SET_ROLE_HOLDER_SELECTOR, STRATEGY);

// Permission IDs for the permission data.
bytes32 setRoleHolderPermissionId = keccak256(abi.encode(setRoleHolderPermission));

// Othes constants.
uint96 DEFAULT_ROLE_QTY = 1;
uint96 EMPTY_ROLE_QTY = 0;
uint64 DEFAULT_ROLE_EXPIRATION = type(uint64).max;

function setUp() public virtual {
vm.createSelectFork(MAINNET_RPC_URL, 18_707_845);
}
}
36 changes: 36 additions & 0 deletions test/mock/MockERC20Votes.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20Votes} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
import {Time} from "lib/openzeppelin-contracts/contracts/utils/types/Time.sol";
import {Nonces} from "lib/openzeppelin-contracts/contracts/utils/Nonces.sol";

contract MockERC20Votes is ERC20, ERC20Permit, ERC20Votes {
constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}

function mint(address to, uint256 amount) public {
_mint(to, amount);
}

function CLOCK_MODE() public view override returns (string memory) {
if (clock() != Time.timestamp()) revert ERC6372InconsistentClock();
return "mode=timestamp";
}

function clock() public view override returns (uint48) {
return Time.timestamp();
}

// The following functions are overrides required by Solidity.

function _update(address from, address to, uint256 value) internal override(ERC20, ERC20Votes) {
super._update(from, to, value);
delegate(to);
}

function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) {
return super.nonces(owner);
}
}
33 changes: 33 additions & 0 deletions test/mock/MockERC721Votes.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {ERC721} from "lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";
import {EIP712} from "lib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol";
import {ERC721Votes} from "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol";

contract MockERC721Votes is ERC721, EIP712, ERC721Votes {
constructor() ERC721("MyToken", "MTK") EIP712("MyToken", "1") {}

function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}

function CLOCK_MODE() public pure override returns (string memory) {
return "mode=timestamp";
}

function clock() public view override returns (uint48) {
return uint48(block.timestamp);
}

// The following functions are overrides required by Solidity.

function _update(address to, uint256 tokenId, address auth) internal override(ERC721, ERC721Votes) returns (address) {
delegate(to);
return super._update(to, tokenId, auth);
}

function _increaseBalance(address account, uint128 amount) internal override(ERC721, ERC721Votes) {
super._increaseBalance(account, amount);
}
}
Loading

0 comments on commit 1de4f17

Please sign in to comment.