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

Commit

Permalink
chore: add script to deploy token voting modules (#74)
Browse files Browse the repository at this point in the history
**Motivation:**

Add support for JSON-based token voting module config deployments. 

**Modifications:**

Added the script, added the commands to the justfile, and added a sample
JSON config.

**Result:**

Developers can deploy token voting modules by creating a JSON config.
  • Loading branch information
AustinGreen authored Dec 18, 2023
1 parent dd2b930 commit 0e843a4
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 31 deletions.
6 changes: 6 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ run-script script_name flags='' sig='' args='':
-vvvv {{flags}}
mv _test test

run-deploy-voting-module-script flags: (run-script 'DeployLlamaTokenVotingModule' flags '--sig "run(address,string)"' '$SCRIPT_DEPLOYER_ADDRESS "tokenVotingModuleConfig.json"')

dry-run-deploy: (run-script 'DeployLlamaTokenVotingFactory')

deploy: (run-script 'DeployLlamaTokenVotingFactory' '--broadcast --verify --build-info --build-info-path build_info')

dry-run-deploy-voting-module: (run-deploy-voting-module-script '')

deploy-voting-module: (run-deploy-voting-module-script '--broadcast --verify')

verify: (run-script 'DeployLlamaTokenVotingFactory' '--verify --resume')
52 changes: 52 additions & 0 deletions script/DeployLlamaTokenVotingModule.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Script, stdJson} from "forge-std/Script.sol";

import {DeployUtils} from "script/DeployUtils.sol";

import {ILlamaCore} from "src/interfaces/ILlamaCore.sol";
import {CasterConfig, LlamaTokenVotingConfig} from "src/lib/Structs.sol";
import {ILlamaTokenAdapter} from "src/token-voting/interfaces/ILlamaTokenAdapter.sol";
import {LlamaTokenActionCreator} from "src/token-voting/LlamaTokenActionCreator.sol";
import {LlamaTokenCaster} from "src/token-voting/LlamaTokenCaster.sol";
import {LlamaTokenVotingFactory} from "src/token-voting/LlamaTokenVotingFactory.sol";
import {DeployUtils} from "script/DeployUtils.sol";

contract DeployLlamaTokenVotingModule is Script {
using stdJson for string;

function run(address deployer, string memory configFile) public {
string memory jsonInput = DeployUtils.readScriptInput(configFile);

LlamaTokenVotingFactory factory = LlamaTokenVotingFactory(jsonInput.readAddress(".factory"));

DeployUtils.print(string.concat("Deploying Llama token voting module to chain:", vm.toString(block.chainid)));

CasterConfig memory casterConfig = CasterConfig(
abi.decode(jsonInput.parseRaw(".casterConfig.voteQuorumPct"), (uint16)),
abi.decode(jsonInput.parseRaw(".casterConfig.vetoQuorumPct"), (uint16)),
abi.decode(jsonInput.parseRaw(".casterConfig.delayPeriodPct"), (uint16)),
abi.decode(jsonInput.parseRaw(".casterConfig.castingPeriodPct"), (uint16)),
abi.decode(jsonInput.parseRaw(".casterConfig.submissionPeriodPct"), (uint16))
);

LlamaTokenVotingConfig memory config = LlamaTokenVotingConfig(
ILlamaCore(jsonInput.readAddress(".llamaCore")),
ILlamaTokenAdapter(jsonInput.readAddress(".tokenAdapterLogic")),
DeployUtils.readTokenAdapter(jsonInput),
abi.decode(jsonInput.parseRaw(".nonce"), (uint256)),
abi.decode(jsonInput.parseRaw(".actionCreatorRole"), (uint8)),
abi.decode(jsonInput.parseRaw(".casterRole"), (uint8)),
abi.decode(jsonInput.parseRaw(".creationThreshold"), (uint256)),
casterConfig
);

vm.broadcast(deployer);
(LlamaTokenActionCreator actionCreator, LlamaTokenCaster caster) = factory.deploy(config);

DeployUtils.print("Successfully deployed a new Llama token voting module");
DeployUtils.print(string.concat(" LlamaTokenActionCreator: ", vm.toString(address(actionCreator))));
DeployUtils.print(string.concat(" LlamaTokenCaster: ", vm.toString(address(caster))));
}
}
21 changes: 20 additions & 1 deletion script/DeployUtils.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {console2} from "forge-std/Script.sol";
import {VmSafe} from "forge-std/Vm.sol";
import {console2, stdJson} from "forge-std/Script.sol";

import {LlamaTokenAdapterVotesTimestamp} from "src/token-voting/token-adapters/LlamaTokenAdapterVotesTimestamp.sol";

library DeployUtils {
using stdJson for string;

address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
VmSafe internal constant VM = VmSafe(VM_ADDRESS);

function readScriptInput(string memory filename) internal view returns (string memory) {
string memory inputDir = string.concat(VM.projectRoot(), "/script/input/");
string memory chainDir = string.concat(VM.toString(block.chainid), "/");
return VM.readFile(string.concat(inputDir, chainDir, filename));
}

function print(string memory message) internal view {
// Avoid getting flooded with logs during tests. Note that fork tests will show logs with this
// approach, because there's currently no way to tell which environment we're in, e.g. script
// or test. This is being tracked in https://github.com/foundry-rs/foundry/issues/2900.
if (block.chainid != 31_337) console2.log(message);
}

function readTokenAdapter(string memory jsonInput) internal pure returns (bytes memory) {
address tokenAddress = jsonInput.readAddress(".tokenAddress");
return abi.encode(LlamaTokenAdapterVotesTimestamp.Config(tokenAddress));
}
}
18 changes: 18 additions & 0 deletions script/input/11155111/tokenVotingModuleConfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"comment": "This is an example token voting module deployment on Sepolia.",
"factory": "0x2997f4D6899DC91dE9Ae0FcD98b49CA88b8Fc85e",
"llamaCore": "0xc68046794327490F953EA15522367FFBA0b64f86",
"tokenAdapterLogic": "0x88D63b8c5F8C3e95743F1d26Df8aDd0669614278",
"tokenAddress": "0xf44d44a54440F22e5DC5adb7efA3233645f04007",
"nonce": 0,
"actionCreatorRole": 0,
"casterRole": 0,
"creationThreshold": 10000e18,
"casterConfig": {
"voteQuorumPct": 2000,
"vetoQuorumPct": 1000,
"delayPeriodPct": 2500,
"castingPeriodPct": 5000,
"submissionPeriodPct": 2500
}
}
14 changes: 14 additions & 0 deletions src/lib/Structs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
pragma solidity ^0.8.23;

import {ILlamaAccount} from "src/interfaces/ILlamaAccount.sol";
import {ILlamaCore} from "src/interfaces/ILlamaCore.sol";
import {ILlamaActionGuard} from "src/interfaces/ILlamaActionGuard.sol";
import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol";
import {ILlamaTokenAdapter} from "src/token-voting/interfaces/ILlamaTokenAdapter.sol";
import {RoleDescription} from "src/lib/UDVTs.sol";

/// @dev Data required to create an action.
Expand Down Expand Up @@ -34,6 +36,18 @@ struct Action {
uint96 totalDisapprovals; // The total quantity of policyholder disapprovals.
}

/// @dev Configuration of a new Llama token voting module.
struct LlamaTokenVotingConfig {
ILlamaCore llamaCore; // The address of the Llama core.
ILlamaTokenAdapter tokenAdapterLogic; // The logic contract of the token adapter.
bytes adapterConfig; // The configuration of the token adapter.
uint256 nonce; // The nonce to be used in the salt of the deterministic deployment.
uint8 actionCreatorRole; // The role required by the `LlamaTokenActionCreator` to create an action.
uint8 casterRole; // The role required by the `LlamaTokenCaster` to cast approvals and disapprovals.
uint256 creationThreshold; // The number of tokens required to create an action.
CasterConfig casterConfig; // The quorum and period data for the `LlamaTokenCaster`.
}

/// @dev Quorum and period data for token voting caster contracts.
struct CasterConfig {
uint16 voteQuorumPct;
Expand Down
2 changes: 1 addition & 1 deletion src/token-voting/LlamaTokenActionCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ contract LlamaTokenActionCreator is Initializable {
_disableInitializers();
}

/// @notice Initializes a new `LlamaERC20TokenActionCreator` clone.
/// @notice Initializes a new `LlamaTokenActionCreator` clone.
/// @dev This function is called by the `deploy` function in the `LlamaTokenVotingFactory` contract.
/// The `initializer` modifier ensures that this function can be invoked at most once.
/// @param _llamaCore The `LlamaCore` contract for this Llama instance.
Expand Down
2 changes: 1 addition & 1 deletion src/token-voting/LlamaTokenCaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ contract LlamaTokenCaster is Initializable {
_disableInitializers();
}

/// @notice Initializes a new `LlamaERC20TokenCaster` clone.
/// @notice Initializes a new `LlamaTokenCaster` clone.
/// @dev This function is called by the `deploy` function in the `LlamaTokenVotingFactory` contract.
/// The `initializer` modifier ensures that this function can be invoked at most once.
/// @param _llamaCore The `LlamaCore` contract for this Llama instance.
Expand Down
18 changes: 1 addition & 17 deletions src/token-voting/LlamaTokenVotingFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.23;
import {Clones} from "@openzeppelin/proxy/Clones.sol";

import {ILlamaCore} from "src/interfaces/ILlamaCore.sol";
import {CasterConfig} from "src/lib/Structs.sol";
import {CasterConfig, LlamaTokenVotingConfig} from "src/lib/Structs.sol";
import {ILlamaTokenAdapter} from "src/token-voting/interfaces/ILlamaTokenAdapter.sol";
import {LlamaTokenActionCreator} from "src/token-voting/LlamaTokenActionCreator.sol";
import {LlamaTokenCaster} from "src/token-voting/LlamaTokenCaster.sol";
Expand All @@ -13,22 +13,6 @@ import {LlamaTokenCaster} from "src/token-voting/LlamaTokenCaster.sol";
/// @author Llama ([email protected])
/// @notice This contract enables Llama instances to deploy a token voting module.
contract LlamaTokenVotingFactory {
// =========================
// ======== Structs ========
// =========================

/// @dev Configuration of a new Llama token voting module.
struct LlamaTokenVotingConfig {
ILlamaCore llamaCore; // The address of the Llama core.
ILlamaTokenAdapter tokenAdapterLogic; // The logic contract of the token adapter.
bytes adapterConfig; // The configuration of the token adapter.
uint256 nonce; // The nonce to be used in the salt of the deterministic deployment.
uint8 actionCreatorRole; // The role required by the `LlamaTokenActionCreator` to create an action.
uint8 casterRole; // The role required by the `LlamaTokenCaster` to cast approvals and disapprovals.
uint256 creationThreshold; // The number of tokens required to create an action.
CasterConfig casterConfig; // The quorum and period data for the `LlamaTokenCaster`.
}

// ========================
// ======== Errors ========
// ========================
Expand Down
3 changes: 2 additions & 1 deletion src/token-voting/interfaces/ILlamaTokenAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ interface ILlamaTokenAdapter {
function clock() external view returns (uint48 timepoint);

/// @notice Reverts if the token's CLOCK_MODE changes from what's in the adapter or if the clock() function doesn't
/// return the correct timepoint based on CLOCK_MODE.
function checkIfInconsistentClock() external view;

/// @notice Converts a timestamp to timepoint units.
Expand All @@ -34,9 +33,11 @@ interface ILlamaTokenAdapter {
/// @notice Get the voting balance of a token holder at a specified past timepoint.
/// @param account The token holder's address.
/// @param timepoint The timepoint at which to get the voting balance.
/// @return The number of votes the account had at timepoint.
function getPastVotes(address account, uint48 timepoint) external view returns (uint256);

/// @notice Get the total supply of a token at a specified past timepoint.
/// @param timepoint The timepoint at which to get the total supply.
/// @return The total supply of the token at timepoint.
function getPastTotalSupply(uint48 timepoint) external view returns (uint256);
}
12 changes: 6 additions & 6 deletions test/token-voting/LlamaTokenVotingFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {Clones} from "@openzeppelin/proxy/Clones.sol";

import {LlamaTokenVotingTestSetup} from "test/token-voting/LlamaTokenVotingTestSetup.sol";

import {ActionInfo, CasterConfig} from "src/lib/Structs.sol";
import {ActionInfo, LlamaTokenVotingConfig} from "src/lib/Structs.sol";
import {ILlamaCore} from "src/interfaces/ILlamaCore.sol";
import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol";
import {ILlamaTokenAdapter} from "src/token-voting/interfaces/ILlamaTokenAdapter.sol";
Expand Down Expand Up @@ -82,7 +82,7 @@ contract DeployTokenVotingModule is LlamaTokenVotingFactoryTest {

function test_CanDeployERC20TokenVotingModule() public {
bytes memory adapterConfig = abi.encode(LlamaTokenAdapterVotesTimestamp.Config(address(erc20VotesToken)));
LlamaTokenVotingFactory.LlamaTokenVotingConfig memory config = LlamaTokenVotingFactory.LlamaTokenVotingConfig(
LlamaTokenVotingConfig memory config = LlamaTokenVotingConfig(
CORE,
llamaTokenAdapterTimestampLogic,
adapterConfig,
Expand Down Expand Up @@ -159,7 +159,7 @@ contract DeployTokenVotingModule is LlamaTokenVotingFactoryTest {

function test_CanDeployERC721TokenVotingModule() public {
bytes memory adapterConfig = abi.encode(LlamaTokenAdapterVotesTimestamp.Config(address(erc721VotesToken)));
LlamaTokenVotingFactory.LlamaTokenVotingConfig memory config = LlamaTokenVotingFactory.LlamaTokenVotingConfig(
LlamaTokenVotingConfig memory config = LlamaTokenVotingConfig(
CORE,
llamaTokenAdapterTimestampLogic,
adapterConfig,
Expand Down Expand Up @@ -285,7 +285,7 @@ contract DeployTokenVotingModule is LlamaTokenVotingFactoryTest {
block.chainid
);

LlamaTokenVotingFactory.LlamaTokenVotingConfig memory config = LlamaTokenVotingFactory.LlamaTokenVotingConfig(
LlamaTokenVotingConfig memory config = LlamaTokenVotingConfig(
CORE,
llamaTokenAdapterTimestampLogic,
adapterConfig,
Expand Down Expand Up @@ -317,7 +317,7 @@ contract DeployTokenVotingModule is LlamaTokenVotingFactoryTest {
/////////////////////

bytes memory adapterConfig = abi.encode(LlamaTokenAdapterVotesTimestamp.Config(address(erc20VotesToken)));
LlamaTokenVotingFactory.LlamaTokenVotingConfig memory config = LlamaTokenVotingFactory.LlamaTokenVotingConfig(
LlamaTokenVotingConfig memory config = LlamaTokenVotingConfig(
CORE,
llamaTokenAdapterTimestampLogic,
adapterConfig,
Expand Down Expand Up @@ -380,7 +380,7 @@ contract DeployTokenVotingModule is LlamaTokenVotingFactoryTest {
//////////////////////

adapterConfig = abi.encode(LlamaTokenAdapterVotesTimestamp.Config(address(erc20VotesToken)));
config = LlamaTokenVotingFactory.LlamaTokenVotingConfig(
config = LlamaTokenVotingConfig(
CORE,
llamaTokenAdapterTimestampLogic,
adapterConfig,
Expand Down
7 changes: 3 additions & 4 deletions test/token-voting/LlamaTokenVotingTestSetup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import {LlamaPeripheryTestSetup} from "test/LlamaPeripheryTestSetup.sol";

import {DeployLlamaTokenVotingFactory} from "script/DeployLlamaTokenVotingFactory.s.sol";

import {Action, ActionInfo, CasterConfig} from "src/lib/Structs.sol";
import {Action, ActionInfo, CasterConfig, LlamaTokenVotingConfig} from "src/lib/Structs.sol";
import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol";
import {ILlamaRelativeStrategyBase} from "src/interfaces/ILlamaRelativeStrategyBase.sol";
import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol";
import {RoleDescription} from "src/lib/UDVTs.sol";
import {LlamaTokenAdapterVotesTimestamp} from "src/token-voting/token-adapters/LlamaTokenAdapterVotesTimestamp.sol";
import {LlamaTokenVotingFactory} from "src/token-voting/LlamaTokenVotingFactory.sol";
import {LlamaTokenActionCreator} from "src/token-voting/LlamaTokenActionCreator.sol";
import {LlamaTokenCaster} from "src/token-voting/LlamaTokenCaster.sol";

Expand Down Expand Up @@ -109,7 +108,7 @@ contract LlamaTokenVotingTestSetup is LlamaPeripheryTestSetup, DeployLlamaTokenV

function _deployERC20TokenVotingModuleAndSetRole() internal returns (LlamaTokenActionCreator, LlamaTokenCaster) {
bytes memory adapterConfig = abi.encode(LlamaTokenAdapterVotesTimestamp.Config(address(erc20VotesToken)));
LlamaTokenVotingFactory.LlamaTokenVotingConfig memory config = LlamaTokenVotingFactory.LlamaTokenVotingConfig(
LlamaTokenVotingConfig memory config = LlamaTokenVotingConfig(
CORE,
llamaTokenAdapterTimestampLogic,
adapterConfig,
Expand Down Expand Up @@ -138,7 +137,7 @@ contract LlamaTokenVotingTestSetup is LlamaPeripheryTestSetup, DeployLlamaTokenV

function _deployERC721TokenVotingModuleAndSetRole() internal returns (LlamaTokenActionCreator, LlamaTokenCaster) {
bytes memory adapterConfig = abi.encode(LlamaTokenAdapterVotesTimestamp.Config(address(erc721VotesToken)));
LlamaTokenVotingFactory.LlamaTokenVotingConfig memory config = LlamaTokenVotingFactory.LlamaTokenVotingConfig(
LlamaTokenVotingConfig memory config = LlamaTokenVotingConfig(
CORE,
llamaTokenAdapterTimestampLogic,
adapterConfig,
Expand Down

0 comments on commit 0e843a4

Please sign in to comment.